001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.jetspeed.aggregator.impl;
019:
020: import java.util.Iterator;
021: import java.util.Map;
022: import java.util.HashMap;
023: import java.util.Collection;
024: import java.util.Collections;
025: import java.util.Arrays;
026:
027: import javax.portlet.UnavailableException;
028: import javax.servlet.ServletRequest;
029: import javax.servlet.http.HttpServletRequest;
030: import javax.servlet.http.HttpServletResponse;
031: import javax.servlet.http.HttpServletRequestWrapper;
032:
033: import org.apache.commons.logging.Log;
034: import org.apache.commons.logging.LogFactory;
035: import org.apache.jetspeed.PortalReservedParameters;
036: import org.apache.jetspeed.aggregator.ContentDispatcherCtrl;
037: import org.apache.jetspeed.aggregator.CurrentWorkerContext;
038: import org.apache.jetspeed.aggregator.PortletContent;
039: import org.apache.jetspeed.aggregator.PortletRenderer;
040: import org.apache.jetspeed.aggregator.PortletTrackingManager;
041: import org.apache.jetspeed.aggregator.RenderingJob;
042: import org.apache.jetspeed.components.portletentity.PortletEntityImpl;
043: import org.apache.jetspeed.om.common.portlet.MutablePortletEntity;
044: import org.apache.jetspeed.om.page.ContentFragment;
045: import org.apache.jetspeed.request.RequestContext;
046: import org.apache.jetspeed.statistics.PortalStatistics;
047: import org.apache.pluto.PortletContainer;
048: import org.apache.pluto.om.portlet.PortletDefinition;
049: import org.apache.pluto.om.window.PortletWindow;
050:
051: /**
052: * The RenderingJob is responsible for storing all necessary objets for
053: * asynchronous portlet rendering as well as implementing the rendering logic
054: * in its Runnable method.
055: *
056: * @author <a href="mailto:raphael@apache.org">Rapha�l Luta</a>
057: * @author <a href="mailto:taylor@apache.org">David Sean Taylor</a>
058: * @author <a>Woonsan Ko</a>
059: * @version $Id: RenderingJobImpl.java 592263 2007-11-06 04:19:20Z woonsan $
060: */
061: public class RenderingJobImpl implements RenderingJob {
062: /** Commons logging */
063: protected final static Log log = LogFactory
064: .getLog(RenderingJobImpl.class);
065:
066: /** WorkerMonitor used to flush the queue */
067: protected PortletWindow window = null;
068: protected HttpServletRequest request = null;
069: protected HttpServletResponse response = null;
070:
071: protected PortletContainer container = null;
072: protected PortletRenderer renderer = null;
073: protected ContentFragment fragment = null;
074: protected RequestContext requestContext = null;
075: protected PortletTrackingManager portletTracking = null;
076:
077: protected PortletDefinition portletDefinition;
078: protected PortletContent portletContent;
079: protected PortalStatistics statistics;
080: protected ContentDispatcherCtrl dispatcher;
081: protected boolean contentIsCached;
082:
083: protected int expirationCache = 0;
084:
085: protected Map workerAttributes;
086:
087: protected long startTimeMillis = 0;
088: protected long timeout;
089:
090: public RenderingJobImpl(PortletContainer container,
091: PortletRenderer renderer,
092: PortletDefinition portletDefinition,
093: PortletContent portletContent, ContentFragment fragment,
094: ContentDispatcherCtrl dispatcher,
095: HttpServletRequest request, HttpServletResponse response,
096: RequestContext requestContext, PortletWindow window,
097: PortalStatistics statistics, int expirationCache,
098: boolean contentIsCached) {
099: this .container = container;
100: this .renderer = renderer;
101: this .portletTracking = renderer.getPortletTrackingManager();
102: this .statistics = statistics;
103: this .portletDefinition = portletDefinition;
104: this .fragment = fragment;
105: this .dispatcher = dispatcher;
106: this .request = request;
107: this .response = response;
108: this .requestContext = requestContext;
109: this .window = window;
110: this .portletContent = portletContent;
111: ((MutablePortletEntity) window.getPortletEntity())
112: .setFragment(fragment);
113: this .expirationCache = expirationCache;
114: this .contentIsCached = contentIsCached;
115: }
116:
117: public RenderingJobImpl(PortletContainer container,
118: PortletRenderer renderer,
119: PortletDefinition portletDefinition,
120: PortletContent portletContent, ContentFragment fragment,
121: ContentDispatcherCtrl dispatcher,
122: HttpServletRequest request, HttpServletResponse response,
123: RequestContext requestContext, PortletWindow window,
124: PortalStatistics statistics, int expirationCache,
125: boolean contentIsCached, Map workerAttrs) {
126: this (container, renderer, portletDefinition, portletContent,
127: fragment, dispatcher, request, response,
128: requestContext, window, statistics, expirationCache,
129: contentIsCached);
130:
131: if (workerAttrs != null) {
132: this .workerAttributes = Collections
133: .synchronizedMap(workerAttrs);
134: }
135: }
136:
137: /**
138: * Sets portlet timout in milliseconds.
139: */
140: public void setTimeout(long timeout) {
141: this .timeout = timeout;
142: }
143:
144: /**
145: * Gets portlet timout in milliseconds.
146: */
147: public long getTimeout() {
148: return this .timeout;
149: }
150:
151: /**
152: * Checks if the portlet rendering is timeout
153: */
154: public boolean isTimeout() {
155: if ((this .timeout > 0) && (this .startTimeMillis > 0)) {
156: return (System.currentTimeMillis() - this .startTimeMillis > this .timeout);
157: }
158:
159: return false;
160: }
161:
162: /**
163: * Checks if queue is empty, if not try to empty it by calling
164: * the WorkerMonitor. When done, pause until next scheduled scan.
165: */
166: public void run() {
167: try {
168: if (this .timeout > 0) {
169: CurrentWorkerContext.setParallelRenderingMode(true);
170: this .startTimeMillis = System.currentTimeMillis();
171: }
172:
173: // A little baby hack to make sure the worker thread has PortletContent to write too.
174: fragment.setPortletContent(portletContent);
175: execute();
176: } finally {
177: synchronized (portletContent) {
178: if (log.isDebugEnabled())
179: log
180: .debug("Notifying completion of rendering job for fragment "
181: + fragment.getId());
182: portletContent.notifyAll();
183: }
184: }
185: }
186:
187: /**
188: * <p>
189: * execute
190: * </p>
191: *
192: *
193: */
194: public void execute() {
195: long start = System.currentTimeMillis();
196: boolean isParallelMode = false;
197: PortletWindow curWindow = this .window;
198: try {
199: if (log.isDebugEnabled())
200: log.debug("Rendering OID " + this .window.getId() + " "
201: + this .request + " " + this .response);
202:
203: // if the current thread is worker, then store attribues in that.
204: if (this .workerAttributes != null) {
205: isParallelMode = CurrentWorkerContext
206: .getParallelRenderingMode();
207: if (isParallelMode) {
208: Collection attrNames = Arrays
209: .asList(this .workerAttributes.keySet()
210: .toArray());
211:
212: Iterator itAttrNames = attrNames.iterator();
213: while (itAttrNames.hasNext()) {
214: String name = (String) itAttrNames.next();
215: CurrentWorkerContext.setAttribute(name,
216: this .workerAttributes.get(name));
217: }
218:
219: // The portletEntity stores its portletDefinition into the ThreadLocal member,
220: // before the worker starts doing a rendering job.
221: // So the thread contexts are different from each other.
222: // Therefore, in parallel mode, we have to clear threadlocal fragmentPortletDefinition cache
223: // of portletEntity and to replace the portletDefinition with one of current worker context.
224: // Refer to org.apache.jetspeed.components.portletentity.PortletEntityImpl class
225:
226: curWindow = (PortletWindow) CurrentWorkerContext
227: .getAttribute(PortalReservedParameters.PORTLET_WINDOW_ATTRIBUTE);
228: PortletEntityImpl curEntity = (PortletEntityImpl) curWindow
229: .getPortletEntity();
230: PortletDefinition oldPortletDefinition = curEntity
231: .getPortletDefinition();
232: PortletDefinition curPortletDefinition = (PortletDefinition) CurrentWorkerContext
233: .getAttribute(PortalReservedParameters.PORTLET_DEFINITION_ATTRIBUTE);
234:
235: if (!oldPortletDefinition.getId().equals(
236: curPortletDefinition.getId())) {
237: curEntity
238: .setPortletDefinition(curPortletDefinition);
239: }
240: }
241: }
242:
243: if (isParallelMode) {
244: ServletRequest servletRequest = ((HttpServletRequestWrapper) ((HttpServletRequestWrapper) this .request)
245: .getRequest()).getRequest();
246:
247: synchronized (servletRequest) {
248: this .request
249: .setAttribute(
250: PortalReservedParameters.FRAGMENT_ATTRIBUTE,
251: fragment);
252: this .request.setAttribute(
253: PortalReservedParameters.PAGE_ATTRIBUTE,
254: requestContext.getPage());
255: this .request
256: .setAttribute(
257: PortalReservedParameters.REQUEST_CONTEXT_ATTRIBUTE,
258: requestContext);
259: this .request
260: .setAttribute(
261: PortalReservedParameters.REQUEST_CONTEXT_OBJECTS,
262: requestContext.getObjects());
263: // this.request.setAttribute(PortalReservedParameters.CONTENT_DISPATCHER_ATTRIBUTE,dispatcher);
264: }
265: } else {
266: this .request.setAttribute(
267: PortalReservedParameters.FRAGMENT_ATTRIBUTE,
268: fragment);
269: this .request.setAttribute(
270: PortalReservedParameters.PAGE_ATTRIBUTE,
271: requestContext.getPage());
272: this .request
273: .setAttribute(
274: PortalReservedParameters.REQUEST_CONTEXT_ATTRIBUTE,
275: requestContext);
276: this .request
277: .setAttribute(
278: PortalReservedParameters.REQUEST_CONTEXT_OBJECTS,
279: requestContext.getObjects());
280: // this.request.setAttribute(PortalReservedParameters.CONTENT_DISPATCHER_ATTRIBUTE,dispatcher);
281: }
282:
283: container.renderPortlet(this .window, this .request,
284: this .response);
285: this .response.flushBuffer();
286: } catch (Throwable t) {
287: if (t instanceof UnavailableException) {
288: // no need to dump a full stack trace to the log
289: log.error("Error rendering portlet OID "
290: + curWindow.getId() + ": " + t.toString());
291: } else {
292: log.error("Error rendering portlet OID "
293: + curWindow.getId(), t);
294: }
295: fragment.overrideRenderedContent(t.getMessage());
296: } finally {
297: try {
298: if (isParallelMode) {
299: this .renderer.addTitleToHeader(curWindow, fragment,
300: this .request, this .response,
301: this .dispatcher, this .contentIsCached);
302:
303: CurrentWorkerContext.removeAllAttributes();
304: }
305:
306: if (fragment.getType().equals(ContentFragment.PORTLET)) {
307: long end = System.currentTimeMillis();
308: boolean exceededTimeout = portletTracking
309: .exceededTimeout(end - start, window);
310:
311: if (statistics != null) {
312: statistics.logPortletAccess(requestContext,
313: fragment.getName(),
314: PortalStatistics.HTTP_OK, end - start);
315: }
316: if (exceededTimeout) {
317: // took too long to render
318: log.info("Portlet Exceeded timeout: "
319: + curWindow.getPortletEntity()
320: .getPortletDefinition()
321: .getName() + " for window "
322: + curWindow.getId());
323: portletTracking
324: .incrementRenderTimeoutCount(curWindow);
325: } else {
326: portletTracking.success(curWindow);
327: }
328: }
329: } finally {
330: synchronized (portletContent) {
331: if (fragment.getOverriddenContent() != null) {
332: portletContent.completeWithError();
333: } else {
334: portletContent.complete();
335: }
336: }
337: }
338: }
339: }
340:
341: /**
342: *
343: * <p>
344: * getWindow
345: * </p>
346: *
347: * @return The window this job is in charge of rendering
348: */
349: public PortletWindow getWindow() {
350: return window;
351: }
352:
353: /**
354: *
355: * <p>
356: * getPortletContent
357: * </p>
358: *
359: * @return The portlet content this job is in charge of rendering
360: */
361: public PortletContent getPortletContent() {
362: return portletContent;
363: }
364:
365: public PortletDefinition getPortletDefinition() {
366: return this .portletDefinition;
367: }
368:
369: public HttpServletRequest getRequest() {
370: return this .request;
371: }
372:
373: public HttpServletResponse getResponse() {
374: return this .response;
375: }
376:
377: public ContentFragment getFragment() {
378: return this .fragment;
379: }
380:
381: public RequestContext getRequestContext() {
382: return this .requestContext;
383: }
384:
385: public int getExpirationCache() {
386: return this .expirationCache;
387: }
388:
389: public ContentDispatcherCtrl getDispatcher() {
390: return this .dispatcher;
391: }
392:
393: public boolean isContentCached() {
394: return this .contentIsCached;
395: }
396:
397: public void setWorkerAttribute(String name, Object value) {
398: if (this .workerAttributes == null) {
399: this .workerAttributes = Collections
400: .synchronizedMap(new HashMap());
401: }
402:
403: if (value != null) {
404: this .workerAttributes.put(name, value);
405: } else {
406: this .workerAttributes.remove(name);
407: }
408: }
409:
410: public Object getWorkerAttribute(String name) {
411: Object value = null;
412:
413: if (this .workerAttributes != null) {
414: value = this .workerAttributes.get(name);
415: }
416:
417: return value;
418: }
419:
420: public void removeWorkerAttribute(String name) {
421: if (this.workerAttributes != null) {
422: this.workerAttributes.remove(name);
423: }
424: }
425: }
|