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.HttpServletRequestWrapper;
34  import javax.servlet.http.HttpServletResponseWrapper;
35  
36  import org.apache.commons.logging.Log;
37  import org.apache.commons.logging.LogFactory;
38  import org.apache.jetspeed.PortalReservedParameters;
39  import org.apache.jetspeed.container.ContainerConstants;
40  import org.apache.jetspeed.container.PortletRequestContext;
41  import org.apache.jetspeed.factory.PortletFactory;
42  import org.apache.jetspeed.factory.PortletInstance;
43  import org.apache.jetspeed.om.common.portlet.MutablePortletApplication;
44  import org.apache.jetspeed.request.RequestContext;
45  import org.apache.jetspeed.aggregator.Worker;
46  import org.apache.jetspeed.aggregator.CurrentWorkerContext;
47  import org.apache.pluto.om.portlet.PortletDefinition;
48  import org.apache.pluto.om.servlet.WebApplicationDefinition;
49  
50  /***
51   * ServletPortletInvoker invokes portlets in another web application, calling a 
52   * portlet's render or action method via a cross context request dispatcher.
53   * In order for this class to work, a servlet must be special servlet must be 
54   * infused into the web (portlet) application. This servlet knows how to delegate
55   * to portlets and package their response back into a servlet response.
56   * The context name of the servlet should be configurable. The default context name is "/container"
57   * ServletPortletInvokerFactory is the factory for creating portlet invokers that 
58   * use Jetspeed Container servlet. 
59   * <h3>Sample Factory Configuration</h3>
60   * <pre>
61   * <code>
62   * factory.invoker.servlet = org.apache.jetspeed.container.invoker.ServletPortletInvoker
63   * factory.invoker.servlet.pool.size = 50
64   * factory.invoker.servlet.mapping.name = /container
65   * </code> 
66   * </pre>
67   * @author <a href="mailto:taylor@apache.org">David Sean Taylor</a>
68   * @version $Id: ServletPortletInvoker.java 554827 2007-07-10 05:12:23Z taylor $
69   */
70  public class ServletPortletInvoker implements JetspeedPortletInvoker
71  {
72      private final static Log log = LogFactory.getLog(ServletPortletInvoker.class);
73  
74      protected PortletFactory portletFactory;
75      protected ServletContext jetspeedContext;
76      protected ServletConfig jetspeedConfig;
77      protected PortletDefinition portletDefinition;
78      protected boolean activated = false;
79      protected String servletMappingName;
80  
81   
82  
83      /* (non-Javadoc)
84       * @see org.apache.jetspeed.container.invoker.JetspeedPortletInvoker#passivate()
85       */
86      public void passivate()
87      {
88          activated = false;
89      }
90  
91      /* (non-Javadoc)
92       * @see org.apache.jetspeed.container.invoker.JetspeedPortletInvoker#isActivated()
93       */
94      public boolean isActivated()
95      {
96          return activated;
97      }
98  
99      /* (non-Javadoc)
100      * @see org.apache.jetspeed.container.invoker.JetspeedPortletInvoker#activate(PortletFactory,org.apache.pluto.om.portlet.PortletDefinition, javax.servlet.ServletConfig)
101      */
102     public void activate(PortletFactory portletFactory, PortletDefinition portletDefinition, ServletConfig servletConfig)
103     {
104         this.portletFactory = portletFactory;
105         this.jetspeedConfig = servletConfig;
106         jetspeedContext = servletConfig.getServletContext();
107         this.portletDefinition = portletDefinition;
108         activated = true;
109     }
110 
111     /* (non-Javadoc)
112      * @see org.apache.jetspeed.container.invoker.JetspeedPortletInvoker#activate(PortletFactory,org.apache.pluto.om.portlet.PortletDefinition, javax.servlet.ServletConfig, java.lang.String)
113      */
114     public void activate(PortletFactory portletFactory, PortletDefinition portletDefinition, ServletConfig servletConfig, String servletMappingName)
115     {
116         this.servletMappingName = servletMappingName;
117         activate(portletFactory, portletDefinition, servletConfig);
118     }
119 
120     /***
121      *
122      * @param request
123      * @param response
124      * @throws PortletException
125      */
126     public void render(RenderRequest request, RenderResponse response) throws PortletException, IOException
127     {
128         invoke(request, response, ContainerConstants.METHOD_RENDER);
129     }
130 
131     /***
132      *
133      */
134     public void action(ActionRequest request, ActionResponse response) throws PortletException, IOException
135     {
136         invoke(request, response, ContainerConstants.METHOD_ACTION);
137     }
138 
139     /***
140      *
141      */
142     public void load(PortletRequest request, RenderResponse response) throws PortletException
143     {
144         try
145         {
146             invoke(request, response, ContainerConstants.METHOD_NOOP);
147         }
148         catch (IOException e)
149         {
150             log.error("ServletPortletInvokerImpl.load() - Error while dispatching portlet.", e);
151             throw new PortletException(e);
152         }
153     }
154 
155     /***
156      * Creates a servlet request dispatcher to dispatch to another web application to render the portlet.
157      * NOTE: this method requires that your container supports cross-context dispatching.
158      * Cross-context dispatching is known to work on Tomcat, Catalina, Tomcat-5.
159      *
160      * @param portletRequest
161      * @param portletResponse
162      * @param methodID
163      * @throws PortletException
164      * @throws IOException
165      */
166     protected void invoke(PortletRequest portletRequest, PortletResponse portletResponse, Integer methodID)
167         throws PortletException, IOException
168     {
169         // In case of parallel mode, the portletDefinition member is not thread-safe.
170         // So, hide the member variable by the following local variable.
171         PortletDefinition portletDefinition = null;
172 
173         // In case of parallel mode, get portlet definition object from the worker thread context.
174         // Otherwise, refer the member variable.
175         boolean isParallelMode = (Thread.currentThread() instanceof Worker || CurrentWorkerContext.getCurrentWorkerContextUsed());
176 
177         if (isParallelMode)
178         {
179             portletDefinition = (PortletDefinition) CurrentWorkerContext.getAttribute(PortalReservedParameters.PORTLET_DEFINITION_ATTRIBUTE);
180         }
181         
182         if (portletDefinition == null)
183         {
184             portletDefinition = this.portletDefinition;
185         }
186         
187         MutablePortletApplication app = (MutablePortletApplication)portletDefinition.getPortletApplicationDefinition();
188 
189         WebApplicationDefinition webApplicationDefinition = app.getWebApplicationDefinition();
190         if(webApplicationDefinition == null)
191         {
192         	throw new IllegalStateException("Portlet application "+app.getName()+ " has no associated web application.");
193         }
194         String portletApplicationName = webApplicationDefinition.getContextRoot();
195 
196         // gather all required data from request and response
197         ServletRequest servletRequest = ((HttpServletRequestWrapper)((HttpServletRequestWrapper)((HttpServletRequestWrapper)portletRequest).getRequest()).getRequest()).getRequest();
198 
199         ServletResponse servletResponse = ((HttpServletResponseWrapper) portletResponse).getResponse();
200 
201         ServletContext appContext = jetspeedContext.getContext(portletApplicationName);
202         if (null == appContext)
203         {
204             String message = "Failed to find Servlet context for Portlet Application: " + portletApplicationName;
205             log.error(message);
206             throw new PortletException(message);
207         }
208         PortletInstance portletInstance = portletFactory.getPortletInstance(appContext, portletDefinition);
209         RequestDispatcher dispatcher = appContext.getRequestDispatcher(servletMappingName);
210         if (null == dispatcher)
211         {
212             String message =
213                 "Failed to get Request Dispatcher for Portlet Application: "
214                     + portletApplicationName
215                     + ", servlet: "
216                     + servletMappingName;
217             log.error(message);
218             throw new PortletException(message);
219         }
220 
221         try
222         {
223             servletRequest.setAttribute(ContainerConstants.PORTLET, portletInstance);
224             servletRequest.setAttribute(ContainerConstants.PORTLET_CONFIG, portletInstance.getConfig());
225             servletRequest.setAttribute(ContainerConstants.PORTLET_REQUEST, portletRequest);
226             servletRequest.setAttribute(ContainerConstants.PORTLET_RESPONSE, portletResponse);
227             servletRequest.setAttribute(ContainerConstants.METHOD_ID, methodID);
228             servletRequest.setAttribute(ContainerConstants.PORTLET_NAME, app.getName()+"::"+portletDefinition.getName());
229             RequestContext requestContext = (RequestContext)servletRequest.getAttribute(PortalReservedParameters.REQUEST_CONTEXT_ATTRIBUTE);
230             servletRequest.setAttribute(ContainerConstants.PORTAL_CONTEXT, requestContext.getRequest().getContextPath());
231 
232             // Store same request attributes into the worker in parallel mode.
233             if (isParallelMode)
234             {
235                 CurrentWorkerContext.setAttribute(ContainerConstants.PORTLET, portletInstance);
236                 CurrentWorkerContext.setAttribute(ContainerConstants.PORTLET_CONFIG, portletInstance.getConfig());
237                 CurrentWorkerContext.setAttribute(ContainerConstants.PORTLET_REQUEST, portletRequest);
238                 CurrentWorkerContext.setAttribute(ContainerConstants.PORTLET_RESPONSE, portletResponse);
239                 CurrentWorkerContext.setAttribute(ContainerConstants.METHOD_ID, methodID);
240                 CurrentWorkerContext.setAttribute(ContainerConstants.PORTLET_NAME, app.getName()+"::"+portletDefinition.getName());
241                 CurrentWorkerContext.setAttribute(ContainerConstants.PORTAL_CONTEXT, requestContext.getRequest().getContextPath());                
242             }
243 
244             PortletRequestContext.createContext(portletDefinition, portletInstance, portletRequest, portletResponse);
245             dispatcher.include(servletRequest, servletResponse);
246             
247         }
248         catch (Exception e)
249         {
250             String message =
251                 "Failed to dispatch.include for Portlet Application: " + portletApplicationName + ", servlet: " + servletMappingName;
252             log.error(message, e);
253             throw new PortletException(message, e);
254         }
255         finally
256         {
257             PortletRequestContext.clearContext();
258 
259             // In parallel mode, remove all attributes of worker context.
260             if (isParallelMode)
261             {
262                 CurrentWorkerContext.removeAttribute(ContainerConstants.PORTLET);
263                 CurrentWorkerContext.removeAttribute(ContainerConstants.PORTLET_CONFIG);
264                 CurrentWorkerContext.removeAttribute(ContainerConstants.PORTLET_REQUEST);
265                 CurrentWorkerContext.removeAttribute(ContainerConstants.PORTLET_RESPONSE);
266                 CurrentWorkerContext.removeAttribute(ContainerConstants.METHOD_ID);
267                 CurrentWorkerContext.removeAttribute(ContainerConstants.PORTLET_NAME);
268                 CurrentWorkerContext.removeAttribute(ContainerConstants.PORTAL_CONTEXT);
269             }
270 
271             servletRequest.removeAttribute(ContainerConstants.PORTLET);
272             servletRequest.removeAttribute(ContainerConstants.PORTLET_CONFIG);
273             servletRequest.removeAttribute(ContainerConstants.PORTLET_REQUEST);
274             servletRequest.removeAttribute(ContainerConstants.PORTLET_RESPONSE);
275             servletRequest.removeAttribute(ContainerConstants.METHOD_ID);
276             servletRequest.removeAttribute(ContainerConstants.PORTLET_NAME);
277             servletRequest.removeAttribute(ContainerConstants.PORTAL_CONTEXT);
278         }
279 
280     }
281 
282 }