001/* 002 * Copyright 2002-2016 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.context.request; 018 019import java.lang.reflect.Method; 020import java.util.Map; 021import javax.faces.context.ExternalContext; 022import javax.faces.context.FacesContext; 023import javax.portlet.PortletSession; 024 025import org.apache.commons.logging.Log; 026import org.apache.commons.logging.LogFactory; 027 028import org.springframework.util.Assert; 029import org.springframework.util.ClassUtils; 030import org.springframework.util.ReflectionUtils; 031import org.springframework.util.StringUtils; 032import org.springframework.web.util.WebUtils; 033 034/** 035 * {@link RequestAttributes} adapter for a JSF {@link javax.faces.context.FacesContext}. 036 * Used as default in a JSF environment, wrapping the current FacesContext. 037 * 038 * <p><b>NOTE:</b> In contrast to {@link ServletRequestAttributes}, this variant does 039 * <i>not</i> support destruction callbacks for scoped attributes, neither for the 040 * request scope nor for the session scope. If you rely on such implicit destruction 041 * callbacks, consider defining a Spring {@link RequestContextListener} in your 042 * {@code web.xml}. 043 * 044 * <p>Requires JSF 2.0 or higher, as of Spring 4.0. 045 * 046 * @author Juergen Hoeller 047 * @since 2.5.2 048 * @see javax.faces.context.FacesContext#getExternalContext() 049 * @see javax.faces.context.ExternalContext#getRequestMap() 050 * @see javax.faces.context.ExternalContext#getSessionMap() 051 * @see RequestContextHolder#currentRequestAttributes() 052 */ 053public class FacesRequestAttributes implements RequestAttributes { 054 055 private static final boolean portletApiPresent = 056 ClassUtils.isPresent("javax.portlet.PortletSession", FacesRequestAttributes.class.getClassLoader()); 057 058 /** 059 * We'll create a lot of these objects, so we don't want a new logger every time. 060 */ 061 private static final Log logger = LogFactory.getLog(FacesRequestAttributes.class); 062 063 private final FacesContext facesContext; 064 065 066 /** 067 * Create a new FacesRequestAttributes adapter for the given FacesContext. 068 * @param facesContext the current FacesContext 069 * @see javax.faces.context.FacesContext#getCurrentInstance() 070 */ 071 public FacesRequestAttributes(FacesContext facesContext) { 072 Assert.notNull(facesContext, "FacesContext must not be null"); 073 this.facesContext = facesContext; 074 } 075 076 077 /** 078 * Return the JSF FacesContext that this adapter operates on. 079 */ 080 protected final FacesContext getFacesContext() { 081 return this.facesContext; 082 } 083 084 /** 085 * Return the JSF ExternalContext that this adapter operates on. 086 * @see javax.faces.context.FacesContext#getExternalContext() 087 */ 088 protected final ExternalContext getExternalContext() { 089 return getFacesContext().getExternalContext(); 090 } 091 092 /** 093 * Return the JSF attribute Map for the specified scope 094 * @param scope constant indicating request or session scope 095 * @return the Map representation of the attributes in the specified scope 096 * @see #SCOPE_REQUEST 097 * @see #SCOPE_SESSION 098 */ 099 protected Map<String, Object> getAttributeMap(int scope) { 100 if (scope == SCOPE_REQUEST) { 101 return getExternalContext().getRequestMap(); 102 } 103 else { 104 return getExternalContext().getSessionMap(); 105 } 106 } 107 108 109 @Override 110 public Object getAttribute(String name, int scope) { 111 if (scope == SCOPE_GLOBAL_SESSION && portletApiPresent) { 112 return PortletSessionAccessor.getAttribute(name, getExternalContext()); 113 } 114 else { 115 return getAttributeMap(scope).get(name); 116 } 117 } 118 119 @Override 120 public void setAttribute(String name, Object value, int scope) { 121 if (scope == SCOPE_GLOBAL_SESSION && portletApiPresent) { 122 PortletSessionAccessor.setAttribute(name, value, getExternalContext()); 123 } 124 else { 125 getAttributeMap(scope).put(name, value); 126 } 127 } 128 129 @Override 130 public void removeAttribute(String name, int scope) { 131 if (scope == SCOPE_GLOBAL_SESSION && portletApiPresent) { 132 PortletSessionAccessor.removeAttribute(name, getExternalContext()); 133 } 134 else { 135 getAttributeMap(scope).remove(name); 136 } 137 } 138 139 @Override 140 public String[] getAttributeNames(int scope) { 141 if (scope == SCOPE_GLOBAL_SESSION && portletApiPresent) { 142 return PortletSessionAccessor.getAttributeNames(getExternalContext()); 143 } 144 else { 145 return StringUtils.toStringArray(getAttributeMap(scope).keySet()); 146 } 147 } 148 149 @Override 150 public void registerDestructionCallback(String name, Runnable callback, int scope) { 151 if (logger.isWarnEnabled()) { 152 logger.warn("Could not register destruction callback [" + callback + "] for attribute '" + name + 153 "' because FacesRequestAttributes does not support such callbacks"); 154 } 155 } 156 157 @Override 158 public Object resolveReference(String key) { 159 if (REFERENCE_REQUEST.equals(key)) { 160 return getExternalContext().getRequest(); 161 } 162 else if (REFERENCE_SESSION.equals(key)) { 163 return getExternalContext().getSession(true); 164 } 165 else if ("application".equals(key)) { 166 return getExternalContext().getContext(); 167 } 168 else if ("requestScope".equals(key)) { 169 return getExternalContext().getRequestMap(); 170 } 171 else if ("sessionScope".equals(key)) { 172 return getExternalContext().getSessionMap(); 173 } 174 else if ("applicationScope".equals(key)) { 175 return getExternalContext().getApplicationMap(); 176 } 177 else if ("facesContext".equals(key)) { 178 return getFacesContext(); 179 } 180 else if ("cookie".equals(key)) { 181 return getExternalContext().getRequestCookieMap(); 182 } 183 else if ("header".equals(key)) { 184 return getExternalContext().getRequestHeaderMap(); 185 } 186 else if ("headerValues".equals(key)) { 187 return getExternalContext().getRequestHeaderValuesMap(); 188 } 189 else if ("param".equals(key)) { 190 return getExternalContext().getRequestParameterMap(); 191 } 192 else if ("paramValues".equals(key)) { 193 return getExternalContext().getRequestParameterValuesMap(); 194 } 195 else if ("initParam".equals(key)) { 196 return getExternalContext().getInitParameterMap(); 197 } 198 else if ("view".equals(key)) { 199 return getFacesContext().getViewRoot(); 200 } 201 else if ("viewScope".equals(key)) { 202 return getFacesContext().getViewRoot().getViewMap(); 203 } 204 else if ("flash".equals(key)) { 205 return getExternalContext().getFlash(); 206 } 207 else if ("resource".equals(key)) { 208 return getFacesContext().getApplication().getResourceHandler(); 209 } 210 else { 211 return null; 212 } 213 } 214 215 @Override 216 public String getSessionId() { 217 Object session = getExternalContext().getSession(true); 218 try { 219 // Both HttpSession and PortletSession have a getId() method. 220 Method getIdMethod = session.getClass().getMethod("getId"); 221 return ReflectionUtils.invokeMethod(getIdMethod, session).toString(); 222 } 223 catch (NoSuchMethodException ex) { 224 throw new IllegalStateException("Session object [" + session + "] does not have a getId() method"); 225 } 226 } 227 228 @Override 229 public Object getSessionMutex() { 230 // Enforce presence of a session first to allow listeners to create the mutex attribute 231 ExternalContext externalContext = getExternalContext(); 232 Object session = externalContext.getSession(true); 233 Object mutex = externalContext.getSessionMap().get(WebUtils.SESSION_MUTEX_ATTRIBUTE); 234 if (mutex == null) { 235 mutex = (session != null ? session : externalContext); 236 } 237 return mutex; 238 } 239 240 241 /** 242 * Inner class to avoid hard-coded Portlet API dependency. 243 */ 244 private static class PortletSessionAccessor { 245 246 public static Object getAttribute(String name, ExternalContext externalContext) { 247 Object session = externalContext.getSession(false); 248 if (session instanceof PortletSession) { 249 return ((PortletSession) session).getAttribute(name, PortletSession.APPLICATION_SCOPE); 250 } 251 else if (session != null) { 252 return externalContext.getSessionMap().get(name); 253 } 254 else { 255 return null; 256 } 257 } 258 259 public static void setAttribute(String name, Object value, ExternalContext externalContext) { 260 Object session = externalContext.getSession(true); 261 if (session instanceof PortletSession) { 262 ((PortletSession) session).setAttribute(name, value, PortletSession.APPLICATION_SCOPE); 263 } 264 else { 265 externalContext.getSessionMap().put(name, value); 266 } 267 } 268 269 public static void removeAttribute(String name, ExternalContext externalContext) { 270 Object session = externalContext.getSession(false); 271 if (session instanceof PortletSession) { 272 ((PortletSession) session).removeAttribute(name, PortletSession.APPLICATION_SCOPE); 273 } 274 else if (session != null) { 275 externalContext.getSessionMap().remove(name); 276 } 277 } 278 279 public static String[] getAttributeNames(ExternalContext externalContext) { 280 Object session = externalContext.getSession(false); 281 if (session instanceof PortletSession) { 282 return StringUtils.toStringArray( 283 ((PortletSession) session).getAttributeNames(PortletSession.APPLICATION_SCOPE)); 284 } 285 else if (session != null) { 286 return StringUtils.toStringArray(externalContext.getSessionMap().keySet()); 287 } 288 else { 289 return new String[0]; 290 } 291 } 292 } 293 294}