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.portlet.context;
018
019import java.io.Serializable;
020import java.util.Collections;
021import java.util.Enumeration;
022import java.util.HashMap;
023import java.util.Map;
024import javax.portlet.PortletConfig;
025import javax.portlet.PortletContext;
026import javax.portlet.PortletRequest;
027import javax.portlet.PortletResponse;
028import javax.portlet.PortletSession;
029import javax.servlet.ServletContext;
030
031import org.springframework.beans.factory.ObjectFactory;
032import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
033import org.springframework.context.ApplicationContext;
034import org.springframework.core.env.MutablePropertySources;
035import org.springframework.util.Assert;
036import org.springframework.web.context.WebApplicationContext;
037import org.springframework.web.context.request.RequestAttributes;
038import org.springframework.web.context.request.RequestContextHolder;
039import org.springframework.web.context.request.RequestScope;
040import org.springframework.web.context.request.SessionScope;
041import org.springframework.web.context.request.WebRequest;
042import org.springframework.web.context.support.WebApplicationContextUtils;
043
044/**
045 * Convenience methods for retrieving the root {@link WebApplicationContext} for
046 * a given {@link PortletContext}. This is useful for programmatically accessing
047 * a Spring application context from within custom Portlet implementations.
048 *
049 * @author Juergen Hoeller
050 * @author John A. Lewis
051 * @since 2.0
052 * @see org.springframework.web.context.ContextLoader
053 * @see org.springframework.web.context.support.WebApplicationContextUtils
054 * @see org.springframework.web.portlet.FrameworkPortlet
055 * @see org.springframework.web.portlet.DispatcherPortlet
056 */
057public abstract class PortletApplicationContextUtils {
058
059        /**
060         * Find the root {@link WebApplicationContext} for this web app, typically
061         * loaded via {@link org.springframework.web.context.ContextLoaderListener}.
062         * <p>Will rethrow an exception that happened on root context startup,
063         * to differentiate between a failed context startup and no context at all.
064         * @param pc PortletContext to find the web application context for
065         * @return the root WebApplicationContext for this web app, or {@code null} if none
066         * (typed to ApplicationContext to avoid a Servlet API dependency; can usually
067         * be casted to WebApplicationContext, but there shouldn't be a need to)
068         * @see org.springframework.web.context.WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
069         */
070        public static ApplicationContext getWebApplicationContext(PortletContext pc) {
071                Assert.notNull(pc, "PortletContext must not be null");
072                Object attr = pc.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
073                if (attr == null) {
074                        return null;
075                }
076                if (attr instanceof RuntimeException) {
077                        throw (RuntimeException) attr;
078                }
079                if (attr instanceof Error) {
080                        throw (Error) attr;
081                }
082                if (!(attr instanceof ApplicationContext)) {
083                        throw new IllegalStateException("Root context attribute is not of type WebApplicationContext: " + attr);
084                }
085                return (ApplicationContext) attr;
086        }
087
088        /**
089         * Find the root {@link WebApplicationContext} for this web app, typically
090         * loaded via {@link org.springframework.web.context.ContextLoaderListener}.
091         * <p>Will rethrow an exception that happened on root context startup,
092         * to differentiate between a failed context startup and no context at all.
093         * @param pc PortletContext to find the web application context for
094         * @return the root WebApplicationContext for this web app
095         * (typed to ApplicationContext to avoid a Servlet API dependency; can usually
096         * be casted to WebApplicationContext, but there shouldn't be a need to)
097         * @throws IllegalStateException if the root WebApplicationContext could not be found
098         * @see org.springframework.web.context.WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
099         */
100        public static ApplicationContext getRequiredWebApplicationContext(PortletContext pc) throws IllegalStateException {
101                ApplicationContext wac = getWebApplicationContext(pc);
102                if (wac == null) {
103                        throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener registered?");
104                }
105                return wac;
106        }
107
108
109        /**
110         * Register web-specific scopes ("request", "session", "globalSession")
111         * with the given BeanFactory, as used by the Portlet ApplicationContext.
112         * @param bf the BeanFactory to configure
113         * @param pc the PortletContext that we're running within
114         */
115        static void registerPortletApplicationScopes(ConfigurableListableBeanFactory bf, PortletContext pc) {
116                bf.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());
117                bf.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope(false));
118                bf.registerScope(WebApplicationContext.SCOPE_GLOBAL_SESSION, new SessionScope(true));
119                if (pc != null) {
120                        PortletContextScope appScope = new PortletContextScope(pc);
121                        bf.registerScope(WebApplicationContext.SCOPE_APPLICATION, appScope);
122                        // Register as PortletContext attribute, for ContextCleanupListener to detect it.
123                        pc.setAttribute(PortletContextScope.class.getName(), appScope);
124                }
125
126                bf.registerResolvableDependency(PortletRequest.class, new RequestObjectFactory());
127                bf.registerResolvableDependency(PortletResponse.class, new ResponseObjectFactory());
128                bf.registerResolvableDependency(PortletSession.class, new SessionObjectFactory());
129                bf.registerResolvableDependency(WebRequest.class, new WebRequestObjectFactory());
130        }
131
132        /**
133         * Register web-specific environment beans ("contextParameters", "contextAttributes")
134         * with the given BeanFactory, as used by the Portlet ApplicationContext.
135         * @param bf the BeanFactory to configure
136         * @param servletContext the ServletContext that we're running within
137         * @param portletContext the PortletContext that we're running within
138         * @param portletConfig the PortletConfig of the containing Portlet
139         */
140        static void registerEnvironmentBeans(ConfigurableListableBeanFactory bf, ServletContext servletContext,
141                        PortletContext portletContext, PortletConfig portletConfig) {
142
143                if (servletContext != null && !bf.containsBean(WebApplicationContext.SERVLET_CONTEXT_BEAN_NAME)) {
144                        bf.registerSingleton(WebApplicationContext.SERVLET_CONTEXT_BEAN_NAME, servletContext);
145                }
146
147                if (portletContext != null && !bf.containsBean(ConfigurablePortletApplicationContext.PORTLET_CONTEXT_BEAN_NAME)) {
148                        bf.registerSingleton(ConfigurablePortletApplicationContext.PORTLET_CONTEXT_BEAN_NAME, portletContext);
149                }
150
151                if (portletConfig != null && !bf.containsBean(ConfigurablePortletApplicationContext.PORTLET_CONFIG_BEAN_NAME)) {
152                        bf.registerSingleton(ConfigurablePortletApplicationContext.PORTLET_CONFIG_BEAN_NAME, portletConfig);
153                }
154
155                if (!bf.containsBean(WebApplicationContext.CONTEXT_PARAMETERS_BEAN_NAME)) {
156                        Map<String, String> parameterMap = new HashMap<String, String>();
157                        if (portletContext != null) {
158                                Enumeration<String> paramNameEnum = portletContext.getInitParameterNames();
159                                while (paramNameEnum.hasMoreElements()) {
160                                        String paramName = paramNameEnum.nextElement();
161                                        parameterMap.put(paramName, portletContext.getInitParameter(paramName));
162                                }
163                        }
164                        if (portletConfig != null) {
165                                Enumeration<String> paramNameEnum = portletConfig.getInitParameterNames();
166                                while (paramNameEnum.hasMoreElements()) {
167                                        String paramName = paramNameEnum.nextElement();
168                                        parameterMap.put(paramName, portletConfig.getInitParameter(paramName));
169                                }
170                        }
171                        bf.registerSingleton(WebApplicationContext.CONTEXT_PARAMETERS_BEAN_NAME,
172                                        Collections.unmodifiableMap(parameterMap));
173                }
174
175                if (!bf.containsBean(WebApplicationContext.CONTEXT_ATTRIBUTES_BEAN_NAME)) {
176                        Map<String, Object> attributeMap = new HashMap<String, Object>();
177                        if (portletContext != null) {
178                                Enumeration<String> attrNameEnum = portletContext.getAttributeNames();
179                                while (attrNameEnum.hasMoreElements()) {
180                                        String attrName = attrNameEnum.nextElement();
181                                        attributeMap.put(attrName, portletContext.getAttribute(attrName));
182                                }
183                        }
184                        bf.registerSingleton(WebApplicationContext.CONTEXT_ATTRIBUTES_BEAN_NAME,
185                                        Collections.unmodifiableMap(attributeMap));
186                }
187        }
188
189        /**
190         * Replace {@code Servlet}- and {@code Portlet}-based {@link
191         * org.springframework.core.env.PropertySource.StubPropertySource stub property
192         * sources} with actual instances populated with the given {@code servletContext},
193         * {@code portletContext} and {@code portletConfig} objects.
194         * <p>This method is idempotent with respect to the fact it may be called any number
195         * of times but will perform replacement of stub property sources with their
196         * corresponding actual property sources once and only once.
197         * @param propertySources the {@link MutablePropertySources} to initialize (must not be {@code null})
198         * @param servletContext the current {@link ServletContext} (ignored if {@code null}
199         * or if the {@link org.springframework.web.context.support.StandardServletEnvironment#SERVLET_CONTEXT_PROPERTY_SOURCE_NAME
200         * servlet context property source} has already been initialized)
201         * @param portletContext the current {@link PortletContext} (ignored if {@code null}
202         * or if the {@link StandardPortletEnvironment#PORTLET_CONTEXT_PROPERTY_SOURCE_NAME
203         * portlet context property source} has already been initialized)
204         * @param portletConfig the current {@link PortletConfig} (ignored if {@code null}
205         * or if the {@link StandardPortletEnvironment#PORTLET_CONFIG_PROPERTY_SOURCE_NAME
206         * portlet config property source} has already been initialized)
207         * @see org.springframework.core.env.PropertySource.StubPropertySource
208         * @see org.springframework.web.context.support.WebApplicationContextUtils#initServletPropertySources(MutablePropertySources, ServletContext)
209         * @see org.springframework.core.env.ConfigurableEnvironment#getPropertySources()
210         */
211        public static void initPortletPropertySources(MutablePropertySources propertySources, ServletContext servletContext,
212                        PortletContext portletContext, PortletConfig portletConfig) {
213
214                Assert.notNull(propertySources, "'propertySources' must not be null");
215                WebApplicationContextUtils.initServletPropertySources(propertySources, servletContext);
216
217                if (portletContext != null && propertySources.contains(StandardPortletEnvironment.PORTLET_CONTEXT_PROPERTY_SOURCE_NAME)) {
218                        propertySources.replace(StandardPortletEnvironment.PORTLET_CONTEXT_PROPERTY_SOURCE_NAME,
219                                        new PortletContextPropertySource(StandardPortletEnvironment.PORTLET_CONTEXT_PROPERTY_SOURCE_NAME, portletContext));
220                }
221                if (portletConfig != null && propertySources.contains(StandardPortletEnvironment.PORTLET_CONFIG_PROPERTY_SOURCE_NAME)) {
222                        propertySources.replace(StandardPortletEnvironment.PORTLET_CONFIG_PROPERTY_SOURCE_NAME,
223                                        new PortletConfigPropertySource(StandardPortletEnvironment.PORTLET_CONFIG_PROPERTY_SOURCE_NAME, portletConfig));
224                }
225        }
226
227        /**
228         * Return the current RequestAttributes instance as PortletRequestAttributes.
229         * @see RequestContextHolder#currentRequestAttributes()
230         */
231        private static PortletRequestAttributes currentRequestAttributes() {
232                RequestAttributes requestAttr = RequestContextHolder.currentRequestAttributes();
233                if (!(requestAttr instanceof PortletRequestAttributes)) {
234                        throw new IllegalStateException("Current request is not a portlet request");
235                }
236                return (PortletRequestAttributes) requestAttr;
237        }
238
239
240        /**
241         * Factory that exposes the current request object on demand.
242         */
243        @SuppressWarnings("serial")
244        private static class RequestObjectFactory implements ObjectFactory<PortletRequest>, Serializable {
245
246                @Override
247                public PortletRequest getObject() {
248                        return currentRequestAttributes().getRequest();
249                }
250
251                @Override
252                public String toString() {
253                        return "Current PortletRequest";
254                }
255        }
256
257
258        /**
259         * Factory that exposes the current response object on demand.
260         */
261        @SuppressWarnings("serial")
262        private static class ResponseObjectFactory implements ObjectFactory<PortletResponse>, Serializable {
263
264                @Override
265                public PortletResponse getObject() {
266                        PortletResponse response = currentRequestAttributes().getResponse();
267                        if (response == null) {
268                                throw new IllegalStateException("Current portlet response not available");
269                        }
270                        return response;
271                }
272
273                @Override
274                public String toString() {
275                        return "Current PortletResponse";
276                }
277        }
278
279
280        /**
281         * Factory that exposes the current session object on demand.
282         */
283        @SuppressWarnings("serial")
284        private static class SessionObjectFactory implements ObjectFactory<PortletSession>, Serializable {
285
286                @Override
287                public PortletSession getObject() {
288                        return currentRequestAttributes().getRequest().getPortletSession();
289                }
290
291                @Override
292                public String toString() {
293                        return "Current PortletSession";
294                }
295        }
296
297
298        /**
299         * Factory that exposes the current WebRequest object on demand.
300         */
301        @SuppressWarnings("serial")
302        private static class WebRequestObjectFactory implements ObjectFactory<WebRequest>, Serializable {
303
304                @Override
305                public WebRequest getObject() {
306                        PortletRequestAttributes requestAttr = currentRequestAttributes();
307                        return new PortletWebRequest(requestAttr.getRequest(), requestAttr.getResponse());
308                }
309
310                @Override
311                public String toString() {
312                        return "Current PortletWebRequest";
313                }
314        }
315
316}