All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javax.portlet.GenericPortlet Maven / Gradle / Ivy

There is a newer version: 3.0.1
Show newest version
/*  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.
 */
/*
 * NOTE: this source code is based on an early draft version of JSR 286 and not intended for product
 * implementations. This file may change or vanish in the final version of the JSR 286 specification.
 */
/*
 * This source code implements specifications defined by the Java
 * Community Process. In order to remain compliant with the specification
 * DO NOT add / change / or delete method signatures!
 */
/**
 * Copyright 2006 IBM Corporation.
 */

package javax.portlet;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import javax.xml.namespace.QName;

/**
 * The GenericPortlet class provides a default implementation for
 * the Portlet interface.
 * 

* It provides an abstract class to be subclassed to create portlets. A subclass * of GenericPortlet should either use one of the following annotations: *

    *
  • @ProcessAction
  • *
  • @ProcessEvent
  • *
  • @RenderMode
  • *
* or override at least one method, usually * one of the following: *
    *
  • processAction, to handle action requests
  • *
  • doView, to handle render requests when in VIEW mode
  • *
  • doEdit, to handle render requests when in EDIT mode
  • *
  • doHelp, to handle render request when in HELP mode
  • *
  • init and destroy, to manage resources that are held for the life of the * servlet
  • *
*

* Normally there is no need to override the render or the doDispatch methods. * Render handles render requests setting the title of the portlet in the * response and invoking doDispatch. doDispatch dispatches the request to one of * the doView, doEdit or doHelp method depending on the portlet mode indicated * in the request. *

* Portlets typically run on multithreaded servers, so please note that a * portlet must handle concurrent requests and be careful to synchronize access * to shared resources. Shared resources include in-memory data such as instance * or class variables and external objects such as files, database connections, * and network connections. */ public abstract class GenericPortlet implements Portlet, PortletConfig, EventPortlet, ResourceServingPortlet { private transient PortletConfig config; private transient Map processActionHandlingMethodsMap = new HashMap(); private transient Map processEventHandlingMethodsMap = new HashMap(); private transient Map renderModeHandlingMethodsMap = new HashMap(); /** * Does nothing. */ public GenericPortlet() { } /** * Called by the portlet container to indicate to a portlet that the portlet * is being placed into service. *

* The default implementation stores the PortletConfig object * and checks for annotated methods with the annotations *

    *
  • @ProcessAction
  • *
  • @ProcessEvent
  • *
  • @RenderMode
  • *
* and stores these in a hashmap for later dispatching. *

* The portlet container calls the init method exactly once * after instantiating the portlet. The init method must * complete successfully before the portlet can receive any requests. * *

* The portlet container cannot place the portlet into service if the * init method does one of the following: *

    *
  1. it throws a PortletException *
  2. it does not return within a time period defined by the Web server *
* * * @param config * a PortletConfig object containing the portlet * configuration and initialization parameters * * @exception PortletException * if an exception has occurred that interferes with the * portlet normal operation. * @exception UnavailableException * if the portlet cannot perform the initialization at this * time. */ public void init(PortletConfig config) throws PortletException { this.config = config; cacheAnnotatedMethods(); this.init(); } /** * * A convenience method which can be overridden so that there's no need to * call super.init(config). * *

* Instead of overriding {@link #init(PortletConfig)}, simply override this * method and it will be called by * GenericPortlet.init(PortletConfig config). The * PortletConfig object can still be retrieved via {@link * #getPortletConfig}. * * @exception PortletException * if an exception has occurred that interferes with the * portlet normal operation. * @exception UnavailableException * if the portlet is unavailable to perform init */ public void init() throws PortletException { } /** * Called by the portlet container to allow the portlet to process an action * request. This method is called if the client request was originated by a * URL created (by the portlet) with the * RenderResponse.createActionURL() method. *

* The default implementation tries to dispatch to a method * annotated with @ProcessAction that matches the action parameter * value ActionRequest.ACTION_NAME or, if no * such method is found throws a PortletException.
* Note that the annotated methods needs to be public in order * to be allowed to be called by GenericPortlet. * * @param request * the action request * @param response * the action response * @exception PortletException * if the portlet cannot fulfilling the request * @exception UnavailableException * if the portlet is unavailable to process the action at * this time * @exception PortletSecurityException * if the portlet cannot fullfill this request because of * security reasons * @exception java.io.IOException * if the streaming causes an I/O problem */ public void processAction(ActionRequest request, ActionResponse response) throws PortletException, java.io.IOException { String action = request.getParameter(ActionRequest.ACTION_NAME); try { // check if action is cached Method actionMethod = processActionHandlingMethodsMap.get(action); if (actionMethod != null) { actionMethod.invoke(this, request, response); return; } } catch (Exception e) { throw new PortletException(e); } // if no action processing method was found throw exc throw new PortletException("processAction method not implemented"); } /** * The default implementation of this method sets the headers using the * doHeaders method, sets the title using the * getTitle method and invokes the doDispatch * method. *

* It also evaluates the RENDER_PART request attribute and if * set calls the doHeaders, getNextPossiblePortletModes and * getTitle methods for the RENDER_HEADERS * part and the doDispatch method for the * RENDER_MARKUP part.
* If the RENDER_PART request attribute is not set all of the * above methods will be called. * * @param request * the render request * @param response * the render response * * @exception PortletException * if the portlet cannot fulfilling the request * @exception UnavailableException * if the portlet is unavailable to perform render at this * time * @exception PortletSecurityException * if the portlet cannot fullfill this request because of * security reasons * @exception java.io.IOException * if the streaming causes an I/O problem * */ public void render(RenderRequest request, RenderResponse response) throws PortletException, java.io.IOException { Object renderPartAttrValue = request.getAttribute(RenderRequest.RENDER_PART); if (renderPartAttrValue != null) { // streaming portal calling if (renderPartAttrValue.equals(RenderRequest.RENDER_HEADERS)) { doHeaders(request, response); Collection nextModes = getNextPossiblePortletModes(request); if (nextModes != null) response.setNextPossiblePortletModes(nextModes); response.setTitle(getTitle(request)); } else if (renderPartAttrValue.equals(RenderRequest.RENDER_MARKUP)) { doDispatch(request, response); } else { throw new PortletException("Unknown value of the 'javax.portlet.render_part' request attribute"); } } else { // buffered portal calling doHeaders(request, response); Collection nextModes = getNextPossiblePortletModes(request); if (nextModes != null) response.setNextPossiblePortletModes(nextModes); response.setTitle(getTitle(request)); doDispatch(request, response); } } /** * Used by the render method to get the title. *

* The default implementation gets the title from the ResourceBundle of the * PortletConfig of the portlet. The title is retrieved using the * 'javax.portlet.title' resource name. *

* Portlets can overwrite this method to provide dynamic titles (e.g. based * on locale, client, and session information). Examples are: *

    *
  • language-dependent titles for multi-lingual portals
  • *
  • shorter titles for WAP phones
  • *
  • the number of messages in a mailbox portlet
  • *
* * @return the portlet title for this window * @throws java.lang.IllegalStateException * if no portlet config object is available */ protected java.lang.String getTitle(RenderRequest request) { if (config == null) throw new java.lang.IllegalStateException( "Config is null, please ensure that your init(config) method calls super.init(config)"); return config.getResourceBundle(request.getLocale()).getString("javax.portlet.title"); } /** * The default implementation of this method routes the render request to: *
    *
  1. method annotated with @RenderMode and the name of the * portlet mode
  2. *
  3. a set of helper methods depending on the current portlet mode the portlet * is currently in. These methods are: *
      *
    • doView for handling view requests
    • *
    • doEdit for handling edit requests
    • *
    • doHelp for handling help requests
    • *
    *
  4. * *

    * If the window state of this portlet is minimized, this * method does not invoke any of the portlet mode rendering methods. *

    * For handling custom portlet modes the portlet should either use the * @RenderMode annotation or override this * method. Note that the annotated methods needs to be public in order * to be allowed to be called by GenericPortlet. * * @param request * the render request * @param response * the render response * * @exception PortletException * if the portlet cannot fulfilling the request * @exception UnavailableException * if the portlet is unavailable to perform render at this * time * @exception PortletSecurityException * if the portlet cannot fullfill this request because of * security reasons * @exception java.io.IOException * if the streaming causes an I/O problem * * @see #doView(RenderRequest, RenderResponse) * @see #doEdit(RenderRequest, RenderResponse) * @see #doHelp(RenderRequest, RenderResponse) */ protected void doDispatch(RenderRequest request, RenderResponse response) throws PortletException, java.io.IOException { WindowState state = request.getWindowState(); if (!state.equals(WindowState.MINIMIZED)) { PortletMode mode = request.getPortletMode(); // first look if there are methods annotated for // handling the rendering of this mode try { // check if mode is cached Method renderMethod = renderModeHandlingMethodsMap.get(mode.toString()); if (renderMethod != null) { renderMethod.invoke(this, request, response); return; } } catch (Exception e) { throw new PortletException(e); } // if not, try the default doXYZ methods if (mode.equals(PortletMode.VIEW)) { doView(request, response); } else if (mode.equals(PortletMode.EDIT)) { doEdit(request, response); } else if (mode.equals(PortletMode.HELP)) { doHelp(request, response); } else { throw new PortletException("unknown portlet mode: " + mode); } } } /** * Helper method to serve up the mandatory view mode. *

    * The default implementation throws an exception. * * @param request * the portlet request * @param response * the render response * * @exception PortletException * if the portlet cannot fulfilling the request * @exception UnavailableException * if the portlet is unavailable to perform render at this * time * @exception PortletSecurityException * if the portlet cannot fullfill this request because of * security reasons * @exception java.io.IOException * if the streaming causes an I/O problem * */ protected void doView(RenderRequest request, RenderResponse response) throws PortletException, java.io.IOException { throw new PortletException("doView method not implemented"); } /** * Helper method to serve up the edit mode. *

    * The default implementation throws an exception. * * @param request * the portlet request * @param response * the render response * * @exception PortletException * if the portlet cannot fulfilling the request * @exception UnavailableException * if the portlet is unavailable to perform render at this * time * @exception PortletSecurityException * if the portlet cannot fullfill this request because of * security reasons * @exception java.io.IOException * if the streaming causes an I/O problem * */ protected void doEdit(RenderRequest request, RenderResponse response) throws PortletException, java.io.IOException { throw new PortletException("doEdit method not implemented"); } /** * Helper method to serve up the help mode. *

    * The default implementation throws an exception. * * @param request * the portlet request * @param response * the render response * * @exception PortletException * if the portlet cannot fulfilling the request * @exception UnavailableException * if the portlet is unavailable to perform render at this * time * @exception PortletSecurityException * if the portlet cannot fullfill this request because of * security reasons * @exception java.io.IOException * if the streaming causes an I/O problem */ protected void doHelp(RenderRequest request, RenderResponse response) throws PortletException, java.io.IOException { throw new PortletException("doHelp method not implemented"); } /** * Returns the PortletConfig object of this portlet. * * @return the PortletConfig object of this portlet */ public PortletConfig getPortletConfig() { return config; } /** * Called by the portlet container to indicate to a portlet that the portlet * is being taken out of service. *

    * The default implementation does nothing. * */ public void destroy() { // do nothing } // ------------------------------------------------------------------------- // implement PortletConfig // ------------------------------------------------------------------------- /** * Returns the name of this portlet. * * @return the portlet name * * @see PortletConfig#getPortletName() */ public String getPortletName() { if (config == null) throw new java.lang.IllegalStateException( "Config is null, please ensure that your init(config) method calls super.init(config)"); return config.getPortletName(); } /** * Returns the PortletContext of the portlet application the * portlet is in. * * @return the portlet application context */ public PortletContext getPortletContext() { if (config == null) throw new java.lang.IllegalStateException( "Config is null, please ensure that your init(config) method calls super.init(config)"); return config.getPortletContext(); } /** * Gets the resource bundle for the given locale based on the resource * bundle defined in the deployment descriptor with * resource-bundle tag or the inlined resources defined in * the deployment descriptor. * * @return the resource bundle for the given locale */ public java.util.ResourceBundle getResourceBundle(java.util.Locale locale) { if (config == null) throw new java.lang.IllegalStateException( "Config is null, please ensure that your init(config) method calls super.init(config)"); return config.getResourceBundle(locale); } /** * Returns a String containing the value of the named initialization * parameter, or null if the parameter does not exist. * * @param name * a String specifying the name of the * initialization parameter * * @return a String containing the value of the * initialization parameter * * @exception java.lang.IllegalArgumentException * if name is null. */ public String getInitParameter(java.lang.String name) { if (config == null) throw new java.lang.IllegalStateException( "Config is null, please ensure that your init(config) method calls super.init(config)"); return config.getInitParameter(name); } /** * Returns the names of the portlet initialization parameters as an * Enumeration of String objects, or an empty Enumeration if the portlet has * no initialization parameters. * * @return an Enumeration of String objects * containing the names of the portlet initialization parameters, or * an empty Enumeration if the portlet has no initialization * parameters. */ public java.util.Enumeration getInitParameterNames() { if (config == null) throw new java.lang.IllegalStateException( "Config is null, please ensure that your init(config) method calls super.init(config)"); return config.getInitParameterNames(); } /* * (non-Javadoc) * * @see javax.portlet.PortletConfig#getProcessingEventQNames() */ public Enumeration getProcessingEventQNames() { if (config == null) throw new java.lang.IllegalStateException( "Config is null, please ensure that your init(config) method calls super.init(config)"); return config.getProcessingEventQNames(); } /* * (non-Javadoc) * * @see javax.portlet.PortletConfig#getPublishingEventQNames() */ public Enumeration getPublishingEventQNames() { if (config == null) throw new java.lang.IllegalStateException( "Config is null, please ensure that your init(config) method calls super.init(config)"); return config.getPublishingEventQNames(); } /* * (non-Javadoc) * * @see javax.portlet.PortletConfig#getSupportedLocales() */ public Enumeration getSupportedLocales() { if (config == null) throw new java.lang.IllegalStateException( "Config is null, please ensure that your init(config) method calls super.init(config)"); return config.getSupportedLocales(); } /* (non-Javadoc) * @see javax.portlet.PortletConfig#getContainerRuntimeOptions() */ public Map getContainerRuntimeOptions() { return config.getContainerRuntimeOptions(); } // ------------------------------------------------------------------------- // V 2.0 additions // ------------------------------------------------------------------------- /** * Default resource serving. *

    * The default implemention of this method is to call a * RequestDispatcher.foward with the ResourceID of the ResourceRequest. *

    * If no ResourceID is set on the resource URL the default implementation * does nothing. * * @since 2.0 */ public void serveResource(ResourceRequest request, ResourceResponse response) throws PortletException, IOException { if (request.getResourceID() != null) { PortletRequestDispatcher rd = getPortletConfig().getPortletContext().getRequestDispatcher( request.getResourceID()); if (rd != null) rd.forward(request, response); } } /** * The default implementation tries to dispatch to a method * annotated with @ProcessEvent that matches the * event name or, if no * such method is found just sets the current render parameters on * the response.
    * Note that the annotated methods needs to be public in order * to be allowed to be called by GenericPortlet. * * @see javax.portlet.EventPortlet#processEvent(javax.portlet.EventRequest, * javax.portlet.EventResponse) * @since 2.0 */ public void processEvent(EventRequest request, EventResponse response) throws PortletException, IOException { String eventName = request.getEvent().getQName().toString(); try { // check for exact match Method eventMethod = processEventHandlingMethodsMap.get(eventName); if (eventMethod != null) { eventMethod.invoke(this, request, response); return; } else { // Search for the longest possible matching wildcard annotation int endPos = eventName.indexOf('}'); int dotPos = eventName.lastIndexOf('.'); while (dotPos > endPos) { String wildcardLookup = eventName.substring(0, dotPos + 1); eventMethod = processEventHandlingMethodsMap.get(wildcardLookup); if (eventMethod != null) { eventMethod.invoke(this, request, response); return; } if (dotPos == 0) { break; } dotPos = eventName.lastIndexOf('.', dotPos - 1); } } } catch (Exception e) { throw new PortletException(e); } // if no event processing method was found just keep render params response.setRenderParameters(request); } /** * Used by the render method to set the response properties and headers. *

    * The portlet should override this method and set its response header using * this method in order to ensure that they are set before anything is * written to the output stream. *

    * The default implemention of this method is emtpy. * * @param request the render request * @param response the render response * @since 2.0 */ protected void doHeaders(RenderRequest request, RenderResponse response) { return; } /** * Used by the render method to set the next possible portlet modes. *

    * The portlet should override this method and set the next possible portlet * modes using this method in order to ensure that they are set before * anything is written to the output stream. *

    * The default implemention of this method returns null. * * @since 2.0 */ protected java.util.Collection getNextPossiblePortletModes(RenderRequest request) { return null; } /** * Returns the names of the public render parameters supported by the * portlet as an Enumeration of String objects, or an empty * Enumeration if the portlet has no public render * parameters. * * @return an Enumeration of String objects * containing the names of the public render parameters, or an empty * Enumeration if the portlet does not define any * public render parameters. * * @see javax.portlet.PortletConfig#getPublicRenderParameterNames() */ public Enumeration getPublicRenderParameterNames() { if (config == null) throw new java.lang.IllegalStateException( "Config is null, please ensure that your init(config) method calls super.init(config)"); return config.getPublicRenderParameterNames(); } /** * Returns the default namespace for events and public parameters. This * namespace is defined in the portlet deployment descriptor with the * default-namespace element. *

    * If no default namespace is defined in the portlet deployment descriptor * this methods returns the XML default namespace * XMLConstants.NULL_NS_URI. * * @return the default namespace defined in the portlet deployment * descriptor, or XMLConstants.NULL_NS_URI is non is * defined. * * @see javax.portlet.PortletConfig#getDefaultNamespace() */ public String getDefaultNamespace() { if (config == null) throw new java.lang.IllegalStateException( "Config is null, please ensure that your init(config) method calls super.init(config)"); return config.getDefaultNamespace(); } private void cacheAnnotatedMethods() { // cache all annotated and visible public methods for (Method method : this.getClass().getMethods()) { Annotation[] annotations = method.getAnnotations(); if (annotations != null) { for (Annotation annotation : annotations) { Class annotationType = annotation.annotationType(); if (ProcessAction.class.equals(annotationType)) { String name = ((ProcessAction) annotation).name(); if (name != null && name.length() > 0) processActionHandlingMethodsMap.put(name, method); } else if (ProcessEvent.class.equals(annotationType)) { String qname = ((ProcessEvent) annotation).qname(); if (qname == null || qname.length() <= 0) { if (config == null) throw new java.lang.IllegalStateException( "Config is null, please ensure that your init(config) method calls super.init(config)"); String name = ((ProcessEvent) annotation).name(); if (name != null && name.length() > 0) { qname = new QName(config.getDefaultNamespace(), name).toString(); processEventHandlingMethodsMap.put(qname, method); } } else processEventHandlingMethodsMap.put(qname, method); } else if (RenderMode.class.equals(annotationType)) { String name = ((RenderMode) annotation).name(); if (name != null && name.length() > 0) renderModeHandlingMethodsMap.put(name.toLowerCase(), method); } } } } } }