1   /**
2    * Copyright (c) 2000-2007 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(Portlet portlet, PortletContext portletCtx,
121                         Integer expCache) {
122 
123         _portlet = portlet;
124         _portletCtx = (PortletContextImpl)portletCtx;
125         _expCache = expCache;
126 
127         if (ClassUtil.isSubclass(
128                 _portlet.getClass(), PortletDeployer.JSF_MYFACES) ||
129             ClassUtil.isSubclass(
130                 _portlet.getClass(), PortletDeployer.JSF_SUN)) {
131 
132             _facesPortlet = true;
133         }
134 
135         _strutsPortlet = ClassUtil.isSubclass(
136             portlet.getClass(), StrutsPortlet.class);
137         _strutsBridgePortlet = ClassUtil.isSubclass(
138             portlet.getClass(),
139             "org.apache.portals.bridges.struts.StrutsPortlet");
140     }
141 
142     public void init(PortletConfig config) throws PortletException {
143         _portletConfig = (PortletConfigImpl)config;
144 
145         _portletId = _portletConfig.getPortletId();
146 
147         ClassLoader contextClassLoader =
148             Thread.currentThread().getContextClassLoader();
149 
150         ClassLoader portletClassLoader = _getPortletClassLoader();
151 
152         try {
153             if (portletClassLoader != null) {
154                 Thread.currentThread().setContextClassLoader(
155                     portletClassLoader);
156             }
157 
158             _portlet.init(config);
159         }
160         finally {
161             if (portletClassLoader != null) {
162                 Thread.currentThread().setContextClassLoader(
163                     contextClassLoader);
164             }
165         }
166 
167         _destroyable = true;
168     }
169 
170     public void processAction(ActionRequest req, ActionResponse res)
171         throws IOException, PortletException {
172 
173         StopWatch stopWatch = null;
174 
175         if (_log.isDebugEnabled()) {
176             stopWatch = new StopWatch();
177 
178             stopWatch.start();
179         }
180 
181         try {
182             _invoke(req, res, true);
183         }
184         catch (PortletException pe) {
185             req.setAttribute(_portletId + PortletException.class.getName(), pe);
186         }
187 
188         if (_log.isDebugEnabled()) {
189             _log.debug(
190                 "processAction for " + _portletId + " takes " +
191                     stopWatch.getTime() + " ms");
192         }
193     }
194 
195     public void render(RenderRequest req, RenderResponse res)
196         throws IOException, PortletException {
197 
198         PortletException portletException = (PortletException)req.getAttribute(
199             _portletId + PortletException.class.getName());
200 
201         if (portletException != null) {
202             throw portletException;
203         }
204 
205         StopWatch stopWatch = null;
206 
207         if (_log.isDebugEnabled()) {
208             stopWatch = new StopWatch();
209 
210             stopWatch.start();
211         }
212 
213         String remoteUser = req.getRemoteUser();
214 
215         if ((remoteUser == null) || (_expCache == null) ||
216             (_expCache.intValue() == 0)) {
217 
218             _invoke(req, res, false);
219         }
220         else {
221             RenderResponseImpl resImpl = (RenderResponseImpl)res;
222 
223             StringServletResponse stringServletRes =
224                 (StringServletResponse)resImpl.getHttpServletResponse();
225 
226             PortletSession ses = req.getPortletSession();
227 
228             long now = System.currentTimeMillis();
229 
230             Layout layout = (Layout)req.getAttribute(WebKeys.LAYOUT);
231 
232             Map sesResponses = getResponses(ses);
233 
234             String sesResponseId = encodeResponseKey(
235                 layout.getPlid(), _portletId, LanguageUtil.getLanguageId(req));
236 
237             CachePortletResponse response =
238                 (CachePortletResponse)sesResponses.get(sesResponseId);
239 
240             if (response == null) {
241                 _invoke(req, res, false);
242 
243                 response = new CachePortletResponse(
244                     resImpl.getTitle(),
245                     stringServletRes.getString(),
246                     now + Time.SECOND * _expCache.intValue());
247 
248                 sesResponses.put(sesResponseId, response);
249             }
250             else if ((response.getTime() < now) &&
251                      (_expCache.intValue() > 0)) {
252 
253                 _invoke(req, res, false);
254 
255                 response.setTitle(resImpl.getTitle());
256                 response.setContent(stringServletRes.getString());
257                 response.setTime(now + Time.SECOND * _expCache.intValue());
258             }
259             else {
260                 resImpl.setTitle(response.getTitle());
261                 stringServletRes.getWriter().print(response.getContent());
262             }
263         }
264 
265         if (_log.isDebugEnabled()) {
266             _log.debug(
267                 "render for " + _portletId + " takes " + stopWatch.getTime() +
268                     " ms");
269         }
270     }
271 
272     public void destroy() {
273         if (_destroyable) {
274             ClassLoader contextClassLoader =
275                 Thread.currentThread().getContextClassLoader();
276 
277             ClassLoader portletClassLoader = _getPortletClassLoader();
278 
279             try {
280                 if (portletClassLoader != null) {
281                     Thread.currentThread().setContextClassLoader(
282                         portletClassLoader);
283                 }
284 
285                 _portlet.destroy();
286             }
287             finally {
288                 if (portletClassLoader != null) {
289                     Thread.currentThread().setContextClassLoader(
290                         contextClassLoader);
291                 }
292             }
293         }
294 
295         _destroyable = false;
296     }
297 
298     public Portlet getPortletInstance() {
299         return _portlet;
300     }
301 
302     public PortletConfigImpl getPortletConfig() {
303         return _portletConfig;
304     }
305 
306     public PortletContextImpl getPortletContext() {
307         return _portletCtx;
308     }
309 
310     public boolean isDestroyable() {
311         return _destroyable;
312     }
313 
314     public boolean isFacesPortlet() {
315         return _facesPortlet;
316     }
317 
318     public boolean isStrutsPortlet() {
319         return _strutsPortlet;
320     }
321 
322     public boolean isStrutsBridgePortlet() {
323         return _strutsBridgePortlet;
324     }
325 
326     private ClassLoader _getPortletClassLoader() {
327         return (ClassLoader)_portletCtx.getAttribute(
328             PortletServlet.PORTLET_CLASS_LOADER);
329     }
330 
331     private void _invoke(
332             PortletRequest req, PortletResponse res, boolean action)
333         throws IOException, PortletException {
334 
335         Map properties = null;
336 
337         if (_portletConfig.isWARFile()) {
338             String path =
339                 StringPool.SLASH + _portletConfig.getPortletName() + "/invoke";
340 
341             RequestDispatcher rd =
342                 _portletCtx.getServletContext().getRequestDispatcher(path);
343 
344             HttpServletRequest httpReq = null;
345             HttpServletResponse httpRes = null;
346 
347             ActionRequestImpl actionReqImpl = null;
348             ActionResponseImpl actionResImpl = null;
349 
350             RenderRequestImpl renderReqImpl = null;
351             RenderResponseImpl renderResImpl = null;
352 
353             if (action) {
354                 actionReqImpl = (ActionRequestImpl)req;
355                 actionResImpl = (ActionResponseImpl)res;
356 
357                 httpReq = actionReqImpl.getHttpServletRequest();
358                 httpRes = actionResImpl.getHttpServletResponse();
359             }
360             else {
361                 renderReqImpl = (RenderRequestImpl)req;
362                 renderResImpl = (RenderResponseImpl)res;
363 
364                 httpReq = renderReqImpl.getHttpServletRequest();
365                 httpRes = renderResImpl.getHttpServletResponse();
366             }
367 
368             httpReq.setAttribute(JavaConstants.JAVAX_PORTLET_PORTLET, _portlet);
369 
370             try {
371                 rd.include(httpReq, httpRes);
372             }
373             catch (ServletException se) {
374                 Throwable cause = se.getRootCause();
375 
376                 if (cause instanceof PortletException) {
377                     throw (PortletException)cause;
378                 }
379 
380                 throw new PortletException(cause);
381             }
382 
383             if (action) {
384                 properties = actionResImpl.getProperties();
385             }
386             else {
387                 properties = renderResImpl.getProperties();
388             }
389         }
390         else {
391             if (action) {
392                 ActionRequestImpl actionReqImpl = (ActionRequestImpl)req;
393                 ActionResponseImpl actionResImpl = (ActionResponseImpl)res;
394 
395                 _portlet.processAction(actionReqImpl, actionResImpl);
396 
397                 properties = actionResImpl.getProperties();
398             }
399             else {
400                 RenderRequestImpl renderReqImpl = (RenderRequestImpl)req;
401                 RenderResponseImpl renderResImpl = (RenderResponseImpl)res;
402 
403                 _portlet.render(renderReqImpl, renderResImpl);
404 
405                 properties = renderResImpl.getProperties();
406             }
407         }
408 
409         if ((properties != null) && (properties.size() > 0)) {
410             if (_expCache != null) {
411                 String[] expCache = (String[])properties.get(
412                     RenderResponse.EXPIRATION_CACHE);
413 
414                 if ((expCache != null) && (expCache.length > 0) &&
415                     (expCache[0] != null)) {
416 
417                     _expCache = new Integer(GetterUtil.getInteger(expCache[0]));
418                 }
419             }
420         }
421     }
422 
423     private static Log _log = LogFactory.getLog(CachePortlet.class);
424 
425     private String _portletId;
426     private Portlet _portlet;
427     private PortletConfigImpl _portletConfig;
428     private PortletContextImpl _portletCtx;
429     private Integer _expCache;
430     private boolean _destroyable;
431     private boolean _facesPortlet;
432     private boolean _strutsPortlet;
433     private boolean _strutsBridgePortlet;
434 
435 }