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.servlet.PortletServlet;
27  import com.liferay.portal.kernel.servlet.StringServletResponse;
28  import com.liferay.portal.kernel.util.ClassUtil;
29  import com.liferay.portal.kernel.util.GetterUtil;
30  import com.liferay.portal.kernel.util.JavaConstants;
31  import com.liferay.portal.kernel.util.StringMaker;
32  import com.liferay.portal.kernel.util.StringPool;
33  import com.liferay.portal.model.Layout;
34  import com.liferay.portal.tools.PortletDeployer;
35  import com.liferay.portal.util.WebKeys;
36  import com.liferay.util.Time;
37  
38  import com.sun.portal.portletcontainer.appengine.filter.FilterChainImpl;
39  
40  import java.io.IOException;
41  
42  import java.util.ArrayList;
43  import java.util.HashMap;
44  import java.util.List;
45  import java.util.Map;
46  import java.util.Set;
47  
48  import javax.portlet.ActionRequest;
49  import javax.portlet.ActionResponse;
50  import javax.portlet.EventPortlet;
51  import javax.portlet.EventRequest;
52  import javax.portlet.EventResponse;
53  import javax.portlet.Portlet;
54  import javax.portlet.PortletConfig;
55  import javax.portlet.PortletContext;
56  import javax.portlet.PortletException;
57  import javax.portlet.PortletRequest;
58  import javax.portlet.PortletResponse;
59  import javax.portlet.PortletSession;
60  import javax.portlet.RenderRequest;
61  import javax.portlet.RenderResponse;
62  import javax.portlet.ResourceRequest;
63  import javax.portlet.ResourceResponse;
64  import javax.portlet.ResourceServingPortlet;
65  import javax.portlet.filter.ActionFilter;
66  import javax.portlet.filter.EventFilter;
67  import javax.portlet.filter.FilterChain;
68  import javax.portlet.filter.PortletFilter;
69  import javax.portlet.filter.RenderFilter;
70  import javax.portlet.filter.ResourceFilter;
71  
72  import javax.servlet.RequestDispatcher;
73  import javax.servlet.ServletException;
74  import javax.servlet.http.HttpServletRequest;
75  import javax.servlet.http.HttpServletResponse;
76  import javax.servlet.http.HttpSession;
77  
78  import org.apache.commons.lang.time.StopWatch;
79  import org.apache.commons.logging.Log;
80  import org.apache.commons.logging.LogFactory;
81  
82  /**
83   * <a href="InvokerPortlet.java.html"><b><i>View Source</i></b></a>
84   *
85   * @author Brian Wing Shun Chan
86   * @author Brian Myunghun Kim
87   *
88   */
89  public class InvokerPortlet
90      implements EventPortlet, Portlet, ResourceServingPortlet {
91  
92      public static void clearResponse(
93          HttpSession ses, long plid, String portletId, String languageId) {
94  
95          String sesResponseId = encodeResponseKey(plid, portletId, languageId);
96  
97          getResponses(ses).remove(sesResponseId);
98      }
99  
100     public static void clearResponses(HttpSession ses) {
101         getResponses(ses).clear();
102     }
103 
104     public static void clearResponses(PortletSession ses) {
105         getResponses(ses).clear();
106     }
107 
108     public static String encodeResponseKey(
109         long plid, String portletId, String languageId) {
110 
111         StringMaker sm = new StringMaker();
112 
113         sm.append(plid);
114         sm.append(StringPool.UNDERLINE);
115         sm.append(portletId);
116         sm.append(StringPool.UNDERLINE);
117         sm.append(languageId);
118 
119         return sm.toString();
120     }
121 
122     public static Map<String, InvokerPortletResponse> getResponses(
123         HttpSession ses) {
124 
125         Map<String, InvokerPortletResponse> responses =
126             (Map<String, InvokerPortletResponse>)ses.getAttribute(
127                 WebKeys.CACHE_PORTLET_RESPONSES);
128 
129         if (responses == null) {
130             responses = new HashMap<String, InvokerPortletResponse>();
131 
132             ses.setAttribute(WebKeys.CACHE_PORTLET_RESPONSES, responses);
133         }
134 
135         return responses;
136     }
137 
138     public static Map<String, InvokerPortletResponse> getResponses(
139         PortletSession ses) {
140 
141         return getResponses(((PortletSessionImpl)ses).getHttpSession());
142     }
143 
144     public InvokerPortlet(
145             com.liferay.portal.model.Portlet portletModel, Portlet portlet,
146             PortletContext portletCtx)
147         throws PortletException {
148 
149         _portletModel = portletModel;
150         _portlet = portlet;
151         _portletCtx = (PortletContextImpl)portletCtx;
152 
153         if (_log.isDebugEnabled()) {
154             _log.debug(
155                 "Create root cache wrapper for " +
156                     _portletCtx.getPortlet().getPortletId());
157         }
158 
159         if (ClassUtil.isSubclass(
160                 _portlet.getClass(), PortletDeployer.JSF_MYFACES) ||
161             ClassUtil.isSubclass(
162                 _portlet.getClass(), PortletDeployer.JSF_SUN)) {
163 
164             _facesPortlet = true;
165         }
166 
167         _strutsPortlet = ClassUtil.isSubclass(
168             portlet.getClass(), StrutsPortlet.class);
169         _strutsBridgePortlet = ClassUtil.isSubclass(
170             portlet.getClass(),
171             "org.apache.portals.bridges.struts.StrutsPortlet");
172         _expCache = portletModel.getExpCache();
173         setPortletFilters();
174     }
175 
176     public InvokerPortlet(
177             com.liferay.portal.model.Portlet portletModel, Portlet portlet,
178             PortletConfig portletConfig, PortletContext portletCtx,
179             boolean facesPortlet, boolean strutsPortlet,
180             boolean strutsBridgePortlet)
181         throws PortletException {
182 
183         // From constructor
184 
185         _portletModel = portletModel;
186         _portlet = portlet;
187         _portletCtx = (PortletContextImpl)portletCtx;
188         _facesPortlet = facesPortlet;
189         _strutsPortlet = strutsPortlet;
190         _strutsBridgePortlet = strutsBridgePortlet;
191         _expCache = portletModel.getExpCache();
192         setPortletFilters();
193 
194         if (_log.isDebugEnabled()) {
195             _log.debug(
196                 "Create instance cache wrapper for " +
197                     _portletCtx.getPortlet().getPortletId());
198         }
199 
200         // From init
201 
202         _portletConfig = (PortletConfigImpl)portletConfig;
203 
204         _portletId = _portletConfig.getPortletId();
205     }
206 
207     public void destroy() {
208         if (_destroyable) {
209             ClassLoader contextClassLoader =
210                 Thread.currentThread().getContextClassLoader();
211 
212             ClassLoader portletClassLoader = getPortletClassLoader();
213 
214             try {
215                 if (portletClassLoader != null) {
216                     Thread.currentThread().setContextClassLoader(
217                         portletClassLoader);
218                 }
219 
220                 _portlet.destroy();
221             }
222             finally {
223                 if (portletClassLoader != null) {
224                     Thread.currentThread().setContextClassLoader(
225                         contextClassLoader);
226                 }
227             }
228         }
229 
230         _destroyable = false;
231     }
232 
233     public ClassLoader getPortletClassLoader() {
234         return (ClassLoader)_portletCtx.getAttribute(
235             PortletServlet.PORTLET_CLASS_LOADER);
236     }
237 
238     public PortletConfigImpl getPortletConfig() {
239         return _portletConfig;
240     }
241 
242     public PortletContextImpl getPortletContext() {
243         return _portletCtx;
244     }
245 
246     public Portlet getPortletInstance() {
247         return _portlet;
248     }
249 
250     public Integer getExpCache() {
251         return _expCache;
252     }
253 
254     public void init(PortletConfig portletConfig) throws PortletException {
255         _portletConfig = (PortletConfigImpl)portletConfig;
256 
257         _portletId = _portletConfig.getPortletId();
258 
259         ClassLoader contextClassLoader =
260             Thread.currentThread().getContextClassLoader();
261 
262         ClassLoader portletClassLoader = getPortletClassLoader();
263 
264         try {
265             if (portletClassLoader != null) {
266                 Thread.currentThread().setContextClassLoader(
267                     portletClassLoader);
268             }
269 
270             _portlet.init(portletConfig);
271         }
272         finally {
273             if (portletClassLoader != null) {
274                 Thread.currentThread().setContextClassLoader(
275                     contextClassLoader);
276             }
277         }
278 
279         _destroyable = true;
280     }
281 
282     public boolean isDestroyable() {
283         return _destroyable;
284     }
285 
286     public boolean isFacesPortlet() {
287         return _facesPortlet;
288     }
289 
290     public boolean isStrutsBridgePortlet() {
291         return _strutsBridgePortlet;
292     }
293 
294     public boolean isStrutsPortlet() {
295         return _strutsPortlet;
296     }
297 
298     public void processAction(ActionRequest req, ActionResponse res)
299         throws IOException, PortletException {
300 
301         StopWatch stopWatch = null;
302 
303         if (_log.isDebugEnabled()) {
304             stopWatch = new StopWatch();
305 
306             stopWatch.start();
307         }
308 
309         try {
310             invoke(req, res, PortletRequest.ACTION_PHASE);
311         }
312         catch (PortletException pe) {
313             req.setAttribute(_portletId + PortletException.class.getName(), pe);
314         }
315 
316         if (_log.isDebugEnabled()) {
317             _log.debug(
318                 "processAction for " + _portletId + " takes " +
319                     stopWatch.getTime() + " ms");
320         }
321     }
322 
323     public void processEvent(EventRequest req, EventResponse res)
324         throws IOException, PortletException {
325 
326         StopWatch stopWatch = null;
327 
328         if (_log.isDebugEnabled()) {
329             stopWatch = new StopWatch();
330 
331             stopWatch.start();
332         }
333 
334         invoke(req, res, PortletRequest.EVENT_PHASE);
335 
336         if (_log.isDebugEnabled()) {
337             _log.debug(
338                 "processEvent for " + _portletId + " takes " +
339                     stopWatch.getTime() + " ms");
340         }
341     }
342 
343     public void render(RenderRequest req, RenderResponse res)
344         throws IOException, PortletException {
345 
346         PortletException portletException = (PortletException)req.getAttribute(
347             _portletId + PortletException.class.getName());
348 
349         if (portletException != null) {
350             throw portletException;
351         }
352 
353         StopWatch stopWatch = null;
354 
355         if (_log.isDebugEnabled()) {
356             stopWatch = new StopWatch();
357 
358             stopWatch.start();
359         }
360 
361         String remoteUser = req.getRemoteUser();
362 
363         if ((remoteUser == null) || (_expCache == null) ||
364             (_expCache.intValue() == 0)) {
365 
366             invoke(req, res, PortletRequest.RENDER_PHASE);
367         }
368         else {
369             RenderResponseImpl resImpl = (RenderResponseImpl)res;
370 
371             StringServletResponse stringServletRes =
372                 (StringServletResponse)resImpl.getHttpServletResponse();
373 
374             PortletSession ses = req.getPortletSession();
375 
376             long now = System.currentTimeMillis();
377 
378             Layout layout = (Layout)req.getAttribute(WebKeys.LAYOUT);
379 
380             Map<String, InvokerPortletResponse> sesResponses =
381                 getResponses(ses);
382 
383             String sesResponseId = encodeResponseKey(
384                 layout.getPlid(), _portletId, LanguageUtil.getLanguageId(req));
385 
386             InvokerPortletResponse response = sesResponses.get(sesResponseId);
387 
388             if (response == null) {
389                 invoke(req, res, PortletRequest.RENDER_PHASE);
390 
391                 response = new InvokerPortletResponse(
392                     resImpl.getTitle(),
393                     stringServletRes.getString(),
394                     now + Time.SECOND * _expCache.intValue());
395 
396                 sesResponses.put(sesResponseId, response);
397             }
398             else if ((response.getTime() < now) &&
399                      (_expCache.intValue() > 0)) {
400 
401                 invoke(req, res, PortletRequest.RENDER_PHASE);
402 
403                 response.setTitle(resImpl.getTitle());
404                 response.setContent(stringServletRes.getString());
405                 response.setTime(now + Time.SECOND * _expCache.intValue());
406             }
407             else {
408                 resImpl.setTitle(response.getTitle());
409                 stringServletRes.getWriter().print(response.getContent());
410             }
411         }
412 
413         if (_log.isDebugEnabled()) {
414             _log.debug(
415                 "render for " + _portletId + " takes " + stopWatch.getTime() +
416                     " ms");
417         }
418     }
419 
420     public void serveResource(ResourceRequest req, ResourceResponse res)
421         throws IOException, PortletException {
422 
423         StopWatch stopWatch = null;
424 
425         if (_log.isDebugEnabled()) {
426             stopWatch = new StopWatch();
427 
428             stopWatch.start();
429         }
430 
431         try {
432             invoke(req, res, PortletRequest.RESOURCE_PHASE);
433         }
434         catch (PortletException pe) {
435             req.setAttribute(_portletId + PortletException.class.getName(), pe);
436         }
437 
438         if (_log.isDebugEnabled()) {
439             _log.debug(
440                 "serveResource for " + _portletId + " takes " +
441                     stopWatch.getTime() + " ms");
442         }
443     }
444 
445     public void setPortletFilters() throws PortletException {
446         Map<String, com.liferay.portal.model.PortletFilter> portletFilters =
447             _portletModel.getPortletFilters();
448 
449         for (Map.Entry<String, com.liferay.portal.model.PortletFilter> entry :
450                 portletFilters.entrySet()) {
451 
452             com.liferay.portal.model.PortletFilter portletFilterModel =
453                 entry.getValue();
454 
455             PortletFilter portletFilter = PortletFilterFactory.create(
456                 portletFilterModel, _portletCtx);
457 
458             Set<String> lifecycles = portletFilterModel.getLifecycles();
459 
460             if (lifecycles.contains(PortletRequest.ACTION_PHASE)) {
461                 _actionFilters.add((ActionFilter)portletFilter);
462             }
463 
464             if (lifecycles.contains(PortletRequest.EVENT_PHASE)) {
465                 _eventFilters.add((EventFilter)portletFilter);
466             }
467 
468             if (lifecycles.contains(PortletRequest.RENDER_PHASE)) {
469                 _renderFilters.add((RenderFilter)portletFilter);
470             }
471 
472             if (lifecycles.contains(PortletRequest.RESOURCE_PHASE)) {
473                 _resourceFilters.add((ResourceFilter)portletFilter);
474             }
475         }
476     }
477 
478     protected void invoke(
479             PortletRequest req, PortletResponse res, String lifecycle)
480         throws IOException, PortletException {
481 
482         FilterChain filterChain = null;
483 
484         Map<String, String[]> properties = null;
485 
486         if (_portletConfig.isWARFile()) {
487             String path =
488                 StringPool.SLASH + _portletConfig.getPortletName() + "/invoke";
489 
490             RequestDispatcher rd =
491                 _portletCtx.getServletContext().getRequestDispatcher(path);
492 
493             HttpServletRequest httpReq = null;
494             HttpServletResponse httpRes = null;
495 
496             ActionRequestImpl actionReqImpl = null;
497             ActionResponseImpl actionResImpl = null;
498 
499             EventRequestImpl eventReqImpl = null;
500             EventResponseImpl eventResImpl = null;
501 
502             RenderRequestImpl renderReqImpl = null;
503             RenderResponseImpl renderResImpl = null;
504 
505             ResourceRequestImpl resourceReqImpl = null;
506             ResourceResponseImpl resourceResImpl = null;
507 
508             if (lifecycle.equals(PortletRequest.ACTION_PHASE)) {
509                 actionReqImpl = (ActionRequestImpl)req;
510                 actionResImpl = (ActionResponseImpl)res;
511 
512                 httpReq = actionReqImpl.getHttpServletRequest();
513                 httpRes = actionResImpl.getHttpServletResponse();
514 
515                 filterChain = new FilterChainImpl(_portlet, _actionFilters);
516             }
517             else if (lifecycle.equals(PortletRequest.EVENT_PHASE)) {
518                 eventReqImpl = (EventRequestImpl)req;
519                 eventResImpl = (EventResponseImpl)res;
520 
521                 httpReq = eventReqImpl.getHttpServletRequest();
522                 httpRes = eventResImpl.getHttpServletResponse();
523 
524                 filterChain = new FilterChainImpl(_portlet, _eventFilters);
525             }
526             else if (lifecycle.equals(PortletRequest.RENDER_PHASE)) {
527                 renderReqImpl = (RenderRequestImpl)req;
528                 renderResImpl = (RenderResponseImpl)res;
529 
530                 httpReq = renderReqImpl.getHttpServletRequest();
531                 httpRes = renderResImpl.getHttpServletResponse();
532 
533                 filterChain = new FilterChainImpl(_portlet, _renderFilters);
534             }
535             else if (lifecycle.equals(PortletRequest.RESOURCE_PHASE)) {
536                 resourceReqImpl = (ResourceRequestImpl)req;
537                 resourceResImpl = (ResourceResponseImpl)res;
538 
539                 httpReq = resourceReqImpl.getHttpServletRequest();
540                 httpRes = resourceResImpl.getHttpServletResponse();
541 
542                 filterChain = new FilterChainImpl(_portlet, _resourceFilters);
543             }
544 
545             httpReq.setAttribute(JavaConstants.JAVAX_PORTLET_PORTLET, _portlet);
546             httpReq.setAttribute(
547                 PortletServlet.PORTLET_SERVLET_FILTER_CHAIN, filterChain);
548 
549             try {
550                 rd.include(httpReq, httpRes);
551             }
552             catch (ServletException se) {
553                 Throwable cause = se.getRootCause();
554 
555                 if (cause instanceof PortletException) {
556                     throw (PortletException)cause;
557                 }
558 
559                 throw new PortletException(cause);
560             }
561 
562             if (lifecycle.equals(PortletRequest.ACTION_PHASE)) {
563                 properties = actionResImpl.getProperties();
564             }
565             else if (lifecycle.equals(PortletRequest.EVENT_PHASE)) {
566                 properties = eventResImpl.getProperties();
567             }
568             else if (lifecycle.equals(PortletRequest.RENDER_PHASE)) {
569                 properties = renderResImpl.getProperties();
570             }
571             else if (lifecycle.equals(PortletRequest.RESOURCE_PHASE)) {
572                 properties = resourceResImpl.getProperties();
573             }
574         }
575         else {
576             if (lifecycle.equals(PortletRequest.ACTION_PHASE)) {
577                 ActionRequestImpl actionReqImpl = (ActionRequestImpl)req;
578                 ActionResponseImpl actionResImpl = (ActionResponseImpl)res;
579 
580                 filterChain = new FilterChainImpl(_portlet, _actionFilters);
581 
582                 filterChain.doFilter(actionReqImpl, actionResImpl);
583 
584                 properties = actionResImpl.getProperties();
585             }
586             else if (lifecycle.equals(PortletRequest.EVENT_PHASE)) {
587                 EventRequestImpl eventReqImpl = (EventRequestImpl)req;
588                 EventResponseImpl eventResImpl = (EventResponseImpl)res;
589 
590                 filterChain = new FilterChainImpl(_portlet, _eventFilters);
591 
592                 filterChain.doFilter(eventReqImpl, eventResImpl);
593 
594                 properties = eventResImpl.getProperties();
595             }
596             else if (lifecycle.equals(PortletRequest.RENDER_PHASE)) {
597                 RenderRequestImpl renderReqImpl = (RenderRequestImpl)req;
598                 RenderResponseImpl renderResImpl = (RenderResponseImpl)res;
599 
600                 filterChain = new FilterChainImpl(_portlet, _renderFilters);
601 
602                 filterChain.doFilter(renderReqImpl, renderResImpl);
603 
604                 properties = renderResImpl.getProperties();
605             }
606             else if (lifecycle.equals(PortletRequest.RESOURCE_PHASE)) {
607                 ResourceRequestImpl resourceReqImpl = (ResourceRequestImpl)req;
608                 ResourceResponseImpl resourceResImpl =
609                     (ResourceResponseImpl)res;
610 
611                 filterChain = new FilterChainImpl(_portlet, _resourceFilters);
612 
613                 filterChain.doFilter(resourceReqImpl, resourceResImpl);
614 
615                 properties = resourceResImpl.getProperties();
616             }
617         }
618 
619         if ((properties != null) && (properties.size() > 0)) {
620             if (_expCache != null) {
621                 String[] expCache = properties.get(
622                     RenderResponse.EXPIRATION_CACHE);
623 
624                 if ((expCache != null) && (expCache.length > 0) &&
625                     (expCache[0] != null)) {
626 
627                     _expCache = new Integer(GetterUtil.getInteger(expCache[0]));
628                 }
629             }
630         }
631     }
632 
633     private static Log _log = LogFactory.getLog(InvokerPortlet.class);
634 
635     private com.liferay.portal.model.Portlet _portletModel;
636     private String _portletId;
637     private Portlet _portlet;
638     private PortletConfigImpl _portletConfig;
639     private PortletContextImpl _portletCtx;
640     private Integer _expCache;
641     private boolean _destroyable;
642     private boolean _facesPortlet;
643     private boolean _strutsPortlet;
644     private boolean _strutsBridgePortlet;
645     private List<ActionFilter> _actionFilters = new ArrayList<ActionFilter>();
646     private List<EventFilter> _eventFilters = new ArrayList<EventFilter>();
647     private List<RenderFilter> _renderFilters = new ArrayList<RenderFilter>();
648     private List<ResourceFilter> _resourceFilters =
649         new ArrayList<ResourceFilter>();
650 
651 }