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.mvc; 018 019import java.util.Collections; 020import java.util.Enumeration; 021import java.util.LinkedHashMap; 022import java.util.Locale; 023import java.util.Map; 024import java.util.ResourceBundle; 025import javax.portlet.ActionRequest; 026import javax.portlet.ActionResponse; 027import javax.portlet.EventPortlet; 028import javax.portlet.EventRequest; 029import javax.portlet.EventResponse; 030import javax.portlet.Portlet; 031import javax.portlet.PortletConfig; 032import javax.portlet.PortletContext; 033import javax.portlet.PortletSession; 034import javax.portlet.RenderRequest; 035import javax.portlet.RenderResponse; 036import javax.portlet.ResourceRequest; 037import javax.portlet.ResourceResponse; 038import javax.portlet.ResourceServingPortlet; 039import javax.xml.XMLConstants; 040import javax.xml.namespace.QName; 041 042import org.springframework.beans.factory.BeanNameAware; 043import org.springframework.beans.factory.DisposableBean; 044import org.springframework.beans.factory.InitializingBean; 045import org.springframework.web.portlet.ModelAndView; 046import org.springframework.web.portlet.NoHandlerFoundException; 047import org.springframework.web.portlet.context.PortletConfigAware; 048import org.springframework.web.portlet.context.PortletContextAware; 049import org.springframework.web.portlet.util.PortletUtils; 050 051/** 052 * {@link Controller} implementation that wraps a portlet instance which it manages 053 * internally. Such a wrapped portlet is not known outside of this controller; 054 * its entire lifecycle is covered here. 055 * 056 * <p>Useful to invoke an existing portlet via Spring's dispatching infrastructure, 057 * for example to apply Spring 058 * {@link org.springframework.web.portlet.HandlerInterceptor HandlerInterceptors} 059 * to its requests. 060 * 061 * <p><b>Example:</b> 062 * 063 * <pre class="code"><bean id="wrappingController" class="org.springframework.web.portlet.mvc.PortletWrappingController"> 064 * <property name="portletClass"> 065 * <value>org.springframework.web.portlet.sample.HelloWorldPortlet</value> 066 * </property> 067 * <property name="portletName"> 068 * <value>hello-world</value> 069 * </property> 070 * <property name="initParameters"> 071 * <props> 072 * <prop key="config">/WEB-INF/hello-world-portlet-config.xml</prop> 073 * </props> 074 * </property> 075 * </bean></pre> 076 * 077 * @author Juergen Hoeller 078 * @author John A. Lewis 079 * @since 2.0 080 */ 081public class PortletWrappingController extends AbstractController 082 implements ResourceAwareController, EventAwareController, 083 BeanNameAware, InitializingBean, DisposableBean, PortletContextAware, PortletConfigAware { 084 085 private boolean useSharedPortletConfig = true; 086 087 private PortletContext portletContext; 088 089 private PortletConfig portletConfig; 090 091 private Class<?> portletClass; 092 093 private String portletName; 094 095 private Map<String, String> initParameters = new LinkedHashMap<String, String>(); 096 097 private String beanName; 098 099 private Portlet portletInstance; 100 101 102 /** 103 * Set whether to use the shared PortletConfig object passed in 104 * through {@code setPortletConfig}, if available. 105 * <p>Default is "true". Turn this setting to "false" to pass in 106 * a mock PortletConfig object with the bean name as portlet name, 107 * holding the current PortletContext. 108 * @see #setPortletConfig 109 */ 110 public void setUseSharedPortletConfig(boolean useSharedPortletConfig) { 111 this.useSharedPortletConfig = useSharedPortletConfig; 112 } 113 114 @Override 115 public void setPortletContext(PortletContext portletContext) { 116 this.portletContext = portletContext; 117 } 118 119 @Override 120 public void setPortletConfig(PortletConfig portletConfig) { 121 this.portletConfig = portletConfig; 122 } 123 124 /** 125 * Set the class of the Portlet to wrap. 126 * Needs to implement {@code javax.portlet.Portlet}. 127 * @see javax.portlet.Portlet 128 */ 129 public void setPortletClass(Class<?> portletClass) { 130 this.portletClass = portletClass; 131 } 132 133 /** 134 * Set the name of the Portlet to wrap. 135 * Default is the bean name of this controller. 136 */ 137 public void setPortletName(String portletName) { 138 this.portletName = portletName; 139 } 140 141 /** 142 * Specify init parameters for the portlet to wrap, 143 * as name-value pairs. 144 */ 145 public void setInitParameters(Map<String, String> initParameters) { 146 this.initParameters = initParameters; 147 } 148 149 @Override 150 public void setBeanName(String name) { 151 this.beanName = name; 152 } 153 154 155 @Override 156 public void afterPropertiesSet() throws Exception { 157 if (this.portletClass == null) { 158 throw new IllegalArgumentException("portletClass is required"); 159 } 160 if (!Portlet.class.isAssignableFrom(this.portletClass)) { 161 throw new IllegalArgumentException("portletClass [" + this.portletClass.getName() + 162 "] needs to implement interface [javax.portlet.Portlet]"); 163 } 164 if (this.portletName == null) { 165 this.portletName = this.beanName; 166 } 167 PortletConfig config = this.portletConfig; 168 if (config == null || !this.useSharedPortletConfig) { 169 config = new DelegatingPortletConfig(); 170 } 171 this.portletInstance = (Portlet) this.portletClass.newInstance(); 172 this.portletInstance.init(config); 173 } 174 175 176 @Override 177 protected void handleActionRequestInternal( 178 ActionRequest request, ActionResponse response) throws Exception { 179 180 this.portletInstance.processAction(request, response); 181 } 182 183 @Override 184 protected ModelAndView handleRenderRequestInternal( 185 RenderRequest request, RenderResponse response) throws Exception { 186 187 this.portletInstance.render(request, response); 188 return null; 189 } 190 191 @Override 192 public ModelAndView handleResourceRequest( 193 ResourceRequest request, ResourceResponse response) throws Exception { 194 195 if (!(this.portletInstance instanceof ResourceServingPortlet)) { 196 throw new NoHandlerFoundException("Cannot handle resource request - target portlet [" + 197 this.portletInstance.getClass() + " does not implement ResourceServingPortlet"); 198 } 199 ResourceServingPortlet resourcePortlet = (ResourceServingPortlet) this.portletInstance; 200 201 // Delegate to PortletContentGenerator for checking and preparing. 202 checkAndPrepare(request, response); 203 204 // Execute in synchronized block if required. 205 if (isSynchronizeOnSession()) { 206 PortletSession session = request.getPortletSession(false); 207 if (session != null) { 208 Object mutex = PortletUtils.getSessionMutex(session); 209 synchronized (mutex) { 210 resourcePortlet.serveResource(request, response); 211 return null; 212 } 213 } 214 } 215 216 resourcePortlet.serveResource(request, response); 217 return null; 218 } 219 220 @Override 221 public void handleEventRequest( 222 EventRequest request, EventResponse response) throws Exception { 223 224 if (!(this.portletInstance instanceof EventPortlet)) { 225 logger.debug("Ignoring event request for non-event target portlet: " + this.portletInstance.getClass()); 226 return; 227 } 228 EventPortlet eventPortlet = (EventPortlet) this.portletInstance; 229 230 // Delegate to PortletContentGenerator for checking and preparing. 231 check(request, response); 232 233 // Execute in synchronized block if required. 234 if (isSynchronizeOnSession()) { 235 PortletSession session = request.getPortletSession(false); 236 if (session != null) { 237 Object mutex = PortletUtils.getSessionMutex(session); 238 synchronized (mutex) { 239 eventPortlet.processEvent(request, response); 240 return; 241 } 242 } 243 } 244 245 eventPortlet.processEvent(request, response); 246 } 247 248 249 @Override 250 public void destroy() { 251 this.portletInstance.destroy(); 252 } 253 254 255 /** 256 * Internal implementation of the PortletConfig interface, to be passed 257 * to the wrapped portlet. 258 * <p>Delegates to {@link PortletWrappingController} fields 259 * and methods to provide init parameters and other environment info. 260 */ 261 private class DelegatingPortletConfig implements PortletConfig { 262 263 @Override 264 public String getPortletName() { 265 return portletName; 266 } 267 268 @Override 269 public PortletContext getPortletContext() { 270 return portletContext; 271 } 272 273 @Override 274 public String getInitParameter(String paramName) { 275 return initParameters.get(paramName); 276 } 277 278 @Override 279 public Enumeration<String> getInitParameterNames() { 280 return Collections.enumeration(initParameters.keySet()); 281 } 282 283 @Override 284 public ResourceBundle getResourceBundle(Locale locale) { 285 return (portletConfig != null ? portletConfig.getResourceBundle(locale) : null); 286 } 287 288 @Override 289 public Enumeration<String> getPublicRenderParameterNames() { 290 return Collections.enumeration(Collections.<String>emptySet()); 291 } 292 293 @Override 294 public String getDefaultNamespace() { 295 return XMLConstants.NULL_NS_URI; 296 } 297 298 @Override 299 public Enumeration<QName> getPublishingEventQNames() { 300 return Collections.enumeration(Collections.<QName>emptySet()); 301 } 302 303 @Override 304 public Enumeration<QName> getProcessingEventQNames() { 305 return Collections.enumeration(Collections.<QName>emptySet()); 306 } 307 308 @Override 309 public Enumeration<Locale> getSupportedLocales() { 310 return Collections.enumeration(Collections.<Locale>emptySet()); 311 } 312 313 @Override 314 public Map<String, String[]> getContainerRuntimeOptions() { 315 return (portletConfig != null ? portletConfig.getContainerRuntimeOptions() : null); 316 } 317 } 318 319}