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: package org.apache.cocoon.portal.coplet.adapter.impl;
018:
019: import java.util.Map;
020: import java.util.Properties;
021:
022: import javax.portlet.PortletMode;
023: import javax.portlet.WindowState;
024: import javax.servlet.ServletConfig;
025: import javax.servlet.http.HttpServletRequest;
026: import javax.servlet.http.HttpServletResponse;
027:
028: import org.apache.avalon.framework.activity.Disposable;
029: import org.apache.avalon.framework.activity.Initializable;
030: import org.apache.avalon.framework.container.ContainerUtil;
031: import org.apache.avalon.framework.context.Context;
032: import org.apache.avalon.framework.context.ContextException;
033: import org.apache.avalon.framework.context.Contextualizable;
034: import org.apache.avalon.framework.parameters.Parameters;
035: import org.apache.avalon.framework.service.ServiceException;
036: import org.apache.avalon.framework.service.ServiceManager;
037: import org.apache.cocoon.ProcessingException;
038: import org.apache.cocoon.components.ContextHelper;
039: import org.apache.cocoon.environment.http.HttpEnvironment;
040: import org.apache.cocoon.portal.PortalManagerAspect;
041: import org.apache.cocoon.portal.PortalManagerAspectPrepareContext;
042: import org.apache.cocoon.portal.PortalManagerAspectRenderContext;
043: import org.apache.cocoon.portal.PortalService;
044: import org.apache.cocoon.portal.coplet.CopletInstanceData;
045: import org.apache.cocoon.portal.event.EventManager;
046: import org.apache.cocoon.portal.event.Receiver;
047: import org.apache.cocoon.portal.pluto.PortletActionProviderImpl;
048: import org.apache.cocoon.portal.pluto.PortletContainerEnvironmentImpl;
049: import org.apache.cocoon.portal.pluto.PortletURLProviderImpl;
050: import org.apache.cocoon.portal.pluto.om.PortletDefinitionRegistry;
051: import org.apache.cocoon.portal.pluto.om.PortletEntityListImpl;
052: import org.apache.cocoon.portal.pluto.om.PortletWindowImpl;
053: import org.apache.cocoon.portal.pluto.om.common.ObjectIDImpl;
054: import org.apache.cocoon.portal.pluto.servlet.ServletRequestImpl;
055: import org.apache.cocoon.portal.pluto.servlet.ServletResponseImpl;
056: import org.apache.cocoon.portal.serialization.IncludingHTMLSerializer;
057: import org.apache.cocoon.portal.util.HtmlSaxParser;
058: import org.apache.cocoon.servlet.CocoonServlet;
059: import org.apache.cocoon.xml.AttributesImpl;
060: import org.apache.pluto.PortletContainer;
061: import org.apache.pluto.PortletContainerException;
062: import org.apache.pluto.PortletContainerImpl;
063: import org.apache.pluto.om.entity.PortletApplicationEntity;
064: import org.apache.pluto.om.entity.PortletEntity;
065: import org.apache.pluto.om.window.PortletWindow;
066: import org.apache.pluto.om.window.PortletWindowCtrl;
067: import org.apache.pluto.om.window.PortletWindowList;
068: import org.apache.pluto.om.window.PortletWindowListCtrl;
069: import org.apache.pluto.services.PortletContainerEnvironment;
070: import org.apache.pluto.services.information.DynamicInformationProvider;
071: import org.apache.pluto.services.information.InformationProviderService;
072: import org.apache.pluto.services.information.PortletActionProvider;
073: import org.xml.sax.ContentHandler;
074: import org.xml.sax.SAXException;
075:
076: /**
077: * This is the adapter to use JSR-168 portlets as coplets.
078: *
079: * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
080: * @version $Id: PortletAdapter.java 433543 2006-08-22 06:22:54Z crossley $
081: */
082: public class PortletAdapter extends AbstractCopletAdapter implements
083: Contextualizable, Initializable, PortalManagerAspect, Receiver,
084: Disposable {
085:
086: /** The avalon context */
087: protected Context context;
088:
089: /** The servlet configuration for pluto */
090: protected ServletConfig servletConfig;
091:
092: /** The Portlet Container */
093: protected PortletContainer portletContainer;
094:
095: /** The Portlet Container environment */
096: protected PortletContainerEnvironmentImpl portletContainerEnvironment;
097:
098: /**
099: * @see org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context)
100: */
101: public void contextualize(Context context) throws ContextException {
102: this .context = context;
103: try {
104: // TODO - we could lookup the component from the adapter selector
105: this .servletConfig = (ServletConfig) context
106: .get(CocoonServlet.CONTEXT_SERVLET_CONFIG);
107: // we have to somehow pass this component down to other components!
108: // This is ugly, but it's the only chance for sofisticated component containers
109: // that wrap component implementations!
110: this .servletConfig.getServletContext().setAttribute(
111: PortletAdapter.class.getName(), this );
112: } catch (ContextException ignore) {
113: // we ignore the context exception
114: // this avoids startup errors if the portal is configured for the CLI
115: // environment
116: this
117: .getLogger()
118: .warn(
119: "The JSR-168 support is disabled as the servlet context is not available.",
120: ignore);
121: }
122: }
123:
124: /**
125: * @see org.apache.cocoon.portal.coplet.adapter.CopletAdapter#login(org.apache.cocoon.portal.coplet.CopletInstanceData)
126: */
127: public void login(CopletInstanceData coplet) {
128: super .login(coplet);
129:
130: if (this .portletContainer == null) {
131: return;
132: }
133: PortletDefinitionRegistry registry = (PortletDefinitionRegistry) portletContainerEnvironment
134: .getContainerService(PortletDefinitionRegistry.class);
135:
136: final String portletEntityId = (String) getConfiguration(
137: coplet, "portlet");
138: if (this .getLogger().isDebugEnabled()) {
139: this .getLogger().debug(
140: "Coplet " + coplet.getId()
141: + " tries to login into portlet "
142: + portletEntityId);
143: }
144:
145: PortletApplicationEntity pae = registry
146: .getPortletApplicationEntityList().get(
147: ObjectIDImpl.createFromString("cocoon"));
148: PortletEntity portletEntity = ((PortletEntityListImpl) pae
149: .getPortletEntityList()).add(pae, portletEntityId,
150: coplet, registry);
151:
152: if (portletEntity.getPortletDefinition() != null) {
153: // create the window
154: PortletWindow portletWindow = new PortletWindowImpl(
155: portletEntityId);
156: ((PortletWindowCtrl) portletWindow).setId(coplet.getId());
157: ((PortletWindowCtrl) portletWindow)
158: .setPortletEntity(portletEntity);
159: PortletWindowList windowList = portletEntity
160: .getPortletWindowList();
161: ((PortletWindowListCtrl) windowList).add(portletWindow);
162: coplet.setTemporaryAttribute("window", portletWindow);
163:
164: // load the portlet
165: final Map objectModel = ContextHelper
166: .getObjectModel(this .context);
167: ServletRequestImpl req = (ServletRequestImpl) objectModel
168: .get("portlet-request");
169: if (req == null) {
170: final HttpServletResponse res = (HttpServletResponse) objectModel
171: .get(HttpEnvironment.HTTP_RESPONSE_OBJECT);
172: objectModel.put("portlet-response",
173: new ServletResponseImpl(res));
174: req = new ServletRequestImpl(
175: (HttpServletRequest) objectModel
176: .get(HttpEnvironment.HTTP_REQUEST_OBJECT),
177: null);
178: objectModel.put("portlet-request", req);
179: }
180: final HttpServletResponse res = (HttpServletResponse) objectModel
181: .get("portlet-response");
182: try {
183: this .portletContainer.portletLoad(portletWindow, req
184: .getRequest(portletWindow), res);
185: } catch (Exception e) {
186: this .getLogger().error(
187: "Error loading portlet " + portletEntityId
188: + " for instance " + coplet.getId(), e);
189: // remove portlet entity
190: coplet.removeTemporaryAttribute("window");
191: ((PortletEntityListImpl) pae.getPortletEntityList())
192: .remove(portletEntity);
193: }
194: } else {
195: this .getLogger().error(
196: "Error finding portlet " + portletEntityId
197: + " for instance " + coplet.getId()
198: + " - no definition found.");
199: }
200: }
201:
202: /**
203: * @see org.apache.cocoon.portal.coplet.adapter.impl.AbstractCopletAdapter#streamContent(org.apache.cocoon.portal.coplet.CopletInstanceData, org.xml.sax.ContentHandler)
204: */
205: public void streamContent(CopletInstanceData coplet,
206: ContentHandler contentHandler) throws SAXException {
207: if (this .portletContainer == null) {
208: throw new SAXException(
209: "Unable to execute JSR-168 portlets because of missing servlet context.");
210: }
211: try {
212: final String portletEntityId = (String) getConfiguration(
213: coplet, "portlet");
214: // get the window
215: final PortletWindow window = (PortletWindow) coplet
216: .getTemporaryAttribute("window");
217: if (window == null) {
218: throw new SAXException("Portlet couldn't be loaded: "
219: + coplet.getId() + "(" + portletEntityId + ")");
220: }
221: final Map objectModel = ContextHelper
222: .getObjectModel(this .context);
223: final ServletRequestImpl req = (ServletRequestImpl) objectModel
224: .get("portlet-request");
225: final HttpServletResponse res = (HttpServletResponse) objectModel
226: .get("portlet-response");
227:
228: // TODO - for parallel processing we have to clone the response!
229: this .portletContainer.renderPortlet(window, req
230: .getRequest(window), res);
231: final String value = this .getResponse(coplet, res);
232:
233: final Boolean usePipeline = (Boolean) this
234: .getConfiguration(coplet, "use-pipeline",
235: Boolean.FALSE);
236: if (usePipeline.booleanValue()) {
237: HtmlSaxParser.parseString(value, HtmlSaxParser
238: .getContentFilter(contentHandler));
239: } else {
240: // stream out the include for the serializer
241: IncludingHTMLSerializer.addPortlet(coplet.getId(),
242: value);
243: contentHandler.startPrefixMapping("portal",
244: IncludingHTMLSerializer.NAMESPACE);
245: AttributesImpl attr = new AttributesImpl();
246: attr.addCDATAAttribute("portlet", coplet.getId());
247: contentHandler.startElement(
248: IncludingHTMLSerializer.NAMESPACE, "include",
249: "portal:include", attr);
250: contentHandler.endElement(
251: IncludingHTMLSerializer.NAMESPACE, "include",
252: "portal:include");
253: contentHandler.endPrefixMapping("portal");
254: }
255: } catch (SAXException se) {
256: throw se;
257: } catch (Exception e) {
258: throw new SAXException(e);
259: }
260: }
261:
262: /**
263: * @see org.apache.cocoon.portal.coplet.adapter.CopletAdapter#logout(org.apache.cocoon.portal.coplet.CopletInstanceData)
264: */
265: public void logout(CopletInstanceData coplet) {
266: super .logout(coplet);
267: if (this .portletContainer == null) {
268: return;
269: }
270: PortletWindow window = (PortletWindow) coplet
271: .getTemporaryAttribute("window");
272: if (window != null) {
273: coplet.removeTemporaryAttribute("window");
274: PortletDefinitionRegistry registry = (PortletDefinitionRegistry) portletContainerEnvironment
275: .getContainerService(PortletDefinitionRegistry.class);
276:
277: PortletApplicationEntity pae = registry
278: .getPortletApplicationEntityList().get(
279: ObjectIDImpl.createFromString("cocoon"));
280: ((PortletEntityListImpl) pae.getPortletEntityList())
281: .remove(window.getPortletEntity());
282: }
283: }
284:
285: /**
286: * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
287: */
288: public void service(ServiceManager manager) throws ServiceException {
289: super .service(manager);
290: EventManager eventManager = null;
291: try {
292: eventManager = (EventManager) this .manager
293: .lookup(EventManager.ROLE);
294: eventManager.subscribe(this );
295: } finally {
296: this .manager.release(eventManager);
297: }
298: }
299:
300: /**
301: * @see org.apache.avalon.framework.activity.Disposable#dispose()
302: */
303: public void dispose() {
304: if (this .manager != null) {
305: EventManager eventManager = null;
306: try {
307: eventManager = (EventManager) this .manager
308: .lookup(EventManager.ROLE);
309: eventManager.unsubscribe(this );
310: } catch (Exception ignore) {
311: // let's ignore it
312: } finally {
313: this .manager.release(eventManager);
314: }
315: this .manager = null;
316: }
317: try {
318: if (this .portletContainer != null) {
319: this .portletContainer.shutdown();
320: this .portletContainer = null;
321: }
322: ContainerUtil.dispose(this .portletContainerEnvironment);
323: this .portletContainerEnvironment = null;
324: } catch (Throwable t) {
325: this .getLogger().error("Destruction failed!", t);
326: }
327: if (this .servletConfig != null) {
328: this .servletConfig.getServletContext().removeAttribute(
329: PortletAdapter.class.getName());
330: this .servletConfig = null;
331: }
332: }
333:
334: /**
335: * @see org.apache.avalon.framework.activity.Initializable#initialize()
336: */
337: public void initialize() throws Exception {
338: if (this .servletConfig != null) {
339: this .initContainer();
340: }
341: }
342:
343: /**
344: * Return the portlet container
345: */
346: public PortletContainer getPortletContainer() {
347: return this .portletContainer;
348: }
349:
350: /**
351: * Return the portlet container environment
352: */
353: public PortletContainerEnvironment getPortletContainerEnvironment() {
354: return this .portletContainerEnvironment;
355: }
356:
357: /**
358: * Initialize the container
359: */
360: public void initContainer() throws Exception {
361: this .portletContainer = new PortletContainerImpl();
362:
363: if (!portletContainer.isInitialized()) {
364: this .getLogger().debug("Initializing PortletContainer...");
365:
366: final String uniqueContainerName = "cocoon-portal";
367:
368: this .portletContainerEnvironment = new PortletContainerEnvironmentImpl();
369: ContainerUtil.enableLogging(
370: this .portletContainerEnvironment, this .getLogger());
371: ContainerUtil.contextualize(
372: this .portletContainerEnvironment, this .context);
373: ContainerUtil.service(this .portletContainerEnvironment,
374: this .manager);
375: ContainerUtil.initialize(this .portletContainerEnvironment);
376:
377: Properties properties = new Properties();
378:
379: try {
380: portletContainer.init(uniqueContainerName,
381: servletConfig,
382: this .portletContainerEnvironment, properties);
383: } catch (PortletContainerException exc) {
384: throw new ProcessingException(
385: "Initialization of the portlet container failed.",
386: exc);
387: }
388: } else {
389: this .getLogger().debug(
390: "PortletContainer already initialized.");
391: }
392:
393: this .getLogger().debug("PortletContainer initialized.");
394:
395: }
396:
397: /**
398: * @see Receiver
399: */
400: public void inform(PortletURLProviderImpl event,
401: PortalService service) {
402: final Map objectModel = ContextHelper
403: .getObjectModel(this .context);
404: final ServletRequestImpl req = new ServletRequestImpl(
405: (HttpServletRequest) objectModel
406: .get(HttpEnvironment.HTTP_REQUEST_OBJECT),
407: event);
408: final HttpServletResponse res = new ServletResponseImpl(
409: (HttpServletResponse) objectModel
410: .get(HttpEnvironment.HTTP_RESPONSE_OBJECT));
411: objectModel.put("portlet-response", res);
412: objectModel.put("portlet-request", req);
413:
414: // change portlet mode and window state
415: final InformationProviderService ips = (InformationProviderService) this .portletContainerEnvironment
416: .getContainerService(InformationProviderService.class);
417: final DynamicInformationProvider dynProv = ips
418: .getDynamicProvider(req);
419: final PortletActionProvider pap = dynProv
420: .getPortletActionProvider(event.getPortletWindow());
421:
422: final PortletMode mode = event.getPortletMode();
423: if (mode != null) {
424: pap.changePortletMode(mode);
425: }
426: final WindowState state = event.getWindowState();
427: if (state != null) {
428: pap.changePortletWindowState(state);
429: }
430: if (event.isAction()) {
431: // This means we can only have ONE portlet event per request!
432: objectModel.put("portlet-event", event);
433: } else {
434: ((PortletActionProviderImpl) pap)
435: .changeRenderParameters(event.getParameters());
436: }
437: }
438:
439: /**
440: * @see org.apache.cocoon.portal.PortalManagerAspect#prepare(org.apache.cocoon.portal.PortalManagerAspectPrepareContext, org.apache.cocoon.portal.PortalService)
441: */
442: public void prepare(
443: PortalManagerAspectPrepareContext aspectContext,
444: PortalService service) throws ProcessingException {
445: // process the events
446: aspectContext.invokeNext();
447:
448: // if we aren't running in a servlet environment, just skip the JSR-168 part
449: if (this .servletConfig == null) {
450: return;
451: }
452:
453: // do we already have an environment?
454: // if not, create one
455: final Map objectModel = aspectContext.getObjectModel();
456:
457: PortletURLProviderImpl event = (PortletURLProviderImpl) objectModel
458: .get("portlet-event");
459: if (event != null) {
460: PortletWindow actionWindow = event.getPortletWindow();
461: try {
462: final ServletRequestImpl req = (ServletRequestImpl) objectModel
463: .get("portlet-request");
464: final ServletResponseImpl res = (ServletResponseImpl) objectModel
465: .get("portlet-response");
466: this .portletContainer
467: .processPortletAction(actionWindow, req
468: .getRequest(actionWindow), res);
469: } catch (Exception ignore) {
470: this .getLogger().error(
471: "Error during processing of portlet action.",
472: ignore);
473: }
474: } else if (objectModel.get("portlet-response") == null) {
475: final HttpServletResponse res = (HttpServletResponse) objectModel
476: .get(HttpEnvironment.HTTP_RESPONSE_OBJECT);
477: objectModel.put("portlet-response",
478: new ServletResponseImpl(res));
479: final ServletRequestImpl req = new ServletRequestImpl(
480: (HttpServletRequest) objectModel
481: .get(HttpEnvironment.HTTP_REQUEST_OBJECT),
482: null);
483: objectModel.put("portlet-request", req);
484: }
485: }
486:
487: /**
488: * @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)
489: */
490: public void render(PortalManagerAspectRenderContext aspectContext,
491: PortalService service, ContentHandler ch,
492: Parameters parameters) throws SAXException {
493: final Map objectModel = aspectContext.getObjectModel();
494:
495: // don't generate a response, if we issued a redirect
496: if (objectModel.remove("portlet-event") == null) {
497: aspectContext.invokeNext(ch, parameters);
498: }
499: }
500:
501: protected String getResponse(CopletInstanceData instance,
502: HttpServletResponse response) {
503: return response.toString();
504: }
505: }
|