1   /**
2    * Copyright (c) 2000-2008 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portlet;
24  
25  import com.liferay.portal.kernel.language.LanguageUtil;
26  import com.liferay.portal.kernel.portlet.LiferayPortletRequest;
27  import com.liferay.portal.kernel.portlet.LiferayPortletResponse;
28  import com.liferay.portal.kernel.portlet.PortletFilterUtil;
29  import com.liferay.portal.kernel.servlet.PortletServlet;
30  import com.liferay.portal.kernel.servlet.StringServletResponse;
31  import com.liferay.portal.kernel.util.ClassUtil;
32  import com.liferay.portal.kernel.util.GetterUtil;
33  import com.liferay.portal.kernel.util.JavaConstants;
34  import com.liferay.portal.kernel.util.StringPool;
35  import com.liferay.portal.kernel.util.Time;
36  import com.liferay.portal.model.Layout;
37  import com.liferay.portal.tools.PortletDeployer;
38  import com.liferay.portal.util.WebKeys;
39  
40  import com.sun.portal.portletcontainer.appengine.filter.FilterChainImpl;
41  
42  import java.io.IOException;
43  
44  import java.util.ArrayList;
45  import java.util.HashMap;
46  import java.util.List;
47  import java.util.Map;
48  import java.util.Set;
49  
50  import javax.portlet.ActionRequest;
51  import javax.portlet.ActionResponse;
52  import javax.portlet.EventPortlet;
53  import javax.portlet.EventRequest;
54  import javax.portlet.EventResponse;
55  import javax.portlet.Portlet;
56  import javax.portlet.PortletConfig;
57  import javax.portlet.PortletContext;
58  import javax.portlet.PortletException;
59  import javax.portlet.PortletRequest;
60  import javax.portlet.PortletSession;
61  import javax.portlet.RenderRequest;
62  import javax.portlet.RenderResponse;
63  import javax.portlet.ResourceRequest;
64  import javax.portlet.ResourceResponse;
65  import javax.portlet.ResourceServingPortlet;
66  import javax.portlet.filter.ActionFilter;
67  import javax.portlet.filter.EventFilter;
68  import javax.portlet.filter.FilterChain;
69  import javax.portlet.filter.PortletFilter;
70  import javax.portlet.filter.RenderFilter;
71  import javax.portlet.filter.ResourceFilter;
72  
73  import javax.servlet.RequestDispatcher;
74  import javax.servlet.ServletException;
75  import javax.servlet.http.HttpServletRequest;
76  import javax.servlet.http.HttpServletResponse;
77  import javax.servlet.http.HttpSession;
78  
79  import org.apache.commons.lang.time.StopWatch;
80  import org.apache.commons.logging.Log;
81  import org.apache.commons.logging.LogFactory;
82  
83  /**
84   * <a href="InvokerPortlet.java.html"><b><i>View Source</i></b></a>
85   *
86   * @author Brian Wing Shun Chan
87   * @author Brian Myunghun Kim
88   *
89   */
90  public class InvokerPortlet
91      implements EventPortlet, Portlet, ResourceServingPortlet {
92  
93      public static void clearResponse(
94          HttpSession session, long plid, String portletId, String languageId) {
95  
96          String sesResponseId = encodeResponseKey(plid, portletId, languageId);
97  
98          getResponses(session).remove(sesResponseId);
99      }
100 
101     public static void clearResponses(HttpSession session) {
102         getResponses(session).clear();
103     }
104 
105     public static void clearResponses(PortletSession session) {
106         getResponses(session).clear();
107     }
108 
109     public static String encodeResponseKey(
110         long plid, String portletId, String languageId) {
111 
112         StringBuilder sb = new StringBuilder();
113 
114         sb.append(plid);
115         sb.append(StringPool.UNDERLINE);
116         sb.append(portletId);
117         sb.append(StringPool.UNDERLINE);
118         sb.append(languageId);
119 
120         return sb.toString();
121     }
122 
123     public static Map<String, InvokerPortletResponse> getResponses(
124         HttpSession session) {
125 
126         Map<String, InvokerPortletResponse> responses =
127             (Map<String, InvokerPortletResponse>)session.getAttribute(
128                 WebKeys.CACHE_PORTLET_RESPONSES);
129 
130         if (responses == null) {
131             responses = new HashMap<String, InvokerPortletResponse>();
132 
133             session.setAttribute(WebKeys.CACHE_PORTLET_RESPONSES, responses);
134         }
135 
136         return responses;
137     }
138 
139     public static Map<String, InvokerPortletResponse> getResponses(
140         PortletSession portletSession) {
141 
142         return getResponses(
143             ((PortletSessionImpl)portletSession).getHttpSession());
144     }
145 
146     public InvokerPortlet(
147             com.liferay.portal.model.Portlet portletModel, Portlet portlet,
148             PortletContext portletContext)
149         throws PortletException {
150 
151         _portletModel = portletModel;
152         _portlet = portlet;
153         _portletContextImpl = (PortletContextImpl)portletContext;
154 
155         if (_log.isDebugEnabled()) {
156             _log.debug(
157                 "Create root cache wrapper for " +
158                     _portletContextImpl.getPortlet().getPortletId());
159         }
160 
161         if (ClassUtil.isSubclass(
162                 _portlet.getClass(), PortletDeployer.JSF_MYFACES) ||
163             ClassUtil.isSubclass(
164                 _portlet.getClass(), PortletDeployer.JSF_SUN)) {
165 
166             _facesPortlet = true;
167         }
168 
169         _strutsPortlet = ClassUtil.isSubclass(
170             portlet.getClass(), StrutsPortlet.class);
171         _strutsBridgePortlet = ClassUtil.isSubclass(
172             portlet.getClass(),
173             "org.apache.portals.bridges.struts.StrutsPortlet");
174         _expCache = portletModel.getExpCache();
175         setPortletFilters();
176     }
177 
178     public InvokerPortlet(
179             com.liferay.portal.model.Portlet portletModel, Portlet portlet,
180             PortletConfig portletConfig, PortletContext portletContext,
181             boolean facesPortlet, boolean strutsPortlet,
182             boolean strutsBridgePortlet)
183         throws PortletException {
184 
185         // From constructor
186 
187         _portletModel = portletModel;
188         _portlet = portlet;
189         _portletContextImpl = (PortletContextImpl)portletContext;
190         _facesPortlet = facesPortlet;
191         _strutsPortlet = strutsPortlet;
192         _strutsBridgePortlet = strutsBridgePortlet;
193         _expCache = portletModel.getExpCache();
194         setPortletFilters();
195 
196         if (_log.isDebugEnabled()) {
197             _log.debug(
198                 "Create instance cache wrapper for " +
199                     _portletContextImpl.getPortlet().getPortletId());
200         }
201 
202         // From init
203 
204         _portletConfigImpl = (PortletConfigImpl)portletConfig;
205 
206         _portletId = _portletConfigImpl.getPortletId();
207     }
208 
209     public void destroy() {
210         if (_destroyable) {
211             ClassLoader contextClassLoader =
212                 Thread.currentThread().getContextClassLoader();
213 
214             ClassLoader portletClassLoader = getPortletClassLoader();
215 
216             try {
217                 if (portletClassLoader != null) {
218                     Thread.currentThread().setContextClassLoader(
219                         portletClassLoader);
220                 }
221 
222                 _portlet.destroy();
223             }
224             finally {
225                 if (portletClassLoader != null) {
226                     Thread.currentThread().setContextClassLoader(
227                         contextClassLoader);
228                 }
229             }
230         }
231 
232         _destroyable = false;
233     }
234 
235     public ClassLoader getPortletClassLoader() {
236         return (ClassLoader)_portletContextImpl.getAttribute(
237             PortletServlet.PORTLET_CLASS_LOADER);
238     }
239 
240     public PortletConfigImpl getPortletConfig() {
241         return _portletConfigImpl;
242     }
243 
244     public PortletContextImpl getPortletContext() {
245         return _portletContextImpl;
246     }
247 
248     public Portlet getPortletInstance() {
249         return _portlet;
250     }
251 
252     public Integer getExpCache() {
253         return _expCache;
254     }
255 
256     public void init(PortletConfig portletConfig) throws PortletException {
257         _portletConfigImpl = (PortletConfigImpl)portletConfig;
258 
259         _portletId = _portletConfigImpl.getPortletId();
260 
261         ClassLoader contextClassLoader =
262             Thread.currentThread().getContextClassLoader();
263 
264         ClassLoader portletClassLoader = getPortletClassLoader();
265 
266         try {
267             if (portletClassLoader != null) {
268                 Thread.currentThread().setContextClassLoader(
269                     portletClassLoader);
270             }
271 
272             _portlet.init(portletConfig);
273         }
274         finally {
275             if (portletClassLoader != null) {
276                 Thread.currentThread().setContextClassLoader(
277                     contextClassLoader);
278             }
279         }
280 
281         _destroyable = true;
282     }
283 
284     public boolean isDestroyable() {
285         return _destroyable;
286     }
287 
288     public boolean isFacesPortlet() {
289         return _facesPortlet;
290     }
291 
292     public boolean isStrutsBridgePortlet() {
293         return _strutsBridgePortlet;
294     }
295 
296     public boolean isStrutsPortlet() {
297         return _strutsPortlet;
298     }
299 
300     public void processAction(
301             ActionRequest actionRequest, ActionResponse actionResponse)
302         throws IOException {
303 
304         StopWatch stopWatch = null;
305 
306         if (_log.isDebugEnabled()) {
307             stopWatch = new StopWatch();
308 
309             stopWatch.start();
310         }
311 
312         try {
313             invokeAction(actionRequest, actionResponse);
314         }
315         catch (PortletException pe) {
316             actionRequest.setAttribute(
317                 _portletId + PortletException.class.getName(), pe);
318         }
319 
320         if (_log.isDebugEnabled()) {
321             _log.debug(
322                 "processAction for " + _portletId + " takes " +
323                     stopWatch.getTime() + " ms");
324         }
325     }
326 
327     public void processEvent(
328             EventRequest eventRequest, EventResponse eventResponse)
329         throws IOException, PortletException {
330 
331         StopWatch stopWatch = null;
332 
333         if (_log.isDebugEnabled()) {
334             stopWatch = new StopWatch();
335 
336             stopWatch.start();
337         }
338 
339         invokeEvent(eventRequest, eventResponse);
340 
341         if (_log.isDebugEnabled()) {
342             _log.debug(
343                 "processEvent for " + _portletId + " takes " +
344                     stopWatch.getTime() + " ms");
345         }
346     }
347 
348     public void render(
349             RenderRequest renderRequest, RenderResponse renderResponse)
350         throws IOException, PortletException {
351 
352         PortletException portletException =
353             (PortletException)renderRequest.getAttribute(
354                 _portletId + PortletException.class.getName());
355 
356         if (portletException != null) {
357             throw portletException;
358         }
359 
360         StopWatch stopWatch = null;
361 
362         if (_log.isDebugEnabled()) {
363             stopWatch = new StopWatch();
364 
365             stopWatch.start();
366         }
367 
368         String remoteUser = renderRequest.getRemoteUser();
369 
370         if ((remoteUser == null) || (_expCache == null) ||
371             (_expCache.intValue() == 0)) {
372 
373             invokeRender(renderRequest, renderResponse);
374         }
375         else {
376             RenderResponseImpl renderResponseImpl =
377                 (RenderResponseImpl)renderResponse;
378 
379             StringServletResponse stringResponse = (StringServletResponse)
380                 renderResponseImpl.getHttpServletResponse();
381 
382             PortletSession portletSession = renderRequest.getPortletSession();
383 
384             long now = System.currentTimeMillis();
385 
386             Layout layout = (Layout)renderRequest.getAttribute(WebKeys.LAYOUT);
387 
388             Map<String, InvokerPortletResponse> sessionResponses =
389                 getResponses(portletSession);
390 
391             String sessionResponseId = encodeResponseKey(
392                 layout.getPlid(), _portletId,
393                 LanguageUtil.getLanguageId(renderRequest));
394 
395             InvokerPortletResponse response = sessionResponses.get(
396                 sessionResponseId);
397 
398             if (response == null) {
399                 String title = invokeRender(renderRequest, renderResponse);
400 
401                 response = new InvokerPortletResponse(
402                     title, stringResponse.getString(),
403                     now + Time.SECOND * _expCache.intValue());
404 
405                 sessionResponses.put(sessionResponseId, response);
406             }
407             else if ((response.getTime() < now) &&
408                      (_expCache.intValue() > 0)) {
409 
410                 String title = invokeRender(renderRequest, renderResponse);
411 
412                 response.setTitle(title);
413                 response.setContent(stringResponse.getString());
414                 response.setTime(now + Time.SECOND * _expCache.intValue());
415             }
416             else {
417                 renderResponseImpl.setTitle(response.getTitle());
418                 stringResponse.getWriter().print(response.getContent());
419             }
420         }
421 
422         Map<String, String[]> properties =
423             ((RenderResponseImpl)renderResponse).getProperties();
424 
425         if (properties.containsKey("clear-request-parameters")) {
426             Map<String, String[]> renderParameters =
427                 ((RenderRequestImpl)renderRequest).getRenderParameters();
428 
429             renderParameters.clear();
430         }
431 
432         if (_log.isDebugEnabled()) {
433             _log.debug(
434                 "render for " + _portletId + " takes " + stopWatch.getTime() +
435                     " ms");
436         }
437     }
438 
439     public void serveResource(
440             ResourceRequest resourceRequest, ResourceResponse resourceResponse)
441         throws IOException {
442 
443         StopWatch stopWatch = null;
444 
445         if (_log.isDebugEnabled()) {
446             stopWatch = new StopWatch();
447 
448             stopWatch.start();
449         }
450 
451         try {
452             invokeResource(resourceRequest, resourceResponse);
453         }
454         catch (PortletException pe) {
455             resourceRequest.setAttribute(
456                 _portletId + PortletException.class.getName(), pe);
457         }
458 
459         if (_log.isDebugEnabled()) {
460             _log.debug(
461                 "serveResource for " + _portletId + " takes " +
462                     stopWatch.getTime() + " ms");
463         }
464     }
465 
466     public void setPortletFilters() throws PortletException {
467         Map<String, com.liferay.portal.model.PortletFilter> portletFilters =
468             _portletModel.getPortletFilters();
469 
470         for (Map.Entry<String, com.liferay.portal.model.PortletFilter> entry :
471                 portletFilters.entrySet()) {
472 
473             com.liferay.portal.model.PortletFilter portletFilterModel =
474                 entry.getValue();
475 
476             PortletFilter portletFilter = PortletFilterFactory.create(
477                 portletFilterModel, _portletContextImpl);
478 
479             Set<String> lifecycles = portletFilterModel.getLifecycles();
480 
481             if (lifecycles.contains(PortletRequest.ACTION_PHASE)) {
482                 _actionFilters.add((ActionFilter)portletFilter);
483             }
484 
485             if (lifecycles.contains(PortletRequest.EVENT_PHASE)) {
486                 _eventFilters.add((EventFilter)portletFilter);
487             }
488 
489             if (lifecycles.contains(PortletRequest.RENDER_PHASE)) {
490                 _renderFilters.add((RenderFilter)portletFilter);
491             }
492 
493             if (lifecycles.contains(PortletRequest.RESOURCE_PHASE)) {
494                 _resourceFilters.add((ResourceFilter)portletFilter);
495             }
496         }
497     }
498 
499     protected void invoke(
500             LiferayPortletRequest portletRequest,
501             LiferayPortletResponse portletResponse, String lifecycle,
502             List<? extends PortletFilter> filters)
503         throws IOException, PortletException {
504 
505         FilterChain filterChain = new FilterChainImpl(_portlet, filters);
506 
507         if (_portletConfigImpl.isWARFile()) {
508             String path =
509                 StringPool.SLASH + _portletConfigImpl.getPortletName() +
510                     "/invoke";
511 
512             RequestDispatcher requestDispatcher =
513                 _portletContextImpl.getServletContext().getRequestDispatcher(
514                     path);
515 
516             HttpServletRequest request = portletRequest.getHttpServletRequest();
517             HttpServletResponse response =
518                 portletResponse.getHttpServletResponse();
519 
520             request.setAttribute(JavaConstants.JAVAX_PORTLET_PORTLET, _portlet);
521             request.setAttribute(PortletRequest.LIFECYCLE_PHASE, lifecycle);
522             request.setAttribute(
523                 PortletServlet.PORTLET_SERVLET_FILTER_CHAIN, filterChain);
524 
525             try {
526 
527                 // Resource phase must be a forward because includes do not
528                 // allow you to specify the content type or headers
529 
530                 if (lifecycle.equals(PortletRequest.RESOURCE_PHASE)) {
531                     requestDispatcher.forward(request, response);
532                 }
533                 else {
534                     requestDispatcher.include(request, response);
535                 }
536             }
537             catch (ServletException se) {
538                 Throwable cause = se.getRootCause();
539 
540                 if (cause instanceof PortletException) {
541                     throw (PortletException)cause;
542                 }
543 
544                 throw new PortletException(cause);
545             }
546         }
547         else {
548             PortletFilterUtil.doFilter(
549                 portletRequest, portletResponse, lifecycle, filterChain);
550         }
551 
552         Map<String, String[]> properties = portletResponse.getProperties();
553 
554         if ((properties != null) && (properties.size() > 0)) {
555             if (_expCache != null) {
556                 String[] expCache = properties.get(
557                     RenderResponse.EXPIRATION_CACHE);
558 
559                 if ((expCache != null) && (expCache.length > 0) &&
560                     (expCache[0] != null)) {
561 
562                     _expCache = new Integer(GetterUtil.getInteger(expCache[0]));
563                 }
564             }
565         }
566     }
567 
568     protected void invokeAction(
569             ActionRequest actionRequest, ActionResponse actionResponse)
570         throws IOException, PortletException {
571 
572         LiferayPortletRequest portletRequest =
573             (LiferayPortletRequest)actionRequest;
574         LiferayPortletResponse portletResponse =
575             (LiferayPortletResponse)actionResponse;
576 
577         invoke(
578             portletRequest, portletResponse, PortletRequest.ACTION_PHASE,
579             _actionFilters);
580     }
581 
582     protected void invokeEvent(
583             EventRequest eventRequest, EventResponse eventResponse)
584         throws IOException, PortletException {
585 
586         LiferayPortletRequest portletRequest =
587             (LiferayPortletRequest)eventRequest;
588         LiferayPortletResponse portletResponse =
589             (LiferayPortletResponse)eventResponse;
590 
591         invoke(
592             portletRequest, portletResponse, PortletRequest.EVENT_PHASE,
593             _eventFilters);
594     }
595 
596     protected String invokeRender(
597             RenderRequest renderRequest, RenderResponse renderResponse)
598         throws IOException, PortletException {
599 
600         LiferayPortletRequest portletRequest =
601             (LiferayPortletRequest)renderRequest;
602         LiferayPortletResponse portletResponse =
603             (LiferayPortletResponse)renderResponse;
604 
605         invoke(
606             portletRequest, portletResponse, PortletRequest.RENDER_PHASE,
607             _renderFilters);
608 
609         RenderResponseImpl renderResponseImpl =
610             (RenderResponseImpl)renderResponse;
611 
612         return renderResponseImpl.getTitle();
613     }
614 
615     protected void invokeResource(
616             ResourceRequest resourceRequest, ResourceResponse resourceResponse)
617         throws IOException, PortletException {
618 
619         LiferayPortletRequest portletRequest =
620             (LiferayPortletRequest)resourceRequest;
621         LiferayPortletResponse portletResponse =
622             (LiferayPortletResponse)resourceResponse;
623 
624         invoke(
625             portletRequest, portletResponse, PortletRequest.RESOURCE_PHASE,
626             _resourceFilters);
627     }
628 
629     private static Log _log = LogFactory.getLog(InvokerPortlet.class);
630 
631     private com.liferay.portal.model.Portlet _portletModel;
632     private String _portletId;
633     private Portlet _portlet;
634     private PortletConfigImpl _portletConfigImpl;
635     private PortletContextImpl _portletContextImpl;
636     private Integer _expCache;
637     private boolean _destroyable;
638     private boolean _facesPortlet;
639     private boolean _strutsPortlet;
640     private boolean _strutsBridgePortlet;
641     private List<ActionFilter> _actionFilters = new ArrayList<ActionFilter>();
642     private List<EventFilter> _eventFilters = new ArrayList<EventFilter>();
643     private List<RenderFilter> _renderFilters = new ArrayList<RenderFilter>();
644     private List<ResourceFilter> _resourceFilters =
645         new ArrayList<ResourceFilter>();
646 
647 }