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