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