View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.jetspeed.aggregator.impl;
19  
20  import java.util.Iterator;
21  import java.util.Map;
22  import java.util.HashMap;
23  import java.util.Collection;
24  import java.util.Collections;
25  import java.util.Arrays;
26  
27  import javax.portlet.UnavailableException;
28  import javax.servlet.ServletRequest;
29  import javax.servlet.http.HttpServletRequest;
30  import javax.servlet.http.HttpServletResponse;
31  import javax.servlet.http.HttpServletRequestWrapper;
32  
33  import org.apache.commons.logging.Log;
34  import org.apache.commons.logging.LogFactory;
35  import org.apache.jetspeed.PortalReservedParameters;
36  import org.apache.jetspeed.aggregator.ContentDispatcherCtrl;
37  import org.apache.jetspeed.aggregator.CurrentWorkerContext;
38  import org.apache.jetspeed.aggregator.PortletContent;
39  import org.apache.jetspeed.aggregator.PortletRenderer;
40  import org.apache.jetspeed.aggregator.PortletTrackingManager;
41  import org.apache.jetspeed.aggregator.RenderingJob;
42  import org.apache.jetspeed.components.portletentity.PortletEntityImpl;
43  import org.apache.jetspeed.om.common.portlet.MutablePortletEntity;
44  import org.apache.jetspeed.om.page.ContentFragment;
45  import org.apache.jetspeed.request.RequestContext;
46  import org.apache.jetspeed.statistics.PortalStatistics;
47  import org.apache.pluto.PortletContainer;
48  import org.apache.pluto.om.portlet.PortletDefinition;
49  import org.apache.pluto.om.window.PortletWindow;
50  
51  /***
52   * The RenderingJob is responsible for storing all necessary objets for
53   * asynchronous portlet rendering as well as implementing the rendering logic
54   * in its Runnable method.
55   *
56   * @author <a href="mailto:raphael@apache.org">Rapha?l Luta</a>
57   * @author <a href="mailto:taylor@apache.org">David Sean Taylor</a>
58   * @author <a>Woonsan Ko</a>
59   * @version $Id: RenderingJobImpl.java 592263 2007-11-06 04:19:20Z woonsan $
60   */
61  public class RenderingJobImpl implements RenderingJob
62  {
63      /*** Commons logging */
64      protected final static Log log = LogFactory.getLog(RenderingJobImpl.class);
65  
66      /*** WorkerMonitor used to flush the queue */
67      protected PortletWindow window = null;
68      protected HttpServletRequest request = null;
69      protected HttpServletResponse response = null;
70      
71      protected PortletContainer container = null;
72      protected PortletRenderer renderer = null;
73      protected ContentFragment fragment = null;
74      protected RequestContext requestContext = null;
75      protected PortletTrackingManager portletTracking = null;
76  
77      protected PortletDefinition portletDefinition;
78      protected PortletContent portletContent;
79      protected PortalStatistics statistics;
80      protected ContentDispatcherCtrl dispatcher;
81      protected boolean contentIsCached;
82      
83      protected int expirationCache = 0;
84  
85      protected Map workerAttributes;
86  
87      protected long startTimeMillis = 0;
88      protected long timeout;
89      
90      public RenderingJobImpl(PortletContainer container,
91                              PortletRenderer renderer,
92                              PortletDefinition portletDefinition,
93                              PortletContent portletContent, 
94                              ContentFragment fragment, 
95                              ContentDispatcherCtrl dispatcher,
96                              HttpServletRequest request, 
97                              HttpServletResponse response, 
98                              RequestContext requestContext, 
99                              PortletWindow window,
100                             PortalStatistics statistics,
101                             int expirationCache,
102                             boolean contentIsCached)
103     {
104         this.container = container;
105         this.renderer = renderer;
106         this.portletTracking = renderer.getPortletTrackingManager();        
107         this.statistics = statistics;
108         this.portletDefinition = portletDefinition;
109         this.fragment = fragment;
110         this.dispatcher = dispatcher;
111         this.request = request;
112         this.response = response;
113         this.requestContext = requestContext; 
114         this.window = window;
115         this.portletContent = portletContent; 
116         ((MutablePortletEntity)window.getPortletEntity()).setFragment(fragment);
117         this.expirationCache = expirationCache;
118         this.contentIsCached = contentIsCached;
119     }
120 
121     public RenderingJobImpl(PortletContainer container, 
122                             PortletRenderer renderer,
123                             PortletDefinition portletDefinition,
124                             PortletContent portletContent, 
125                             ContentFragment fragment,
126                             ContentDispatcherCtrl dispatcher,
127                             HttpServletRequest request, 
128                             HttpServletResponse response, 
129                             RequestContext requestContext, 
130                             PortletWindow window,
131                             PortalStatistics statistics,
132                             int expirationCache,
133                             boolean contentIsCached,
134                             Map workerAttrs)
135     {
136         this(container, renderer, portletDefinition, portletContent, fragment, dispatcher,
137                         request, response, requestContext, window, statistics, expirationCache, contentIsCached);
138         
139         if (workerAttrs != null)
140         {
141             this.workerAttributes = Collections.synchronizedMap(workerAttrs);
142         }
143     }
144 
145     /***
146      * Sets portlet timout in milliseconds.
147      */
148     public void setTimeout(long timeout) {
149         this.timeout = timeout;
150     }
151 
152     /***
153      * Gets portlet timout in milliseconds.
154      */
155     public long getTimeout() {
156         return this.timeout;
157     }
158 
159     /***
160      * Checks if the portlet rendering is timeout
161      */
162     public boolean isTimeout() {
163         if ((this.timeout > 0) && (this.startTimeMillis > 0)) {
164             return (System.currentTimeMillis() - this.startTimeMillis > this.timeout);
165         }
166 
167         return false;
168     }
169 
170     /***
171      * Checks if queue is empty, if not try to empty it by calling
172      * the WorkerMonitor. When done, pause until next scheduled scan.
173      */
174     public void run()
175     {       
176         try
177         {
178             if (this.timeout > 0) 
179             {
180                 CurrentWorkerContext.setParallelRenderingMode(true);
181                 this.startTimeMillis = System.currentTimeMillis();
182             }
183 
184             // A little baby hack to make sure the worker thread has PortletContent to write too.
185             fragment.setPortletContent(portletContent);
186             execute();                     
187         }
188         finally
189         {
190             synchronized (portletContent)
191             {
192                if (log.isDebugEnabled()) log.debug("Notifying completion of rendering job for fragment " + fragment.getId());                
193                portletContent.notifyAll();
194             }
195         }
196     }
197     
198     /***
199      * <p>
200      * execute
201      * </p>
202      *
203      * 
204      */
205     public void execute()
206     {
207         long start = System.currentTimeMillis();
208         boolean isParallelMode = false;
209         PortletWindow curWindow = this.window;
210         try
211         {
212             if (log.isDebugEnabled()) log.debug("Rendering OID "+this.window.getId()+" "+ this.request +" "+this.response);
213 
214             // if the current thread is worker, then store attribues in that.
215             if (this.workerAttributes != null)
216             {
217                 isParallelMode = CurrentWorkerContext.getParallelRenderingMode();
218                 if (isParallelMode)
219                 {
220                     Collection attrNames = Arrays.asList(this.workerAttributes.keySet().toArray());
221                     
222                     Iterator itAttrNames = attrNames.iterator();
223                     while (itAttrNames.hasNext()) 
224                     {
225                         String name = (String) itAttrNames.next();
226                         CurrentWorkerContext.setAttribute(name, this.workerAttributes.get(name));
227                     }
228                     
229                     // The portletEntity stores its portletDefinition into the ThreadLocal member,
230                     // before the worker starts doing a rendering job.
231                     // So the thread contexts are different from each other.
232                     // Therefore, in parallel mode, we have to clear threadlocal fragmentPortletDefinition cache
233                     // of portletEntity and to replace the portletDefinition with one of current worker context.
234                     // Refer to org.apache.jetspeed.components.portletentity.PortletEntityImpl class
235                     
236                     curWindow = (PortletWindow) 
237                         CurrentWorkerContext.getAttribute(PortalReservedParameters.PORTLET_WINDOW_ATTRIBUTE); 
238                     PortletEntityImpl curEntity = (PortletEntityImpl) curWindow.getPortletEntity();
239                     PortletDefinition oldPortletDefinition = curEntity.getPortletDefinition();
240                     PortletDefinition curPortletDefinition = (PortletDefinition)
241                         CurrentWorkerContext.getAttribute(PortalReservedParameters.PORTLET_DEFINITION_ATTRIBUTE);
242                     
243                     if (!oldPortletDefinition.getId().equals(curPortletDefinition.getId())) {
244                         curEntity.setPortletDefinition(curPortletDefinition);
245                     }
246                 }
247             }
248             
249             if (isParallelMode)
250             {
251                 ServletRequest servletRequest = ((HttpServletRequestWrapper)((HttpServletRequestWrapper) this.request).getRequest()).getRequest();
252                 
253                 synchronized (servletRequest)
254                 {
255                     this.request.setAttribute(PortalReservedParameters.FRAGMENT_ATTRIBUTE, fragment);
256                     this.request.setAttribute(PortalReservedParameters.PAGE_ATTRIBUTE, requestContext.getPage());
257                     this.request.setAttribute(PortalReservedParameters.REQUEST_CONTEXT_ATTRIBUTE, requestContext);
258                     this.request.setAttribute(PortalReservedParameters.REQUEST_CONTEXT_OBJECTS, requestContext.getObjects());            
259                   //  this.request.setAttribute(PortalReservedParameters.CONTENT_DISPATCHER_ATTRIBUTE,dispatcher);
260                 }
261             }
262             else
263             {
264                 this.request.setAttribute(PortalReservedParameters.FRAGMENT_ATTRIBUTE, fragment);
265                 this.request.setAttribute(PortalReservedParameters.PAGE_ATTRIBUTE, requestContext.getPage());
266                 this.request.setAttribute(PortalReservedParameters.REQUEST_CONTEXT_ATTRIBUTE, requestContext);
267                 this.request.setAttribute(PortalReservedParameters.REQUEST_CONTEXT_OBJECTS, requestContext.getObjects());            
268               //  this.request.setAttribute(PortalReservedParameters.CONTENT_DISPATCHER_ATTRIBUTE,dispatcher);
269             }
270             
271             container.renderPortlet(this.window, this.request, this.response);               
272             this.response.flushBuffer();                           
273         }
274         catch (Throwable t)
275         {
276             if (t instanceof UnavailableException)
277             {
278                 // no need to dump a full stack trace to the log
279                 log.error("Error rendering portlet OID " + curWindow.getId() + ": " + t.toString());
280             }
281             else
282             {
283                 log.error("Error rendering portlet OID " + curWindow.getId(), t);
284             }
285             fragment.overrideRenderedContent(t.getMessage());
286         }
287         finally
288         {
289             try
290             {
291                 if (isParallelMode)
292                 {
293                     this.renderer.addTitleToHeader(curWindow, fragment,
294                                                    this.request, this.response,
295                                                    this.dispatcher, this.contentIsCached);
296                 
297                     CurrentWorkerContext.removeAllAttributes();
298                 }
299                 
300                 if (fragment.getType().equals(ContentFragment.PORTLET))
301                 {
302                     long end = System.currentTimeMillis();
303                     boolean exceededTimeout = portletTracking.exceededTimeout(end - start, window);
304                     
305                     if (statistics != null)
306                     {
307                         statistics.logPortletAccess(requestContext, fragment.getName(), PortalStatistics.HTTP_OK, end - start);
308                     }
309                     if (exceededTimeout)
310                     {
311                         // took too long to render
312                         log.info("Portlet Exceeded timeout: " + curWindow.getPortletEntity().getPortletDefinition().getName() + " for window " + curWindow.getId());
313                         portletTracking.incrementRenderTimeoutCount(curWindow);
314                     }
315                     else
316                     {
317                         portletTracking.success(curWindow);
318                     }
319                 }
320             }
321             finally
322             {
323                 synchronized (portletContent)
324                 {
325                     if (fragment.getOverriddenContent() != null)
326                     {
327                         portletContent.completeWithError();
328                     }
329                     else
330                     {
331                         portletContent.complete();
332                     }
333                 }
334             }
335         }
336     }
337  
338     /***
339      * 
340      * <p>
341      * getWindow
342      * </p>
343      *
344      * @return The window this job is in charge of rendering
345      */
346     public PortletWindow getWindow()
347     {
348         return window;
349     }
350 
351     /***
352      * 
353      * <p>
354      * getPortletContent
355      * </p>
356      *
357      * @return The portlet content this job is in charge of rendering
358      */
359     public PortletContent getPortletContent()
360     {
361         return portletContent;
362     }
363 
364     public PortletDefinition getPortletDefinition()
365     {
366         return this.portletDefinition;
367     }
368 
369     public HttpServletRequest getRequest()
370     {
371         return this.request;
372     }
373 
374     public HttpServletResponse getResponse()
375     {
376         return this.response;
377     }
378 
379     public ContentFragment getFragment()
380     {
381         return this.fragment;
382     }
383 
384     public RequestContext getRequestContext()
385     {
386         return this.requestContext;
387     }
388 
389     public int getExpirationCache()
390     {
391         return this.expirationCache;
392     }
393 
394     public ContentDispatcherCtrl getDispatcher()
395     {
396         return this.dispatcher;
397     }
398 
399     public boolean isContentCached() 
400     {
401         return this.contentIsCached;
402     }
403     
404     public void setWorkerAttribute(String name, Object value)
405     {
406         if (this.workerAttributes == null)
407         {
408             this.workerAttributes = Collections.synchronizedMap(new HashMap());
409         }
410         
411         if (value != null)
412         {
413             this.workerAttributes.put(name, value);
414         }
415         else
416         {
417             this.workerAttributes.remove(name);
418         }
419     }
420     
421     public Object getWorkerAttribute(String name)
422     {
423         Object value = null;
424         
425         if (this.workerAttributes != null)
426         {
427             value = this.workerAttributes.get(name);
428         }
429         
430         return value;
431     }
432     
433     public void removeWorkerAttribute(String name)
434     {
435         if (this.workerAttributes != null)
436         {
437             this.workerAttributes.remove(name);
438         }
439     }
440 }