blob: b20cc567a8b673f7e9912501727bc612525fccb4 [file] [log] [blame]
/*
* 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.pluto.container.driver;
import java.io.IOException;
import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.EventPortlet;
import javax.portlet.EventRequest;
import javax.portlet.EventResponse;
import javax.portlet.Portlet;
import javax.portlet.PortletConfig;
import javax.portlet.PortletException;
import javax.portlet.PortletRequest;
import javax.portlet.PortletResponse;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.portlet.ResourceRequest;
import javax.portlet.ResourceResponse;
import javax.portlet.ResourceServingPortlet;
import javax.portlet.UnavailableException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.pluto.container.FilterManager;
import org.apache.pluto.container.PortletInvokerService;
import org.apache.pluto.container.PortletRequestContext;
import org.apache.pluto.container.PortletResponseContext;
import org.apache.pluto.container.PortletWindow;
import org.apache.pluto.container.om.portlet.PortletDefinition;
/**
* Portlet Invocation Servlet. This servlet recieves cross context requests from
* the the container and services the portlet request for the specified method.
*
* @version 1.1
* @since 09/22/2004
*/
public class PortletServlet extends HttpServlet implements PortletServletLifecycleCallback
{
private static final long serialVersionUID = -5096339022539360365L;
private static final String CLASSLOADER_CONTEXT_KEY = PortletServlet.class.getName() + ".WEBAPP_CLASSLOADER";
private static final Object REGISTRATION_MUTEX = new Object(){};
/**
* Used to support portlets that don't handle event or resource requests
*/
static class NullPortlet implements EventPortlet, ResourceServingPortlet, Portlet
{
public void processEvent(EventRequest arg0, EventResponse arg1)
throws PortletException, IOException
{
}
public void serveResource(ResourceRequest arg0, ResourceResponse arg1)
throws PortletException, IOException
{
}
public void destroy()
{
}
public void init(PortletConfig arg0) throws PortletException
{
}
public void processAction(ActionRequest arg0, ActionResponse arg1)
throws PortletException, IOException
{
}
public void render(RenderRequest arg0, RenderResponse arg1)
throws PortletException, IOException
{
}
}
// Private Member Variables ------------------------------------------------
/**
* The portlet name as defined in the portlet app descriptor.
*/
private String portletName;
/**
* The portlet instance wrapped by this servlet.
*/
private volatile Portlet portlet;
/**
* The Event Portlet instance (the same object as portlet) wrapped by this servlet.
*/
private volatile EventPortlet eventPortlet;
/**
* The Resource Portlet instance (the same object as portlet) wrapped by this servlet.
*/
private volatile ResourceServingPortlet resourceServingPortlet;
/**
* The internal portlet context instance.
*/
private volatile DriverPortletContext portletAppContext;
/**
* The internal portlet config instance.
*/
private volatile DriverPortletConfig portletContext;
/**
* Reference to the portal provided services API
*/
private volatile PortalDriverContainerServices portalDriverServices;
private volatile boolean started = false;
// HttpServlet Impl --------------------------------------------------------
public String getServletInfo()
{
return "Pluto PortletServlet [" + portletName + "]";
}
/**
* Initialize the portlet invocation servlet.
*
* @throws ServletException
* if an error occurs while loading portlet.
*/
public void init(final ServletConfig config) throws ServletException
{
started = false;
// Call the super initialization method.
super.init(config);
// Retrieve portlet name as defined as an initialization parameter.
portletName = getInitParameter("portlet-name");
//Grab a reference to the portlet's classloader and store it in the servlet context
final ClassLoader paClassLoader = Thread.currentThread().getContextClassLoader();
final ServletContext servletContext = config.getServletContext();
servletContext.setAttribute(CLASSLOADER_CONTEXT_KEY, paClassLoader);
PlutoServicesRegistry.register(portletName, config, paClassLoader, this);
}
public void registered(PortalDriverContainerServices portalDriverServices, DriverPortletContext portletContext, DriverPortletConfig portletConfig) {
//sync is to make sure that modification of the portlet/portal references is atomic
synchronized (REGISTRATION_MUTEX) {
//Get references to pluto services and portlet configuration data
this.portalDriverServices = portalDriverServices;
this.portletAppContext = portletContext;
this.portletContext = portletConfig;
final PortletDefinition portletDD = portletConfig.getPortletDefinition();
this.portlet = loadPortlet(portletDD.getPortletClass());
if (this.portlet != null) {
try {
portlet.init(portletConfig);
//Init event and resource portlet references
initializeEventPortlet();
initializeResourceServingPortlet();
}
catch (PortletException e) {
final ServletContext servletContext = this.getServletContext();
servletContext.log("Failed to initialize portlet '" + this.portletName + "' in web application " + this.getServletContext().getServletContextName(), e);
//failed to init portlet class, do cleanup
unregistered();
}
}
else {
//failed to load portlet class, do cleanup
unregistered();
}
//Mark the portlet as started
this.started = true;
}
}
protected Portlet loadPortlet(String portletClass) {
final ServletContext servletContext = this.getServletContext();
final ClassLoader paClassLoader = (ClassLoader)servletContext.getAttribute(CLASSLOADER_CONTEXT_KEY);
final Class<?> clazz;
try {
clazz = paClassLoader.loadClass(portletClass);
}
catch (ClassNotFoundException e) {
servletContext.log("Failed to load portlet-class '" + portletClass + "' for " + getPortletNameForLogging(), e);
return null;
}
try {
return (Portlet) clazz.newInstance();
}
catch (InstantiationException e) {
servletContext.log("Failed to instantiate portlet-class '" + portletClass + "' for " + getPortletNameForLogging(), e);
return null;
}
catch (IllegalAccessException e) {
servletContext.log("Failed to instantiate portlet-class '" + portletClass + "' for " + getPortletNameForLogging(), e);
return null;
}
}
public void destroy()
{
this.started = false;
final ServletConfig servletConfig = this.getServletConfig();
PlutoServicesRegistry.unregister(servletConfig, this.portletAppContext, this.portletContext, this);
}
public void unregistered() {
//sync is to make sure that modification of the portlet/portal references is atomic
synchronized (REGISTRATION_MUTEX) {
this.started = false;
try {
portlet.destroy();
}
catch (Throwable th) {
// Don't care for Exception
this.getServletContext().log("Error destroying " + getPortletNameForLogging(), th);
}
this.portalDriverServices = null;
this.portletAppContext = null;
this.portletContext = null;
this.portlet = null;
this.resourceServingPortlet = null;
this.eventPortlet = null;
super.destroy();
}
}
protected String getPortletNameForLogging() {
return "portlet '" + this.portletName + "' in web application " + this.getServletContext().getServletContextName();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
dispatch(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,
IOException {
dispatch(request, response);
}
protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
dispatch(request, response);
}
// Private Methods ---------------------------------------------------------
/**
* Dispatch the request to the appropriate portlet methods. This method
* assumes that the following attributes are set in the servlet request
* scope:
* <ul>
* <li>METHOD_ID: indicating which method to dispatch.</li>
* <li>PORTLET_REQUEST: the internal portlet request.</li>
* <li>PORTLET_RESPONSE: the internal portlet response.</li>
* </ul>
*
* @param request
* the servlet request.
* @param response
* the servlet response.
* @throws ServletException
* @throws IOException
*/
private void dispatch(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException
{
if (!started) {
throw new javax.servlet.UnavailableException(getPortletNameForLogging() + " has not been initialized", 5);
}
if (portlet == null) {
throw new javax.servlet.UnavailableException(getPortletNameForLogging() + " is unavailable, there was a problem during initialization check the container logs for more details");
}
// Retrieve attributes from the servlet request.
PortletInvokerService.Method method = (PortletInvokerService.Method) request.getAttribute(PortletInvokerService.METHOD_ID);
final PortletRequest portletRequest = (PortletRequest)request.getAttribute(PortletInvokerService.PORTLET_REQUEST);
final PortletResponse portletResponse = (PortletResponse)request.getAttribute(PortletInvokerService.PORTLET_RESPONSE);
final PortletRequestContext requestContext = (PortletRequestContext)portletRequest.getAttribute(PortletInvokerService.REQUEST_CONTEXT);
final PortletResponseContext responseContext = (PortletResponseContext)portletRequest.getAttribute(PortletInvokerService.RESPONSE_CONTEXT);
final FilterManager filterManager = (FilterManager)request.getAttribute(PortletInvokerService.FILTER_MANAGER);
request.removeAttribute(PortletInvokerService.METHOD_ID);
request.removeAttribute(PortletInvokerService.PORTLET_REQUEST);
request.removeAttribute(PortletInvokerService.PORTLET_RESPONSE);
request.removeAttribute(PortletInvokerService.FILTER_MANAGER);
requestContext.init(portletContext, getServletContext(), request, response);
responseContext.init(request, response);
PortletWindow window = requestContext.getPortletWindow();
PortletInvocationEvent event = new PortletInvocationEvent(portletRequest, window, method);
notify(event, true, null);
// FilterManager filtermanager = (FilterManager) request.getAttribute(
// "filter-manager");
try
{
switch (method) {
// The requested method is RENDER: call Portlet.render(..)
case RENDER: {
RenderRequest renderRequest = (RenderRequest) portletRequest;
RenderResponse renderResponse = (RenderResponse) portletResponse;
filterManager.processFilter(renderRequest, renderResponse, portlet, portletAppContext);
break;
}
// The requested method is RESOURCE: call
// ResourceServingPortlet.serveResource(..)
case RESOURCE: {
ResourceRequest resourceRequest = (ResourceRequest) portletRequest;
ResourceResponse resourceResponse = (ResourceResponse) portletResponse;
filterManager.processFilter(resourceRequest, resourceResponse,
resourceServingPortlet, portletAppContext);
break;
}
// The requested method is ACTION: call Portlet.processAction(..)
case ACTION: {
ActionRequest actionRequest = (ActionRequest) portletRequest;
ActionResponse actionResponse = (ActionResponse) portletResponse;
filterManager.processFilter(actionRequest, actionResponse,
portlet, portletAppContext);
break;
}
// The request methode is Event: call Portlet.processEvent(..)
case EVENT: {
EventRequest eventRequest = (EventRequest) portletRequest;
EventResponse eventResponse = (EventResponse) portletResponse;
filterManager.processFilter(eventRequest, eventResponse,
eventPortlet, portletAppContext);
break;
}
// The requested method is ADMIN: call handlers.
case ADMIN: {
PortalAdministrationService pas = this.portalDriverServices.getPortalAdministrationService();
for (AdministrativeRequestListener l : pas.getAdministrativeRequestListeners())
{
l.administer(portletRequest, portletResponse);
}
break;
}
// The requested method is LOAD: do nothing.
case LOAD: {
// Nothing to do
}
}
notify(event, false, null);
}
catch (UnavailableException e)
{
if (e.isPermanent()) {
this.destroy();
this.getServletContext().log(getPortletNameForLogging() + " threw a permanent UnavailableException, taking the portlet out of service", e);
final javax.servlet.UnavailableException se = new javax.servlet.UnavailableException(e.getMessage());
se.initCause(e); //make sure we don't lose the original stack trace
throw se;
}
//not permanent, mark the portlet unavailable for the specified time period
//Note when the unavailable time ends
final javax.servlet.UnavailableException se = new javax.servlet.UnavailableException(e.getMessage(), e.getUnavailableSeconds());
se.initCause(e); //make sure we don't lose the original stack trace
throw se;
}
catch (PortletException ex)
{
notify(event, false, ex);
throw new ServletException(getPortletNameForLogging() + " threw an exception when processing a " + method + " request", ex);
}
}
protected void notify(PortletInvocationEvent event, boolean pre, Throwable e)
{
PortalAdministrationService pas = this.portalDriverServices.getPortalAdministrationService();
for (PortletInvocationListener listener : pas.getPortletInvocationListeners())
{
if (pre)
{
listener.onBegin(event);
}
else if (e == null)
{
listener.onEnd(event);
}
else
{
listener.onError(event, e);
}
}
}
private void initializeEventPortlet()
{
if (portlet instanceof EventPortlet)
{
eventPortlet = (EventPortlet) portlet;
}
else if (portlet != null)
{
eventPortlet = new NullPortlet();
}
else
{
eventPortlet = null;
}
}
private void initializeResourceServingPortlet()
{
if (portlet instanceof ResourceServingPortlet)
{
resourceServingPortlet = (ResourceServingPortlet) portlet;
}
else if (portlet != null)
{
resourceServingPortlet = new NullPortlet();
}
else
{
resourceServingPortlet = null;
}
}
}