001/*
002 * Copyright 2002-2018 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;
021
022import javax.faces.context.ExternalContext;
023import javax.faces.context.FacesContext;
024
025import org.apache.commons.logging.Log;
026import org.apache.commons.logging.LogFactory;
027
028import org.springframework.util.Assert;
029import org.springframework.util.ReflectionUtils;
030import org.springframework.util.StringUtils;
031import org.springframework.web.util.WebUtils;
032
033/**
034 * {@link RequestAttributes} adapter for a JSF {@link javax.faces.context.FacesContext}.
035 * Used as default in a JSF environment, wrapping the current FacesContext.
036 *
037 * <p><b>NOTE:</b> In contrast to {@link ServletRequestAttributes}, this variant does
038 * <i>not</i> support destruction callbacks for scoped attributes, neither for the
039 * request scope nor for the session scope. If you rely on such implicit destruction
040 * callbacks, consider defining a Spring {@link RequestContextListener} in your
041 * {@code web.xml}.
042 *
043 * <p>Requires JSF 2.0 or higher, as of Spring 4.0.
044 *
045 * @author Juergen Hoeller
046 * @since 2.5.2
047 * @see javax.faces.context.FacesContext#getExternalContext()
048 * @see javax.faces.context.ExternalContext#getRequestMap()
049 * @see javax.faces.context.ExternalContext#getSessionMap()
050 * @see RequestContextHolder#currentRequestAttributes()
051 */
052public class FacesRequestAttributes implements RequestAttributes {
053
054        /**
055         * We'll create a lot of these objects, so we don't want a new logger every time.
056         */
057        private static final Log logger = LogFactory.getLog(FacesRequestAttributes.class);
058
059        private final FacesContext facesContext;
060
061
062        /**
063         * Create a new FacesRequestAttributes adapter for the given FacesContext.
064         * @param facesContext the current FacesContext
065         * @see javax.faces.context.FacesContext#getCurrentInstance()
066         */
067        public FacesRequestAttributes(FacesContext facesContext) {
068                Assert.notNull(facesContext, "FacesContext must not be null");
069                this.facesContext = facesContext;
070        }
071
072
073        /**
074         * Return the JSF FacesContext that this adapter operates on.
075         */
076        protected final FacesContext getFacesContext() {
077                return this.facesContext;
078        }
079
080        /**
081         * Return the JSF ExternalContext that this adapter operates on.
082         * @see javax.faces.context.FacesContext#getExternalContext()
083         */
084        protected final ExternalContext getExternalContext() {
085                return getFacesContext().getExternalContext();
086        }
087
088        /**
089         * Return the JSF attribute Map for the specified scope.
090         * @param scope constant indicating request or session scope
091         * @return the Map representation of the attributes in the specified scope
092         * @see #SCOPE_REQUEST
093         * @see #SCOPE_SESSION
094         */
095        protected Map<String, Object> getAttributeMap(int scope) {
096                if (scope == SCOPE_REQUEST) {
097                        return getExternalContext().getRequestMap();
098                }
099                else {
100                        return getExternalContext().getSessionMap();
101                }
102        }
103
104
105        @Override
106        public Object getAttribute(String name, int scope) {
107                return getAttributeMap(scope).get(name);
108        }
109
110        @Override
111        public void setAttribute(String name, Object value, int scope) {
112                getAttributeMap(scope).put(name, value);
113        }
114
115        @Override
116        public void removeAttribute(String name, int scope) {
117                getAttributeMap(scope).remove(name);
118        }
119
120        @Override
121        public String[] getAttributeNames(int scope) {
122                return StringUtils.toStringArray(getAttributeMap(scope).keySet());
123        }
124
125        @Override
126        public void registerDestructionCallback(String name, Runnable callback, int scope) {
127                if (logger.isWarnEnabled()) {
128                        logger.warn("Could not register destruction callback [" + callback + "] for attribute '" + name +
129                                        "' because FacesRequestAttributes does not support such callbacks");
130                }
131        }
132
133        @Override
134        public Object resolveReference(String key) {
135                if (REFERENCE_REQUEST.equals(key)) {
136                        return getExternalContext().getRequest();
137                }
138                else if (REFERENCE_SESSION.equals(key)) {
139                        return getExternalContext().getSession(true);
140                }
141                else if ("application".equals(key)) {
142                        return getExternalContext().getContext();
143                }
144                else if ("requestScope".equals(key)) {
145                        return getExternalContext().getRequestMap();
146                }
147                else if ("sessionScope".equals(key)) {
148                        return getExternalContext().getSessionMap();
149                }
150                else if ("applicationScope".equals(key)) {
151                        return getExternalContext().getApplicationMap();
152                }
153                else if ("facesContext".equals(key)) {
154                        return getFacesContext();
155                }
156                else if ("cookie".equals(key)) {
157                        return getExternalContext().getRequestCookieMap();
158                }
159                else if ("header".equals(key)) {
160                        return getExternalContext().getRequestHeaderMap();
161                }
162                else if ("headerValues".equals(key)) {
163                        return getExternalContext().getRequestHeaderValuesMap();
164                }
165                else if ("param".equals(key)) {
166                        return getExternalContext().getRequestParameterMap();
167                }
168                else if ("paramValues".equals(key)) {
169                        return getExternalContext().getRequestParameterValuesMap();
170                }
171                else if ("initParam".equals(key)) {
172                        return getExternalContext().getInitParameterMap();
173                }
174                else if ("view".equals(key)) {
175                        return getFacesContext().getViewRoot();
176                }
177                else if ("viewScope".equals(key)) {
178                        return getFacesContext().getViewRoot().getViewMap();
179                }
180                else if ("flash".equals(key)) {
181                        return getExternalContext().getFlash();
182                }
183                else if ("resource".equals(key)) {
184                        return getFacesContext().getApplication().getResourceHandler();
185                }
186                else {
187                        return null;
188                }
189        }
190
191        @Override
192        public String getSessionId() {
193                Object session = getExternalContext().getSession(true);
194                try {
195                        // HttpSession has a getId() method.
196                        Method getIdMethod = session.getClass().getMethod("getId");
197                        return String.valueOf(ReflectionUtils.invokeMethod(getIdMethod, session));
198                }
199                catch (NoSuchMethodException ex) {
200                        throw new IllegalStateException("Session object [" + session + "] does not have a getId() method");
201                }
202        }
203
204        @Override
205        public Object getSessionMutex() {
206                // Enforce presence of a session first to allow listeners to create the mutex attribute
207                ExternalContext externalContext = getExternalContext();
208                Object session = externalContext.getSession(true);
209                Object mutex = externalContext.getSessionMap().get(WebUtils.SESSION_MUTEX_ATTRIBUTE);
210                if (mutex == null) {
211                        mutex = (session != null ? session : externalContext);
212                }
213                return mutex;
214        }
215
216}