javax.portlet.GenericPortlet Maven / Gradle / Ivy
/* 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:
*
* - it throws a
PortletException
* - 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:
*
* - method annotated with
@RenderMode and the name of the
* portlet mode
* - 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
*
*
*
*
* 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);
}
}
}
}
}
}