/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jetspeed.aggregator.impl;
import java.util.Map;
import java.util.HashMap;
import java.util.Collections;
import javax.portlet.UnavailableException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.jetspeed.aggregator.PortletContent;
import org.apache.jetspeed.aggregator.PortletRenderer;
import org.apache.jetspeed.aggregator.PortletTrackingManager;
import org.apache.jetspeed.aggregator.RenderingJob;
import org.apache.jetspeed.om.page.ContentFragment;
import org.apache.jetspeed.om.page.Fragment;
import org.apache.jetspeed.request.RequestContext;
import org.apache.jetspeed.statistics.PortalStatistics;
import org.apache.pluto.container.PortletContainer;
import org.apache.jetspeed.om.portlet.PortletDefinition;
import org.apache.jetspeed.container.PortletWindow;
/**
* The RenderingJob is responsible for storing all necessary objets for
* asynchronous portlet rendering as well as implementing the rendering logic
* in its Runnable method.
*
* @author <a href="mailto:raphael@apache.org">Rapha�l Luta</a>
* @author <a href="mailto:taylor@apache.org">David Sean Taylor</a>
* @author <a>Woonsan Ko</a>
* @version $Id: RenderingJobImpl.java 767444 2009-04-22 10:08:53Z ate $
*/
public class RenderingJobImpl implements RenderingJob
{
/** Commons logging */
protected final static Logger log = LoggerFactory.getLogger(RenderingJobImpl.class);
/** WorkerMonitor used to flush the queue */
protected PortletWindow window = null;
protected HttpServletRequest request = null;
protected HttpServletResponse response = null;
protected PortletContainer container = null;
protected PortletRenderer renderer = null;
protected RequestContext requestContext = null;
protected PortletTrackingManager portletTracking = null;
protected PortletDefinition portletDefinition;
protected PortalStatistics statistics;
protected int expirationCache = 0;
protected Map<String, Object> workerAttributes;
protected boolean parallel;
protected long startTimeMillis = 0;
protected long timeout;
public RenderingJobImpl(PortletContainer container,
PortletRenderer renderer,
PortletDefinition portletDefinition,
HttpServletRequest request,
HttpServletResponse response,
RequestContext requestContext,
PortletWindow window,
PortalStatistics statistics,
int expirationCache)
{
this.container = container;
this.renderer = renderer;
this.portletTracking = renderer.getPortletTrackingManager();
this.statistics = statistics;
this.portletDefinition = portletDefinition;
this.request = request;
this.response = response;
this.requestContext = requestContext;
this.window = window;
this.expirationCache = expirationCache;
}
public PortletRenderer getRenderer()
{
return renderer;
}
/**
* Sets portlet timout in milliseconds.
*/
public void setTimeout(long timeout)
{
this.timeout = timeout;
}
/**
* Gets portlet timout in milliseconds.
*/
public long getTimeout()
{
return this.timeout;
}
/**
* Checks if the portlet rendering is timeout
*/
public boolean isTimeout()
{
if ((this.timeout > 0) && (this.startTimeMillis > 0))
{
return (System.currentTimeMillis() - this.startTimeMillis > this.timeout);
}
return false;
}
/**
* Checks if queue is empty, if not try to empty it by calling
* the WorkerMonitor. When done, pause until next scheduled scan.
*/
public void run()
{
parallel = true;
boolean clearContext = requestContext.ensureThreadContext();
try
{
if (this.timeout > 0)
{
this.startTimeMillis = System.currentTimeMillis();
}
execute();
}
finally
{
if (clearContext)
{
requestContext.clearThreadContext();
}
parallel = false;
synchronized (window.getFragment().getPortletContent())
{
if (log.isDebugEnabled()) log.debug("Notifying completion of rendering job for portlet window " + this.window.getId());
window.getFragment().getPortletContent().notifyAll();
}
}
}
/**
* <p>
* execute
* </p>
*
*
*/
public void execute()
{
long start = System.currentTimeMillis();
ContentFragment fragment = this.window.getFragment();
try
{
if (log.isDebugEnabled())
{
log.debug("Rendering OID "+this.window.getId()+" "+ this.request +" "+this.response);
}
container.doRender(this.window, this.request, this.response);
}
catch (Throwable t)
{
if (t instanceof UnavailableException)
{
// no need to dump a full stack trace to the log
log.error("Error rendering portlet OID " + this.window.getId() + ": " + t.toString());
}
else
{
log.error("Error rendering portlet OID " + this.window.getId(), t);
}
fragment.overrideRenderedContent(t.getMessage());
}
finally
{
try
{
if (fragment.getType().equals(Fragment.PORTLET))
{
long end = System.currentTimeMillis();
boolean exceededTimeout = portletTracking.exceededTimeout(end - start, window);
if (statistics != null)
{
statistics.logPortletAccess(requestContext, fragment.getName(), PortalStatistics.HTTP_OK, end - start);
}
if (exceededTimeout)
{
// took too long to render
log.info("Portlet Exceeded timeout: " + this.window.getPortletDefinition().getPortletName() + " for window " + this.window.getId());
portletTracking.incrementRenderTimeoutCount(this.window);
}
else
{
portletTracking.success(this.window);
}
}
}
finally
{
synchronized (fragment.getPortletContent())
{
fragment.getPortletContent().complete();
if (fragment.getOverriddenContent() == null)
{
renderer.notifyContentComplete(requestContext, window);
}
}
}
}
}
/**
*
* <p>
* getWindow
* </p>
*
* @return The window this job is in charge of rendering
*/
public PortletWindow getWindow()
{
return window;
}
/**
*
* <p>
* getPortletContent
* </p>
*
* @return The portlet content this job is in charge of rendering
*/
public PortletContent getPortletContent()
{
return window.getFragment().getPortletContent();
}
public PortletDefinition getPortletDefinition()
{
return this.portletDefinition;
}
public HttpServletRequest getRequest()
{
return this.request;
}
public HttpServletResponse getResponse()
{
return this.response;
}
public ContentFragment getFragment()
{
return this.window.getFragment();
}
public RequestContext getRequestContext()
{
return this.requestContext;
}
public int getExpirationCache()
{
return this.expirationCache;
}
public void setWorkerAttribute(String name, Object value)
{
if (this.workerAttributes == null)
{
this.workerAttributes = Collections.synchronizedMap(new HashMap<String, Object>());
}
if (value != null)
{
this.workerAttributes.put(name, value);
}
else
{
this.workerAttributes.remove(name);
}
}
public Object getWorkerAttribute(String name)
{
Object value = null;
if (this.workerAttributes != null)
{
value = this.workerAttributes.get(name);
}
return value;
}
public void removeWorkerAttribute(String name)
{
if (this.workerAttributes != null)
{
this.workerAttributes.remove(name);
}
}
}