| /* |
| * 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(); |
| } |
| } |