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}