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.CollectionFactory;
37  import com.liferay.util.Time;
38  
39  import java.io.IOException;
40  
41  import java.util.Map;
42  
43  import javax.portlet.ActionRequest;
44  import javax.portlet.ActionResponse;
45  import javax.portlet.Portlet;
46  import javax.portlet.PortletConfig;
47  import javax.portlet.PortletContext;
48  import javax.portlet.PortletException;
49  import javax.portlet.PortletRequest;
50  import javax.portlet.PortletResponse;
51  import javax.portlet.PortletSession;
52  import javax.portlet.RenderRequest;
53  import javax.portlet.RenderResponse;
54  
55  import javax.servlet.RequestDispatcher;
56  import javax.servlet.ServletException;
57  import javax.servlet.http.HttpServletRequest;
58  import javax.servlet.http.HttpServletResponse;
59  import javax.servlet.http.HttpSession;
60  
61  import org.apache.commons.lang.time.StopWatch;
62  import org.apache.commons.logging.Log;
63  import org.apache.commons.logging.LogFactory;
64  
65  /**
66   * <a href="CachePortlet.java.html"><b><i>View Source</i></b></a>
67   *
68   * @author Brian Wing Shun Chan
69   * @author Brian Myunghun Kim
70   *
71   */
72  public class CachePortlet implements Portlet {
73  
74      public static void clearResponse(
75          HttpSession ses, long plid, String portletId, String languageId) {
76  
77          String sesResponseId = encodeResponseKey(plid, portletId, languageId);
78  
79          getResponses(ses).remove(sesResponseId);
80      }
81  
82      public static void clearResponses(HttpSession ses) {
83          getResponses(ses).clear();
84      }
85  
86      public static void clearResponses(PortletSession ses) {
87          getResponses(ses).clear();
88      }
89  
90      public static String encodeResponseKey(
91          long plid, String portletId, String languageId) {
92  
93          StringMaker sm = new StringMaker();
94  
95          sm.append(plid);
96          sm.append(StringPool.UNDERLINE);
97          sm.append(portletId);
98          sm.append(StringPool.UNDERLINE);
99          sm.append(languageId);
100 
101         return sm.toString();
102     }
103 
104     public static Map getResponses(HttpSession ses) {
105         Map responses = (Map)ses.getAttribute(WebKeys.CACHE_PORTLET_RESPONSES);
106 
107         if (responses == null) {
108             responses = CollectionFactory.getHashMap();
109 
110             ses.setAttribute(WebKeys.CACHE_PORTLET_RESPONSES, responses);
111         }
112 
113         return responses;
114     }
115 
116     public static Map getResponses(PortletSession ses) {
117         return getResponses(((PortletSessionImpl)ses).getHttpSession());
118     }
119 
120     public CachePortlet(
121         Portlet portlet, PortletContext portletCtx, Integer expCache) {
122 
123         _portlet = portlet;
124         _portletCtx = (PortletContextImpl)portletCtx;
125         _expCache = expCache;
126 
127         if (_log.isDebugEnabled()) {
128             _log.debug(
129                 "Create root cache wrapper for " +
130                     _portletCtx.getPortlet().getPortletId());
131         }
132 
133         if (ClassUtil.isSubclass(
134                 _portlet.getClass(), PortletDeployer.JSF_MYFACES) ||
135             ClassUtil.isSubclass(
136                 _portlet.getClass(), PortletDeployer.JSF_SUN)) {
137 
138             _facesPortlet = true;
139         }
140 
141         _strutsPortlet = ClassUtil.isSubclass(
142             portlet.getClass(), StrutsPortlet.class);
143         _strutsBridgePortlet = ClassUtil.isSubclass(
144             portlet.getClass(),
145             "org.apache.portals.bridges.struts.StrutsPortlet");
146     }
147 
148     public CachePortlet(
149         Portlet portlet, PortletConfig portletConfig, PortletContext portletCtx,
150         Integer expCache, boolean facesPortlet, boolean strutsPortlet,
151         boolean strutsBridgePortlet) {
152 
153         // From constructor
154 
155         _portlet = portlet;
156         _portletCtx = (PortletContextImpl)portletCtx;
157         _expCache = expCache;
158         _facesPortlet = facesPortlet;
159         _strutsPortlet = strutsPortlet;
160         _strutsBridgePortlet = strutsBridgePortlet;
161 
162         if (_log.isDebugEnabled()) {
163             _log.debug(
164                 "Create instance cache wrapper for " +
165                     _portletCtx.getPortlet().getPortletId());
166         }
167 
168         // From init
169 
170         _portletConfig = (PortletConfigImpl)portletConfig;
171 
172         _portletId = _portletConfig.getPortletId();
173     }
174 
175     public void init(PortletConfig portletConfig) throws PortletException {
176         _portletConfig = (PortletConfigImpl)portletConfig;
177 
178         _portletId = _portletConfig.getPortletId();
179 
180         ClassLoader contextClassLoader =
181             Thread.currentThread().getContextClassLoader();
182 
183         ClassLoader portletClassLoader = _getPortletClassLoader();
184 
185         try {
186             if (portletClassLoader != null) {
187                 Thread.currentThread().setContextClassLoader(
188                     portletClassLoader);
189             }
190 
191             _portlet.init(portletConfig);
192         }
193         finally {
194             if (portletClassLoader != null) {
195                 Thread.currentThread().setContextClassLoader(
196                     contextClassLoader);
197             }
198         }
199 
200         _destroyable = true;
201     }
202 
203     public void processAction(ActionRequest req, ActionResponse res)
204         throws IOException, PortletException {
205 
206         StopWatch stopWatch = null;
207 
208         if (_log.isDebugEnabled()) {
209             stopWatch = new StopWatch();
210 
211             stopWatch.start();
212         }
213 
214         try {
215             _invoke(req, res, true);
216         }
217         catch (PortletException pe) {
218             req.setAttribute(_portletId + PortletException.class.getName(), pe);
219         }
220 
221         if (_log.isDebugEnabled()) {
222             _log.debug(
223                 "processAction for " + _portletId + " takes " +
224                     stopWatch.getTime() + " ms");
225         }
226     }
227 
228     public void render(RenderRequest req, RenderResponse res)
229         throws IOException, PortletException {
230 
231         PortletException portletException = (PortletException)req.getAttribute(
232             _portletId + PortletException.class.getName());
233 
234         if (portletException != null) {
235             throw portletException;
236         }
237 
238         StopWatch stopWatch = null;
239 
240         if (_log.isDebugEnabled()) {
241             stopWatch = new StopWatch();
242 
243             stopWatch.start();
244         }
245 
246         String remoteUser = req.getRemoteUser();
247 
248         if ((remoteUser == null) || (_expCache == null) ||
249             (_expCache.intValue() == 0)) {
250 
251             _invoke(req, res, false);
252         }
253         else {
254             RenderResponseImpl resImpl = (RenderResponseImpl)res;
255 
256             StringServletResponse stringServletRes =
257                 (StringServletResponse)resImpl.getHttpServletResponse();
258 
259             PortletSession ses = req.getPortletSession();
260 
261             long now = System.currentTimeMillis();
262 
263             Layout layout = (Layout)req.getAttribute(WebKeys.LAYOUT);
264 
265             Map sesResponses = getResponses(ses);
266 
267             String sesResponseId = encodeResponseKey(
268                 layout.getPlid(), _portletId, LanguageUtil.getLanguageId(req));
269 
270             CachePortletResponse response =
271                 (CachePortletResponse)sesResponses.get(sesResponseId);
272 
273             if (response == null) {
274                 _invoke(req, res, false);
275 
276                 response = new CachePortletResponse(
277                     resImpl.getTitle(),
278                     stringServletRes.getString(),
279                     now + Time.SECOND * _expCache.intValue());
280 
281                 sesResponses.put(sesResponseId, response);
282             }
283             else if ((response.getTime() < now) &&
284                      (_expCache.intValue() > 0)) {
285 
286                 _invoke(req, res, false);
287 
288                 response.setTitle(resImpl.getTitle());
289                 response.setContent(stringServletRes.getString());
290                 response.setTime(now + Time.SECOND * _expCache.intValue());
291             }
292             else {
293                 resImpl.setTitle(response.getTitle());
294                 stringServletRes.getWriter().print(response.getContent());
295             }
296         }
297 
298         if (_log.isDebugEnabled()) {
299             _log.debug(
300                 "render for " + _portletId + " takes " + stopWatch.getTime() +
301                     " ms");
302         }
303     }
304 
305     public void destroy() {
306         if (_destroyable) {
307             ClassLoader contextClassLoader =
308                 Thread.currentThread().getContextClassLoader();
309 
310             ClassLoader portletClassLoader = _getPortletClassLoader();
311 
312             try {
313                 if (portletClassLoader != null) {
314                     Thread.currentThread().setContextClassLoader(
315                         portletClassLoader);
316                 }
317 
318                 _portlet.destroy();
319             }
320             finally {
321                 if (portletClassLoader != null) {
322                     Thread.currentThread().setContextClassLoader(
323                         contextClassLoader);
324                 }
325             }
326         }
327 
328         _destroyable = false;
329     }
330 
331     public Portlet getPortletInstance() {
332         return _portlet;
333     }
334 
335     public PortletConfigImpl getPortletConfig() {
336         return _portletConfig;
337     }
338 
339     public PortletContextImpl getPortletContext() {
340         return _portletCtx;
341     }
342 
343     public Integer getExpCache() {
344         return _expCache;
345     }
346 
347     public boolean isDestroyable() {
348         return _destroyable;
349     }
350 
351     public boolean isFacesPortlet() {
352         return _facesPortlet;
353     }
354 
355     public boolean isStrutsPortlet() {
356         return _strutsPortlet;
357     }
358 
359     public boolean isStrutsBridgePortlet() {
360         return _strutsBridgePortlet;
361     }
362 
363     private ClassLoader _getPortletClassLoader() {
364         return (ClassLoader)_portletCtx.getAttribute(
365             PortletServlet.PORTLET_CLASS_LOADER);
366     }
367 
368     private void _invoke(
369             PortletRequest req, PortletResponse res, boolean action)
370         throws IOException, PortletException {
371 
372         Map properties = null;
373 
374         if (_portletConfig.isWARFile()) {
375             String path =
376                 StringPool.SLASH + _portletConfig.getPortletName() + "/invoke";
377 
378             RequestDispatcher rd =
379                 _portletCtx.getServletContext().getRequestDispatcher(path);
380 
381             HttpServletRequest httpReq = null;
382             HttpServletResponse httpRes = null;
383 
384             ActionRequestImpl actionReqImpl = null;
385             ActionResponseImpl actionResImpl = null;
386 
387             RenderRequestImpl renderReqImpl = null;
388             RenderResponseImpl renderResImpl = null;
389 
390             if (action) {
391                 actionReqImpl = (ActionRequestImpl)req;
392                 actionResImpl = (ActionResponseImpl)res;
393 
394                 httpReq = actionReqImpl.getHttpServletRequest();
395                 httpRes = actionResImpl.getHttpServletResponse();
396             }
397             else {
398                 renderReqImpl = (RenderRequestImpl)req;
399                 renderResImpl = (RenderResponseImpl)res;
400 
401                 httpReq = renderReqImpl.getHttpServletRequest();
402                 httpRes = renderResImpl.getHttpServletResponse();
403             }
404 
405             httpReq.setAttribute(JavaConstants.JAVAX_PORTLET_PORTLET, _portlet);
406 
407             try {
408                 rd.include(httpReq, httpRes);
409             }
410             catch (ServletException se) {
411                 Throwable cause = se.getRootCause();
412 
413                 if (cause instanceof PortletException) {
414                     throw (PortletException)cause;
415                 }
416 
417                 throw new PortletException(cause);
418             }
419 
420             if (action) {
421                 properties = actionResImpl.getProperties();
422             }
423             else {
424                 properties = renderResImpl.getProperties();
425             }
426         }
427         else {
428             if (action) {
429                 ActionRequestImpl actionReqImpl = (ActionRequestImpl)req;
430                 ActionResponseImpl actionResImpl = (ActionResponseImpl)res;
431 
432                 _portlet.processAction(actionReqImpl, actionResImpl);
433 
434                 properties = actionResImpl.getProperties();
435             }
436             else {
437                 RenderRequestImpl renderReqImpl = (RenderRequestImpl)req;
438                 RenderResponseImpl renderResImpl = (RenderResponseImpl)res;
439 
440                 _portlet.render(renderReqImpl, renderResImpl);
441 
442                 properties = renderResImpl.getProperties();
443             }
444         }
445 
446         if ((properties != null) && (properties.size() > 0)) {
447             if (_expCache != null) {
448                 String[] expCache = (String[])properties.get(
449                     RenderResponse.EXPIRATION_CACHE);
450 
451                 if ((expCache != null) && (expCache.length > 0) &&
452                     (expCache[0] != null)) {
453 
454                     _expCache = new Integer(GetterUtil.getInteger(expCache[0]));
455                 }
456             }
457         }
458     }
459 
460     private static Log _log = LogFactory.getLog(CachePortlet.class);
461 
462     private String _portletId;
463     private Portlet _portlet;
464     private PortletConfigImpl _portletConfig;
465     private PortletContextImpl _portletCtx;
466     private Integer _expCache;
467     private boolean _destroyable;
468     private boolean _facesPortlet;
469     private boolean _strutsPortlet;
470     private boolean _strutsBridgePortlet;
471 
472 }