View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.jetspeed.container.invoker;
18  
19  import java.io.IOException;
20  
21  import javax.portlet.ActionRequest;
22  import javax.portlet.ActionResponse;
23  import javax.portlet.PortletException;
24  import javax.portlet.PortletRequest;
25  import javax.portlet.PortletResponse;
26  import javax.portlet.RenderRequest;
27  import javax.portlet.RenderResponse;
28  import javax.servlet.RequestDispatcher;
29  import javax.servlet.ServletConfig;
30  import javax.servlet.ServletContext;
31  import javax.servlet.ServletRequest;
32  import javax.servlet.ServletResponse;
33  import javax.servlet.http.HttpServletRequest;
34  
35  import org.apache.commons.logging.Log;
36  import org.apache.commons.logging.LogFactory;
37  import org.apache.jetspeed.PortalReservedParameters;
38  import org.apache.jetspeed.container.ContainerConstants;
39  import org.apache.jetspeed.container.PortletRequestContext;
40  import org.apache.jetspeed.factory.PortletFactory;
41  import org.apache.jetspeed.factory.PortletInstance;
42  import org.apache.jetspeed.om.common.portlet.MutablePortletApplication;
43  import org.apache.jetspeed.request.RequestContext;
44  import org.apache.jetspeed.aggregator.CurrentWorkerContext;
45  import org.apache.pluto.om.portlet.PortletDefinition;
46  import org.apache.pluto.om.servlet.WebApplicationDefinition;
47  
48  /***
49   * ServletPortletInvoker invokes portlets in another web application, calling a 
50   * portlet's render or action method via a cross context request dispatcher.
51   * In order for this class to work, a servlet must be special servlet must be 
52   * infused into the web (portlet) application. This servlet knows how to delegate
53   * to portlets and package their response back into a servlet response.
54   * The context name of the servlet should be configurable. The default context name is "/container"
55   * ServletPortletInvokerFactory is the factory for creating portlet invokers that 
56   * use Jetspeed Container servlet. 
57   * <h3>Sample Factory Configuration</h3>
58   * <pre>
59   * <code>
60   * factory.invoker.servlet = org.apache.jetspeed.container.invoker.ServletPortletInvoker
61   * factory.invoker.servlet.pool.size = 50
62   * factory.invoker.servlet.mapping.name = /container
63   * </code> 
64   * </pre>
65   * @author <a href="mailto:taylor@apache.org">David Sean Taylor</a>
66   * @version $Id: ServletPortletInvoker.java 598155 2007-11-26 07:41:26Z woonsan $
67   */
68  public class ServletPortletInvoker implements JetspeedPortletInvoker
69  {
70      private final static Log log = LogFactory.getLog(ServletPortletInvoker.class);
71  
72      protected PortletFactory portletFactory;
73      protected ServletContext jetspeedContext;
74      protected ServletConfig jetspeedConfig;
75      protected PortletDefinition portletDefinition;
76      protected boolean activated = false;
77      protected String servletMappingName;
78      
79      /***
80       * requestResponseUnwrapper used to unwrap portlet request or portlet response
81       * to find the real servlet request or servlet response.
82       */
83      protected PortletRequestResponseUnwrapper requestResponseUnwrapper;
84  
85      public ServletPortletInvoker()
86      {
87          this(new DefaultPortletRequestResponseUnwrapper());
88      }
89      
90      public ServletPortletInvoker(PortletRequestResponseUnwrapper requestResponseUnwrapper)
91      {
92          this.requestResponseUnwrapper = requestResponseUnwrapper;
93      }
94  
95      /* (non-Javadoc)
96       * @see org.apache.jetspeed.container.invoker.JetspeedPortletInvoker#passivate()
97       */
98      public void passivate()
99      {
100         activated = false;
101     }
102 
103     /* (non-Javadoc)
104      * @see org.apache.jetspeed.container.invoker.JetspeedPortletInvoker#isActivated()
105      */
106     public boolean isActivated()
107     {
108         return activated;
109     }
110 
111     /* (non-Javadoc)
112      * @see org.apache.jetspeed.container.invoker.JetspeedPortletInvoker#activate(PortletFactory,org.apache.pluto.om.portlet.PortletDefinition, javax.servlet.ServletConfig)
113      */
114     public void activate(PortletFactory portletFactory, PortletDefinition portletDefinition, ServletConfig servletConfig)
115     {
116         this.portletFactory = portletFactory;
117         this.jetspeedConfig = servletConfig;
118         jetspeedContext = servletConfig.getServletContext();
119         this.portletDefinition = portletDefinition;
120         activated = true;
121     }
122 
123     /* (non-Javadoc)
124      * @see org.apache.jetspeed.container.invoker.JetspeedPortletInvoker#activate(PortletFactory,org.apache.pluto.om.portlet.PortletDefinition, javax.servlet.ServletConfig, java.lang.String)
125      */
126     public void activate(PortletFactory portletFactory, PortletDefinition portletDefinition, ServletConfig servletConfig, String servletMappingName)
127     {
128         this.servletMappingName = servletMappingName;
129         activate(portletFactory, portletDefinition, servletConfig);
130     }
131 
132     /***
133      *
134      * @param request
135      * @param response
136      * @throws PortletException
137      */
138     public void render(RenderRequest request, RenderResponse response) throws PortletException, IOException
139     {
140         invoke(request, response, ContainerConstants.METHOD_RENDER);
141     }
142 
143     /***
144      *
145      */
146     public void action(ActionRequest request, ActionResponse response) throws PortletException, IOException
147     {
148         invoke(request, response, ContainerConstants.METHOD_ACTION);
149     }
150 
151     /***
152      *
153      */
154     public void load(PortletRequest request, RenderResponse response) throws PortletException
155     {
156         try
157         {
158             invoke(request, response, ContainerConstants.METHOD_NOOP);
159         }
160         catch (IOException e)
161         {
162             log.error("ServletPortletInvokerImpl.load() - Error while dispatching portlet.", e);
163             throw new PortletException(e);
164         }
165     }
166 
167     /***
168      * Creates a servlet request dispatcher to dispatch to another web application to render the portlet.
169      * NOTE: this method requires that your container supports cross-context dispatching.
170      * Cross-context dispatching is known to work on Tomcat, Catalina, Tomcat-5.
171      *
172      * @param portletRequest
173      * @param portletResponse
174      * @param methodID
175      * @throws PortletException
176      * @throws IOException
177      */
178     protected void invoke(PortletRequest portletRequest, PortletResponse portletResponse, Integer methodID)
179         throws PortletException, IOException
180     {
181         // In case of parallel mode, the portletDefinition member is not thread-safe.
182         // So, hide the member variable by the following local variable.
183         PortletDefinition portletDefinition = null;
184 
185         // In case of parallel mode, get portlet definition object from the worker thread context.
186         // Otherwise, refer the member variable.
187         boolean isParallelMode = CurrentWorkerContext.getParallelRenderingMode();
188 
189         if (isParallelMode)
190         {
191             portletDefinition = (PortletDefinition) CurrentWorkerContext.getAttribute(PortalReservedParameters.PORTLET_DEFINITION_ATTRIBUTE);
192         }
193         
194         if (portletDefinition == null)
195         {
196             portletDefinition = this.portletDefinition;
197         }
198         
199         MutablePortletApplication app = (MutablePortletApplication)portletDefinition.getPortletApplicationDefinition();
200 
201         WebApplicationDefinition webApplicationDefinition = app.getWebApplicationDefinition();
202         if(webApplicationDefinition == null)
203         {
204         	throw new IllegalStateException("Portlet application "+app.getName()+ " has no associated web application.");
205         }
206         String portletApplicationName = webApplicationDefinition.getContextRoot();
207 
208         ServletContext appContext = jetspeedContext.getContext(portletApplicationName);
209         if (null == appContext)
210         {
211             String message = "Failed to find Servlet context for Portlet Application: " + portletApplicationName;
212             log.error(message);
213             throw new PortletException(message);
214         }
215         PortletInstance portletInstance = portletFactory.getPortletInstance(appContext, portletDefinition);
216         RequestDispatcher dispatcher = appContext.getRequestDispatcher(servletMappingName);
217         if (null == dispatcher)
218         {
219             String message =
220                 "Failed to get Request Dispatcher for Portlet Application: "
221                     + portletApplicationName
222                     + ", servlet: "
223                     + servletMappingName;
224             log.error(message);
225             throw new PortletException(message);
226         }
227 
228         // gather all required data from request and response
229         ServletRequest servletRequest = this.requestResponseUnwrapper.unwrapPortletRequest(portletRequest);
230         ServletResponse servletResponse = this.requestResponseUnwrapper.unwrapPortletResponse(portletResponse);
231 
232         try
233         {
234             RequestContext requestContext = (RequestContext) servletRequest.getAttribute(PortalReservedParameters.REQUEST_CONTEXT_ATTRIBUTE);
235             
236             if (isParallelMode)
237             {
238                 synchronized (servletRequest)
239                 {
240                     servletRequest.setAttribute(ContainerConstants.PORTLET, portletInstance);
241                     servletRequest.setAttribute(ContainerConstants.PORTLET_CONFIG, portletInstance.getConfig());
242                     servletRequest.setAttribute(ContainerConstants.PORTLET_REQUEST, portletRequest);
243                     servletRequest.setAttribute(ContainerConstants.PORTLET_RESPONSE, portletResponse);
244                     servletRequest.setAttribute(ContainerConstants.METHOD_ID, methodID);
245                     servletRequest.setAttribute(ContainerConstants.PORTLET_NAME, app.getName()+"::"+portletDefinition.getName());
246                     servletRequest.setAttribute(ContainerConstants.PORTAL_CONTEXT, ((HttpServletRequest) servletRequest).getContextPath());
247                 }
248             }
249             else
250             {
251                 servletRequest.setAttribute(ContainerConstants.PORTLET, portletInstance);
252                 servletRequest.setAttribute(ContainerConstants.PORTLET_CONFIG, portletInstance.getConfig());
253                 servletRequest.setAttribute(ContainerConstants.PORTLET_REQUEST, portletRequest);
254                 servletRequest.setAttribute(ContainerConstants.PORTLET_RESPONSE, portletResponse);
255                 servletRequest.setAttribute(ContainerConstants.METHOD_ID, methodID);
256                 servletRequest.setAttribute(ContainerConstants.PORTLET_NAME, app.getName()+"::"+portletDefinition.getName());
257                 servletRequest.setAttribute(ContainerConstants.PORTAL_CONTEXT, requestContext.getRequest().getContextPath());
258             }
259 
260             // Store same request attributes into the worker in parallel mode.
261             if (isParallelMode)
262             {
263                 CurrentWorkerContext.setAttribute(ContainerConstants.PORTLET, portletInstance);
264                 CurrentWorkerContext.setAttribute(ContainerConstants.PORTLET_CONFIG, portletInstance.getConfig());
265                 CurrentWorkerContext.setAttribute(ContainerConstants.PORTLET_REQUEST, portletRequest);
266                 CurrentWorkerContext.setAttribute(ContainerConstants.PORTLET_RESPONSE, portletResponse);
267                 CurrentWorkerContext.setAttribute(ContainerConstants.METHOD_ID, methodID);
268                 CurrentWorkerContext.setAttribute(ContainerConstants.PORTLET_NAME, app.getName()+"::"+portletDefinition.getName());
269                 CurrentWorkerContext.setAttribute(ContainerConstants.PORTAL_CONTEXT, ((HttpServletRequest) servletRequest).getContextPath());                
270             }
271 
272             PortletRequestContext.createContext(portletDefinition, portletInstance, portletRequest, portletResponse);
273             dispatcher.include(servletRequest, servletResponse);
274             
275         }
276         catch (Exception e)
277         {
278             String message =
279                 "Failed to dispatch.include for Portlet Application: " + portletApplicationName + ", servlet: " + servletMappingName;
280             log.error(message, e);
281             throw new PortletException(message, e);
282         }
283         finally
284         {
285             PortletRequestContext.clearContext();
286 
287             // In parallel mode, remove all attributes of worker context.
288             if (isParallelMode)
289             {
290                 CurrentWorkerContext.removeAttribute(ContainerConstants.PORTLET);
291                 CurrentWorkerContext.removeAttribute(ContainerConstants.PORTLET_CONFIG);
292                 CurrentWorkerContext.removeAttribute(ContainerConstants.PORTLET_REQUEST);
293                 CurrentWorkerContext.removeAttribute(ContainerConstants.PORTLET_RESPONSE);
294                 CurrentWorkerContext.removeAttribute(ContainerConstants.METHOD_ID);
295                 CurrentWorkerContext.removeAttribute(ContainerConstants.PORTLET_NAME);
296                 CurrentWorkerContext.removeAttribute(ContainerConstants.PORTAL_CONTEXT);
297             }
298 
299             servletRequest.removeAttribute(ContainerConstants.PORTLET);
300             servletRequest.removeAttribute(ContainerConstants.PORTLET_CONFIG);
301             servletRequest.removeAttribute(ContainerConstants.PORTLET_REQUEST);
302             servletRequest.removeAttribute(ContainerConstants.PORTLET_RESPONSE);
303             servletRequest.removeAttribute(ContainerConstants.METHOD_ID);
304             servletRequest.removeAttribute(ContainerConstants.PORTLET_NAME);
305             servletRequest.removeAttribute(ContainerConstants.PORTAL_CONTEXT);
306         }
307 
308     }
309 
310 }