blob: 833cb6a19192ae8f5784b54ca691908fb963313e [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.cocoon.portal.coplet.adapter.impl;
import java.util.Map;
import java.util.Properties;
import javax.portlet.PortletMode;
import javax.portlet.WindowState;
import javax.servlet.ServletConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.activity.Initializable;
import org.apache.avalon.framework.container.ContainerUtil;
import org.apache.avalon.framework.context.Context;
import org.apache.avalon.framework.context.ContextException;
import org.apache.avalon.framework.context.Contextualizable;
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.avalon.framework.service.ServiceException;
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.cocoon.ProcessingException;
import org.apache.cocoon.components.ContextHelper;
import org.apache.cocoon.environment.http.HttpEnvironment;
import org.apache.cocoon.portal.PortalManagerAspect;
import org.apache.cocoon.portal.PortalManagerAspectPrepareContext;
import org.apache.cocoon.portal.PortalManagerAspectRenderContext;
import org.apache.cocoon.portal.PortalService;
import org.apache.cocoon.portal.coplet.CopletInstanceData;
import org.apache.cocoon.portal.event.EventManager;
import org.apache.cocoon.portal.event.Receiver;
import org.apache.cocoon.portal.pluto.PortletActionProviderImpl;
import org.apache.cocoon.portal.pluto.PortletContainerEnvironmentImpl;
import org.apache.cocoon.portal.pluto.PortletURLProviderImpl;
import org.apache.cocoon.portal.pluto.om.PortletDefinitionRegistry;
import org.apache.cocoon.portal.pluto.om.PortletEntityListImpl;
import org.apache.cocoon.portal.pluto.om.PortletWindowImpl;
import org.apache.cocoon.portal.pluto.om.common.ObjectIDImpl;
import org.apache.cocoon.portal.pluto.servlet.ServletRequestImpl;
import org.apache.cocoon.portal.pluto.servlet.ServletResponseImpl;
import org.apache.cocoon.portal.serialization.IncludingHTMLSerializer;
import org.apache.cocoon.portal.util.HtmlSaxParser;
import org.apache.cocoon.servlet.CocoonServlet;
import org.apache.cocoon.xml.AttributesImpl;
import org.apache.pluto.PortletContainer;
import org.apache.pluto.PortletContainerException;
import org.apache.pluto.PortletContainerImpl;
import org.apache.pluto.om.entity.PortletApplicationEntity;
import org.apache.pluto.om.entity.PortletEntity;
import org.apache.pluto.om.window.PortletWindow;
import org.apache.pluto.om.window.PortletWindowCtrl;
import org.apache.pluto.om.window.PortletWindowList;
import org.apache.pluto.om.window.PortletWindowListCtrl;
import org.apache.pluto.services.PortletContainerEnvironment;
import org.apache.pluto.services.information.DynamicInformationProvider;
import org.apache.pluto.services.information.InformationProviderService;
import org.apache.pluto.services.information.PortletActionProvider;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
/**
* This is the adapter to use JSR-168 portlets as coplets.
*
* @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
* @version $Id$
*/
public class PortletAdapter
extends AbstractCopletAdapter
implements Contextualizable, Initializable, PortalManagerAspect, Receiver, Disposable {
/** The avalon context */
protected Context context;
/** The servlet configuration for pluto */
protected ServletConfig servletConfig;
/** The Portlet Container */
protected PortletContainer portletContainer;
/** The Portlet Container environment */
protected PortletContainerEnvironmentImpl portletContainerEnvironment;
/**
* @see org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context)
*/
public void contextualize(Context context) throws ContextException {
this.context = context;
try {
// TODO - we could lookup the component from the adapter selector
this.servletConfig = (ServletConfig) context.get(CocoonServlet.CONTEXT_SERVLET_CONFIG);
// we have to somehow pass this component down to other components!
// This is ugly, but it's the only chance for sofisticated component containers
// that wrap component implementations!
this.servletConfig.getServletContext().setAttribute(PortletAdapter.class.getName(), this);
} catch (ContextException ignore) {
// we ignore the context exception
// this avoids startup errors if the portal is configured for the CLI
// environment
this.getLogger().warn("The JSR-168 support is disabled as the servlet context is not available.", ignore);
}
}
/**
* @see org.apache.cocoon.portal.coplet.adapter.CopletAdapter#login(org.apache.cocoon.portal.coplet.CopletInstanceData)
*/
public void login(CopletInstanceData coplet) {
super.login(coplet);
if ( this.portletContainer == null ) {
return;
}
PortletDefinitionRegistry registry = (PortletDefinitionRegistry) portletContainerEnvironment.getContainerService(PortletDefinitionRegistry.class);
final String portletEntityId = (String) getConfiguration(coplet, "portlet");
if ( this.getLogger().isDebugEnabled() ) {
this.getLogger().debug("Coplet " + coplet.getId() + " tries to login into portlet " + portletEntityId);
}
PortletApplicationEntity pae = registry.getPortletApplicationEntityList().get(ObjectIDImpl.createFromString("cocoon"));
PortletEntity portletEntity = ((PortletEntityListImpl)pae.getPortletEntityList()).add(pae, portletEntityId, coplet, registry);
if ( portletEntity.getPortletDefinition() != null ) {
// create the window
PortletWindow portletWindow = new PortletWindowImpl(portletEntityId);
((PortletWindowCtrl)portletWindow).setId(coplet.getId());
((PortletWindowCtrl)portletWindow).setPortletEntity(portletEntity);
PortletWindowList windowList = portletEntity.getPortletWindowList();
((PortletWindowListCtrl)windowList).add(portletWindow);
coplet.setTemporaryAttribute("window", portletWindow);
// load the portlet
final Map objectModel = ContextHelper.getObjectModel(this.context);
ServletRequestImpl req = (ServletRequestImpl) objectModel.get("portlet-request");
if ( req == null ) {
final HttpServletResponse res = (HttpServletResponse) objectModel.get(HttpEnvironment.HTTP_RESPONSE_OBJECT);
objectModel.put("portlet-response", new ServletResponseImpl(res));
req = new ServletRequestImpl((HttpServletRequest) objectModel.get(HttpEnvironment.HTTP_REQUEST_OBJECT), null);
objectModel.put("portlet-request", req);
}
final HttpServletResponse res = (HttpServletResponse) objectModel.get("portlet-response");
try {
this.portletContainer.portletLoad(portletWindow, req.getRequest(portletWindow),
res);
} catch (Exception e) {
this.getLogger().error("Error loading portlet " + portletEntityId + " for instance " + coplet.getId(), e);
// remove portlet entity
coplet.removeTemporaryAttribute("window");
((PortletEntityListImpl)pae.getPortletEntityList()).remove(portletEntity);
}
} else {
this.getLogger().error("Error finding portlet " + portletEntityId + " for instance " + coplet.getId() + " - no definition found.");
}
}
/**
* @see org.apache.cocoon.portal.coplet.adapter.impl.AbstractCopletAdapter#streamContent(org.apache.cocoon.portal.coplet.CopletInstanceData, org.xml.sax.ContentHandler)
*/
public void streamContent(CopletInstanceData coplet,
ContentHandler contentHandler)
throws SAXException {
if ( this.portletContainer == null ) {
throw new SAXException("Unable to execute JSR-168 portlets because of missing servlet context.");
}
try {
final String portletEntityId = (String) getConfiguration(coplet, "portlet");
// get the window
final PortletWindow window = (PortletWindow)coplet.getTemporaryAttribute("window");
if ( window == null ) {
throw new SAXException("Portlet couldn't be loaded: " + coplet.getId() + "(" + portletEntityId + ")");
}
final Map objectModel = ContextHelper.getObjectModel(this.context);
final ServletRequestImpl req = (ServletRequestImpl) objectModel.get("portlet-request");
final HttpServletResponse res = (HttpServletResponse) objectModel.get("portlet-response");
// TODO - for parallel processing we have to clone the response!
this.portletContainer.renderPortlet(window, req.getRequest(window), res);
final String value = this.getResponse(coplet, res);
final Boolean usePipeline = (Boolean)this.getConfiguration(coplet, "use-pipeline", Boolean.FALSE);
if ( usePipeline.booleanValue() ) {
HtmlSaxParser.parseString(value, HtmlSaxParser.getContentFilter(contentHandler));
} else {
// stream out the include for the serializer
IncludingHTMLSerializer.addPortlet(coplet.getId(), value);
contentHandler.startPrefixMapping("portal", IncludingHTMLSerializer.NAMESPACE);
AttributesImpl attr = new AttributesImpl();
attr.addCDATAAttribute("portlet", coplet.getId());
contentHandler.startElement(IncludingHTMLSerializer.NAMESPACE,
"include", "portal:include", attr);
contentHandler.endElement(IncludingHTMLSerializer.NAMESPACE,
"include", "portal:include");
contentHandler.endPrefixMapping("portal");
}
} catch (SAXException se) {
throw se;
} catch (Exception e) {
throw new SAXException(e);
}
}
/**
* @see org.apache.cocoon.portal.coplet.adapter.CopletAdapter#logout(org.apache.cocoon.portal.coplet.CopletInstanceData)
*/
public void logout(CopletInstanceData coplet) {
super.logout(coplet);
if ( this.portletContainer == null ) {
return;
}
PortletWindow window = (PortletWindow)coplet.getTemporaryAttribute("window");
if ( window != null ) {
coplet.removeTemporaryAttribute("window");
PortletDefinitionRegistry registry = (PortletDefinitionRegistry) portletContainerEnvironment.getContainerService(PortletDefinitionRegistry.class);
PortletApplicationEntity pae = registry.getPortletApplicationEntityList().get(ObjectIDImpl.createFromString("cocoon"));
((PortletEntityListImpl)pae.getPortletEntityList()).remove(window.getPortletEntity());
}
}
/**
* @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
*/
public void service(ServiceManager manager) throws ServiceException {
super.service(manager);
EventManager eventManager = null;
try {
eventManager = (EventManager)this.manager.lookup(EventManager.ROLE);
eventManager.subscribe(this);
} finally {
this.manager.release(eventManager);
}
}
/**
* @see org.apache.avalon.framework.activity.Disposable#dispose()
*/
public void dispose() {
if ( this.manager != null ) {
EventManager eventManager = null;
try {
eventManager = (EventManager)this.manager.lookup(EventManager.ROLE);
eventManager.unsubscribe(this);
} catch (Exception ignore) {
// let's ignore it
} finally {
this.manager.release(eventManager);
}
this.manager = null;
}
try {
if (this.portletContainer != null ) {
this.portletContainer.shutdown();
this.portletContainer = null;
}
ContainerUtil.dispose(this.portletContainerEnvironment);
this.portletContainerEnvironment = null;
} catch (Throwable t) {
this.getLogger().error("Destruction failed!", t);
}
if ( this.servletConfig != null ) {
this.servletConfig.getServletContext().removeAttribute(PortletAdapter.class.getName());
this.servletConfig = null;
}
}
/**
* @see org.apache.avalon.framework.activity.Initializable#initialize()
*/
public void initialize() throws Exception {
if ( this.servletConfig != null ) {
this.initContainer();
}
}
/**
* Return the portlet container
*/
public PortletContainer getPortletContainer() {
return this.portletContainer;
}
/**
* Return the portlet container environment
*/
public PortletContainerEnvironment getPortletContainerEnvironment() {
return this.portletContainerEnvironment;
}
/**
* Initialize the container
*/
public void initContainer() throws Exception {
this.portletContainer = new PortletContainerImpl();
if (!portletContainer.isInitialized()) {
this.getLogger().debug ("Initializing PortletContainer...");
final String uniqueContainerName = "cocoon-portal";
this.portletContainerEnvironment = new PortletContainerEnvironmentImpl();
ContainerUtil.enableLogging(this.portletContainerEnvironment, this.getLogger());
ContainerUtil.contextualize(this.portletContainerEnvironment, this.context);
ContainerUtil.service(this.portletContainerEnvironment, this.manager);
ContainerUtil.initialize(this.portletContainerEnvironment);
Properties properties = new Properties();
try {
portletContainer.init(uniqueContainerName, servletConfig, this.portletContainerEnvironment, properties);
} catch (PortletContainerException exc) {
throw new ProcessingException("Initialization of the portlet container failed.", exc);
}
} else {
this.getLogger().debug("PortletContainer already initialized.");
}
this.getLogger().debug("PortletContainer initialized.");
}
/**
* @see Receiver
*/
public void inform(PortletURLProviderImpl event, PortalService service) {
final Map objectModel = ContextHelper.getObjectModel(this.context);
final ServletRequestImpl req = new ServletRequestImpl((HttpServletRequest) objectModel.get(HttpEnvironment.HTTP_REQUEST_OBJECT), event);
final HttpServletResponse res = new ServletResponseImpl((HttpServletResponse) objectModel.get(HttpEnvironment.HTTP_RESPONSE_OBJECT));
objectModel.put("portlet-response", res);
objectModel.put("portlet-request", req);
// change portlet mode and window state
final InformationProviderService ips = (InformationProviderService)this.portletContainerEnvironment.getContainerService(InformationProviderService.class);
final DynamicInformationProvider dynProv = ips.getDynamicProvider(req);
final PortletActionProvider pap = dynProv.getPortletActionProvider(event.getPortletWindow());
final PortletMode mode = event.getPortletMode();
if ( mode != null ) {
pap.changePortletMode(mode);
}
final WindowState state = event.getWindowState();
if ( state != null ) {
pap.changePortletWindowState(state);
}
if ( event.isAction() ) {
// This means we can only have ONE portlet event per request!
objectModel.put("portlet-event", event);
} else {
((PortletActionProviderImpl)pap).changeRenderParameters(event.getParameters());
}
}
/**
* @see org.apache.cocoon.portal.PortalManagerAspect#prepare(org.apache.cocoon.portal.PortalManagerAspectPrepareContext, org.apache.cocoon.portal.PortalService)
*/
public void prepare(PortalManagerAspectPrepareContext aspectContext,
PortalService service)
throws ProcessingException {
// process the events
aspectContext.invokeNext();
// if we aren't running in a servlet environment, just skip the JSR-168 part
if ( this.servletConfig == null ) {
return;
}
// do we already have an environment?
// if not, create one
final Map objectModel = aspectContext.getObjectModel();
PortletURLProviderImpl event = (PortletURLProviderImpl) objectModel.get("portlet-event");
if ( event != null ) {
PortletWindow actionWindow = event.getPortletWindow();
try {
final ServletRequestImpl req = (ServletRequestImpl) objectModel.get("portlet-request");
final ServletResponseImpl res= (ServletResponseImpl)objectModel.get("portlet-response");
this.portletContainer.processPortletAction(actionWindow, req.getRequest(actionWindow), res);
} catch (Exception ignore) {
this.getLogger().error("Error during processing of portlet action.", ignore);
}
} else if ( objectModel.get("portlet-response") == null ) {
final HttpServletResponse res = (HttpServletResponse) objectModel.get(HttpEnvironment.HTTP_RESPONSE_OBJECT);
objectModel.put("portlet-response", new ServletResponseImpl(res));
final ServletRequestImpl req = new ServletRequestImpl((HttpServletRequest) objectModel.get(HttpEnvironment.HTTP_REQUEST_OBJECT), null);
objectModel.put("portlet-request", req);
}
}
/**
* @see org.apache.cocoon.portal.PortalManagerAspect#render(org.apache.cocoon.portal.PortalManagerAspectRenderContext, org.apache.cocoon.portal.PortalService, org.xml.sax.ContentHandler, org.apache.avalon.framework.parameters.Parameters)
*/
public void render(PortalManagerAspectRenderContext aspectContext,
PortalService service,
ContentHandler ch,
Parameters parameters)
throws SAXException {
final Map objectModel = aspectContext.getObjectModel();
// don't generate a response, if we issued a redirect
if (objectModel.remove("portlet-event") == null) {
aspectContext.invokeNext(ch, parameters);
}
}
protected String getResponse(CopletInstanceData instance, HttpServletResponse response) {
return response.toString();
}
}