Initial import of portlet module
This commit is contained in:
parent
995a323a6f
commit
4ea298a7b0
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project name="org.springframework.web.portlet">
|
||||||
|
<property file="${basedir}/../build.properties"/>
|
||||||
|
<import file="${basedir}/../build-spring-framework/package-bundle.xml"/>
|
||||||
|
<import file="${basedir}/../spring-build/standard/default.xml"/>
|
||||||
|
</project>
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<?xml-stylesheet type="text/xsl" href="http://ivyrep.jayasoft.org/ivy-doc.xsl"?>
|
||||||
|
<ivy-module
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="http://incubator.apache.org/ivy/schemas/ivy.xsd"
|
||||||
|
version="1.3">
|
||||||
|
|
||||||
|
<info organisation="org.springframework" module="${ant.project.name}">
|
||||||
|
<license name="Apache 2.0" url="http://www.apache.org/licenses/LICENSE-2.0"/>
|
||||||
|
</info>
|
||||||
|
|
||||||
|
<configurations>
|
||||||
|
<include file="${spring.build.dir}/common/default-ivy-configurations.xml"/>
|
||||||
|
<conf name="commons-fileupload" extends="runtime" description="JARs needed to run with Commons Fileupload"/>
|
||||||
|
</configurations>
|
||||||
|
|
||||||
|
<publications>
|
||||||
|
<artifact name="${ant.project.name}"/>
|
||||||
|
<artifact name="${ant.project.name}-sources" type="src" ext="jar"/>
|
||||||
|
</publications>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency org="javax.portlet" name="com.springsource.javax.portlet" rev="1.0.0" conf="provided->compile"/>
|
||||||
|
<dependency org="javax.servlet" name="com.springsource.javax.servlet" rev="2.4.0" conf="provided->compile"/>
|
||||||
|
<dependency org="org.apache.commons" name="com.springsource.org.apache.commons.fileupload" rev="1.2.0" conf="optional, commons-fileupload->compile"/>
|
||||||
|
<dependency org="org.apache.commons" name="com.springsource.org.apache.commons.logging" rev="1.1.1" conf="compile->compile"/>
|
||||||
|
<dependency org="org.springframework" name="org.springframework.beans" rev="latest.integration" conf="compile->compile"/>
|
||||||
|
<dependency org="org.springframework" name="org.springframework.context" rev="latest.integration" conf="compile->compile"/>
|
||||||
|
<dependency org="org.springframework" name="org.springframework.core" rev="latest.integration" conf="compile->compile"/>
|
||||||
|
<dependency org="org.springframework" name="org.springframework.web" rev="latest.integration" conf="compile->compile"/>
|
||||||
|
<dependency org="org.springframework" name="org.springframework.web.servlet" rev="latest.integration" conf="compile->compile"/>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</ivy-module>
|
|
@ -0,0 +1,86 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>org.springframework.parent</artifactId>
|
||||||
|
<version>3.0-M1-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>org.springframework.web</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<name>Spring Framework: Web</name>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>org.springframework.core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>org.springframework.beans</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>org.springframework.aop</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>org.springframework.context</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.servlet</groupId>
|
||||||
|
<artifactId>com.springsource.javax.servlet</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.el</groupId>
|
||||||
|
<artifactId>com.springsource.javax.el</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.servlet</groupId>
|
||||||
|
<artifactId>com.springsource.javax.servlet.jsp</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.xml.rpc</groupId>
|
||||||
|
<artifactId>com.springsource.javax.xml.rpc</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.aopalliance</groupId>
|
||||||
|
<artifactId>com.springsource.org.aopalliance</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.myfaces</groupId>
|
||||||
|
<artifactId>com.springsource.org.apache.myfaces.javax.faces</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.caucho</groupId>
|
||||||
|
<artifactId>com.springsource.com.caucho</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.log4j</groupId>
|
||||||
|
<artifactId>com.springsource.org.apache.log4j</artifactId>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>com.springsource.org.apache.commons.httpclient</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.axis</groupId>
|
||||||
|
<artifactId>com.springsource.org.apache.axis</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.taglibs</groupId>
|
||||||
|
<artifactId>com.springsource.org.apache.taglibs.standard</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,10 @@
|
||||||
|
# Default implementation classes for DispatcherPortlet's strategy interfaces.
|
||||||
|
# Used as fallback when no matching beans are found in the DispatcherPortlet context.
|
||||||
|
# Not meant to be customized by application developers.
|
||||||
|
|
||||||
|
org.springframework.web.portlet.HandlerMapping=org.springframework.web.portlet.mvc.annotation.DefaultAnnotationHandlerMapping
|
||||||
|
|
||||||
|
org.springframework.web.portlet.HandlerAdapter=org.springframework.web.portlet.mvc.SimpleControllerHandlerAdapter,\
|
||||||
|
org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter
|
||||||
|
|
||||||
|
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
|
|
@ -0,0 +1,599 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2008 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.portlet.ActionRequest;
|
||||||
|
import javax.portlet.ActionResponse;
|
||||||
|
import javax.portlet.PortletException;
|
||||||
|
import javax.portlet.PortletRequest;
|
||||||
|
import javax.portlet.PortletResponse;
|
||||||
|
import javax.portlet.RenderRequest;
|
||||||
|
import javax.portlet.RenderResponse;
|
||||||
|
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.ApplicationContextException;
|
||||||
|
import org.springframework.context.ApplicationEvent;
|
||||||
|
import org.springframework.context.ApplicationListener;
|
||||||
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
import org.springframework.context.event.ContextRefreshedEvent;
|
||||||
|
import org.springframework.context.event.SourceFilteringListener;
|
||||||
|
import org.springframework.web.portlet.context.ConfigurablePortletApplicationContext;
|
||||||
|
import org.springframework.web.portlet.context.PortletApplicationContextUtils;
|
||||||
|
import org.springframework.web.portlet.context.PortletRequestHandledEvent;
|
||||||
|
import org.springframework.web.portlet.context.XmlPortletApplicationContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base portlet for Spring's portlet framework. Provides integration with
|
||||||
|
* a Spring application context, in a JavaBean-based overall solution.
|
||||||
|
*
|
||||||
|
* <p>This class offers the following functionality:
|
||||||
|
* <ul>
|
||||||
|
* <li>Manages a Portlet {@link org.springframework.context.ApplicationContext}
|
||||||
|
* instance per portlet. The portlet's configuration is determined by beans
|
||||||
|
* in the portlet's namespace.
|
||||||
|
* <li>Publishes events on request processing, whether or not a request is
|
||||||
|
* successfully handled.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>Subclasses must implement {@link #doActionService} and {@link #doRenderService}
|
||||||
|
* to handle action and render requests. Because this extends {@link GenericPortletBean}
|
||||||
|
* rather than Portlet directly, bean properties are mapped onto it. Subclasses can
|
||||||
|
* override {@link #initFrameworkPortlet()} for custom initialization.
|
||||||
|
*
|
||||||
|
* <p>Regards a "contextClass" parameter at the portlet init-param level,
|
||||||
|
* falling back to the default context class
|
||||||
|
* ({@link org.springframework.web.portlet.context.XmlPortletApplicationContext})
|
||||||
|
* if not found. Note that, with the default FrameworkPortlet,
|
||||||
|
* a context class needs to implement the
|
||||||
|
* {@link org.springframework.web.portlet.context.ConfigurablePortletApplicationContext} SPI.
|
||||||
|
*
|
||||||
|
* <p>Passes a "contextConfigLocation" portlet init-param to the context instance,
|
||||||
|
* parsing it into potentially multiple file paths which can be separated by any
|
||||||
|
* number of commas and spaces, like "test-portlet.xml, myPortlet.xml".
|
||||||
|
* If not explicitly specified, the context implementation is supposed to build a
|
||||||
|
* default location from the namespace of the portlet.
|
||||||
|
*
|
||||||
|
* <p>Note: In case of multiple config locations, later bean definitions will
|
||||||
|
* override ones defined in earlier loaded files, at least when using one of
|
||||||
|
* Spring's default ApplicationContext implementations. This can be leveraged
|
||||||
|
* to deliberately override certain bean definitions via an extra XML file.
|
||||||
|
*
|
||||||
|
* <p>The default namespace is "'portlet-name'-portlet", e.g. "test-portlet" for a
|
||||||
|
* portlet-name "test" (leading to a "/WEB-INF/test-portlet.xml" default location
|
||||||
|
* with XmlPortletApplicationContext). The namespace can also be set explicitly via
|
||||||
|
* the "namespace" portlet init-param.
|
||||||
|
*
|
||||||
|
* @author William G. Thompson, Jr.
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 2.0
|
||||||
|
* @see #doActionService
|
||||||
|
* @see #doRenderService
|
||||||
|
* @see #setContextClass
|
||||||
|
* @see #setContextConfigLocation
|
||||||
|
* @see #setNamespace
|
||||||
|
*/
|
||||||
|
public abstract class FrameworkPortlet extends GenericPortletBean implements ApplicationListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default context class for FrameworkPortlet.
|
||||||
|
* @see org.springframework.web.portlet.context.XmlPortletApplicationContext
|
||||||
|
*/
|
||||||
|
public static final Class DEFAULT_CONTEXT_CLASS = XmlPortletApplicationContext.class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Suffix for Portlet ApplicationContext namespaces. If a portlet of this class is
|
||||||
|
* given the name "test" in a context, the namespace used by the portlet will
|
||||||
|
* resolve to "test-portlet".
|
||||||
|
*/
|
||||||
|
public static final String DEFAULT_NAMESPACE_SUFFIX = "-portlet";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prefix for the PortletContext attribute for the Portlet ApplicationContext.
|
||||||
|
* The completion is the portlet name.
|
||||||
|
*/
|
||||||
|
public static final String PORTLET_CONTEXT_PREFIX = FrameworkPortlet.class.getName() + ".CONTEXT.";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default USER_INFO attribute names to search for the current username:
|
||||||
|
* "user.login.id", "user.name".
|
||||||
|
*/
|
||||||
|
public static final String[] DEFAULT_USERINFO_ATTRIBUTE_NAMES = {"user.login.id", "user.name"};
|
||||||
|
|
||||||
|
|
||||||
|
/** Portlet ApplicationContext implementation class to use */
|
||||||
|
private Class contextClass = DEFAULT_CONTEXT_CLASS;
|
||||||
|
|
||||||
|
/** Namespace for this portlet */
|
||||||
|
private String namespace;
|
||||||
|
|
||||||
|
/** Explicit context config location */
|
||||||
|
private String contextConfigLocation;
|
||||||
|
|
||||||
|
/** Should we publish the context as a PortletContext attribute? */
|
||||||
|
private boolean publishContext = true;
|
||||||
|
|
||||||
|
/** Should we publish a PortletRequestHandledEvent at the end of each request? */
|
||||||
|
private boolean publishEvents = true;
|
||||||
|
|
||||||
|
/** USER_INFO attributes that may contain the username of the current user */
|
||||||
|
private String[] userinfoUsernameAttributes = DEFAULT_USERINFO_ATTRIBUTE_NAMES;
|
||||||
|
|
||||||
|
/** ApplicationContext for this portlet */
|
||||||
|
private ApplicationContext portletApplicationContext;
|
||||||
|
|
||||||
|
/** Flag used to detect whether onRefresh has already been called */
|
||||||
|
private boolean refreshEventReceived = false;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a custom context class. This class must be of type ApplicationContext;
|
||||||
|
* when using the default FrameworkPortlet implementation, the context class
|
||||||
|
* must also implement ConfigurablePortletApplicationContext.
|
||||||
|
* @see #createPortletApplicationContext
|
||||||
|
*/
|
||||||
|
public void setContextClass(Class contextClass) {
|
||||||
|
this.contextClass = contextClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the custom context class.
|
||||||
|
*/
|
||||||
|
public Class getContextClass() {
|
||||||
|
return this.contextClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a custom namespace for this portlet,
|
||||||
|
* to be used for building a default context config location.
|
||||||
|
*/
|
||||||
|
public void setNamespace(String namespace) {
|
||||||
|
this.namespace = namespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the namespace for this portlet, falling back to default scheme if
|
||||||
|
* no custom namespace was set. (e.g. "test-portlet" for a portlet named "test")
|
||||||
|
*/
|
||||||
|
public String getNamespace() {
|
||||||
|
return (this.namespace != null) ? this.namespace : getPortletName() + DEFAULT_NAMESPACE_SUFFIX;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the context config location explicitly, instead of relying on the default
|
||||||
|
* location built from the namespace. This location string can consist of
|
||||||
|
* multiple locations separated by any number of commas and spaces.
|
||||||
|
*/
|
||||||
|
public void setContextConfigLocation(String contextConfigLocation) {
|
||||||
|
this.contextConfigLocation = contextConfigLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the explicit context config location, if any.
|
||||||
|
*/
|
||||||
|
public String getContextConfigLocation() {
|
||||||
|
return this.contextConfigLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether to publish this portlet's context as a PortletContext attribute,
|
||||||
|
* available to all objects in the web container. Default is true.
|
||||||
|
* <p>This is especially handy during testing, although it is debatable whether
|
||||||
|
* it's good practice to let other application objects access the context this way.
|
||||||
|
*/
|
||||||
|
public void setPublishContext(boolean publishContext) {
|
||||||
|
this.publishContext = publishContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether to publish this portlet's context as a PortletContext attribute.
|
||||||
|
*/
|
||||||
|
public boolean isPublishContext() {
|
||||||
|
return this.publishContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether this portlet should publish a PortletRequestHandledEvent at the end
|
||||||
|
* of each request. Default is true; can be turned off for a slight performance
|
||||||
|
* improvement, provided that no ApplicationListeners rely on such events.
|
||||||
|
* @see org.springframework.web.portlet.context.PortletRequestHandledEvent
|
||||||
|
*/
|
||||||
|
public void setPublishEvents(boolean publishEvents) {
|
||||||
|
this.publishEvents = publishEvents;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether this portlet should publish a PortletRequestHandledEvent at the end
|
||||||
|
* of each request.
|
||||||
|
*/
|
||||||
|
public boolean isPublishEvents() {
|
||||||
|
return this.publishEvents;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the list of attributes to search in the USER_INFO map when trying
|
||||||
|
* to find the username of the current user.
|
||||||
|
* @see #getUsernameForRequest
|
||||||
|
*/
|
||||||
|
public void setUserinfoUsernameAttributes(String[] userinfoUsernameAttributes) {
|
||||||
|
this.userinfoUsernameAttributes = userinfoUsernameAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of attributes that will be searched in the USER_INFO map
|
||||||
|
* when trying to find the username of the current user
|
||||||
|
* @see #getUsernameForRequest
|
||||||
|
*/
|
||||||
|
public String[] getUserinfoUsernameAttributes() {
|
||||||
|
return this.userinfoUsernameAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overridden method of GenericPortletBean, invoked after any bean properties
|
||||||
|
* have been set. Creates this portlet's ApplicationContext.
|
||||||
|
*/
|
||||||
|
protected final void initPortletBean() throws PortletException, BeansException {
|
||||||
|
getPortletContext().log("Initializing Spring FrameworkPortlet '" + getPortletName() + "'");
|
||||||
|
if (logger.isInfoEnabled()) {
|
||||||
|
logger.info("FrameworkPortlet '" + getPortletName() + "': initialization started");
|
||||||
|
}
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.portletApplicationContext = initPortletApplicationContext();
|
||||||
|
initFrameworkPortlet();
|
||||||
|
}
|
||||||
|
catch (PortletException ex) {
|
||||||
|
logger.error("Context initialization failed", ex);
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
catch (BeansException ex) {
|
||||||
|
logger.error("Context initialization failed", ex);
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logger.isInfoEnabled()) {
|
||||||
|
long elapsedTime = System.currentTimeMillis() - startTime;
|
||||||
|
logger.info("FrameworkPortlet '" + getPortletName() + "': initialization completed in " + elapsedTime + " ms");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize and publish the Portlet ApplicationContext for this portlet.
|
||||||
|
* <p>Delegates to {@link #createPortletApplicationContext} for actual creation.
|
||||||
|
* Can be overridden in subclasses.
|
||||||
|
* @return the ApplicationContext for this portlet
|
||||||
|
* @throws BeansException if the context couldn't be initialized
|
||||||
|
*/
|
||||||
|
protected ApplicationContext initPortletApplicationContext() throws BeansException {
|
||||||
|
ApplicationContext parent = PortletApplicationContextUtils.getWebApplicationContext(getPortletContext());
|
||||||
|
ApplicationContext pac = createPortletApplicationContext(parent);
|
||||||
|
|
||||||
|
if (!this.refreshEventReceived) {
|
||||||
|
// Apparently not a ConfigurableApplicationContext with refresh support:
|
||||||
|
// triggering initial onRefresh manually here.
|
||||||
|
onRefresh(pac);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPublishContext()) {
|
||||||
|
// publish the context as a portlet context attribute
|
||||||
|
String attName = getPortletContextAttributeName();
|
||||||
|
getPortletContext().setAttribute(attName, pac);
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Published ApplicationContext of portlet '" + getPortletName() +
|
||||||
|
"' as PortletContext attribute with name [" + attName + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pac;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiate the Portlet ApplicationContext for this portlet, either a default
|
||||||
|
* XmlPortletApplicationContext or a custom context class if set.
|
||||||
|
* <p>This implementation expects custom contexts to implement
|
||||||
|
* ConfigurablePortletApplicationContext. Can be overridden in subclasses.
|
||||||
|
* @param parent the parent ApplicationContext to use, or null if none
|
||||||
|
* @return the Portlet ApplicationContext for this portlet
|
||||||
|
* @throws BeansException if the context couldn't be initialized
|
||||||
|
* @see #setContextClass
|
||||||
|
* @see org.springframework.web.portlet.context.XmlPortletApplicationContext
|
||||||
|
*/
|
||||||
|
protected ApplicationContext createPortletApplicationContext(ApplicationContext parent)
|
||||||
|
throws BeansException {
|
||||||
|
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Portlet with name '" + getPortletName() +
|
||||||
|
"' will try to create custom ApplicationContext context of class '" +
|
||||||
|
getContextClass().getName() + "'" + ", using parent context [" + parent + "]");
|
||||||
|
}
|
||||||
|
if (!ConfigurablePortletApplicationContext.class.isAssignableFrom(getContextClass())) {
|
||||||
|
throw new ApplicationContextException("Fatal initialization error in portlet with name '" + getPortletName() +
|
||||||
|
"': custom ApplicationContext class [" + getContextClass().getName() +
|
||||||
|
"] is not of type ConfigurablePortletApplicationContext");
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigurablePortletApplicationContext pac =
|
||||||
|
(ConfigurablePortletApplicationContext) BeanUtils.instantiateClass(getContextClass());
|
||||||
|
pac.setParent(parent);
|
||||||
|
pac.setPortletContext(getPortletContext());
|
||||||
|
pac.setPortletConfig(getPortletConfig());
|
||||||
|
pac.setNamespace(getNamespace());
|
||||||
|
pac.setConfigLocation(getContextConfigLocation());
|
||||||
|
pac.addApplicationListener(new SourceFilteringListener(pac, this));
|
||||||
|
|
||||||
|
postProcessPortletApplicationContext(pac);
|
||||||
|
pac.refresh();
|
||||||
|
|
||||||
|
return pac;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post-process the given Portlet ApplicationContext before it is refreshed
|
||||||
|
* and activated as context for this portlet.
|
||||||
|
* <p>The default implementation is empty. <code>refresh()</code> will
|
||||||
|
* be called automatically after this method returns.
|
||||||
|
* @param pac the configured Portlet ApplicationContext (not refreshed yet)
|
||||||
|
* @see #createPortletApplicationContext
|
||||||
|
* @see ConfigurableApplicationContext#refresh()
|
||||||
|
*/
|
||||||
|
protected void postProcessPortletApplicationContext(ConfigurableApplicationContext pac) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the PortletContext attribute name for this portlets's ApplicationContext.
|
||||||
|
* <p>The default implementation returns PORTLET_CONTEXT_PREFIX + portlet name.
|
||||||
|
* @see #PORTLET_CONTEXT_PREFIX
|
||||||
|
* @see #getPortletName
|
||||||
|
*/
|
||||||
|
public String getPortletContextAttributeName() {
|
||||||
|
return PORTLET_CONTEXT_PREFIX + getPortletName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return this portlet's ApplicationContext.
|
||||||
|
*/
|
||||||
|
public final ApplicationContext getPortletApplicationContext() {
|
||||||
|
return this.portletApplicationContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will be invoked after any bean properties have been set and
|
||||||
|
* the ApplicationContext has been loaded.
|
||||||
|
* <p>The default implementation is empty; subclasses may override this method
|
||||||
|
* to perform any initialization they require.
|
||||||
|
* @throws PortletException in case of an initialization exception
|
||||||
|
* @throws BeansException if thrown by ApplicationContext methods
|
||||||
|
*/
|
||||||
|
protected void initFrameworkPortlet() throws PortletException, BeansException {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh this portlet's application context, as well as the
|
||||||
|
* dependent state of the portlet.
|
||||||
|
* @throws BeansException in case of errors
|
||||||
|
* @see #getPortletApplicationContext()
|
||||||
|
* @see org.springframework.context.ConfigurableApplicationContext#refresh()
|
||||||
|
*/
|
||||||
|
public void refresh() throws BeansException {
|
||||||
|
ApplicationContext pac = getPortletApplicationContext();
|
||||||
|
if (!(pac instanceof ConfigurableApplicationContext)) {
|
||||||
|
throw new IllegalStateException("Portlet ApplicationContext does not support refresh: " + pac);
|
||||||
|
}
|
||||||
|
((ConfigurableApplicationContext) pac).refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ApplicationListener endpoint that receives events from this servlet's
|
||||||
|
* WebApplicationContext.
|
||||||
|
* <p>The default implementation calls {@link #onRefresh} in case of a
|
||||||
|
* {@link org.springframework.context.event.ContextRefreshedEvent},
|
||||||
|
* triggering a refresh of this servlet's context-dependent state.
|
||||||
|
* @param event the incoming ApplicationContext event
|
||||||
|
*/
|
||||||
|
public void onApplicationEvent(ApplicationEvent event) {
|
||||||
|
if (event instanceof ContextRefreshedEvent) {
|
||||||
|
this.refreshEventReceived = true;
|
||||||
|
onRefresh(((ContextRefreshedEvent) event).getApplicationContext());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Template method which can be overridden to add portlet-specific refresh work.
|
||||||
|
* Called after successful context refresh.
|
||||||
|
* <p>This implementation is empty.
|
||||||
|
* @param context the current Portlet ApplicationContext
|
||||||
|
* @throws BeansException in case of errors
|
||||||
|
* @see #refresh()
|
||||||
|
*/
|
||||||
|
protected void onRefresh(ApplicationContext context) throws BeansException {
|
||||||
|
// For subclasses: do nothing by default.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overridden for friendlier behavior in unit tests.
|
||||||
|
*/
|
||||||
|
protected String getTitle(RenderRequest renderRequest) {
|
||||||
|
try {
|
||||||
|
return super.getTitle(renderRequest);
|
||||||
|
}
|
||||||
|
catch (NullPointerException ex) {
|
||||||
|
return getPortletName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delegate render requests to processRequest/doRenderService.
|
||||||
|
*/
|
||||||
|
protected final void doDispatch(RenderRequest request, RenderResponse response)
|
||||||
|
throws PortletException, IOException {
|
||||||
|
|
||||||
|
processRequest(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delegate action requests to processRequest/doActionService.
|
||||||
|
*/
|
||||||
|
public final void processAction(ActionRequest request, ActionResponse response)
|
||||||
|
throws PortletException, IOException {
|
||||||
|
|
||||||
|
processRequest(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process this request, publishing an event regardless of the outcome.
|
||||||
|
* The actual event handling is performed by the abstract
|
||||||
|
* <code>doActionService()</code> and <code>doRenderService()</code> template methods.
|
||||||
|
* @see #doActionService
|
||||||
|
* @see #doRenderService
|
||||||
|
*/
|
||||||
|
protected final void processRequest(PortletRequest request, PortletResponse response)
|
||||||
|
throws PortletException, IOException {
|
||||||
|
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
Throwable failureCause = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (request instanceof ActionRequest) {
|
||||||
|
doActionService((ActionRequest) request, (ActionResponse) response);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
doRenderService((RenderRequest) request, (RenderResponse) response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (PortletException ex) {
|
||||||
|
failureCause = ex;
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
failureCause = ex;
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
catch (Throwable ex) {
|
||||||
|
failureCause = ex;
|
||||||
|
throw new PortletException("Request processing failed", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
finally {
|
||||||
|
if (failureCause != null) {
|
||||||
|
logger.error("Could not complete request", failureCause);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logger.debug("Successfully completed request");
|
||||||
|
}
|
||||||
|
if (isPublishEvents()) {
|
||||||
|
// Whether or not we succeeded, publish an event.
|
||||||
|
long processingTime = System.currentTimeMillis() - startTime;
|
||||||
|
this.portletApplicationContext.publishEvent(
|
||||||
|
new PortletRequestHandledEvent(this,
|
||||||
|
getPortletConfig().getPortletName(), request.getPortletMode().toString(),
|
||||||
|
(request instanceof ActionRequest ? "action" : "render"),
|
||||||
|
request.getRequestedSessionId(), getUsernameForRequest(request),
|
||||||
|
processingTime, failureCause));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the username for the given request.
|
||||||
|
* <p>The default implementation first tries the UserPrincipal.
|
||||||
|
* If that does not exist, then it checks the USER_INFO map.
|
||||||
|
* Can be overridden in subclasses.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @return the username, or <code>null</code> if none found
|
||||||
|
* @see javax.portlet.PortletRequest#getUserPrincipal()
|
||||||
|
* @see javax.portlet.PortletRequest#getRemoteUser()
|
||||||
|
* @see javax.portlet.PortletRequest#USER_INFO
|
||||||
|
* @see #setUserinfoUsernameAttributes
|
||||||
|
*/
|
||||||
|
protected String getUsernameForRequest(PortletRequest request) {
|
||||||
|
// Try the principal.
|
||||||
|
Principal userPrincipal = request.getUserPrincipal();
|
||||||
|
if (userPrincipal != null) {
|
||||||
|
return userPrincipal.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try the remote user name.
|
||||||
|
String userName = request.getRemoteUser();
|
||||||
|
if (userName != null) {
|
||||||
|
return userName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try the Portlet USER_INFO map.
|
||||||
|
Map userInfo = (Map) request.getAttribute(PortletRequest.USER_INFO);
|
||||||
|
if (userInfo != null) {
|
||||||
|
for (int i = 0, n = this.userinfoUsernameAttributes.length; i < n; i++) {
|
||||||
|
userName = (String) userInfo.get(this.userinfoUsernameAttributes[i]);
|
||||||
|
if (userName != null) {
|
||||||
|
return userName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nothing worked...
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses must implement this method to do the work of render request handling.
|
||||||
|
* <p>The contract is essentially the same as that for the <code>doDispatch</code>
|
||||||
|
* method of GenericPortlet.
|
||||||
|
* <p>This class intercepts calls to ensure that exception handling and
|
||||||
|
* event publication takes place.
|
||||||
|
* @param request current render request
|
||||||
|
* @param response current render response
|
||||||
|
* @throws Exception in case of any kind of processing failure
|
||||||
|
* @see javax.portlet.GenericPortlet#doDispatch
|
||||||
|
*/
|
||||||
|
protected abstract void doRenderService(RenderRequest request, RenderResponse response)
|
||||||
|
throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses must implement this method to do the work of action request handling.
|
||||||
|
* <p>The contract is essentially the same as that for the <code>processAction</code>
|
||||||
|
* method of GenericPortlet.
|
||||||
|
* <p>This class intercepts calls to ensure that exception handling and
|
||||||
|
* event publication takes place.
|
||||||
|
* @param request current action request
|
||||||
|
* @param response current action response
|
||||||
|
* @throws Exception in case of any kind of processing failure
|
||||||
|
* @see javax.portlet.GenericPortlet#processAction
|
||||||
|
*/
|
||||||
|
protected abstract void doActionService(ActionRequest request, ActionResponse response)
|
||||||
|
throws Exception;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the ApplicationContext of this portlet.
|
||||||
|
* @see org.springframework.context.ConfigurableApplicationContext#close()
|
||||||
|
*/
|
||||||
|
public void destroy() {
|
||||||
|
getPortletContext().log("Destroying Spring FrameworkPortlet '" + getPortletName() + "'");
|
||||||
|
if (this.portletApplicationContext instanceof ConfigurableApplicationContext) {
|
||||||
|
((ConfigurableApplicationContext) this.portletApplicationContext).close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,200 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2008 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet;
|
||||||
|
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.portlet.GenericPortlet;
|
||||||
|
import javax.portlet.PortletConfig;
|
||||||
|
import javax.portlet.PortletContext;
|
||||||
|
import javax.portlet.PortletException;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import org.springframework.beans.BeanWrapper;
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.beans.MutablePropertyValues;
|
||||||
|
import org.springframework.beans.PropertyAccessorFactory;
|
||||||
|
import org.springframework.beans.PropertyValue;
|
||||||
|
import org.springframework.beans.PropertyValues;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.core.io.ResourceEditor;
|
||||||
|
import org.springframework.core.io.ResourceLoader;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.web.portlet.context.PortletContextResourceLoader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple extension of <code>javax.portlet.GenericPortlet</code> that treats
|
||||||
|
* its config parameters as bean properties.
|
||||||
|
*
|
||||||
|
* <p>A very handy superclass for any type of portlet. Type conversion is automatic.
|
||||||
|
* It is also possible for subclasses to specify required properties.
|
||||||
|
*
|
||||||
|
* <p>This portlet leaves request handling to subclasses, inheriting the default
|
||||||
|
* behaviour of GenericPortlet (<code>doDispatch</code>, <code>processAction</code>, etc).
|
||||||
|
*
|
||||||
|
* <p>This portlet superclass has no dependency on a Spring application context,
|
||||||
|
* in contrast to the FrameworkPortlet class which loads its own context.
|
||||||
|
*
|
||||||
|
* @author William G. Thompson, Jr.
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 2.0
|
||||||
|
* @see #addRequiredProperty
|
||||||
|
* @see #initPortletBean
|
||||||
|
* @see #doDispatch
|
||||||
|
* @see #processAction
|
||||||
|
* @see FrameworkPortlet
|
||||||
|
*/
|
||||||
|
public abstract class GenericPortletBean extends GenericPortlet {
|
||||||
|
|
||||||
|
/** Logger available to subclasses */
|
||||||
|
protected final Log logger = LogFactory.getLog(getClass());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set of required properties (Strings) that must be supplied as
|
||||||
|
* config parameters to this portlet.
|
||||||
|
*/
|
||||||
|
private final Set requiredProperties = new HashSet();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses can invoke this method to specify that this property
|
||||||
|
* (which must match a JavaBean property they expose) is mandatory,
|
||||||
|
* and must be supplied as a config parameter. This method would
|
||||||
|
* normally be called from a subclass constructor.
|
||||||
|
* @param property name of the required property
|
||||||
|
*/
|
||||||
|
protected final void addRequiredProperty(String property) {
|
||||||
|
this.requiredProperties.add(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map config parameters onto bean properties of this portlet, and
|
||||||
|
* invoke subclass initialization.
|
||||||
|
* @throws PortletException if bean properties are invalid (or required
|
||||||
|
* properties are missing), or if subclass initialization fails.
|
||||||
|
*/
|
||||||
|
public final void init() throws PortletException {
|
||||||
|
if (logger.isInfoEnabled()) {
|
||||||
|
logger.info("Initializing portlet '" + getPortletName() + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set bean properties from init parameters.
|
||||||
|
try {
|
||||||
|
PropertyValues pvs = new PortletConfigPropertyValues(getPortletConfig(), this.requiredProperties);
|
||||||
|
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
|
||||||
|
ResourceLoader resourceLoader = new PortletContextResourceLoader(getPortletContext());
|
||||||
|
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader));
|
||||||
|
initBeanWrapper(bw);
|
||||||
|
bw.setPropertyValues(pvs, true);
|
||||||
|
}
|
||||||
|
catch (BeansException ex) {
|
||||||
|
logger.error("Failed to set bean properties on portlet '" + getPortletName() + "'", ex);
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// let subclasses do whatever initialization they like
|
||||||
|
initPortletBean();
|
||||||
|
|
||||||
|
if (logger.isInfoEnabled()) {
|
||||||
|
logger.info("Portlet '" + getPortletName() + "' configured successfully");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the BeanWrapper for this GenericPortletBean,
|
||||||
|
* possibly with custom editors.
|
||||||
|
* @param bw the BeanWrapper to initialize
|
||||||
|
* @throws BeansException if thrown by BeanWrapper methods
|
||||||
|
* @see org.springframework.beans.BeanWrapper#registerCustomEditor
|
||||||
|
*/
|
||||||
|
protected void initBeanWrapper(BeanWrapper bw) throws BeansException {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overridden method that simply returns <code>null</code> when no
|
||||||
|
* PortletConfig set yet.
|
||||||
|
* @see #getPortletConfig()
|
||||||
|
*/
|
||||||
|
public final String getPortletName() {
|
||||||
|
return (getPortletConfig() != null ? getPortletConfig().getPortletName() : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overridden method that simply returns <code>null</code> when no
|
||||||
|
* PortletConfig set yet.
|
||||||
|
* @see #getPortletConfig()
|
||||||
|
*/
|
||||||
|
public final PortletContext getPortletContext() {
|
||||||
|
return (getPortletConfig() != null ? getPortletConfig().getPortletContext() : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses may override this to perform custom initialization.
|
||||||
|
* All bean properties of this portlet will have been set before this
|
||||||
|
* method is invoked. This default implementation does nothing.
|
||||||
|
* @throws PortletException if subclass initialization fails
|
||||||
|
*/
|
||||||
|
protected void initPortletBean() throws PortletException {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PropertyValues implementation created from PortletConfig init parameters.
|
||||||
|
*/
|
||||||
|
private static class PortletConfigPropertyValues extends MutablePropertyValues {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create new PortletConfigPropertyValues.
|
||||||
|
* @param config PortletConfig we'll use to take PropertyValues from
|
||||||
|
* @param requiredProperties set of property names we need, where
|
||||||
|
* we can't accept default values
|
||||||
|
* @throws PortletException if any required properties are missing
|
||||||
|
*/
|
||||||
|
private PortletConfigPropertyValues(PortletConfig config, Set requiredProperties)
|
||||||
|
throws PortletException {
|
||||||
|
|
||||||
|
Set missingProps = (requiredProperties != null && !requiredProperties.isEmpty()) ?
|
||||||
|
new HashSet(requiredProperties) : null;
|
||||||
|
|
||||||
|
Enumeration en = config.getInitParameterNames();
|
||||||
|
while (en.hasMoreElements()) {
|
||||||
|
String property = (String) en.nextElement();
|
||||||
|
Object value = config.getInitParameter(property);
|
||||||
|
addPropertyValue(new PropertyValue(property, value));
|
||||||
|
if (missingProps != null) {
|
||||||
|
missingProps.remove(property);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fail if we are still missing properties
|
||||||
|
if (missingProps != null && missingProps.size() > 0) {
|
||||||
|
throw new PortletException(
|
||||||
|
"Initialization from PortletConfig for portlet '" + config.getPortletName() +
|
||||||
|
"' failed; the following required properties were missing: " +
|
||||||
|
StringUtils.collectionToDelimitedString(missingProps, ", "));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2006 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet;
|
||||||
|
|
||||||
|
import javax.portlet.ActionRequest;
|
||||||
|
import javax.portlet.ActionResponse;
|
||||||
|
import javax.portlet.RenderRequest;
|
||||||
|
import javax.portlet.RenderResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Portlet MVC framework SPI interface, allowing parameterization of core MVC workflow.
|
||||||
|
*
|
||||||
|
* <p>Interface that must be implemented for each handler type to handle a request.
|
||||||
|
* This interface is used to allow the DispatcherPortlet to be indefinitely
|
||||||
|
* extensible. The DispatcherPortlet accesses all installed handlers through this
|
||||||
|
* interface, meaning that it does not contain code specific to any handler type.
|
||||||
|
*
|
||||||
|
* <p>Note that a handler can be of type Object. This is to enable handlers from
|
||||||
|
* other frameworks to be integrated with this framework without custom coding.
|
||||||
|
*
|
||||||
|
* <p>This interface is not intended for application developers. It is available
|
||||||
|
* to handlers who want to develop their own web workflow.
|
||||||
|
*
|
||||||
|
* <p>Note: Implementations can implement the Ordered interface to be able to
|
||||||
|
* specify a sorting order and thus a priority for getting applied by
|
||||||
|
* DispatcherPortlet. Non-Ordered instances get treated as lowest priority.
|
||||||
|
*
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @since 2.0
|
||||||
|
* @see org.springframework.web.portlet.mvc.SimpleControllerHandlerAdapter
|
||||||
|
*/
|
||||||
|
public interface HandlerAdapter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a handler instance, return whether or not this HandlerAdapter can
|
||||||
|
* support it. Typical HandlerAdapters will base the decision on the handler
|
||||||
|
* type. HandlerAdapters will usually only support one handler type each.
|
||||||
|
* <p>A typical implementation:
|
||||||
|
* <p><code>
|
||||||
|
* return (handler instanceof MyHandler);
|
||||||
|
* </code>
|
||||||
|
* @param handler handler object to check
|
||||||
|
* @return whether or not this object can use the given handler
|
||||||
|
*/
|
||||||
|
boolean supports(Object handler);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use the given handler to handle this action request.
|
||||||
|
* The workflow that is required may vary widely.
|
||||||
|
* @param request current action request
|
||||||
|
* @param response current action response
|
||||||
|
* @param handler handler to use. This object must have previously been passed
|
||||||
|
* to the <code>supports</code> method of this interface, which must have
|
||||||
|
* returned true.
|
||||||
|
* @throws Exception in case of errors
|
||||||
|
*/
|
||||||
|
void handleAction(ActionRequest request, ActionResponse response, Object handler) throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use the given handler to handle this render request.
|
||||||
|
* The workflow that is required may vary widely.
|
||||||
|
* @param request current render request
|
||||||
|
* @param response current render response
|
||||||
|
* @param handler handler to use. This object must have previously been passed
|
||||||
|
* to the <code>supports</code> method of this interface, which must have
|
||||||
|
* returned <code>true</code>.
|
||||||
|
* @throws Exception in case of errors
|
||||||
|
* @return ModelAndView object with the name of the view and the required
|
||||||
|
* model data, or <code>null</code> if the request has been handled directly
|
||||||
|
*/
|
||||||
|
ModelAndView handleRender(RenderRequest request, RenderResponse response, Object handler) throws Exception;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2005 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet;
|
||||||
|
|
||||||
|
import javax.portlet.RenderRequest;
|
||||||
|
import javax.portlet.RenderResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface to be implemented by objects than can resolve exceptions thrown
|
||||||
|
* during handler mapping or execution, in the typical case to error views.
|
||||||
|
* Implementors are typically registered as beans in the application context.
|
||||||
|
*
|
||||||
|
* <p>Error views are analogous to the error page JSPs, but can be used with
|
||||||
|
* any kind of exception including any checked exception, with potentially
|
||||||
|
* fine-granular mappings for specific handlers.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public interface HandlerExceptionResolver {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to resolve the given exception that got thrown during on handler execution,
|
||||||
|
* returning a ModelAndView that represents a specific error page if appropriate.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param response current portlet response
|
||||||
|
* @param handler the executed handler, or null if none chosen at the time of
|
||||||
|
* the exception (for example, if multipart resolution failed)
|
||||||
|
* @param ex the exception that got thrown during handler execution
|
||||||
|
* @return a corresponding ModelAndView to forward to, or null for default processing
|
||||||
|
*/
|
||||||
|
ModelAndView resolveException(
|
||||||
|
RenderRequest request, RenderResponse response, Object handler, Exception ex);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2007 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler execution chain, consisting of handler object and any handler interceptors.
|
||||||
|
* Returned by HandlerMapping's {@link HandlerMapping#getHandler} method.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @since 2.0
|
||||||
|
* @see HandlerInterceptor
|
||||||
|
*/
|
||||||
|
public class HandlerExecutionChain {
|
||||||
|
|
||||||
|
private final Object handler;
|
||||||
|
|
||||||
|
private HandlerInterceptor[] interceptors;
|
||||||
|
|
||||||
|
private List interceptorList;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new HandlerExecutionChain.
|
||||||
|
* @param handler the handler object to execute
|
||||||
|
*/
|
||||||
|
public HandlerExecutionChain(Object handler) {
|
||||||
|
this(handler, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new HandlerExecutionChain.
|
||||||
|
* @param handler the handler object to execute
|
||||||
|
* @param interceptors the array of interceptors to apply
|
||||||
|
* (in the given order) before the handler itself executes
|
||||||
|
*/
|
||||||
|
public HandlerExecutionChain(Object handler, HandlerInterceptor[] interceptors) {
|
||||||
|
if (handler instanceof HandlerExecutionChain) {
|
||||||
|
HandlerExecutionChain originalChain = (HandlerExecutionChain) handler;
|
||||||
|
this.handler = originalChain.getHandler();
|
||||||
|
this.interceptorList = new ArrayList();
|
||||||
|
CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList);
|
||||||
|
CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.handler = handler;
|
||||||
|
this.interceptors = interceptors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the handler object to execute.
|
||||||
|
* @return the handler object
|
||||||
|
*/
|
||||||
|
public Object getHandler() {
|
||||||
|
return this.handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addInterceptor(HandlerInterceptor interceptor) {
|
||||||
|
initInterceptorList();
|
||||||
|
this.interceptorList.add(interceptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addInterceptors(HandlerInterceptor[] interceptors) {
|
||||||
|
if (interceptors != null) {
|
||||||
|
initInterceptorList();
|
||||||
|
for (int i = 0; i < interceptors.length; i++) {
|
||||||
|
this.interceptorList.add(interceptors[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initInterceptorList() {
|
||||||
|
if (this.interceptorList == null) {
|
||||||
|
this.interceptorList = new ArrayList();
|
||||||
|
}
|
||||||
|
if (this.interceptors != null) {
|
||||||
|
for (int i = 0; i < this.interceptors.length; i++) {
|
||||||
|
this.interceptorList.add(this.interceptors[i]);
|
||||||
|
}
|
||||||
|
this.interceptors = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the array of interceptors to apply (in the given order).
|
||||||
|
* @return the array of HandlerInterceptors instances (may be <code>null</code>)
|
||||||
|
*/
|
||||||
|
public HandlerInterceptor[] getInterceptors() {
|
||||||
|
if (this.interceptors == null && this.interceptorList != null) {
|
||||||
|
this.interceptors = (HandlerInterceptor[])
|
||||||
|
this.interceptorList.toArray(new HandlerInterceptor[this.interceptorList.size()]);
|
||||||
|
}
|
||||||
|
return this.interceptors;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,211 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2007 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet;
|
||||||
|
|
||||||
|
import javax.portlet.ActionRequest;
|
||||||
|
import javax.portlet.ActionResponse;
|
||||||
|
import javax.portlet.RenderRequest;
|
||||||
|
import javax.portlet.RenderResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Workflow interface that allows for customized handler execution chains.
|
||||||
|
* Applications can register any number of existing or custom interceptors
|
||||||
|
* for certain groups of handlers, to add common pre-processing behavior
|
||||||
|
* without needing to modify each handler implementation.
|
||||||
|
*
|
||||||
|
* <p>A <code>HandlerInterceptor</code> gets called before the appropriate
|
||||||
|
* {@link org.springframework.web.portlet.HandlerAdapter} triggers the
|
||||||
|
* execution of the handler itself. This mechanism can be used for a large
|
||||||
|
* field of preprocessing aspects, e.g. for authorization checks,
|
||||||
|
* or common handler behavior like locale or theme changes. Its main purpose
|
||||||
|
* is to permit the factoring out of otherwise repetitive handler code.
|
||||||
|
*
|
||||||
|
* <p>Typically an interceptor chain is defined per
|
||||||
|
* {@link org.springframework.web.portlet.HandlerMapping} bean, sharing its
|
||||||
|
* granularity. To be able to apply a certain interceptor chain to a group of
|
||||||
|
* handlers, one needs to map the desired handlers via one
|
||||||
|
* <code>HandlerMapping</code> bean. The interceptors themselves are defined as
|
||||||
|
* beans in the application context, referenced by the mapping bean definition
|
||||||
|
* via its
|
||||||
|
* {@link org.springframework.web.portlet.handler.AbstractHandlerMapping#setInterceptors "interceptors"}
|
||||||
|
* property (in XML: a <list> of <ref> elements).
|
||||||
|
*
|
||||||
|
* <p>A <code>HandlerInterceptor</code> is basically similar to a Servlet
|
||||||
|
* {@link javax.servlet.Filter}, but in contrast to the latter it allows
|
||||||
|
* custom pre-processing with the option to prohibit the execution of the handler
|
||||||
|
* itself, and custom post-processing. <code>Filters</code> are more powerful;
|
||||||
|
* for example they allow for exchanging the request and response objects that
|
||||||
|
* are handed down the chain. Note that a filter gets configured in
|
||||||
|
* <code>web.xml</code>, a <code>HandlerInterceptor</code> in the application context.
|
||||||
|
*
|
||||||
|
* <p>As a basic guideline, fine-grained handler-related pre-processing tasks are
|
||||||
|
* candidates for <code>HandlerInterceptor</code> implementations, especially
|
||||||
|
* factored-out common handler code and authorization checks. On the other hand,
|
||||||
|
* a <code>Filter</code> is well-suited for request content and view content
|
||||||
|
* handling, like multipart forms and GZIP compression. This typically shows when
|
||||||
|
* one needs to map the filter to certain content types (e.g. images), or to all
|
||||||
|
* requests.
|
||||||
|
*
|
||||||
|
* <p>Be aware that filters cannot be applied to portlet requests (they
|
||||||
|
* only operate on servlet requests), so for portlet requests interceptors are
|
||||||
|
* essential.
|
||||||
|
*
|
||||||
|
* <p>If we assume a "sunny day" request cycle (i.e. a request where nothing goes wrong
|
||||||
|
* and all is well), the workflow of a <code>HandlerInterceptor</code> will be as
|
||||||
|
* follows:
|
||||||
|
*
|
||||||
|
* <p><b>Action Request:</b><p>
|
||||||
|
* <ol>
|
||||||
|
* <li><code>DispatcherPortlet</code> maps the action request to a particular handler
|
||||||
|
* and assembles a handler execution chain consisting of the handler that
|
||||||
|
* is to be invoked and all of the <code>HandlerInterceptor</code>
|
||||||
|
* instances that apply to the request.</li>
|
||||||
|
* <li>{@link org.springframework.web.portlet.HandlerInterceptor#preHandleAction(javax.portlet.ActionRequest, javax.portlet.ActionResponse, Object) preHandleAction(..)}
|
||||||
|
* is called; if the invocation of this method returns <code>true</code> then
|
||||||
|
* this workflow continues</li>
|
||||||
|
* <li>The target handler handles the action request (via
|
||||||
|
* {@link org.springframework.web.portlet.HandlerAdapter#handleAction(javax.portlet.ActionRequest, javax.portlet.ActionResponse, Object) HandlerAdapter.handleAction(..)})</li>
|
||||||
|
* <li>{@link org.springframework.web.portlet.HandlerInterceptor#afterActionCompletion(javax.portlet.ActionRequest, javax.portlet.ActionResponse, Object, Exception) afterActionCompletion(..)}
|
||||||
|
* is called</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* <p><b>Render Request:</b><p>
|
||||||
|
* <ol>
|
||||||
|
* <li><code>DispatcherPortlet</code> maps the render request to a particular handler
|
||||||
|
* and assembles a handler execution chain consisting of the handler that
|
||||||
|
* is to be invoked and all of the <code>HandlerInterceptor</code>
|
||||||
|
* instances that apply to the request.</li>
|
||||||
|
* <li>{@link org.springframework.web.portlet.HandlerInterceptor#preHandleRender(javax.portlet.RenderRequest, javax.portlet.RenderResponse, Object) preHandleRender(..)}
|
||||||
|
* is called; if the invocation of this method returns <code>true</code> then
|
||||||
|
* this workflow continues</li>
|
||||||
|
* <li>The target handler handles the render request (via
|
||||||
|
* {@link org.springframework.web.portlet.HandlerAdapter#handleRender(javax.portlet.RenderRequest, javax.portlet.RenderResponse, Object) HandlerAdapter.handleRender(..)})</li>
|
||||||
|
* <li>{@link org.springframework.web.portlet.HandlerInterceptor#postHandleRender(javax.portlet.RenderRequest, javax.portlet.RenderResponse, Object, ModelAndView) postHandleRender(..)}
|
||||||
|
* is called</li>
|
||||||
|
* <li>If the <code>HandlerAdapter</code> returned a <code>ModelAndView</code>,
|
||||||
|
* then <code>DispatcherPortlet</code> renders the view accordingly
|
||||||
|
* <li>{@link org.springframework.web.portlet.HandlerInterceptor#afterRenderCompletion(javax.portlet.RenderRequest, javax.portlet.RenderResponse, Object, Exception) afterRenderCompletion(..)}
|
||||||
|
* is called</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @since 2.0
|
||||||
|
* @see HandlerExecutionChain#getInterceptors
|
||||||
|
* @see org.springframework.web.portlet.HandlerMapping
|
||||||
|
* @see org.springframework.web.portlet.handler.AbstractHandlerMapping#setInterceptors
|
||||||
|
* @see org.springframework.web.portlet.HandlerExecutionChain
|
||||||
|
*/
|
||||||
|
public interface HandlerInterceptor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Intercept the execution of a handler in the action phase.
|
||||||
|
* <p>Called after a HandlerMapping determines an appropriate handler object
|
||||||
|
* to handle an {@link ActionRequest}, but before said HandlerAdapter actually
|
||||||
|
* invokes the handler.
|
||||||
|
* <p>{@link DispatcherPortlet} processes a handler in an execution chain,
|
||||||
|
* consisting of any number of interceptors, with the handler itself at the end.
|
||||||
|
* With this method, each interceptor can decide to abort the execution chain,
|
||||||
|
* typically throwing an exception or writing a custom response.
|
||||||
|
* @param request current portlet action request
|
||||||
|
* @param response current portlet action response
|
||||||
|
* @param handler chosen handler to execute, for type and/or instance evaluation
|
||||||
|
* @return <code>true</code> if the execution chain should proceed with the
|
||||||
|
* next interceptor or the handler itself. Else, <code>DispatcherPortlet</code>
|
||||||
|
* assumes that this interceptor has already dealt with the response itself
|
||||||
|
* @throws Exception in case of errors
|
||||||
|
*/
|
||||||
|
boolean preHandleAction(ActionRequest request, ActionResponse response, Object handler)
|
||||||
|
throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback after completion of request processing in the action phase, that is,
|
||||||
|
* after rendering the view. Will be called on any outcome of handler execution,
|
||||||
|
* thus allowing for proper resource cleanup.
|
||||||
|
* <p>Note: Will only be called if this interceptor's
|
||||||
|
* {@link #preHandleAction(javax.portlet.ActionRequest, javax.portlet.ActionResponse, Object)}
|
||||||
|
* method has successfully completed and returned <code>true</code>!
|
||||||
|
* @param request current portlet action request
|
||||||
|
* @param response current portlet action response
|
||||||
|
* @param handler chosen handler to execute, for type and/or instance examination
|
||||||
|
* @param ex exception thrown on handler execution, if any (only included as
|
||||||
|
* additional context information for the case where a handler threw an exception;
|
||||||
|
* request execution may have failed even when this argument is <code>null</code>)
|
||||||
|
* @throws Exception in case of errors
|
||||||
|
*/
|
||||||
|
void afterActionCompletion(
|
||||||
|
ActionRequest request, ActionResponse response, Object handler, Exception ex)
|
||||||
|
throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Intercept the execution of a handler in the render phase.
|
||||||
|
* <p>Called after a HandlerMapping determines an appropriate handler object
|
||||||
|
* to handle a {@link RenderRequest}, but before said HandlerAdapter actually
|
||||||
|
* invokes the handler.
|
||||||
|
* <p>{@link DispatcherPortlet} processes a handler in an execution chain,
|
||||||
|
* consisting of any number of interceptors, with the handler itself at the end.
|
||||||
|
* With this method, each interceptor can decide to abort the execution chain,
|
||||||
|
* typically throwing an exception or writing a custom response.
|
||||||
|
* @param request current portlet render request
|
||||||
|
* @param response current portlet render response
|
||||||
|
* @param handler chosen handler to execute, for type and/or instance evaluation
|
||||||
|
* @return <code>true</code> if the execution chain should proceed with the
|
||||||
|
* next interceptor or the handler itself. Else, <code>DispatcherPortlet</code>
|
||||||
|
* assumes that this interceptor has already dealt with the response itself
|
||||||
|
* @throws Exception in case of errors
|
||||||
|
*/
|
||||||
|
boolean preHandleRender(RenderRequest request, RenderResponse response, Object handler)
|
||||||
|
throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Intercept the execution of a handler in the render phase.
|
||||||
|
* <p>Called after a {@link HandlerAdapter} actually invoked the handler, but
|
||||||
|
* before the <code>DispatcherPortlet</code> renders the view. Can thus expose
|
||||||
|
* additional model objects to the view via the given {@link ModelAndView}.
|
||||||
|
* <p><code>DispatcherPortlet</code> processes a handler in an execution chain,
|
||||||
|
* consisting of any number of interceptors, with the handler itself at the end.
|
||||||
|
* With this method, each interceptor can post-process an execution, getting
|
||||||
|
* applied in inverse order of the execution chain.
|
||||||
|
* @param request current portlet render request
|
||||||
|
* @param response current portlet render response
|
||||||
|
* @param handler chosen handler to execute, for type and/or instance examination
|
||||||
|
* @param modelAndView the <code>ModelAndView</code> that the handler returned
|
||||||
|
* (can also be <code>null</code>)
|
||||||
|
* @throws Exception in case of errors
|
||||||
|
*/
|
||||||
|
void postHandleRender(
|
||||||
|
RenderRequest request, RenderResponse response, Object handler, ModelAndView modelAndView)
|
||||||
|
throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback after completion of request processing, that is, after rendering
|
||||||
|
* the view. Will be called on any outcome of handler execution, thus allowing
|
||||||
|
* for proper resource cleanup.
|
||||||
|
* <p>Note: Will only be called if this interceptor's
|
||||||
|
* {@link #preHandleRender(javax.portlet.RenderRequest, javax.portlet.RenderResponse, Object)}
|
||||||
|
* method has successfully completed and returned <code>true</code>!
|
||||||
|
* @param request current portlet render request
|
||||||
|
* @param response current portlet render response
|
||||||
|
* @param handler chosen handler to execute, for type and/or instance examination
|
||||||
|
* @param ex exception thrown on handler execution, if any
|
||||||
|
* @throws Exception in case of errors
|
||||||
|
*/
|
||||||
|
void afterRenderCompletion(
|
||||||
|
RenderRequest request, RenderResponse response, Object handler, Exception ex)
|
||||||
|
throws Exception;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2007 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet;
|
||||||
|
|
||||||
|
import javax.portlet.PortletRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface to be implemented by objects that define a mapping between
|
||||||
|
* requests and handler objects.
|
||||||
|
*
|
||||||
|
* <p>This class can be implemented by application developers, although this is not
|
||||||
|
* necessary, as {@link org.springframework.web.portlet.handler.PortletModeHandlerMapping},
|
||||||
|
* {@link org.springframework.web.portlet.handler.ParameterHandlerMapping} and
|
||||||
|
* {@link org.springframework.web.portlet.handler.PortletModeParameterHandlerMapping}
|
||||||
|
* are included in the framework. The first is the default if no HandlerMapping
|
||||||
|
* bean is registered in the portlet application context.
|
||||||
|
*
|
||||||
|
* <p>HandlerMapping implementations can support mapped interceptors but do not
|
||||||
|
* have to. A handler will always be wrapped in a {@link HandlerExecutionChain}
|
||||||
|
* instance, optionally accompanied by some {@link HandlerInterceptor} instances.
|
||||||
|
* The DispatcherPortlet will first call each HandlerInterceptor's
|
||||||
|
* <code>preHandle</code> method in the given order, finally invoking the handler
|
||||||
|
* itself if all <code>preHandle</code> methods have returned <code>true</code>.
|
||||||
|
*
|
||||||
|
* <p>The ability to parameterize this mapping is a powerful and unusual
|
||||||
|
* capability of this Portlet MVC framework. For example, it is possible to
|
||||||
|
* write a custom mapping based on session state, cookie state or many other
|
||||||
|
* variables. No other MVC framework seems to be equally flexible.
|
||||||
|
*
|
||||||
|
* <p>Note: Implementations can implement the {@link org.springframework.core.Ordered}
|
||||||
|
* interface to be able to specify a sorting order and thus a priority for getting
|
||||||
|
* applied by DispatcherPortlet. Non-Ordered instances get treated as lowest priority.
|
||||||
|
*
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @see org.springframework.core.Ordered
|
||||||
|
* @see org.springframework.web.portlet.handler.AbstractHandlerMapping
|
||||||
|
* @see org.springframework.web.portlet.handler.PortletModeHandlerMapping
|
||||||
|
* @see org.springframework.web.portlet.handler.ParameterHandlerMapping
|
||||||
|
* @see org.springframework.web.portlet.handler.PortletModeParameterHandlerMapping
|
||||||
|
*/
|
||||||
|
public interface HandlerMapping {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a handler and any interceptors for this request. The choice may be made
|
||||||
|
* on portlet mode, session state, or any factor the implementing class chooses.
|
||||||
|
* <p>The returned HandlerExecutionChain contains a handler Object, rather than
|
||||||
|
* even a tag interface, so that handlers are not constrained in any way.
|
||||||
|
* For example, a HandlerAdapter could be written to allow another framework's
|
||||||
|
* handler objects to be used.
|
||||||
|
* <p>Returns <code>null</code> if no match was found. This is not an error.
|
||||||
|
* The DispatcherPortlet will query all registered HandlerMapping beans to find
|
||||||
|
* a match, and only decide there is an error if none can find a handler.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @return a HandlerExecutionChain instance containing handler object and
|
||||||
|
* any interceptors, or null if no mapping found
|
||||||
|
* @throws Exception if there is an internal error
|
||||||
|
*/
|
||||||
|
HandlerExecutionChain getHandler(PortletRequest request) throws Exception;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,303 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2006 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.ui.ModelMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holder for both Model and View in the web MVC framework.
|
||||||
|
* Note that these are entirely distinct. This class merely holds
|
||||||
|
* both to make it possible for a controller to return both model
|
||||||
|
* and view in a single return value.
|
||||||
|
*
|
||||||
|
* <p>Represents a model and view returned by a handler, to be resolved
|
||||||
|
* by a DispatcherPortlet. The view can take the form of a String
|
||||||
|
* view name which will need to be resolved by a ViewResolver object;
|
||||||
|
* alternatively a view object can be specified directly. The model
|
||||||
|
* is a Map, allowing the use of multiple objects keyed by name.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 2.0
|
||||||
|
* @see org.springframework.web.portlet.DispatcherPortlet
|
||||||
|
* @see org.springframework.web.servlet.ViewResolver
|
||||||
|
* @see org.springframework.web.portlet.HandlerAdapter
|
||||||
|
* @see org.springframework.web.portlet.mvc.Controller
|
||||||
|
*/
|
||||||
|
public class ModelAndView {
|
||||||
|
|
||||||
|
/** View instance or view name String */
|
||||||
|
private Object view;
|
||||||
|
|
||||||
|
/** Model Map */
|
||||||
|
private ModelMap model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether or not this instance has been cleared with a call to {@link #clear()}.
|
||||||
|
*/
|
||||||
|
private boolean cleared;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default constructor for bean-style usage: populating bean
|
||||||
|
* properties instead of passing in constructor arguments.
|
||||||
|
* @see #setView(Object)
|
||||||
|
* @see #setViewName(String)
|
||||||
|
*/
|
||||||
|
public ModelAndView() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenient constructor when there is no model data to expose.
|
||||||
|
* Can also be used in conjunction with <code>addObject</code>.
|
||||||
|
* @param viewName name of the View to render, to be resolved
|
||||||
|
* by the DispatcherPortlet's ViewResolver
|
||||||
|
* @see #addObject
|
||||||
|
*/
|
||||||
|
public ModelAndView(String viewName) {
|
||||||
|
this.view = viewName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenient constructor when there is no model data to expose.
|
||||||
|
* Can also be used in conjunction with <code>addObject</code>.
|
||||||
|
* @param view View object to render (usually a Servlet MVC View object)
|
||||||
|
* @see #addObject
|
||||||
|
*/
|
||||||
|
public ModelAndView(Object view) {
|
||||||
|
this.view = view;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new ModelAndView given a view name and a model.
|
||||||
|
* @param viewName name of the View to render, to be resolved
|
||||||
|
* by the DispatcherPortlet's ViewResolver
|
||||||
|
* @param model Map of model names (Strings) to model objects
|
||||||
|
* (Objects). Model entries may not be <code>null</code>, but the
|
||||||
|
* model Map may be <code>null</code> if there is no model data.
|
||||||
|
*/
|
||||||
|
public ModelAndView(String viewName, Map model) {
|
||||||
|
this.view = viewName;
|
||||||
|
if (model != null) {
|
||||||
|
getModelMap().addAllAttributes(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new ModelAndView given a View object and a model.
|
||||||
|
* @param view View object to render (usually a Servlet MVC View object)
|
||||||
|
* @param model Map of model names (Strings) to model objects
|
||||||
|
* (Objects). Model entries may not be <code>null</code>, but the
|
||||||
|
* model Map may be <code>null</code> if there is no model data.
|
||||||
|
*/
|
||||||
|
public ModelAndView(Object view, Map model) {
|
||||||
|
this.view = view;
|
||||||
|
if (model != null) {
|
||||||
|
getModelMap().addAllAttributes(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenient constructor to take a single model object.
|
||||||
|
* @param viewName name of the View to render, to be resolved
|
||||||
|
* by the DispatcherPortlet's ViewResolver
|
||||||
|
* @param modelName name of the single entry in the model
|
||||||
|
* @param modelObject the single model object
|
||||||
|
*/
|
||||||
|
public ModelAndView(String viewName, String modelName, Object modelObject) {
|
||||||
|
this.view = viewName;
|
||||||
|
addObject(modelName, modelObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenient constructor to take a single model object.
|
||||||
|
* @param view View object to render (usually a Servlet MVC View object)
|
||||||
|
* @param modelName name of the single entry in the model
|
||||||
|
* @param modelObject the single model object
|
||||||
|
*/
|
||||||
|
public ModelAndView(Object view, String modelName, Object modelObject) {
|
||||||
|
this.view = view;
|
||||||
|
addObject(modelName, modelObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a view name for this ModelAndView, to be resolved by the
|
||||||
|
* DispatcherPortlet via a ViewResolver. Will override any
|
||||||
|
* pre-existing view name or View.
|
||||||
|
*/
|
||||||
|
public void setViewName(String viewName) {
|
||||||
|
this.view = viewName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the view name to be resolved by the DispatcherPortlet
|
||||||
|
* via a ViewResolver, or <code>null</code> if we are using a view object.
|
||||||
|
*/
|
||||||
|
public String getViewName() {
|
||||||
|
return (this.view instanceof String ? (String) this.view : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a View object for this ModelAndView. Will override any
|
||||||
|
* pre-existing view name or View.
|
||||||
|
* <p>The given View object will usually be a Servlet MVC View object.
|
||||||
|
* This is nevertheless typed as Object to avoid a Servlet API dependency
|
||||||
|
* in the Portlet ModelAndView class.
|
||||||
|
*/
|
||||||
|
public void setView(Object view) {
|
||||||
|
this.view = view;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the View object, or <code>null</code> if we are using a view name
|
||||||
|
* to be resolved by the DispatcherPortlet via a ViewResolver.
|
||||||
|
*/
|
||||||
|
public Object getView() {
|
||||||
|
return (!(this.view instanceof String) ? this.view : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate whether or not this <code>ModelAndView</code> has a view, either
|
||||||
|
* as a view name or as a direct view instance.
|
||||||
|
*/
|
||||||
|
public boolean hasView() {
|
||||||
|
return (this.view != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether we use a view reference, i.e. <code>true</code>
|
||||||
|
* if the view has been specified via a name to be resolved by the
|
||||||
|
* DispatcherPortlet via a ViewResolver.
|
||||||
|
*/
|
||||||
|
public boolean isReference() {
|
||||||
|
return (this.view instanceof String);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the model map. May return <code>null</code>.
|
||||||
|
* Called by DispatcherPortlet for evaluation of the model.
|
||||||
|
*/
|
||||||
|
protected Map getModelInternal() {
|
||||||
|
return this.model;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the underlying <code>ModelMap</code> instance (never <code>null</code>).
|
||||||
|
*/
|
||||||
|
public ModelMap getModelMap() {
|
||||||
|
if (this.model == null) {
|
||||||
|
this.model = new ModelMap();
|
||||||
|
}
|
||||||
|
return this.model;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the model map. Never returns <code>null</code>.
|
||||||
|
* To be called by application code for modifying the model.
|
||||||
|
*/
|
||||||
|
public Map getModel() {
|
||||||
|
return getModelMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an attribute to the model.
|
||||||
|
* @param attributeName name of the object to add to the model
|
||||||
|
* @param attributeValue object to add to the model (never <code>null</code>)
|
||||||
|
* @see ModelMap#addAttribute(String, Object)
|
||||||
|
* @see #getModelMap()
|
||||||
|
*/
|
||||||
|
public ModelAndView addObject(String attributeName, Object attributeValue) {
|
||||||
|
getModelMap().addAttribute(attributeName, attributeValue);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an attribute to the model using parameter name generation.
|
||||||
|
* @param attributeValue the object to add to the model (never <code>null</code>)
|
||||||
|
* @see ModelMap#addAttribute(Object)
|
||||||
|
* @see #getModelMap()
|
||||||
|
*/
|
||||||
|
public ModelAndView addObject(Object attributeValue) {
|
||||||
|
getModelMap().addAttribute(attributeValue);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add all attributes contained in the provided Map to the model.
|
||||||
|
* @param modelMap a Map of attributeName -> attributeValue pairs
|
||||||
|
* @see ModelMap#addAllAttributes(Map)
|
||||||
|
* @see #getModelMap()
|
||||||
|
*/
|
||||||
|
public ModelAndView addAllObjects(Map modelMap) {
|
||||||
|
getModelMap().addAllAttributes(modelMap);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the state of this ModelAndView object.
|
||||||
|
* The object will be empty afterwards.
|
||||||
|
* <p>Can be used to suppress rendering of a given ModelAndView object
|
||||||
|
* in the <code>postHandleRender</code> method of a HandlerInterceptor.
|
||||||
|
* @see #isEmpty()
|
||||||
|
* @see HandlerInterceptor#postHandleRender
|
||||||
|
*/
|
||||||
|
public void clear() {
|
||||||
|
this.view = null;
|
||||||
|
this.model = null;
|
||||||
|
this.cleared = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether this ModelAndView object is empty
|
||||||
|
* i.e. whether it does not hold any view and does not contain a model.
|
||||||
|
*/
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return (this.view == null && this.model == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether this ModelAndView object is empty as a result of a call to {@link #clear}
|
||||||
|
* i.e. whether it does not hold any view and does not contain a model.
|
||||||
|
* Returns <code>false</code> if any additional state was added to the instance
|
||||||
|
* <strong>after</strong> the call to {@link #clear}.
|
||||||
|
* @see #clear()
|
||||||
|
*/
|
||||||
|
public boolean wasCleared() {
|
||||||
|
return (this.cleared && isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return diagnostic information about this model and view.
|
||||||
|
*/
|
||||||
|
public String toString() {
|
||||||
|
StringBuffer buf = new StringBuffer("ModelAndView: ");
|
||||||
|
if (isReference()) {
|
||||||
|
buf.append("reference to view with name '").append(this.view).append("'");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
buf.append("materialized View is [").append(this.view).append(']');
|
||||||
|
}
|
||||||
|
buf.append("; model is ").append(this.model);
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2005 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet;
|
||||||
|
|
||||||
|
import javax.portlet.PortletException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception to be thrown on error conditions that should forward
|
||||||
|
* to a specific view with a specific model.
|
||||||
|
*
|
||||||
|
* <p>Can be thrown at any time during handler processing.
|
||||||
|
* This includes any template methods of pre-built controllers.
|
||||||
|
* For example, a form controller might abort to a specific error page
|
||||||
|
* if certain parameters do not allow to proceed with the normal workflow.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public class ModelAndViewDefiningException extends PortletException {
|
||||||
|
|
||||||
|
private ModelAndView modelAndView;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create new ModelAndViewDefiningException with the given ModelAndView,
|
||||||
|
* typically representing a specific error page.
|
||||||
|
* @param modelAndView ModelAndView with view to forward to and model to expose
|
||||||
|
*/
|
||||||
|
public ModelAndViewDefiningException(ModelAndView modelAndView) {
|
||||||
|
if (modelAndView == null) {
|
||||||
|
throw new IllegalArgumentException("modelAndView must not be null in ModelAndViewDefiningException");
|
||||||
|
}
|
||||||
|
this.modelAndView = modelAndView;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the ModelAndView that this exception contains for forwarding to.
|
||||||
|
*/
|
||||||
|
public ModelAndView getModelAndView() {
|
||||||
|
return modelAndView;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2007 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.bind;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PortletRequestBindingException subclass that indicates a missing parameter.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 2.0.2
|
||||||
|
*/
|
||||||
|
public class MissingPortletRequestParameterException extends PortletRequestBindingException {
|
||||||
|
|
||||||
|
private String parameterName;
|
||||||
|
|
||||||
|
private String parameterType;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for MissingPortletRequestParameterException.
|
||||||
|
* @param parameterName the name of the missing parameter
|
||||||
|
* @param parameterType the expected type of the missing parameter
|
||||||
|
*/
|
||||||
|
public MissingPortletRequestParameterException(String parameterName, String parameterType) {
|
||||||
|
super("");
|
||||||
|
this.parameterName = parameterName;
|
||||||
|
this.parameterType = parameterType;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return "Required " + this.parameterType + " parameter '" + parameterName + "' is not present";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of the offending parameter.
|
||||||
|
*/
|
||||||
|
public String getParameterName() {
|
||||||
|
return this.parameterName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the expected type of the offending parameter.
|
||||||
|
*/
|
||||||
|
public String getParameterType() {
|
||||||
|
return this.parameterType;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2006 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.bind;
|
||||||
|
|
||||||
|
import javax.portlet.PortletException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fatal binding exception, thrown when we want to
|
||||||
|
* treat binding exceptions as unrecoverable.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public class PortletRequestBindingException extends PortletException {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for PortletRequestBindingException.
|
||||||
|
* @param msg the detail message
|
||||||
|
*/
|
||||||
|
public PortletRequestBindingException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for PortletRequestBindingException.
|
||||||
|
* @param msg the detail message
|
||||||
|
* @param cause the root cause
|
||||||
|
*/
|
||||||
|
public PortletRequestBindingException(String msg, Throwable cause) {
|
||||||
|
super(msg, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,129 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2008 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.bind;
|
||||||
|
|
||||||
|
import javax.portlet.PortletRequest;
|
||||||
|
|
||||||
|
import org.springframework.beans.MutablePropertyValues;
|
||||||
|
import org.springframework.validation.BindException;
|
||||||
|
import org.springframework.web.bind.WebDataBinder;
|
||||||
|
import org.springframework.web.portlet.multipart.MultipartActionRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special {@link org.springframework.validation.DataBinder} to perform data binding
|
||||||
|
* from portlet request parameters to JavaBeans, including support for multipart files.
|
||||||
|
*
|
||||||
|
* <p>See the DataBinder/WebDataBinder superclasses for customization options,
|
||||||
|
* which include specifying allowed/required fields, and registering custom
|
||||||
|
* property editors.
|
||||||
|
*
|
||||||
|
* <p>Used by Spring Portlet MVC's BaseCommandController.
|
||||||
|
* Note that BaseCommandController and its subclasses allow for easy customization
|
||||||
|
* of the binder instances that they use through overriding <code>initBinder</code>.
|
||||||
|
*
|
||||||
|
* <p>Can also be used for manual data binding in custom web controllers:
|
||||||
|
* for example, in a plain Portlet Controller implementation. Simply instantiate
|
||||||
|
* a PortletRequestDataBinder for each binding process, and invoke <code>bind</code>
|
||||||
|
* with the current PortletRequest as argument:
|
||||||
|
*
|
||||||
|
* <pre class="code">
|
||||||
|
* MyBean myBean = new MyBean();
|
||||||
|
* // apply binder to custom target object
|
||||||
|
* PortletRequestDataBinder binder = new PortletRequestDataBinder(myBean);
|
||||||
|
* // register custom editors, if desired
|
||||||
|
* binder.registerCustomEditor(...);
|
||||||
|
* // trigger actual binding of request parameters
|
||||||
|
* binder.bind(request);
|
||||||
|
* // optionally evaluate binding errors
|
||||||
|
* Errors errors = binder.getErrors();
|
||||||
|
* ...</pre>
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @since 2.0
|
||||||
|
* @see #bind(javax.portlet.PortletRequest)
|
||||||
|
* @see #registerCustomEditor
|
||||||
|
* @see #setAllowedFields
|
||||||
|
* @see #setRequiredFields
|
||||||
|
* @see #setFieldMarkerPrefix
|
||||||
|
* @see org.springframework.web.portlet.mvc.BaseCommandController#initBinder
|
||||||
|
*/
|
||||||
|
public class PortletRequestDataBinder extends WebDataBinder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new PortletRequestDataBinder instance, with default object name.
|
||||||
|
* @param target the target object to bind onto (or <code>null</code>
|
||||||
|
* if the binder is just used to convert a plain parameter value)
|
||||||
|
* @see #DEFAULT_OBJECT_NAME
|
||||||
|
*/
|
||||||
|
public PortletRequestDataBinder(Object target) {
|
||||||
|
super(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new PortletRequestDataBinder instance.
|
||||||
|
* @param target the target object to bind onto (or <code>null</code>
|
||||||
|
* if the binder is just used to convert a plain parameter value)
|
||||||
|
* @param objectName the name of the target object
|
||||||
|
*/
|
||||||
|
public PortletRequestDataBinder(Object target, String objectName) {
|
||||||
|
super(target, objectName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bind the parameters of the given request to this binder's target,
|
||||||
|
* also binding multipart files in case of a multipart request.
|
||||||
|
* <p>This call can create field errors, representing basic binding
|
||||||
|
* errors like a required field (code "required"), or type mismatch
|
||||||
|
* between value and bean property (code "typeMismatch").
|
||||||
|
* <p>Multipart files are bound via their parameter name, just like normal
|
||||||
|
* HTTP parameters: i.e. "uploadedFile" to an "uploadedFile" bean property,
|
||||||
|
* invoking a "setUploadedFile" setter method.
|
||||||
|
* <p>The type of the target property for a multipart file can be MultipartFile,
|
||||||
|
* byte[], or String. The latter two receive the contents of the uploaded file;
|
||||||
|
* all metadata like original file name, content type, etc are lost in those cases.
|
||||||
|
* @param request request with parameters to bind (can be multipart)
|
||||||
|
* @see org.springframework.web.portlet.multipart.MultipartActionRequest
|
||||||
|
* @see org.springframework.web.multipart.MultipartFile
|
||||||
|
* @see #bindMultipartFiles
|
||||||
|
* @see #bind(org.springframework.beans.PropertyValues)
|
||||||
|
*/
|
||||||
|
public void bind(PortletRequest request) {
|
||||||
|
MutablePropertyValues mpvs = new PortletRequestParameterPropertyValues(request);
|
||||||
|
if (request instanceof MultipartActionRequest) {
|
||||||
|
MultipartActionRequest multipartRequest = (MultipartActionRequest) request;
|
||||||
|
bindMultipartFiles(multipartRequest.getFileMap(), mpvs);
|
||||||
|
}
|
||||||
|
doBind(mpvs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Treats errors as fatal.
|
||||||
|
* <p>Use this method only if it's an error if the input isn't valid.
|
||||||
|
* This might be appropriate if all input is from dropdowns, for example.
|
||||||
|
* @throws PortletRequestBindingException subclass of PortletException on any binding problem
|
||||||
|
*/
|
||||||
|
public void closeNoCatch() throws PortletRequestBindingException {
|
||||||
|
if (getBindingResult().hasErrors()) {
|
||||||
|
throw new PortletRequestBindingException(
|
||||||
|
"Errors binding onto object '" + getBindingResult().getObjectName() + "'",
|
||||||
|
new BindException(getBindingResult()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2005 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.bind;
|
||||||
|
|
||||||
|
import javax.portlet.PortletRequest;
|
||||||
|
|
||||||
|
import org.springframework.beans.MutablePropertyValues;
|
||||||
|
import org.springframework.web.portlet.util.PortletUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PropertyValues implementation created from parameters in a PortletRequest.
|
||||||
|
* Can look for all property values beginning with a certain prefix and
|
||||||
|
* prefix separator (default is "_").
|
||||||
|
*
|
||||||
|
* <p>For example, with a prefix of "spring", "spring_param1" and
|
||||||
|
* "spring_param2" result in a Map with "param1" and "param2" as keys.
|
||||||
|
*
|
||||||
|
* <p>This class is not immutable to be able to efficiently remove property
|
||||||
|
* values that should be ignored for binding.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @since 2.0
|
||||||
|
* @see org.springframework.web.portlet.util.PortletUtils#getParametersStartingWith
|
||||||
|
*/
|
||||||
|
public class PortletRequestParameterPropertyValues extends MutablePropertyValues {
|
||||||
|
|
||||||
|
/** Default prefix separator */
|
||||||
|
public static final String DEFAULT_PREFIX_SEPARATOR = "_";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create new PortletRequestPropertyValues using no prefix
|
||||||
|
* (and hence, no prefix separator).
|
||||||
|
* @param request portlet request
|
||||||
|
*/
|
||||||
|
public PortletRequestParameterPropertyValues(PortletRequest request) {
|
||||||
|
this(request, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create new PortletRequestPropertyValues using the given prefix and
|
||||||
|
* the default prefix separator (the underscore character "_").
|
||||||
|
* @param request portlet request
|
||||||
|
* @param prefix the prefix for parameters (the full prefix will
|
||||||
|
* consist of this plus the separator)
|
||||||
|
* @see #DEFAULT_PREFIX_SEPARATOR
|
||||||
|
*/
|
||||||
|
public PortletRequestParameterPropertyValues(PortletRequest request, String prefix) {
|
||||||
|
this(request, prefix, DEFAULT_PREFIX_SEPARATOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create new PortletRequestPropertyValues supplying both prefix and
|
||||||
|
* prefix separator.
|
||||||
|
* @param request portlet request
|
||||||
|
* @param prefix the prefix for parameters (the full prefix will
|
||||||
|
* consist of this plus the separator)
|
||||||
|
* @param prefixSeparator separator delimiting prefix (e.g. "spring")
|
||||||
|
* and the rest of the parameter name ("param1", "param2")
|
||||||
|
*/
|
||||||
|
public PortletRequestParameterPropertyValues(PortletRequest request, String prefix, String prefixSeparator) {
|
||||||
|
super(PortletUtils.getParametersStartingWith(
|
||||||
|
request, (prefix != null) ? prefix + prefixSeparator : null));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,699 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2007 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.bind;
|
||||||
|
|
||||||
|
import javax.portlet.PortletRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parameter extraction methods, for an approach distinct from data binding,
|
||||||
|
* in which parameters of specific types are required.
|
||||||
|
*
|
||||||
|
* <p>This approach is very useful for simple submissions, where binding
|
||||||
|
* request parameters to a command object would be overkill.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author Keith Donald
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public abstract class PortletRequestUtils {
|
||||||
|
|
||||||
|
private static final IntParser INT_PARSER = new IntParser();
|
||||||
|
|
||||||
|
private static final LongParser LONG_PARSER = new LongParser();
|
||||||
|
|
||||||
|
private static final FloatParser FLOAT_PARSER = new FloatParser();
|
||||||
|
|
||||||
|
private static final DoubleParser DOUBLE_PARSER = new DoubleParser();
|
||||||
|
|
||||||
|
private static final BooleanParser BOOLEAN_PARSER = new BooleanParser();
|
||||||
|
|
||||||
|
private static final StringParser STRING_PARSER = new StringParser();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an Integer parameter, or <code>null</code> if not present.
|
||||||
|
* Throws an exception if it the parameter value isn't a number.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the parameter
|
||||||
|
* @return the Integer value, or <code>null</code> if not present
|
||||||
|
* @throws PortletRequestBindingException a subclass of PortletException,
|
||||||
|
* so it doesn't need to be caught
|
||||||
|
*/
|
||||||
|
public static Integer getIntParameter(PortletRequest request, String name)
|
||||||
|
throws PortletRequestBindingException {
|
||||||
|
|
||||||
|
if (request.getParameter(name) == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new Integer(getRequiredIntParameter(request, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an int parameter, with a fallback value. Never throws an exception.
|
||||||
|
* Can pass a distinguished value as default to enable checks of whether it was supplied.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the parameter
|
||||||
|
* @param defaultVal the default value to use as fallback
|
||||||
|
*/
|
||||||
|
public static int getIntParameter(PortletRequest request, String name, int defaultVal) {
|
||||||
|
if (request.getParameter(name) == null) {
|
||||||
|
return defaultVal;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return getRequiredIntParameter(request, name);
|
||||||
|
}
|
||||||
|
catch (PortletRequestBindingException ex) {
|
||||||
|
return defaultVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an array of int parameters, return an empty array if not found.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the parameter with multiple possible values
|
||||||
|
*/
|
||||||
|
public static int[] getIntParameters(PortletRequest request, String name) {
|
||||||
|
try {
|
||||||
|
return getRequiredIntParameters(request, name);
|
||||||
|
}
|
||||||
|
catch (PortletRequestBindingException ex) {
|
||||||
|
return new int[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an int parameter, throwing an exception if it isn't found or isn't a number.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the parameter
|
||||||
|
* @throws PortletRequestBindingException a subclass of PortletException,
|
||||||
|
* so it doesn't need to be caught
|
||||||
|
*/
|
||||||
|
public static int getRequiredIntParameter(PortletRequest request, String name)
|
||||||
|
throws PortletRequestBindingException {
|
||||||
|
|
||||||
|
return INT_PARSER.parseInt(name, request.getParameter(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an array of int parameters, throwing an exception if not found or one is not a number..
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the parameter with multiple possible values
|
||||||
|
* @throws PortletRequestBindingException a subclass of PortletException,
|
||||||
|
* so it doesn't need to be caught
|
||||||
|
*/
|
||||||
|
public static int[] getRequiredIntParameters(PortletRequest request, String name)
|
||||||
|
throws PortletRequestBindingException {
|
||||||
|
|
||||||
|
return INT_PARSER.parseInts(name, request.getParameterValues(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a Long parameter, or <code>null</code> if not present.
|
||||||
|
* Throws an exception if it the parameter value isn't a number.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the parameter
|
||||||
|
* @return the Long value, or <code>null</code> if not present
|
||||||
|
* @throws PortletRequestBindingException a subclass of PortletException,
|
||||||
|
* so it doesn't need to be caught
|
||||||
|
*/
|
||||||
|
public static Long getLongParameter(PortletRequest request, String name)
|
||||||
|
throws PortletRequestBindingException {
|
||||||
|
|
||||||
|
if (request.getParameter(name) == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new Long(getRequiredLongParameter(request, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a long parameter, with a fallback value. Never throws an exception.
|
||||||
|
* Can pass a distinguished value as default to enable checks of whether it was supplied.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the parameter
|
||||||
|
* @param defaultVal the default value to use as fallback
|
||||||
|
*/
|
||||||
|
public static long getLongParameter(PortletRequest request, String name, long defaultVal) {
|
||||||
|
if (request.getParameter(name) == null) {
|
||||||
|
return defaultVal;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return getRequiredLongParameter(request, name);
|
||||||
|
}
|
||||||
|
catch (PortletRequestBindingException ex) {
|
||||||
|
return defaultVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an array of long parameters, return an empty array if not found.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the parameter with multiple possible values
|
||||||
|
*/
|
||||||
|
public static long[] getLongParameters(PortletRequest request, String name) {
|
||||||
|
try {
|
||||||
|
return getRequiredLongParameters(request, name);
|
||||||
|
}
|
||||||
|
catch (PortletRequestBindingException ex) {
|
||||||
|
return new long[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a long parameter, throwing an exception if it isn't found or isn't a number.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the parameter
|
||||||
|
* @throws PortletRequestBindingException a subclass of PortletException,
|
||||||
|
* so it doesn't need to be caught
|
||||||
|
*/
|
||||||
|
public static long getRequiredLongParameter(PortletRequest request, String name)
|
||||||
|
throws PortletRequestBindingException {
|
||||||
|
|
||||||
|
return LONG_PARSER.parseLong(name, request.getParameter(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an array of long parameters, throwing an exception if not found or one is not a number.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the parameter with multiple possible values
|
||||||
|
* @throws PortletRequestBindingException a subclass of PortletException,
|
||||||
|
* so it doesn't need to be caught
|
||||||
|
*/
|
||||||
|
public static long[] getRequiredLongParameters(PortletRequest request, String name)
|
||||||
|
throws PortletRequestBindingException {
|
||||||
|
|
||||||
|
return LONG_PARSER.parseLongs(name, request.getParameterValues(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a Float parameter, or <code>null</code> if not present.
|
||||||
|
* Throws an exception if it the parameter value isn't a number.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the parameter
|
||||||
|
* @return the Float value, or <code>null</code> if not present
|
||||||
|
* @throws PortletRequestBindingException a subclass of PortletException,
|
||||||
|
* so it doesn't need to be caught
|
||||||
|
*/
|
||||||
|
public static Float getFloatParameter(PortletRequest request, String name)
|
||||||
|
throws PortletRequestBindingException {
|
||||||
|
|
||||||
|
if (request.getParameter(name) == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new Float(getRequiredFloatParameter(request, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a float parameter, with a fallback value. Never throws an exception.
|
||||||
|
* Can pass a distinguished value as default to enable checks of whether it was supplied.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the parameter
|
||||||
|
* @param defaultVal the default value to use as fallback
|
||||||
|
*/
|
||||||
|
public static float getFloatParameter(PortletRequest request, String name, float defaultVal) {
|
||||||
|
if (request.getParameter(name) == null) {
|
||||||
|
return defaultVal;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return getRequiredFloatParameter(request, name);
|
||||||
|
}
|
||||||
|
catch (PortletRequestBindingException ex) {
|
||||||
|
return defaultVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an array of float parameters, return an empty array if not found.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the parameter with multiple possible values
|
||||||
|
*/
|
||||||
|
public static float[] getFloatParameters(PortletRequest request, String name) {
|
||||||
|
try {
|
||||||
|
return getRequiredFloatParameters(request, name);
|
||||||
|
}
|
||||||
|
catch (PortletRequestBindingException ex) {
|
||||||
|
return new float[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a float parameter, throwing an exception if it isn't found or isn't a number.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the parameter
|
||||||
|
* @throws PortletRequestBindingException a subclass of PortletException,
|
||||||
|
* so it doesn't need to be caught
|
||||||
|
*/
|
||||||
|
public static float getRequiredFloatParameter(PortletRequest request, String name)
|
||||||
|
throws PortletRequestBindingException {
|
||||||
|
|
||||||
|
return FLOAT_PARSER.parseFloat(name, request.getParameter(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an array of float parameters, throwing an exception if not found or one is not a number.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the parameter with multiple possible values
|
||||||
|
* @throws PortletRequestBindingException a subclass of PortletException,
|
||||||
|
* so it doesn't need to be caught
|
||||||
|
*/
|
||||||
|
public static float[] getRequiredFloatParameters(PortletRequest request, String name)
|
||||||
|
throws PortletRequestBindingException {
|
||||||
|
|
||||||
|
return FLOAT_PARSER.parseFloats(name, request.getParameterValues(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a Double parameter, or <code>null</code> if not present.
|
||||||
|
* Throws an exception if it the parameter value isn't a number.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the parameter
|
||||||
|
* @return the Double value, or <code>null</code> if not present
|
||||||
|
* @throws PortletRequestBindingException a subclass of PortletException,
|
||||||
|
* so it doesn't need to be caught
|
||||||
|
*/
|
||||||
|
public static Double getDoubleParameter(PortletRequest request, String name)
|
||||||
|
throws PortletRequestBindingException {
|
||||||
|
|
||||||
|
if (request.getParameter(name) == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new Double(getRequiredDoubleParameter(request, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a double parameter, with a fallback value. Never throws an exception.
|
||||||
|
* Can pass a distinguished value as default to enable checks of whether it was supplied.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the parameter
|
||||||
|
* @param defaultVal the default value to use as fallback
|
||||||
|
*/
|
||||||
|
public static double getDoubleParameter(PortletRequest request, String name, double defaultVal) {
|
||||||
|
if (request.getParameter(name) == null) {
|
||||||
|
return defaultVal;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return getRequiredDoubleParameter(request, name);
|
||||||
|
}
|
||||||
|
catch (PortletRequestBindingException ex) {
|
||||||
|
return defaultVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an array of double parameters, return an empty array if not found.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the parameter with multiple possible values
|
||||||
|
*/
|
||||||
|
public static double[] getDoubleParameters(PortletRequest request, String name) {
|
||||||
|
try {
|
||||||
|
return getRequiredDoubleParameters(request, name);
|
||||||
|
}
|
||||||
|
catch (PortletRequestBindingException ex) {
|
||||||
|
return new double[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a double parameter, throwing an exception if it isn't found or isn't a number.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the parameter
|
||||||
|
* @throws PortletRequestBindingException a subclass of PortletException,
|
||||||
|
* so it doesn't need to be caught
|
||||||
|
*/
|
||||||
|
public static double getRequiredDoubleParameter(PortletRequest request, String name)
|
||||||
|
throws PortletRequestBindingException {
|
||||||
|
|
||||||
|
return DOUBLE_PARSER.parseDouble(name, request.getParameter(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an array of double parameters, throwing an exception if not found or one is not a number.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the parameter with multiple possible values
|
||||||
|
* @throws PortletRequestBindingException a subclass of PortletException,
|
||||||
|
* so it doesn't need to be caught
|
||||||
|
*/
|
||||||
|
public static double[] getRequiredDoubleParameters(PortletRequest request, String name)
|
||||||
|
throws PortletRequestBindingException {
|
||||||
|
|
||||||
|
return DOUBLE_PARSER.parseDoubles(name, request.getParameterValues(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a Boolean parameter, or <code>null</code> if not present.
|
||||||
|
* Throws an exception if it the parameter value isn't a boolean.
|
||||||
|
* <p>Accepts "true", "on", "yes" (any case) and "1" as values for true;
|
||||||
|
* treats every other non-empty value as false (i.e. parses leniently).
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the parameter
|
||||||
|
* @return the Boolean value, or <code>null</code> if not present
|
||||||
|
* @throws PortletRequestBindingException a subclass of PortletException,
|
||||||
|
* so it doesn't need to be caught
|
||||||
|
*/
|
||||||
|
public static Boolean getBooleanParameter(PortletRequest request, String name)
|
||||||
|
throws PortletRequestBindingException {
|
||||||
|
|
||||||
|
if (request.getParameter(name) == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (getRequiredBooleanParameter(request, name) ? Boolean.TRUE : Boolean.FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a boolean parameter, with a fallback value. Never throws an exception.
|
||||||
|
* Can pass a distinguished value as default to enable checks of whether it was supplied.
|
||||||
|
* <p>Accepts "true", "on", "yes" (any case) and "1" as values for true;
|
||||||
|
* treats every other non-empty value as false (i.e. parses leniently).
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the parameter
|
||||||
|
* @param defaultVal the default value to use as fallback
|
||||||
|
*/
|
||||||
|
public static boolean getBooleanParameter(PortletRequest request, String name, boolean defaultVal) {
|
||||||
|
if (request.getParameter(name) == null) {
|
||||||
|
return defaultVal;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return getRequiredBooleanParameter(request, name);
|
||||||
|
}
|
||||||
|
catch (PortletRequestBindingException ex) {
|
||||||
|
return defaultVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an array of boolean parameters, return an empty array if not found.
|
||||||
|
* <p>Accepts "true", "on", "yes" (any case) and "1" as values for true;
|
||||||
|
* treats every other non-empty value as false (i.e. parses leniently).
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the parameter with multiple possible values
|
||||||
|
*/
|
||||||
|
public static boolean[] getBooleanParameters(PortletRequest request, String name) {
|
||||||
|
try {
|
||||||
|
return getRequiredBooleanParameters(request, name);
|
||||||
|
}
|
||||||
|
catch (PortletRequestBindingException ex) {
|
||||||
|
return new boolean[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a boolean parameter, throwing an exception if it isn't found
|
||||||
|
* or isn't a boolean.
|
||||||
|
* <p>Accepts "true", "on", "yes" (any case) and "1" as values for true;
|
||||||
|
* treats every other non-empty value as false (i.e. parses leniently).
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the parameter
|
||||||
|
* @throws PortletRequestBindingException a subclass of PortletException,
|
||||||
|
* so it doesn't need to be caught
|
||||||
|
*/
|
||||||
|
public static boolean getRequiredBooleanParameter(PortletRequest request, String name)
|
||||||
|
throws PortletRequestBindingException {
|
||||||
|
|
||||||
|
return BOOLEAN_PARSER.parseBoolean(name, request.getParameter(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an array of boolean parameters, throwing an exception if not found
|
||||||
|
* or one isn't a boolean.
|
||||||
|
* <p>Accepts "true", "on", "yes" (any case) and "1" as values for true;
|
||||||
|
* treats every other non-empty value as false (i.e. parses leniently).
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the parameter
|
||||||
|
* @throws PortletRequestBindingException a subclass of PortletException,
|
||||||
|
* so it doesn't need to be caught
|
||||||
|
*/
|
||||||
|
public static boolean[] getRequiredBooleanParameters(PortletRequest request, String name)
|
||||||
|
throws PortletRequestBindingException {
|
||||||
|
|
||||||
|
return BOOLEAN_PARSER.parseBooleans(name, request.getParameterValues(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a String parameter, or <code>null</code> if not present.
|
||||||
|
* Throws an exception if it the parameter value is empty.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the parameter
|
||||||
|
* @return the String value, or <code>null</code> if not present
|
||||||
|
* @throws PortletRequestBindingException a subclass of PortletException,
|
||||||
|
* so it doesn't need to be caught
|
||||||
|
*/
|
||||||
|
public static String getStringParameter(PortletRequest request, String name)
|
||||||
|
throws PortletRequestBindingException {
|
||||||
|
|
||||||
|
if (request.getParameter(name) == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return getRequiredStringParameter(request, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a String parameter, with a fallback value. Never throws an exception.
|
||||||
|
* Can pass a distinguished value to default to enable checks of whether it was supplied.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the parameter
|
||||||
|
* @param defaultVal the default value to use as fallback
|
||||||
|
*/
|
||||||
|
public static String getStringParameter(PortletRequest request, String name, String defaultVal) {
|
||||||
|
String val = request.getParameter(name);
|
||||||
|
return (val != null ? val : defaultVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an array of String parameters, return an empty array if not found.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the parameter with multiple possible values
|
||||||
|
*/
|
||||||
|
public static String[] getStringParameters(PortletRequest request, String name) {
|
||||||
|
try {
|
||||||
|
return getRequiredStringParameters(request, name);
|
||||||
|
}
|
||||||
|
catch (PortletRequestBindingException ex) {
|
||||||
|
return new String[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a String parameter, throwing an exception if it isn't found or is empty.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the parameter
|
||||||
|
* @throws PortletRequestBindingException a subclass of PortletException,
|
||||||
|
* so it doesn't need to be caught
|
||||||
|
*/
|
||||||
|
public static String getRequiredStringParameter(PortletRequest request, String name)
|
||||||
|
throws PortletRequestBindingException {
|
||||||
|
|
||||||
|
return STRING_PARSER.validateRequiredString(name, request.getParameter(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an array of String parameters, throwing an exception if not found or one is empty.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the parameter
|
||||||
|
* @throws PortletRequestBindingException a subclass of PortletException,
|
||||||
|
* so it doesn't need to be caught
|
||||||
|
*/
|
||||||
|
public static String[] getRequiredStringParameters(PortletRequest request, String name)
|
||||||
|
throws PortletRequestBindingException {
|
||||||
|
|
||||||
|
return STRING_PARSER.validateRequiredStrings(name, request.getParameterValues(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private abstract static class ParameterParser {
|
||||||
|
|
||||||
|
protected final Object parse(String name, String parameter) throws PortletRequestBindingException {
|
||||||
|
validateRequiredParameter(name, parameter);
|
||||||
|
try {
|
||||||
|
return doParse(parameter);
|
||||||
|
}
|
||||||
|
catch (NumberFormatException ex) {
|
||||||
|
throw new PortletRequestBindingException(
|
||||||
|
"Required " + getType() + " parameter '" + name + "' with value of '" +
|
||||||
|
parameter + "' is not a valid number", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final void validateRequiredParameter(String name, Object parameter)
|
||||||
|
throws PortletRequestBindingException {
|
||||||
|
|
||||||
|
if (parameter == null) {
|
||||||
|
throw new MissingPortletRequestParameterException(name, getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract String getType();
|
||||||
|
|
||||||
|
protected abstract Object doParse(String parameter) throws NumberFormatException;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class IntParser extends ParameterParser {
|
||||||
|
|
||||||
|
protected String getType() {
|
||||||
|
return "int";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Object doParse(String s) throws NumberFormatException {
|
||||||
|
return Integer.valueOf(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int parseInt(String name, String parameter) throws PortletRequestBindingException {
|
||||||
|
return ((Number) parse(name, parameter)).intValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[] parseInts(String name, String[] values) throws PortletRequestBindingException {
|
||||||
|
validateRequiredParameter(name, values);
|
||||||
|
int[] parameters = new int[values.length];
|
||||||
|
for (int i = 0; i < values.length; i++) {
|
||||||
|
parameters[i] = parseInt(name, values[i]);
|
||||||
|
}
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class LongParser extends ParameterParser {
|
||||||
|
|
||||||
|
protected String getType() {
|
||||||
|
return "long";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Object doParse(String parameter) throws NumberFormatException {
|
||||||
|
return Long.valueOf(parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long parseLong(String name, String parameter) throws PortletRequestBindingException {
|
||||||
|
return ((Number) parse(name, parameter)).longValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long[] parseLongs(String name, String[] values) throws PortletRequestBindingException {
|
||||||
|
validateRequiredParameter(name, values);
|
||||||
|
long[] parameters = new long[values.length];
|
||||||
|
for (int i = 0; i < values.length; i++) {
|
||||||
|
parameters[i] = parseLong(name, values[i]);
|
||||||
|
}
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class FloatParser extends ParameterParser {
|
||||||
|
|
||||||
|
protected String getType() {
|
||||||
|
return "float";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Object doParse(String parameter) throws NumberFormatException {
|
||||||
|
return Float.valueOf(parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float parseFloat(String name, String parameter) throws PortletRequestBindingException {
|
||||||
|
return ((Number) parse(name, parameter)).floatValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public float[] parseFloats(String name, String[] values) throws PortletRequestBindingException {
|
||||||
|
validateRequiredParameter(name, values);
|
||||||
|
float[] parameters = new float[values.length];
|
||||||
|
for (int i = 0; i < values.length; i++) {
|
||||||
|
parameters[i] = parseFloat(name, values[i]);
|
||||||
|
}
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class DoubleParser extends ParameterParser {
|
||||||
|
|
||||||
|
protected String getType() {
|
||||||
|
return "double";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Object doParse(String parameter) throws NumberFormatException {
|
||||||
|
return Double.valueOf(parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double parseDouble(String name, String parameter) throws PortletRequestBindingException {
|
||||||
|
return ((Number) parse(name, parameter)).doubleValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public double[] parseDoubles(String name, String[] values) throws PortletRequestBindingException {
|
||||||
|
validateRequiredParameter(name, values);
|
||||||
|
double[] parameters = new double[values.length];
|
||||||
|
for (int i = 0; i < values.length; i++) {
|
||||||
|
parameters[i] = parseDouble(name, values[i]);
|
||||||
|
}
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class BooleanParser extends ParameterParser {
|
||||||
|
|
||||||
|
protected String getType() {
|
||||||
|
return "boolean";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Object doParse(String parameter) throws NumberFormatException {
|
||||||
|
return (parameter.equalsIgnoreCase("true") || parameter.equalsIgnoreCase("on") ||
|
||||||
|
parameter.equalsIgnoreCase("yes") || parameter.equals("1") ? Boolean.TRUE : Boolean.FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean parseBoolean(String name, String parameter) throws PortletRequestBindingException {
|
||||||
|
return ((Boolean) parse(name, parameter)).booleanValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean[] parseBooleans(String name, String[] values) throws PortletRequestBindingException {
|
||||||
|
validateRequiredParameter(name, values);
|
||||||
|
boolean[] parameters = new boolean[values.length];
|
||||||
|
for (int i = 0; i < values.length; i++) {
|
||||||
|
parameters[i] = parseBoolean(name, values[i]);
|
||||||
|
}
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class StringParser extends ParameterParser {
|
||||||
|
|
||||||
|
protected String getType() {
|
||||||
|
return "string";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Object doParse(String parameter) throws NumberFormatException {
|
||||||
|
return parameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String validateRequiredString(String name, String value) throws PortletRequestBindingException {
|
||||||
|
validateRequiredParameter(name, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] validateRequiredStrings(String name, String[] values) throws PortletRequestBindingException {
|
||||||
|
validateRequiredParameter(name, values);
|
||||||
|
for (int i = 0; i < values.length; i++) {
|
||||||
|
validateRequiredParameter(name, values[i]);
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
Provides portlet-specific data binding functionality.
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,166 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2008 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.context;
|
||||||
|
|
||||||
|
import javax.portlet.PortletConfig;
|
||||||
|
import javax.portlet.PortletContext;
|
||||||
|
import javax.servlet.ServletContext;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.support.AbstractRefreshableConfigApplicationContext;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||||
|
import org.springframework.web.context.ServletContextAware;
|
||||||
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
|
import org.springframework.web.context.support.ServletContextAwareProcessor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link org.springframework.context.support.AbstractRefreshableApplicationContext}
|
||||||
|
* subclass which implements the {@link ConfigurablePortletApplicationContext}
|
||||||
|
* interface for portlet environments. Provides a "configLocations" property,
|
||||||
|
* to be populated through the ConfigurablePortletApplicationContext interface
|
||||||
|
* on portlet application startup.
|
||||||
|
*
|
||||||
|
* <p>This class is as easy to subclass as AbstractRefreshableApplicationContext:
|
||||||
|
* All you need to implements is the {@link #loadBeanDefinitions} method;
|
||||||
|
* see the superclass javadoc for details. Note that implementations are supposed
|
||||||
|
* to load bean definitions from the files specified by the locations returned
|
||||||
|
* by the {@link #getConfigLocations} method.
|
||||||
|
*
|
||||||
|
* <p>Interprets resource paths as servlet context resources, i.e. as paths beneath
|
||||||
|
* the web application root. Absolute paths, e.g. for files outside the web app root,
|
||||||
|
* can be accessed via "file:" URLs, as implemented by
|
||||||
|
* {@link org.springframework.core.io.DefaultResourceLoader}.
|
||||||
|
*
|
||||||
|
* <p><b>This is the portlet context to be subclassed for a different bean definition format.</b>
|
||||||
|
* Such a context implementation can be specified as "contextClass" init-param
|
||||||
|
* for FrameworkPortlet, replacing the default {@link XmlPortletApplicationContext}.
|
||||||
|
* It will then automatically receive the "contextConfigLocation" init-param.
|
||||||
|
*
|
||||||
|
* <p>Note that Portlet-based context implementations are generally supposed
|
||||||
|
* to configure themselves based on the configuration received through the
|
||||||
|
* {@link ConfigurablePortletApplicationContext} interface. In contrast, a standalone
|
||||||
|
* application context might allow for configuration in custom startup code
|
||||||
|
* (for example, {@link org.springframework.context.support.GenericApplicationContext}).
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @since 2.0
|
||||||
|
* @see #loadBeanDefinitions
|
||||||
|
* @see org.springframework.web.portlet.context.ConfigurablePortletApplicationContext#setConfigLocations
|
||||||
|
* @see XmlPortletApplicationContext
|
||||||
|
*/
|
||||||
|
public abstract class AbstractRefreshablePortletApplicationContext extends AbstractRefreshableConfigApplicationContext
|
||||||
|
implements WebApplicationContext, ConfigurablePortletApplicationContext {
|
||||||
|
|
||||||
|
/** Servlet context that this context runs in */
|
||||||
|
private ServletContext servletContext;
|
||||||
|
|
||||||
|
/** Portlet context that this context runs in */
|
||||||
|
private PortletContext portletContext;
|
||||||
|
|
||||||
|
/** Portlet config that this context runs in */
|
||||||
|
private PortletConfig portletConfig;
|
||||||
|
|
||||||
|
/** Namespace of this context, or null if root */
|
||||||
|
private String namespace;
|
||||||
|
|
||||||
|
|
||||||
|
public AbstractRefreshablePortletApplicationContext() {
|
||||||
|
setDisplayName("Root PortletApplicationContext");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParent(ApplicationContext parent) {
|
||||||
|
super.setParent(parent);
|
||||||
|
if (parent instanceof WebApplicationContext) {
|
||||||
|
this.servletContext = ((WebApplicationContext) parent).getServletContext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServletContext getServletContext() {
|
||||||
|
return this.servletContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPortletContext(PortletContext portletContext) {
|
||||||
|
this.portletContext = portletContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PortletContext getPortletContext() {
|
||||||
|
return this.portletContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPortletConfig(PortletConfig portletConfig) {
|
||||||
|
this.portletConfig = portletConfig;
|
||||||
|
if (portletConfig != null && this.portletContext == null) {
|
||||||
|
this.portletContext = portletConfig.getPortletContext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PortletConfig getPortletConfig() {
|
||||||
|
return this.portletConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNamespace(String namespace) {
|
||||||
|
this.namespace = namespace;
|
||||||
|
if (namespace != null) {
|
||||||
|
setDisplayName("PortletApplicationContext for namespace '" + namespace + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNamespace() {
|
||||||
|
return this.namespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getConfigLocations() {
|
||||||
|
return super.getConfigLocations();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register request/session scopes, a {@link PortletContextAwareProcessor}, etc.
|
||||||
|
*/
|
||||||
|
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
|
||||||
|
beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext));
|
||||||
|
beanFactory.addBeanPostProcessor(new PortletContextAwareProcessor(this.portletContext, this.portletConfig));
|
||||||
|
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
|
||||||
|
beanFactory.ignoreDependencyInterface(PortletContextAware.class);
|
||||||
|
beanFactory.ignoreDependencyInterface(PortletConfigAware.class);
|
||||||
|
beanFactory.registerResolvableDependency(ServletContext.class, this.servletContext);
|
||||||
|
beanFactory.registerResolvableDependency(PortletContext.class, this.portletContext);
|
||||||
|
beanFactory.registerResolvableDependency(PortletConfig.class, this.portletConfig);
|
||||||
|
|
||||||
|
PortletApplicationContextUtils.registerPortletApplicationScopes(beanFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This implementation supports file paths beneath the root of the PortletContext.
|
||||||
|
* @see PortletContextResource
|
||||||
|
*/
|
||||||
|
protected Resource getResourceByPath(String path) {
|
||||||
|
return new PortletContextResource(this.portletContext, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This implementation supports pattern matching in unexpanded WARs too.
|
||||||
|
* @see PortletContextResourcePatternResolver
|
||||||
|
*/
|
||||||
|
protected ResourcePatternResolver getResourcePatternResolver() {
|
||||||
|
return new PortletContextResourcePatternResolver(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2008 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.context;
|
||||||
|
|
||||||
|
import javax.portlet.PortletConfig;
|
||||||
|
import javax.portlet.PortletContext;
|
||||||
|
|
||||||
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface to be implemented by configurable portlet application contexts.
|
||||||
|
* Supported by {@link org.springframework.web.portlet.FrameworkPortlet}.
|
||||||
|
*
|
||||||
|
* <p>Note: The setters of this interface need to be called before an
|
||||||
|
* invocation of the {@link #refresh} method inherited from
|
||||||
|
* {@link org.springframework.context.ConfigurableApplicationContext}.
|
||||||
|
* They do not cause an initialization of the context on their own.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author William G. Thompson, Jr.
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @since 2.0
|
||||||
|
* @see #refresh
|
||||||
|
* @see org.springframework.web.context.ContextLoader#createWebApplicationContext
|
||||||
|
* @see org.springframework.web.portlet.FrameworkPortlet#createPortletApplicationContext
|
||||||
|
* @see org.springframework.web.context.ConfigurableWebApplicationContext
|
||||||
|
*/
|
||||||
|
public interface ConfigurablePortletApplicationContext
|
||||||
|
extends WebApplicationContext, ConfigurableApplicationContext {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the PortletContext for this portlet application context.
|
||||||
|
* <p>Does not cause an initialization of the context: refresh needs to be
|
||||||
|
* called after the setting of all configuration properties.
|
||||||
|
* @see #refresh()
|
||||||
|
*/
|
||||||
|
void setPortletContext(PortletContext portletContext);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the standard Portlet API PortletContext for this application.
|
||||||
|
*/
|
||||||
|
PortletContext getPortletContext();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the PortletConfig for this portlet application context.
|
||||||
|
* @see #refresh()
|
||||||
|
*/
|
||||||
|
void setPortletConfig(PortletConfig portletConfig);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the PortletConfig for this portlet application context, if any.
|
||||||
|
*/
|
||||||
|
PortletConfig getPortletConfig();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the namespace for this portlet application context,
|
||||||
|
* to be used for building a default context config location.
|
||||||
|
*/
|
||||||
|
void setNamespace(String namespace);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the namespace for this web application context, if any.
|
||||||
|
*/
|
||||||
|
String getNamespace();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the config locations for this portlet application context in init-param style,
|
||||||
|
* i.e. with distinct locations separated by commas, semicolons or whitespace.
|
||||||
|
* <p>If not set, the implementation is supposed to use a default for the
|
||||||
|
* given namespace.
|
||||||
|
*/
|
||||||
|
void setConfigLocation(String configLocation);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the config locations for this portlet application context.
|
||||||
|
* <p>If not set, the implementation is supposed to use a default for the
|
||||||
|
* given namespace.
|
||||||
|
*/
|
||||||
|
void setConfigLocations(String[] configLocations);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the config locations for this web application context,
|
||||||
|
* or <code>null</code> if none specified.
|
||||||
|
*/
|
||||||
|
String[] getConfigLocations();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,130 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2007 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.context;
|
||||||
|
|
||||||
|
import javax.portlet.PortletContext;
|
||||||
|
import javax.portlet.PortletRequest;
|
||||||
|
import javax.portlet.PortletSession;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.ObjectFactory;
|
||||||
|
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
|
import org.springframework.web.context.request.RequestAttributes;
|
||||||
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
|
import org.springframework.web.context.request.RequestScope;
|
||||||
|
import org.springframework.web.context.request.SessionScope;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience methods for retrieving the root WebApplicationContext for a given
|
||||||
|
* PortletContext. This is e.g. useful for accessing a Spring context from
|
||||||
|
* within custom Portlet implementations.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @since 2.0
|
||||||
|
* @see org.springframework.web.context.ContextLoader
|
||||||
|
* @see org.springframework.web.context.support.WebApplicationContextUtils
|
||||||
|
* @see org.springframework.web.portlet.FrameworkPortlet
|
||||||
|
* @see org.springframework.web.portlet.DispatcherPortlet
|
||||||
|
*/
|
||||||
|
public abstract class PortletApplicationContextUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the root WebApplicationContext for this portlet application, which is
|
||||||
|
* typically loaded via ContextLoaderListener or ContextLoaderServlet.
|
||||||
|
* <p>Will rethrow an exception that happened on root context startup,
|
||||||
|
* to differentiate between a failed context startup and no context at all.
|
||||||
|
* @param pc PortletContext to find the web application context for
|
||||||
|
* @return the root WebApplicationContext for this web app, or <code>null</code> if none
|
||||||
|
* (typed to ApplicationContext to avoid a Servlet API dependency; can usually
|
||||||
|
* be casted to WebApplicationContext, but there shouldn't be a need to)
|
||||||
|
* @see org.springframework.web.context.WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
|
||||||
|
*/
|
||||||
|
public static ApplicationContext getWebApplicationContext(PortletContext pc) {
|
||||||
|
Assert.notNull(pc, "PortletContext must not be null");
|
||||||
|
Object attr = pc.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
|
||||||
|
if (attr == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (attr instanceof RuntimeException) {
|
||||||
|
throw (RuntimeException) attr;
|
||||||
|
}
|
||||||
|
if (attr instanceof Error) {
|
||||||
|
throw (Error) attr;
|
||||||
|
}
|
||||||
|
if (!(attr instanceof ApplicationContext)) {
|
||||||
|
throw new IllegalStateException("Root context attribute is not of type WebApplicationContext: " + attr);
|
||||||
|
}
|
||||||
|
return (ApplicationContext) attr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the root WebApplicationContext for this portlet application, which is
|
||||||
|
* typically loaded via ContextLoaderListener or ContextLoaderServlet.
|
||||||
|
* <p>Will rethrow an exception that happened on root context startup,
|
||||||
|
* to differentiate between a failed context startup and no context at all.
|
||||||
|
* @param pc PortletContext to find the web application context for
|
||||||
|
* @return the root WebApplicationContext for this web app
|
||||||
|
* (typed to ApplicationContext to avoid a Servlet API dependency; can usually
|
||||||
|
* be casted to WebApplicationContext, but there shouldn't be a need to)
|
||||||
|
* @throws IllegalStateException if the root WebApplicationContext could not be found
|
||||||
|
* @see org.springframework.web.context.WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
|
||||||
|
*/
|
||||||
|
public static ApplicationContext getRequiredWebApplicationContext(PortletContext pc)
|
||||||
|
throws IllegalStateException {
|
||||||
|
|
||||||
|
ApplicationContext wac = getWebApplicationContext(pc);
|
||||||
|
if (wac == null) {
|
||||||
|
throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener registered?");
|
||||||
|
}
|
||||||
|
return wac;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register portlet-specific scopes with the given BeanFactory,
|
||||||
|
* as used by the Portlet ApplicationContext.
|
||||||
|
* @param beanFactory the BeanFactory to configure
|
||||||
|
*/
|
||||||
|
static void registerPortletApplicationScopes(ConfigurableListableBeanFactory beanFactory) {
|
||||||
|
beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());
|
||||||
|
beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope(false));
|
||||||
|
beanFactory.registerScope(WebApplicationContext.SCOPE_GLOBAL_SESSION, new SessionScope(true));
|
||||||
|
|
||||||
|
beanFactory.registerResolvableDependency(PortletRequest.class, new ObjectFactory() {
|
||||||
|
public Object getObject() {
|
||||||
|
RequestAttributes requestAttr = RequestContextHolder.currentRequestAttributes();
|
||||||
|
if (!(requestAttr instanceof PortletRequestAttributes)) {
|
||||||
|
throw new IllegalStateException("Current request is not a portlet request");
|
||||||
|
}
|
||||||
|
return ((PortletRequestAttributes) requestAttr).getRequest();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
beanFactory.registerResolvableDependency(PortletSession.class, new ObjectFactory() {
|
||||||
|
public Object getObject() {
|
||||||
|
RequestAttributes requestAttr = RequestContextHolder.currentRequestAttributes();
|
||||||
|
if (!(requestAttr instanceof PortletRequestAttributes)) {
|
||||||
|
throw new IllegalStateException("Current request is not a portlet request");
|
||||||
|
}
|
||||||
|
return ((PortletRequestAttributes) requestAttr).getRequest().getPortletSession();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2005 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.context;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import javax.portlet.PortletContext;
|
||||||
|
|
||||||
|
import org.springframework.context.support.ApplicationObjectSupport;
|
||||||
|
import org.springframework.web.portlet.util.PortletUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenient superclass for application objects running in a Portlet ApplicationContext.
|
||||||
|
* Provides getApplicationContext, getServletContext, and getTempDir methods.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public abstract class PortletApplicationObjectSupport extends ApplicationObjectSupport
|
||||||
|
implements PortletContextAware {
|
||||||
|
|
||||||
|
private PortletContext portletContext;
|
||||||
|
|
||||||
|
|
||||||
|
public void setPortletContext(PortletContext portletContext) {
|
||||||
|
this.portletContext = portletContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides the base class behavior to enforce running in an ApplicationContext.
|
||||||
|
* All accessors will throw IllegalStateException if not running in a context.
|
||||||
|
* @see #getApplicationContext()
|
||||||
|
* @see #getMessageSourceAccessor()
|
||||||
|
* @see #getPortletContext()
|
||||||
|
* @see #getTempDir()
|
||||||
|
*/
|
||||||
|
protected boolean isContextRequired() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the current PortletContext.
|
||||||
|
* @throws IllegalStateException if not running within a PortletContext
|
||||||
|
*/
|
||||||
|
protected final PortletContext getPortletContext() throws IllegalStateException {
|
||||||
|
if (this.portletContext == null) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"PortletApplicationObjectSupport instance [" + this + "] does not run within a PortletContext");
|
||||||
|
}
|
||||||
|
return this.portletContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the temporary directory for the current web application,
|
||||||
|
* as provided by the servlet container.
|
||||||
|
* @return the File representing the temporary directory
|
||||||
|
* @throws IllegalStateException if not running within a PortletContext
|
||||||
|
* @see org.springframework.web.portlet.util.PortletUtils#getTempDir(javax.portlet.PortletContext)
|
||||||
|
*/
|
||||||
|
protected final File getTempDir() throws IllegalStateException {
|
||||||
|
return PortletUtils.getTempDir(getPortletContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2005 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.context;
|
||||||
|
|
||||||
|
import javax.portlet.PortletConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface to be implemented by any object that wishes to be notified
|
||||||
|
* of the PortletConfig (typically determined by the PortletApplicationContext)
|
||||||
|
* that it runs in.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 2.0
|
||||||
|
* @see PortletContextAware
|
||||||
|
*/
|
||||||
|
public interface PortletConfigAware {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the PortletConfigthat this object runs in.
|
||||||
|
* <p>Invoked after population of normal bean properties but before an init
|
||||||
|
* callback like InitializingBean's afterPropertiesSet or a custom init-method.
|
||||||
|
* Invoked after ApplicationContextAware's setApplicationContext.
|
||||||
|
* @param portletConfig PortletConfig object to be used by this object
|
||||||
|
*/
|
||||||
|
void setPortletConfig(PortletConfig portletConfig);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2005 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.context;
|
||||||
|
|
||||||
|
import javax.portlet.PortletContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface to be implemented by any object that wishes to be notified
|
||||||
|
* of the PortletContext (typically determined by the PortletApplicationContext)
|
||||||
|
* that it runs in.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author William G. Thompson, Jr.
|
||||||
|
* @since 2.0
|
||||||
|
* @see PortletConfigAware
|
||||||
|
*/
|
||||||
|
public interface PortletContextAware {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the PortletContext that this object runs in.
|
||||||
|
* <p>Invoked after population of normal bean properties but before an init
|
||||||
|
* callback like InitializingBean's afterPropertiesSet or a custom init-method.
|
||||||
|
* Invoked after ApplicationContextAware's setApplicationContext.
|
||||||
|
* @param portletContext PortletContext object to be used by this object
|
||||||
|
*/
|
||||||
|
void setPortletContext(PortletContext portletContext);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2007 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.context;
|
||||||
|
|
||||||
|
import javax.portlet.PortletConfig;
|
||||||
|
import javax.portlet.PortletContext;
|
||||||
|
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link org.springframework.beans.factory.config.BeanPostProcessor}
|
||||||
|
* implementation that passes the PortletContext to beans that implement
|
||||||
|
* the {@link PortletContextAware} interface.
|
||||||
|
*
|
||||||
|
* <p>Portlet application contexts will automatically register this with their
|
||||||
|
* underlying bean factory. Applications do not use this directly.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @since 2.0
|
||||||
|
* @see org.springframework.web.portlet.context.PortletContextAware
|
||||||
|
* @see org.springframework.web.portlet.context.XmlPortletApplicationContext#postProcessBeanFactory
|
||||||
|
*/
|
||||||
|
public class PortletContextAwareProcessor implements BeanPostProcessor {
|
||||||
|
|
||||||
|
private PortletContext portletContext;
|
||||||
|
|
||||||
|
private PortletConfig portletConfig;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new PortletContextAwareProcessor for the given context.
|
||||||
|
*/
|
||||||
|
public PortletContextAwareProcessor(PortletContext portletContext) {
|
||||||
|
this(portletContext, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new PortletContextAwareProcessor for the given config.
|
||||||
|
*/
|
||||||
|
public PortletContextAwareProcessor(PortletConfig portletConfig) {
|
||||||
|
this(null, portletConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new PortletContextAwareProcessor for the given context and config.
|
||||||
|
*/
|
||||||
|
public PortletContextAwareProcessor(PortletContext portletContext, PortletConfig portletConfig) {
|
||||||
|
this.portletContext = portletContext;
|
||||||
|
this.portletConfig = portletConfig;
|
||||||
|
if (portletContext == null && portletConfig != null) {
|
||||||
|
this.portletContext = portletConfig.getPortletContext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||||
|
if (this.portletContext != null && bean instanceof PortletContextAware) {
|
||||||
|
((PortletContextAware) bean).setPortletContext(this.portletContext);
|
||||||
|
}
|
||||||
|
if (this.portletConfig != null && bean instanceof PortletConfigAware) {
|
||||||
|
((PortletConfigAware) bean).setPortletConfig(this.portletConfig);
|
||||||
|
}
|
||||||
|
return bean;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object postProcessAfterInitialization(Object bean, String beanName) {
|
||||||
|
return bean;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,179 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2008 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.context;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
import javax.portlet.PortletContext;
|
||||||
|
|
||||||
|
import org.springframework.core.io.AbstractResource;
|
||||||
|
import org.springframework.core.io.ContextResource;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.web.portlet.util.PortletUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link org.springframework.core.io.Resource} implementation for
|
||||||
|
* {@link javax.portlet.PortletContext} resources, interpreting
|
||||||
|
* relative paths within the portlet application root directory.
|
||||||
|
*
|
||||||
|
* <p>Always supports stream access and URL access, but only allows
|
||||||
|
* <code>java.io.File</code> access when the portlet application archive
|
||||||
|
* is expanded.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @since 2.0
|
||||||
|
* @see javax.portlet.PortletContext#getResourceAsStream
|
||||||
|
* @see javax.portlet.PortletContext#getRealPath
|
||||||
|
*/
|
||||||
|
public class PortletContextResource extends AbstractResource implements ContextResource {
|
||||||
|
|
||||||
|
private final PortletContext portletContext;
|
||||||
|
|
||||||
|
private final String path;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new PortletContextResource.
|
||||||
|
* <p>The Portlet spec requires that resource paths start with a slash,
|
||||||
|
* even if many containers accept paths without leading slash too.
|
||||||
|
* Consequently, the given path will be prepended with a slash if it
|
||||||
|
* doesn't already start with one.
|
||||||
|
* @param portletContext the PortletContext to load from
|
||||||
|
* @param path the path of the resource
|
||||||
|
*/
|
||||||
|
public PortletContextResource(PortletContext portletContext, String path) {
|
||||||
|
// check PortletContext
|
||||||
|
Assert.notNull(portletContext, "Cannot resolve PortletContextResource without PortletContext");
|
||||||
|
this.portletContext = portletContext;
|
||||||
|
|
||||||
|
// check path
|
||||||
|
Assert.notNull(path, "Path is required");
|
||||||
|
String pathToUse = StringUtils.cleanPath(path);
|
||||||
|
if (!pathToUse.startsWith("/")) {
|
||||||
|
pathToUse = "/" + pathToUse;
|
||||||
|
}
|
||||||
|
this.path = pathToUse;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the PortletContext for this resource.
|
||||||
|
*/
|
||||||
|
public final PortletContext getPortletContext() {
|
||||||
|
return this.portletContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the path for this resource.
|
||||||
|
*/
|
||||||
|
public final String getPath() {
|
||||||
|
return this.path;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This implementation checks <code>PortletContext.getResource</code>.
|
||||||
|
* @see javax.portlet.PortletContext#getResource(String)
|
||||||
|
*/
|
||||||
|
public boolean exists() {
|
||||||
|
try {
|
||||||
|
URL url = this.portletContext.getResource(this.path);
|
||||||
|
return (url != null);
|
||||||
|
}
|
||||||
|
catch (MalformedURLException ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This implementation delegates to <code>PortletContext.getResourceAsStream</code>,
|
||||||
|
* but throws a FileNotFoundException if not found.
|
||||||
|
* @see javax.portlet.PortletContext#getResourceAsStream(String)
|
||||||
|
*/
|
||||||
|
public InputStream getInputStream() throws IOException {
|
||||||
|
InputStream is = this.portletContext.getResourceAsStream(this.path);
|
||||||
|
if (is == null) {
|
||||||
|
throw new FileNotFoundException("Could not open " + getDescription());
|
||||||
|
}
|
||||||
|
return is;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This implementation delegates to <code>PortletContext.getResource</code>,
|
||||||
|
* but throws a FileNotFoundException if no resource found.
|
||||||
|
* @see javax.portlet.PortletContext#getResource(String)
|
||||||
|
*/
|
||||||
|
public URL getURL() throws IOException {
|
||||||
|
URL url = this.portletContext.getResource(this.path);
|
||||||
|
if (url == null) {
|
||||||
|
throw new FileNotFoundException(
|
||||||
|
getDescription() + " cannot be resolved to URL because it does not exist");
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This implementation delegates to <code>PortletContext.getRealPath</code>,
|
||||||
|
* but throws a FileNotFoundException if not found or not resolvable.
|
||||||
|
* @see javax.portlet.PortletContext#getRealPath(String)
|
||||||
|
*/
|
||||||
|
public File getFile() throws IOException {
|
||||||
|
String realPath = PortletUtils.getRealPath(this.portletContext, this.path);
|
||||||
|
return new File(realPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Resource createRelative(String relativePath) {
|
||||||
|
String pathToUse = StringUtils.applyRelativePath(this.path, relativePath);
|
||||||
|
return new PortletContextResource(this.portletContext, pathToUse);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFilename() {
|
||||||
|
return StringUtils.getFilename(this.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return "PortletContext resource [" + this.path + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPathWithinContext() {
|
||||||
|
return this.path;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj == this) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj instanceof PortletContextResource) {
|
||||||
|
PortletContextResource otherRes = (PortletContextResource) obj;
|
||||||
|
return (this.portletContext.equals(otherRes.portletContext) && this.path.equals(otherRes.path));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int hashCode() {
|
||||||
|
return this.path.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2006 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.context;
|
||||||
|
|
||||||
|
import javax.portlet.PortletContext;
|
||||||
|
|
||||||
|
import org.springframework.core.io.DefaultResourceLoader;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ResourceLoader implementation that resolves paths as PortletContext
|
||||||
|
* resources, for use outside a Portlet ApplicationContext (for example,
|
||||||
|
* in a GenericPortletBean subclass).
|
||||||
|
*
|
||||||
|
* <p>Within a WebApplicationContext, resource paths are automatically
|
||||||
|
* resolved as PortletContext resources by the context implementation.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @since 2.0
|
||||||
|
* @see #getResourceByPath
|
||||||
|
* @see PortletContextResource
|
||||||
|
* @see org.springframework.web.portlet.GenericPortletBean
|
||||||
|
*/
|
||||||
|
public class PortletContextResourceLoader extends DefaultResourceLoader {
|
||||||
|
|
||||||
|
private final PortletContext portletContext;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new PortletContextResourceLoader.
|
||||||
|
* @param portletContext the PortletContext to load resources with
|
||||||
|
*/
|
||||||
|
public PortletContextResourceLoader(PortletContext portletContext) {
|
||||||
|
this.portletContext = portletContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This implementation supports file paths beneath the root of the web application.
|
||||||
|
* @see PortletContextResource
|
||||||
|
*/
|
||||||
|
protected Resource getResourceByPath(String path) {
|
||||||
|
return new PortletContextResource(this.portletContext, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,119 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2007 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.context;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.portlet.PortletContext;
|
||||||
|
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.core.io.ResourceLoader;
|
||||||
|
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PortletContext-aware subclass of {@link PathMatchingResourcePatternResolver},
|
||||||
|
* able to find matching resources below the web application root directory
|
||||||
|
* via Portlet API's <code>PortletContext.getResourcePaths</code>.
|
||||||
|
* Falls back to the superclass' file system checking for other resources.
|
||||||
|
*
|
||||||
|
* <p>The advantage of using <code>PortletContext.getResourcePaths</code> to
|
||||||
|
* find matching files is that it will work in a WAR file which has not been
|
||||||
|
* expanded too.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public class PortletContextResourcePatternResolver extends PathMatchingResourcePatternResolver {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new PortletContextResourcePatternResolver.
|
||||||
|
* @param portletContext the PortletContext to load resources with
|
||||||
|
* @see PortletContextResourceLoader#PortletContextResourceLoader(javax.portlet.PortletContext)
|
||||||
|
*/
|
||||||
|
public PortletContextResourcePatternResolver(PortletContext portletContext) {
|
||||||
|
super(new PortletContextResourceLoader(portletContext));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new PortletContextResourcePatternResolver.
|
||||||
|
* @param resourceLoader the ResourceLoader to load root directories and
|
||||||
|
* actual resources with
|
||||||
|
*/
|
||||||
|
public PortletContextResourcePatternResolver(ResourceLoader resourceLoader) {
|
||||||
|
super(resourceLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overridden version which checks for PortletContextResource
|
||||||
|
* and uses <code>PortletContext.getResourcePaths</code> to find
|
||||||
|
* matching resources below the web application root directory.
|
||||||
|
* In case of other resources, delegates to the superclass version.
|
||||||
|
* @see #doRetrieveMatchingPortletContextResources
|
||||||
|
* @see org.springframework.web.portlet.context.PortletContextResource
|
||||||
|
* @see javax.portlet.PortletContext#getResourcePaths
|
||||||
|
*/
|
||||||
|
protected Set doFindPathMatchingFileResources(Resource rootDirResource, String subPattern) throws IOException {
|
||||||
|
if (rootDirResource instanceof PortletContextResource) {
|
||||||
|
PortletContextResource pcResource = (PortletContextResource) rootDirResource;
|
||||||
|
PortletContext pc = pcResource.getPortletContext();
|
||||||
|
String fullPattern = pcResource.getPath() + subPattern;
|
||||||
|
Set result = new HashSet();
|
||||||
|
doRetrieveMatchingPortletContextResources(pc, fullPattern, pcResource.getPath(), result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return super.doFindPathMatchingFileResources(rootDirResource, subPattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively retrieve PortletContextResources that match the given pattern,
|
||||||
|
* adding them to the given result set.
|
||||||
|
* @param portletContext the PortletContext to work on
|
||||||
|
* @param fullPattern the pattern to match against,
|
||||||
|
* with preprended root directory path
|
||||||
|
* @param dir the current directory
|
||||||
|
* @param result the Set of matching Resources to add to
|
||||||
|
* @throws IOException if directory contents could not be retrieved
|
||||||
|
* @see org.springframework.web.portlet.context.PortletContextResource
|
||||||
|
* @see javax.portlet.PortletContext#getResourcePaths
|
||||||
|
*/
|
||||||
|
protected void doRetrieveMatchingPortletContextResources(
|
||||||
|
PortletContext portletContext, String fullPattern, String dir, Set result) throws IOException {
|
||||||
|
|
||||||
|
Set candidates = portletContext.getResourcePaths(dir);
|
||||||
|
if (candidates != null) {
|
||||||
|
boolean dirDepthNotFixed = (fullPattern.indexOf("**") != -1);
|
||||||
|
for (Iterator it = candidates.iterator(); it.hasNext();) {
|
||||||
|
String currPath = (String) it.next();
|
||||||
|
if (currPath.endsWith("/") &&
|
||||||
|
(dirDepthNotFixed ||
|
||||||
|
StringUtils.countOccurrencesOf(currPath, "/") <= StringUtils.countOccurrencesOf(fullPattern, "/"))) {
|
||||||
|
doRetrieveMatchingPortletContextResources(portletContext, fullPattern, currPath, result);
|
||||||
|
}
|
||||||
|
if (getPathMatcher().match(fullPattern, currPath)) {
|
||||||
|
result.add(new PortletContextResource(portletContext, currPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,287 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2008 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.context;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.portlet.PortletRequest;
|
||||||
|
import javax.portlet.PortletSession;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.web.context.request.AbstractRequestAttributes;
|
||||||
|
import org.springframework.web.context.request.RequestAttributes;
|
||||||
|
import org.springframework.web.portlet.util.PortletUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Portlet-based implementation of the
|
||||||
|
* {@link org.springframework.web.context.request.RequestAttributes} interface.
|
||||||
|
*
|
||||||
|
* <p>Accesses objects from portlet request and portlet session scope,
|
||||||
|
* with a distinction between "session" (the PortletSession's "portlet scope")
|
||||||
|
* and "global session" (the PortletSession's "application scope").
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 2.0
|
||||||
|
* @see javax.portlet.PortletRequest#getAttribute
|
||||||
|
* @see javax.portlet.PortletSession#getAttribute
|
||||||
|
* @see javax.portlet.PortletSession#PORTLET_SCOPE
|
||||||
|
* @see javax.portlet.PortletSession#APPLICATION_SCOPE
|
||||||
|
* @see RequestAttributes#SCOPE_SESSION
|
||||||
|
* @see RequestAttributes#SCOPE_GLOBAL_SESSION
|
||||||
|
*/
|
||||||
|
public class PortletRequestAttributes extends AbstractRequestAttributes {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We'll create a lot of these objects, so we don't want a new logger every time.
|
||||||
|
*/
|
||||||
|
private static final Log logger = LogFactory.getLog(PortletRequestAttributes.class);
|
||||||
|
|
||||||
|
|
||||||
|
private final PortletRequest request;
|
||||||
|
|
||||||
|
private volatile PortletSession session;
|
||||||
|
|
||||||
|
private final Map sessionAttributesToUpdate = new HashMap();
|
||||||
|
|
||||||
|
private final Map globalSessionAttributesToUpdate = new HashMap();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new PortletRequestAttributes instance for the given request.
|
||||||
|
* @param request current portlet request
|
||||||
|
*/
|
||||||
|
public PortletRequestAttributes(PortletRequest request) {
|
||||||
|
Assert.notNull(request, "Request must not be null");
|
||||||
|
this.request = request;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exposes the native {@link PortletRequest} that we're wrapping.
|
||||||
|
*/
|
||||||
|
public final PortletRequest getRequest() {
|
||||||
|
return this.request;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exposes the {@link PortletSession} that we're wrapping.
|
||||||
|
* @param allowCreate whether to allow creation of a new session if none exists yet
|
||||||
|
*/
|
||||||
|
protected final PortletSession getSession(boolean allowCreate) {
|
||||||
|
if (isRequestActive()) {
|
||||||
|
return this.request.getPortletSession(allowCreate);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Access through stored session reference, if any...
|
||||||
|
if (this.session == null && allowCreate) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"No session found and request already completed - cannot create new session!");
|
||||||
|
}
|
||||||
|
return this.session;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Object getAttribute(String name, int scope) {
|
||||||
|
if (scope == SCOPE_REQUEST) {
|
||||||
|
if (!isRequestActive()) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Cannot ask for request attribute - request is not active anymore!");
|
||||||
|
}
|
||||||
|
return this.request.getAttribute(name);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
PortletSession session = getSession(false);
|
||||||
|
if (session != null) {
|
||||||
|
if (scope == SCOPE_GLOBAL_SESSION) {
|
||||||
|
Object value = session.getAttribute(name, PortletSession.APPLICATION_SCOPE);
|
||||||
|
if (value != null) {
|
||||||
|
synchronized (this.globalSessionAttributesToUpdate) {
|
||||||
|
this.globalSessionAttributesToUpdate.put(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Object value = session.getAttribute(name);
|
||||||
|
if (value != null) {
|
||||||
|
synchronized (this.sessionAttributesToUpdate) {
|
||||||
|
this.sessionAttributesToUpdate.put(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttribute(String name, Object value, int scope) {
|
||||||
|
if (scope == SCOPE_REQUEST) {
|
||||||
|
if (!isRequestActive()) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Cannot set request attribute - request is not active anymore!");
|
||||||
|
}
|
||||||
|
this.request.setAttribute(name, value);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
PortletSession session = getSession(true);
|
||||||
|
if (scope == SCOPE_GLOBAL_SESSION) {
|
||||||
|
session.setAttribute(name, value, PortletSession.APPLICATION_SCOPE);
|
||||||
|
synchronized (this.globalSessionAttributesToUpdate) {
|
||||||
|
this.globalSessionAttributesToUpdate.remove(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
session.setAttribute(name, value);
|
||||||
|
synchronized (this.sessionAttributesToUpdate) {
|
||||||
|
this.sessionAttributesToUpdate.remove(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeAttribute(String name, int scope) {
|
||||||
|
if (scope == SCOPE_REQUEST) {
|
||||||
|
if (isRequestActive()) {
|
||||||
|
this.request.removeAttribute(name);
|
||||||
|
removeRequestDestructionCallback(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
PortletSession session = getSession(false);
|
||||||
|
if (session != null) {
|
||||||
|
if (scope == SCOPE_GLOBAL_SESSION) {
|
||||||
|
session.removeAttribute(name, PortletSession.APPLICATION_SCOPE);
|
||||||
|
synchronized (this.globalSessionAttributesToUpdate) {
|
||||||
|
this.globalSessionAttributesToUpdate.remove(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
session.removeAttribute(name);
|
||||||
|
synchronized (this.sessionAttributesToUpdate) {
|
||||||
|
this.sessionAttributesToUpdate.remove(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getAttributeNames(int scope) {
|
||||||
|
if (scope == SCOPE_REQUEST) {
|
||||||
|
if (!isRequestActive()) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Cannot ask for request attributes - request is not active anymore!");
|
||||||
|
}
|
||||||
|
return StringUtils.toStringArray(this.request.getAttributeNames());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
PortletSession session = getSession(false);
|
||||||
|
if (session != null) {
|
||||||
|
if (scope == SCOPE_GLOBAL_SESSION) {
|
||||||
|
return StringUtils.toStringArray(session.getAttributeNames(PortletSession.APPLICATION_SCOPE));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return StringUtils.toStringArray(session.getAttributeNames());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return new String[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerDestructionCallback(String name, Runnable callback, int scope) {
|
||||||
|
if (scope == SCOPE_REQUEST) {
|
||||||
|
registerRequestDestructionCallback(name, callback);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
registerSessionDestructionCallback(name, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSessionId() {
|
||||||
|
return getSession(true).getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getSessionMutex() {
|
||||||
|
return PortletUtils.getSessionMutex(getSession(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update all accessed session attributes through <code>session.setAttribute</code>
|
||||||
|
* calls, explicitly indicating to the container that they might have been modified.
|
||||||
|
*/
|
||||||
|
protected void updateAccessedSessionAttributes() {
|
||||||
|
this.session = this.request.getPortletSession(false);
|
||||||
|
synchronized (this.sessionAttributesToUpdate) {
|
||||||
|
if (this.session != null) {
|
||||||
|
for (Iterator it = this.sessionAttributesToUpdate.entrySet().iterator(); it.hasNext();) {
|
||||||
|
Map.Entry entry = (Map.Entry) it.next();
|
||||||
|
String name = (String) entry.getKey();
|
||||||
|
Object newValue = entry.getValue();
|
||||||
|
Object oldValue = this.session.getAttribute(name);
|
||||||
|
if (oldValue == newValue) {
|
||||||
|
this.session.setAttribute(name, newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.sessionAttributesToUpdate.clear();
|
||||||
|
}
|
||||||
|
synchronized (this.globalSessionAttributesToUpdate) {
|
||||||
|
if (this.session != null) {
|
||||||
|
for (Iterator it = this.globalSessionAttributesToUpdate.entrySet().iterator(); it.hasNext();) {
|
||||||
|
Map.Entry entry = (Map.Entry) it.next();
|
||||||
|
String name = (String) entry.getKey();
|
||||||
|
Object newValue = entry.getValue();
|
||||||
|
Object oldValue = this.session.getAttribute(name, PortletSession.APPLICATION_SCOPE);
|
||||||
|
if (oldValue == newValue) {
|
||||||
|
this.session.setAttribute(name, newValue, PortletSession.APPLICATION_SCOPE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.globalSessionAttributesToUpdate.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the given callback as to be executed after session termination.
|
||||||
|
* @param name the name of the attribute to register the callback for
|
||||||
|
* @param callback the callback to be executed for destruction
|
||||||
|
*/
|
||||||
|
private void registerSessionDestructionCallback(String name, Runnable callback) {
|
||||||
|
if (logger.isWarnEnabled()) {
|
||||||
|
logger.warn("Could not register destruction callback [" + callback + "] for attribute '" + name +
|
||||||
|
"' for session scope because Portlet API 1.0 does not support session attribute callbacks");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return this.request.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,129 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2007 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.context;
|
||||||
|
|
||||||
|
import org.springframework.web.context.support.RequestHandledEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Portlet-specific subclass of RequestHandledEvent,
|
||||||
|
* adding portlet-specific context information.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @since 2.0
|
||||||
|
* @see org.springframework.web.portlet.FrameworkPortlet
|
||||||
|
* @see org.springframework.context.ApplicationContext#publishEvent
|
||||||
|
*/
|
||||||
|
public class PortletRequestHandledEvent extends RequestHandledEvent {
|
||||||
|
|
||||||
|
/** Name of the portlet that handled the request */
|
||||||
|
private final String portletName;
|
||||||
|
|
||||||
|
/** PortletMode of the request */
|
||||||
|
private final String portletMode;
|
||||||
|
|
||||||
|
/** Type of Portlet Request */
|
||||||
|
private final String requestType;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new PortletRequestHandledEvent.
|
||||||
|
* @param source the component that published the event
|
||||||
|
* @param portletName the name of the portlet that handled the request
|
||||||
|
* @param portletMode the PortletMode of the request (usually 'view', 'edit', or 'help')
|
||||||
|
* @param requestType the type of Portlet request ('action' or 'render')
|
||||||
|
* @param sessionId the id of the HTTP session, if any
|
||||||
|
* @param userName the name of the user that was associated with the
|
||||||
|
* request, if any (usually the UserPrincipal)
|
||||||
|
* @param processingTimeMillis the processing time of the request in milliseconds
|
||||||
|
*/
|
||||||
|
public PortletRequestHandledEvent(Object source, String portletName,
|
||||||
|
String portletMode, String requestType, String sessionId,
|
||||||
|
String userName, long processingTimeMillis) {
|
||||||
|
|
||||||
|
super(source, sessionId, userName, processingTimeMillis);
|
||||||
|
this.portletName = portletName;
|
||||||
|
this.portletMode = portletMode;
|
||||||
|
this.requestType = requestType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new PortletRequestHandledEvent.
|
||||||
|
* @param source the component that published the event
|
||||||
|
* @param portletName the name of the portlet that handled the request
|
||||||
|
* @param portletMode the PortletMode of the request (usually 'view', 'edit', or 'help')
|
||||||
|
* @param requestType the type of Portlet request ('action' or 'render')
|
||||||
|
* @param sessionId the id of the HTTP session, if any
|
||||||
|
* @param userName the name of the user that was associated with the
|
||||||
|
* request, if any (usually the UserPrincipal)
|
||||||
|
* @param processingTimeMillis the processing time of the request in milliseconds
|
||||||
|
* @param failureCause the cause of failure, if any
|
||||||
|
*/
|
||||||
|
public PortletRequestHandledEvent(Object source, String portletName,
|
||||||
|
String portletMode, String requestType, String sessionId,
|
||||||
|
String userName, long processingTimeMillis, Throwable failureCause) {
|
||||||
|
|
||||||
|
super(source, sessionId, userName, processingTimeMillis, failureCause);
|
||||||
|
this.portletName = portletName;
|
||||||
|
this.portletMode = portletMode;
|
||||||
|
this.requestType = requestType;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of the portlet that handled the request.
|
||||||
|
*/
|
||||||
|
public String getPortletName() {
|
||||||
|
return this.portletName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the mode of the portlet request (usually 'view', 'edit', or 'help').
|
||||||
|
*/
|
||||||
|
public String getPortletMode() {
|
||||||
|
return this.portletMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the the type of Portlet Request ('action' or 'render').
|
||||||
|
*/
|
||||||
|
public String getRequestType() {
|
||||||
|
return this.requestType;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getShortDescription() {
|
||||||
|
StringBuffer sb = new StringBuffer();
|
||||||
|
sb.append("portlet=[").append(this.portletName).append("]; ");
|
||||||
|
sb.append(super.getShortDescription());
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
StringBuffer sb = new StringBuffer();
|
||||||
|
sb.append("portlet=[").append(this.portletName).append("]; ");
|
||||||
|
sb.append("mode=[").append(this.portletMode).append("]; ");
|
||||||
|
sb.append("type=[").append(this.requestType).append("]; ");
|
||||||
|
sb.append(super.getDescription());
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return "PortletRequestHandledEvent: " + getDescription();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,143 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2008 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.context;
|
||||||
|
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.portlet.PortletRequest;
|
||||||
|
import javax.portlet.PortletResponse;
|
||||||
|
import javax.portlet.PortletSession;
|
||||||
|
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.web.context.request.NativeWebRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link org.springframework.web.context.request.WebRequest} adapter
|
||||||
|
* for a {@link javax.portlet.PortletRequest}.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public class PortletWebRequest extends PortletRequestAttributes implements NativeWebRequest {
|
||||||
|
|
||||||
|
private PortletResponse response;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new PortletWebRequest instance for the given request.
|
||||||
|
* @param request current portlet request
|
||||||
|
*/
|
||||||
|
public PortletWebRequest(PortletRequest request) {
|
||||||
|
super(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new PortletWebRequest instance for the given request/response pair.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param response current portlet response
|
||||||
|
*/
|
||||||
|
public PortletWebRequest(PortletRequest request, PortletResponse response) {
|
||||||
|
super(request);
|
||||||
|
this.response = response;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exposes the native {@link PortletResponse} that we're wrapping (if any).
|
||||||
|
*/
|
||||||
|
public final PortletResponse getResponse() {
|
||||||
|
return this.response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getNativeRequest() {
|
||||||
|
return getRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getNativeResponse() {
|
||||||
|
return getResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getParameter(String paramName) {
|
||||||
|
return getRequest().getParameter(paramName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getParameterValues(String paramName) {
|
||||||
|
return getRequest().getParameterValues(paramName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map getParameterMap() {
|
||||||
|
return getRequest().getParameterMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Locale getLocale() {
|
||||||
|
return getRequest().getLocale();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContextPath() {
|
||||||
|
return getRequest().getContextPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRemoteUser() {
|
||||||
|
return getRequest().getRemoteUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Principal getUserPrincipal() {
|
||||||
|
return getRequest().getUserPrincipal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUserInRole(String role) {
|
||||||
|
return getRequest().isUserInRole(role);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSecure() {
|
||||||
|
return getRequest().isSecure();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Last-modified handling not supported for portlet requests:
|
||||||
|
* As a consequence, this method always returns <code>false</code>.
|
||||||
|
*/
|
||||||
|
public boolean checkNotModified(long lastModifiedTimestamp) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getDescription(boolean includeClientInfo) {
|
||||||
|
PortletRequest request = getRequest();
|
||||||
|
StringBuffer buffer = new StringBuffer();
|
||||||
|
buffer.append("context=").append(request.getContextPath());
|
||||||
|
if (includeClientInfo) {
|
||||||
|
PortletSession session = request.getPortletSession(false);
|
||||||
|
if (session != null) {
|
||||||
|
buffer.append(";session=").append(session.getId());
|
||||||
|
}
|
||||||
|
String user = getRequest().getRemoteUser();
|
||||||
|
if (StringUtils.hasLength(user)) {
|
||||||
|
buffer.append(";user=").append(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return "PortletWebRequest: " + getDescription(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,163 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2008 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.context;
|
||||||
|
|
||||||
|
import javax.portlet.PortletConfig;
|
||||||
|
import javax.portlet.PortletContext;
|
||||||
|
import javax.servlet.ServletContext;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.support.StaticApplicationContext;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||||
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
|
import org.springframework.web.context.support.ServletContextAwareProcessor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static Portlet-based {@link org.springframework.context.ApplicationContext}
|
||||||
|
* implementation for testing. Not intended for use in production applications.
|
||||||
|
*
|
||||||
|
* <p>Implements the
|
||||||
|
* {@link org.springframework.web.portlet.context.ConfigurablePortletApplicationContext}
|
||||||
|
* interface to allow for direct replacement of an {@link XmlPortletApplicationContext},
|
||||||
|
* despite not actually supporting external configuration files.
|
||||||
|
*
|
||||||
|
* <p>Interprets resource paths as portlet context resources, that is, as paths
|
||||||
|
* beneath the portlet application root. Absolute paths, for example for files
|
||||||
|
* outside the portlet app root, can be accessed via "file:" URLs, as implemented
|
||||||
|
* by {@link org.springframework.core.io.DefaultResourceLoader}.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author Mark Fisher
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public class StaticPortletApplicationContext extends StaticApplicationContext
|
||||||
|
implements ConfigurablePortletApplicationContext {
|
||||||
|
|
||||||
|
private ServletContext servletContext;
|
||||||
|
|
||||||
|
private PortletContext portletContext;
|
||||||
|
|
||||||
|
private PortletConfig portletConfig;
|
||||||
|
|
||||||
|
private String namespace;
|
||||||
|
|
||||||
|
|
||||||
|
public StaticPortletApplicationContext() {
|
||||||
|
setDisplayName("Root Portlet ApplicationContext");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setParent(ApplicationContext parent) {
|
||||||
|
super.setParent(parent);
|
||||||
|
if (parent instanceof WebApplicationContext) {
|
||||||
|
this.servletContext = ((WebApplicationContext) parent).getServletContext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServletContext getServletContext() {
|
||||||
|
return this.servletContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPortletContext(PortletContext portletContext) {
|
||||||
|
this.portletContext = portletContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PortletContext getPortletContext() {
|
||||||
|
return this.portletContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPortletConfig(PortletConfig portletConfig) {
|
||||||
|
this.portletConfig = portletConfig;
|
||||||
|
if (portletConfig != null && this.portletContext == null) {
|
||||||
|
this.portletContext = portletConfig.getPortletContext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PortletConfig getPortletConfig() {
|
||||||
|
return this.portletConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNamespace(String namespace) {
|
||||||
|
this.namespace = namespace;
|
||||||
|
if (namespace != null) {
|
||||||
|
setDisplayName("Portlet ApplicationContext for namespace '" + namespace + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNamespace() {
|
||||||
|
return this.namespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link StaticPortletApplicationContext} class does not support this method.
|
||||||
|
* @throws UnsupportedOperationException <b>always</b>
|
||||||
|
*/
|
||||||
|
public void setConfigLocation(String configLocation) {
|
||||||
|
if (configLocation != null) {
|
||||||
|
throw new UnsupportedOperationException("StaticPortletApplicationContext does not support config locations");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link StaticPortletApplicationContext} class does not support this method.
|
||||||
|
* @throws UnsupportedOperationException <b>always</b>
|
||||||
|
*/
|
||||||
|
public void setConfigLocations(String[] configLocations) {
|
||||||
|
if (configLocations != null) {
|
||||||
|
throw new UnsupportedOperationException("StaticPortletApplicationContext does not support config locations");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getConfigLocations() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register request/session scopes, a {@link PortletContextAwareProcessor}, etc.
|
||||||
|
*/
|
||||||
|
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
|
||||||
|
beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext));
|
||||||
|
beanFactory.addBeanPostProcessor(new PortletContextAwareProcessor(this.portletContext, this.portletConfig));
|
||||||
|
beanFactory.ignoreDependencyInterface(PortletContextAware.class);
|
||||||
|
beanFactory.ignoreDependencyInterface(PortletConfigAware.class);
|
||||||
|
beanFactory.registerResolvableDependency(ServletContext.class, this.servletContext);
|
||||||
|
beanFactory.registerResolvableDependency(PortletContext.class, this.portletContext);
|
||||||
|
beanFactory.registerResolvableDependency(PortletConfig.class, this.portletConfig);
|
||||||
|
|
||||||
|
PortletApplicationContextUtils.registerPortletApplicationScopes(beanFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This implementation supports file paths beneath the root of the PortletContext.
|
||||||
|
* @see PortletContextResource
|
||||||
|
*/
|
||||||
|
protected Resource getResourceByPath(String path) {
|
||||||
|
return new PortletContextResource(this.portletContext, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This implementation supports pattern matching in unexpanded WARs too.
|
||||||
|
* @see PortletContextResourcePatternResolver
|
||||||
|
*/
|
||||||
|
protected ResourcePatternResolver getResourcePatternResolver() {
|
||||||
|
return new PortletContextResourcePatternResolver(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,142 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2007 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.context;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||||
|
import org.springframework.beans.factory.xml.ResourceEntityResolver;
|
||||||
|
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Portlet-based {@link org.springframework.web.context.WebApplicationContext}
|
||||||
|
* implementation which takes its configuration from XML documents, understood
|
||||||
|
* by an {@link org.springframework.beans.factory.xml.XmlBeanDefinitionReader}.
|
||||||
|
* This is essentially the equivalent of
|
||||||
|
* {@link org.springframework.context.support.AbstractXmlApplicationContext}
|
||||||
|
* for a portlet environment.
|
||||||
|
*
|
||||||
|
* <p>By default, the configuration will be taken from "/WEB-INF/applicationContext.xml"
|
||||||
|
* for the root context, and "/WEB-INF/test-portlet.xml" for a context with the namespace
|
||||||
|
* "test-portlet" (like for a DispatcherPortlet instance with the portlet-name "test").
|
||||||
|
*
|
||||||
|
* <p>The config location defaults can be overridden via the "contextConfigLocation"
|
||||||
|
* portlet init-param of {@link org.springframework.web.portlet.FrameworkPortlet}.
|
||||||
|
* Config locations can either denote concrete files like "/WEB-INF/context.xml"
|
||||||
|
* or Ant-style patterns like "/WEB-INF/*-context.xml" (see
|
||||||
|
* {@link org.springframework.util.PathMatcher} javadoc for pattern details).
|
||||||
|
*
|
||||||
|
* <p>Note: In case of multiple config locations, later bean definitions will
|
||||||
|
* override ones defined in earlier loaded files. This can be leveraged to
|
||||||
|
* deliberately override certain bean definitions via an extra XML file.
|
||||||
|
*
|
||||||
|
* <p><b>For a Portlet-based context that reads in a different bean definition format,
|
||||||
|
* create an analogous subclass of {@link AbstractRefreshablePortletApplicationContext}.</b>
|
||||||
|
* Such a context implementation can be specified as "contextClass" init-param
|
||||||
|
* for a FrameworkPortlet instance.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @since 2.0
|
||||||
|
* @see #setNamespace
|
||||||
|
* @see #setConfigLocations
|
||||||
|
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
|
||||||
|
* @see org.springframework.web.portlet.FrameworkPortlet#initPortletApplicationContext
|
||||||
|
*/
|
||||||
|
public class XmlPortletApplicationContext extends AbstractRefreshablePortletApplicationContext {
|
||||||
|
|
||||||
|
/** Default config location for the root context */
|
||||||
|
public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";
|
||||||
|
|
||||||
|
/** Default prefix for building a config location for a namespace */
|
||||||
|
public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";
|
||||||
|
|
||||||
|
/** Default suffix for building a config location for a namespace */
|
||||||
|
public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the bean definitions via an XmlBeanDefinitionReader.
|
||||||
|
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
|
||||||
|
* @see #initBeanDefinitionReader
|
||||||
|
* @see #loadBeanDefinitions
|
||||||
|
*/
|
||||||
|
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {
|
||||||
|
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
|
||||||
|
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
|
||||||
|
|
||||||
|
// Configure the bean definition reader with this context's
|
||||||
|
// resource loading environment.
|
||||||
|
beanDefinitionReader.setResourceLoader(this);
|
||||||
|
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
|
||||||
|
|
||||||
|
// Allow a subclass to provide custom initialization of the reader,
|
||||||
|
// then proceed with actually loading the bean definitions.
|
||||||
|
initBeanDefinitionReader(beanDefinitionReader);
|
||||||
|
loadBeanDefinitions(beanDefinitionReader);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the bean definition reader used for loading the bean
|
||||||
|
* definitions of this context. Default implementation is empty.
|
||||||
|
* <p>Can be overridden in subclasses, e.g. for turning off XML validation
|
||||||
|
* or using a different XmlBeanDefinitionParser implementation.
|
||||||
|
* @param beanDefinitionReader the bean definition reader used by this context
|
||||||
|
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader#setValidationMode
|
||||||
|
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader#setDocumentReaderClass
|
||||||
|
*/
|
||||||
|
protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the bean definitions with the given XmlBeanDefinitionReader.
|
||||||
|
* <p>The lifecycle of the bean factory is handled by the refreshBeanFactory method;
|
||||||
|
* therefore this method is just supposed to load and/or register bean definitions.
|
||||||
|
* <p>Delegates to a ResourcePatternResolver for resolving location patterns
|
||||||
|
* into Resource instances.
|
||||||
|
* @throws org.springframework.beans.BeansException in case of bean registration errors
|
||||||
|
* @throws java.io.IOException if the required XML document isn't found
|
||||||
|
* @see #refreshBeanFactory
|
||||||
|
* @see #getConfigLocations
|
||||||
|
* @see #getResources
|
||||||
|
* @see #getResourcePatternResolver
|
||||||
|
*/
|
||||||
|
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
|
||||||
|
String[] configLocations = getConfigLocations();
|
||||||
|
if (configLocations != null) {
|
||||||
|
for (int i = 0; i < configLocations.length; i++) {
|
||||||
|
reader.loadBeanDefinitions(configLocations[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default location for the root context is "/WEB-INF/applicationContext.xml",
|
||||||
|
* and "/WEB-INF/test-portlet.xml" for a context with the namespace "test-portlet"
|
||||||
|
* (like for a DispatcherPortlet instance with the portlet-name "test").
|
||||||
|
*/
|
||||||
|
protected String[] getDefaultConfigLocations() {
|
||||||
|
if (getNamespace() != null) {
|
||||||
|
return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return new String[] {DEFAULT_CONFIG_LOCATION};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
Support for Spring's application context concept in a portlet environment,
|
||||||
|
including ApplicationContext implementations and various utility classes.
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,261 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2007 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.handler;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.portlet.PortletRequest;
|
||||||
|
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.context.support.ApplicationObjectSupport;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.web.context.request.WebRequestInterceptor;
|
||||||
|
import org.springframework.web.portlet.HandlerExecutionChain;
|
||||||
|
import org.springframework.web.portlet.HandlerInterceptor;
|
||||||
|
import org.springframework.web.portlet.HandlerMapping;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract base class for {@link org.springframework.web.portlet.HandlerMapping}
|
||||||
|
* implementations. Supports ordering, a default handler, and handler interceptors.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @since 2.0
|
||||||
|
* @see #getHandlerInternal
|
||||||
|
* @see #setDefaultHandler
|
||||||
|
* @see #setInterceptors
|
||||||
|
* @see org.springframework.web.portlet.HandlerInterceptor
|
||||||
|
*/
|
||||||
|
public abstract class AbstractHandlerMapping extends ApplicationObjectSupport
|
||||||
|
implements HandlerMapping, Ordered {
|
||||||
|
|
||||||
|
private int order = Integer.MAX_VALUE; // default: same as non-Ordered
|
||||||
|
|
||||||
|
private Object defaultHandler;
|
||||||
|
|
||||||
|
private final List interceptors = new ArrayList();
|
||||||
|
|
||||||
|
private boolean applyWebRequestInterceptorsToRenderPhaseOnly = true;
|
||||||
|
|
||||||
|
private HandlerInterceptor[] adaptedInterceptors;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify the order value for this HandlerMapping bean.
|
||||||
|
* <p>Default value is <code>Integer.MAX_VALUE</code>, meaning that it's non-ordered.
|
||||||
|
* @see org.springframework.core.Ordered#getOrder()
|
||||||
|
*/
|
||||||
|
public final void setOrder(int order) {
|
||||||
|
this.order = order;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final int getOrder() {
|
||||||
|
return this.order;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the default handler for this handler mapping.
|
||||||
|
* This handler will be returned if no specific mapping was found.
|
||||||
|
* <p>Default is <code>null</code>, indicating no default handler.
|
||||||
|
*/
|
||||||
|
public void setDefaultHandler(Object defaultHandler) {
|
||||||
|
this.defaultHandler = defaultHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the default handler for this handler mapping,
|
||||||
|
* or <code>null</code> if none.
|
||||||
|
*/
|
||||||
|
public Object getDefaultHandler() {
|
||||||
|
return this.defaultHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the interceptors to apply for all handlers mapped by this handler mapping.
|
||||||
|
* <p>Supported interceptor types are HandlerInterceptor and WebRequestInterceptor.
|
||||||
|
* Each given WebRequestInterceptor will be wrapped in a WebRequestHandlerInterceptorAdapter.
|
||||||
|
* @param interceptors array of handler interceptors, or <code>null</code> if none
|
||||||
|
* @see #adaptInterceptor
|
||||||
|
* @see org.springframework.web.portlet.HandlerInterceptor
|
||||||
|
* @see org.springframework.web.context.request.WebRequestInterceptor
|
||||||
|
*/
|
||||||
|
public void setInterceptors(Object[] interceptors) {
|
||||||
|
this.interceptors.addAll(Arrays.asList(interceptors));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify whether to apply WebRequestInterceptors to the Portlet render phase
|
||||||
|
* only ("true", or whether to apply them to the Portlet action phase as well
|
||||||
|
* ("false").
|
||||||
|
* <p>Default is "true", since WebRequestInterceptors are usually built for
|
||||||
|
* MVC-style handler execution plus rendering process (which is, for example,
|
||||||
|
* the primary target scenario for "Open Session in View" interceptors,
|
||||||
|
* offering lazy loading of persistent objects during view rendering).
|
||||||
|
* Set this to "false" to have WebRequestInterceptors apply to the action
|
||||||
|
* phase as well (for example, in case of an "Open Session in View" interceptor,
|
||||||
|
* to allow for lazy loading outside of a transaction during the action phase).
|
||||||
|
* @see #setInterceptors
|
||||||
|
* @see org.springframework.web.context.request.WebRequestInterceptor
|
||||||
|
* @see WebRequestHandlerInterceptorAdapter#WebRequestHandlerInterceptorAdapter(WebRequestInterceptor, boolean)
|
||||||
|
* @see org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor
|
||||||
|
*/
|
||||||
|
public void setApplyWebRequestInterceptorsToRenderPhaseOnly(boolean applyWebRequestInterceptorsToRenderPhaseOnly) {
|
||||||
|
this.applyWebRequestInterceptorsToRenderPhaseOnly = applyWebRequestInterceptorsToRenderPhaseOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the interceptors.
|
||||||
|
* @see #extendInterceptors(java.util.List)
|
||||||
|
* @see #initInterceptors()
|
||||||
|
*/
|
||||||
|
protected void initApplicationContext() throws BeansException {
|
||||||
|
extendInterceptors(this.interceptors);
|
||||||
|
initInterceptors();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension hook that subclasses can override to register additional interceptors,
|
||||||
|
* given the configured interceptors (see {@link #setInterceptors}).
|
||||||
|
* <p>Will be invoked before {@link #initInterceptors()} adapts the specified
|
||||||
|
* interceptors into {@link HandlerInterceptor} instances.
|
||||||
|
* <p>The default implementation is empty.
|
||||||
|
* @param interceptors the configured interceptor List (never <code>null</code>),
|
||||||
|
* allowing to add further interceptors before as well as after the existing
|
||||||
|
* interceptors
|
||||||
|
*/
|
||||||
|
protected void extendInterceptors(List interceptors) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the specified interceptors, adapting them where necessary.
|
||||||
|
* @see #setInterceptors
|
||||||
|
* @see #adaptInterceptor
|
||||||
|
*/
|
||||||
|
protected void initInterceptors() {
|
||||||
|
if (!this.interceptors.isEmpty()) {
|
||||||
|
this.adaptedInterceptors = new HandlerInterceptor[this.interceptors.size()];
|
||||||
|
for (int i = 0; i < this.interceptors.size(); i++) {
|
||||||
|
Object interceptor = this.interceptors.get(i);
|
||||||
|
if (interceptor == null) {
|
||||||
|
throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
|
||||||
|
}
|
||||||
|
this.adaptedInterceptors[i] = adaptInterceptor(interceptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapt the given interceptor object to the HandlerInterceptor interface.
|
||||||
|
* <p>Supported interceptor types are HandlerInterceptor and WebRequestInterceptor.
|
||||||
|
* Each given WebRequestInterceptor will be wrapped in a WebRequestHandlerInterceptorAdapter.
|
||||||
|
* Can be overridden in subclasses.
|
||||||
|
* @param interceptor the specified interceptor object
|
||||||
|
* @return the interceptor wrapped as HandlerInterceptor
|
||||||
|
* @see #setApplyWebRequestInterceptorsToRenderPhaseOnly
|
||||||
|
* @see org.springframework.web.portlet.HandlerInterceptor
|
||||||
|
* @see org.springframework.web.context.request.WebRequestInterceptor
|
||||||
|
* @see WebRequestHandlerInterceptorAdapter
|
||||||
|
*/
|
||||||
|
protected HandlerInterceptor adaptInterceptor(Object interceptor) {
|
||||||
|
if (interceptor instanceof HandlerInterceptor) {
|
||||||
|
return (HandlerInterceptor) interceptor;
|
||||||
|
}
|
||||||
|
else if (interceptor instanceof WebRequestInterceptor) {
|
||||||
|
return new WebRequestHandlerInterceptorAdapter(
|
||||||
|
(WebRequestInterceptor) interceptor, this.applyWebRequestInterceptorsToRenderPhaseOnly);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the adapted interceptors as HandlerInterceptor array.
|
||||||
|
* @return the array of HandlerInterceptors, or <code>null</code> if none
|
||||||
|
*/
|
||||||
|
protected final HandlerInterceptor[] getAdaptedInterceptors() {
|
||||||
|
return this.adaptedInterceptors;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Look up a handler for the given request, falling back to the default
|
||||||
|
* handler if no specific one is found.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @return the corresponding handler instance, or the default handler
|
||||||
|
* @see #getHandlerInternal
|
||||||
|
*/
|
||||||
|
public final HandlerExecutionChain getHandler(PortletRequest request) throws Exception {
|
||||||
|
Object handler = getHandlerInternal(request);
|
||||||
|
if (handler == null) {
|
||||||
|
handler = getDefaultHandler();
|
||||||
|
}
|
||||||
|
if (handler == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// Bean name or resolved handler?
|
||||||
|
if (handler instanceof String) {
|
||||||
|
String handlerName = (String) handler;
|
||||||
|
handler = getApplicationContext().getBean(handlerName);
|
||||||
|
}
|
||||||
|
return getHandlerExecutionChain(handler, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Look up a handler for the given request, returning <code>null</code> if no
|
||||||
|
* specific one is found. This method is called by {@link #getHandler};
|
||||||
|
* a <code>null</code> return value will lead to the default handler, if one is set.
|
||||||
|
* <p>Note: This method may also return a pre-built {@link HandlerExecutionChain},
|
||||||
|
* combining a handler object with dynamically determined interceptors.
|
||||||
|
* Statically specified interceptors will get merged into such an existing chain.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @return the corresponding handler instance, or <code>null</code> if none found
|
||||||
|
* @throws Exception if there is an internal error
|
||||||
|
* @see #getHandler
|
||||||
|
*/
|
||||||
|
protected abstract Object getHandlerInternal(PortletRequest request) throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a HandlerExecutionChain for the given handler, including applicable interceptors.
|
||||||
|
* <p>The default implementation simply builds a standard HandlerExecutionChain with
|
||||||
|
* the given handler and this handler mapping's common interceptors. Subclasses may
|
||||||
|
* override this in order to extend/rearrange the list of interceptors.
|
||||||
|
* <p><b>NOTE:</b> The passed-in handler object may be a raw handler or a pre-built
|
||||||
|
* HandlerExecutionChain. This method should handle those two cases explicitly,
|
||||||
|
* either building a new HandlerExecutionChain or extending the existing chain.
|
||||||
|
* <p>For simply adding an interceptor, consider calling <code>super.getHandlerExecutionChain</code>
|
||||||
|
* and invoking {@link HandlerExecutionChain#addInterceptor} on the returned chain object.
|
||||||
|
* @param handler the resolved handler instance (never <code>null</code>)
|
||||||
|
* @param request current HTTP request
|
||||||
|
* @return the HandlerExecutionChain (never <code>null</code>)
|
||||||
|
* @see #getAdaptedInterceptors()
|
||||||
|
*/
|
||||||
|
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, PortletRequest request) {
|
||||||
|
if (handler instanceof HandlerExecutionChain) {
|
||||||
|
HandlerExecutionChain chain = (HandlerExecutionChain) handler;
|
||||||
|
chain.addInterceptors(getAdaptedInterceptors());
|
||||||
|
return chain;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return new HandlerExecutionChain(handler, getAdaptedInterceptors());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,190 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2008 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.handler;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.portlet.PortletRequest;
|
||||||
|
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract base class for {@link org.springframework.web.portlet.HandlerMapping}
|
||||||
|
* implementations that rely on a map which caches handler objects per lookup key.
|
||||||
|
* Supports arbitrary lookup keys, and automatically resolves handler bean names
|
||||||
|
* into handler bean instances.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 2.0
|
||||||
|
* @see #getLookupKey(javax.portlet.PortletRequest)
|
||||||
|
* @see #registerHandler(Object, Object)
|
||||||
|
*/
|
||||||
|
public abstract class AbstractMapBasedHandlerMapping extends AbstractHandlerMapping {
|
||||||
|
|
||||||
|
private boolean lazyInitHandlers = false;
|
||||||
|
|
||||||
|
private final Map handlerMap = new HashMap();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether to lazily initialize handlers. Only applicable to
|
||||||
|
* singleton handlers, as prototypes are always lazily initialized.
|
||||||
|
* Default is false, as eager initialization allows for more efficiency
|
||||||
|
* through referencing the handler objects directly.
|
||||||
|
* <p>If you want to allow your handlers to be lazily initialized,
|
||||||
|
* make them "lazy-init" and set this flag to true. Just making them
|
||||||
|
* "lazy-init" will not work, as they are initialized through the
|
||||||
|
* references from the handler mapping in this case.
|
||||||
|
*/
|
||||||
|
public void setLazyInitHandlers(boolean lazyInitHandlers) {
|
||||||
|
this.lazyInitHandlers = lazyInitHandlers;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines a handler for the computed lookup key for the given request.
|
||||||
|
* @see #getLookupKey
|
||||||
|
*/
|
||||||
|
protected Object getHandlerInternal(PortletRequest request) throws Exception {
|
||||||
|
Object lookupKey = getLookupKey(request);
|
||||||
|
Object handler = this.handlerMap.get(lookupKey);
|
||||||
|
if (handler != null && logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Key [" + lookupKey + "] -> handler [" + handler + "]");
|
||||||
|
}
|
||||||
|
if (handler instanceof Map) {
|
||||||
|
Map predicateMap = (Map) handler;
|
||||||
|
List predicates = new LinkedList(predicateMap.keySet());
|
||||||
|
Collections.sort(predicates);
|
||||||
|
for (Iterator it = predicates.iterator(); it.hasNext();) {
|
||||||
|
PortletRequestMappingPredicate predicate = (PortletRequestMappingPredicate) it.next();
|
||||||
|
if (predicate.match(request)) {
|
||||||
|
return predicateMap.get(predicate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a lookup key for the given request.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @return the lookup key (never <code>null</code>)
|
||||||
|
* @throws Exception if key computation failed
|
||||||
|
*/
|
||||||
|
protected abstract Object getLookupKey(PortletRequest request) throws Exception;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register all handlers specified in the Portlet mode map for the corresponding modes.
|
||||||
|
* @param handlerMap Map with lookup keys as keys and handler beans or bean names as values
|
||||||
|
* @throws BeansException if the handler couldn't be registered
|
||||||
|
*/
|
||||||
|
protected void registerHandlers(Map handlerMap) throws BeansException {
|
||||||
|
Assert.notNull(handlerMap, "Handler Map must not be null");
|
||||||
|
for (Iterator it = handlerMap.entrySet().iterator(); it.hasNext();) {
|
||||||
|
Map.Entry entry = (Map.Entry) it.next();
|
||||||
|
registerHandler(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the given handler instance for the given parameter value.
|
||||||
|
* @param lookupKey the key to map the handler onto
|
||||||
|
* @param handler the handler instance or handler bean name String
|
||||||
|
* (a bean name will automatically be resolved into the corresponding handler bean)
|
||||||
|
* @throws BeansException if the handler couldn't be registered
|
||||||
|
* @throws IllegalStateException if there is a conflicting handler registered
|
||||||
|
*/
|
||||||
|
protected void registerHandler(Object lookupKey, Object handler) throws BeansException, IllegalStateException {
|
||||||
|
registerHandler(lookupKey, handler, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the given handler instance for the given parameter value.
|
||||||
|
* @param lookupKey the key to map the handler onto
|
||||||
|
* @param handler the handler instance or handler bean name String
|
||||||
|
* (a bean name will automatically be resolved into the corresponding handler bean)
|
||||||
|
* @param predicate a predicate object for this handler (may be <code>null</code>),
|
||||||
|
* determining a match with the primary lookup key
|
||||||
|
* @throws BeansException if the handler couldn't be registered
|
||||||
|
* @throws IllegalStateException if there is a conflicting handler registered
|
||||||
|
*/
|
||||||
|
protected void registerHandler(Object lookupKey, Object handler, PortletRequestMappingPredicate predicate)
|
||||||
|
throws BeansException, IllegalStateException {
|
||||||
|
|
||||||
|
Assert.notNull(lookupKey, "Lookup key must not be null");
|
||||||
|
Assert.notNull(handler, "Handler object must not be null");
|
||||||
|
Object resolvedHandler = handler;
|
||||||
|
|
||||||
|
// Eagerly resolve handler if referencing singleton via name.
|
||||||
|
if (!this.lazyInitHandlers && handler instanceof String) {
|
||||||
|
String handlerName = (String) handler;
|
||||||
|
if (getApplicationContext().isSingleton(handlerName)) {
|
||||||
|
resolvedHandler = getApplicationContext().getBean(handlerName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for duplicate mapping.
|
||||||
|
Object mappedHandler = this.handlerMap.get(lookupKey);
|
||||||
|
if (mappedHandler != null && !(mappedHandler instanceof Map)) {
|
||||||
|
if (mappedHandler != resolvedHandler) {
|
||||||
|
throw new IllegalStateException("Cannot map handler [" + handler + "] to key [" + lookupKey +
|
||||||
|
"]: There's already handler [" + mappedHandler + "] mapped.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (predicate != null) {
|
||||||
|
// Add the handler to the predicate map.
|
||||||
|
Map predicateMap = (Map) mappedHandler;
|
||||||
|
if (predicateMap == null) {
|
||||||
|
predicateMap = new LinkedHashMap();
|
||||||
|
this.handlerMap.put(lookupKey, predicateMap);
|
||||||
|
}
|
||||||
|
predicateMap.put(predicate, resolvedHandler);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Add the single handler to the map.
|
||||||
|
this.handlerMap.put(lookupKey, resolvedHandler);
|
||||||
|
}
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Mapped key [" + lookupKey + "] onto handler [" + resolvedHandler + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Predicate interface for determining a match with a given request.
|
||||||
|
*/
|
||||||
|
protected interface PortletRequestMappingPredicate extends Comparable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the given request matches this predicate.
|
||||||
|
* @param request current portlet request
|
||||||
|
*/
|
||||||
|
boolean match(PortletRequest request);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2005 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.handler;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.portlet.ActionRequest;
|
||||||
|
import javax.portlet.ActionResponse;
|
||||||
|
import javax.portlet.RenderRequest;
|
||||||
|
import javax.portlet.RenderResponse;
|
||||||
|
import javax.portlet.PortletRequest;
|
||||||
|
import javax.portlet.PortletResponse;
|
||||||
|
import javax.portlet.PortletException;
|
||||||
|
|
||||||
|
import org.springframework.web.portlet.HandlerInterceptor;
|
||||||
|
import org.springframework.web.portlet.ModelAndView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract adapter class for the HandlerInterceptor interface,
|
||||||
|
* for simplified implementation of pre-only/post-only interceptors.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public abstract class HandlerInterceptorAdapter implements HandlerInterceptor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This implementation delegates to <code>preHandle</code>.
|
||||||
|
* @see #preHandle
|
||||||
|
*/
|
||||||
|
public boolean preHandleAction(ActionRequest request, ActionResponse response, Object handler) throws Exception {
|
||||||
|
return preHandle(request, response, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This implementation delegates to <code>afterCompletion</code>.
|
||||||
|
* @see #afterCompletion
|
||||||
|
*/
|
||||||
|
public void afterActionCompletion(
|
||||||
|
ActionRequest request, ActionResponse response, Object handler, Exception ex) throws Exception {
|
||||||
|
|
||||||
|
afterCompletion(request, response, handler, ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This implementation delegates to <code>preHandle</code>.
|
||||||
|
* @see #preHandle
|
||||||
|
*/
|
||||||
|
public boolean preHandleRender(RenderRequest request, RenderResponse response, Object handler) throws Exception {
|
||||||
|
return preHandle(request, response, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This implementation is empty.
|
||||||
|
*/
|
||||||
|
public void postHandleRender(
|
||||||
|
RenderRequest request, RenderResponse response, Object handler, ModelAndView modelAndView) throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This implementation delegates to <code>afterCompletion</code>.
|
||||||
|
* @see #afterCompletion
|
||||||
|
*/
|
||||||
|
public void afterRenderCompletion(
|
||||||
|
RenderRequest request, RenderResponse response, Object handler, Exception ex) throws Exception {
|
||||||
|
|
||||||
|
afterCompletion(request, response, handler, ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default callback that both <code>preHandleRender</code>
|
||||||
|
* and <code>preHandleAction</code> delegate to.
|
||||||
|
* <p>This implementation always returns <code>true</code>.
|
||||||
|
* @see #preHandleRender
|
||||||
|
* @see #preHandleAction
|
||||||
|
*/
|
||||||
|
protected boolean preHandle(PortletRequest request, PortletResponse response, Object handler)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default callback that both <code>preHandleRender</code>
|
||||||
|
* and <code>preHandleAction</code> delegate to.
|
||||||
|
* <p>This implementation is empty.
|
||||||
|
* @see #afterRenderCompletion
|
||||||
|
* @see #afterActionCompletion
|
||||||
|
*/
|
||||||
|
protected void afterCompletion(
|
||||||
|
PortletRequest request, PortletResponse response, Object handler, Exception ex) throws Exception {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,119 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2007 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.handler;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.portlet.PortletRequest;
|
||||||
|
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of the {@link org.springframework.web.portlet.HandlerMapping}
|
||||||
|
* to map from a request parameter to request handler beans.
|
||||||
|
*
|
||||||
|
* <p>The default name of the parameter is "action", but can be changed using
|
||||||
|
* {@link #setParameterName setParameterName()}.
|
||||||
|
*
|
||||||
|
* <p>The bean configuration for this mapping will look somthing like this:
|
||||||
|
*
|
||||||
|
* <pre class="code">
|
||||||
|
* <bean id="parameterHandlerMapping" class="org.springframework.web.portlet.handler.ParameterHandlerMapping">
|
||||||
|
* <property name="parameterMap">
|
||||||
|
* <map>
|
||||||
|
* <entry key="add"><ref bean="addItemHandler"/></entry>
|
||||||
|
* <entry key="edit"><ref bean="editItemHandler"/></entry>
|
||||||
|
* <entry key="delete"><ref bean="deleteItemHandler"/></entry>
|
||||||
|
* </map>
|
||||||
|
* </property>
|
||||||
|
* </bean></pre>
|
||||||
|
*
|
||||||
|
* Thanks to Rainer Schmitz for suggesting this mapping strategy!
|
||||||
|
*
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 2.0
|
||||||
|
* @see ParameterMappingInterceptor
|
||||||
|
*/
|
||||||
|
public class ParameterHandlerMapping extends AbstractMapBasedHandlerMapping {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default request parameter name to use for mapping to handlers: "action".
|
||||||
|
*/
|
||||||
|
public final static String DEFAULT_PARAMETER_NAME = "action";
|
||||||
|
|
||||||
|
|
||||||
|
private String parameterName = DEFAULT_PARAMETER_NAME;
|
||||||
|
|
||||||
|
private Map parameterMap;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the name of the parameter used for mapping to handlers.
|
||||||
|
* <p>Default is "action".
|
||||||
|
*/
|
||||||
|
public void setParameterName(String parameterName) {
|
||||||
|
Assert.hasText(parameterName, "'parameterName' must not be empty");
|
||||||
|
this.parameterName = parameterName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a Map with parameters as keys and handler beans or bean names as values.
|
||||||
|
* Convenient for population with bean references.
|
||||||
|
* @param parameterMap map with parameters as keys and beans as values
|
||||||
|
*/
|
||||||
|
public void setParameterMap(Map parameterMap) {
|
||||||
|
this.parameterMap = parameterMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls the <code>registerHandlers</code> method in addition
|
||||||
|
* to the superclass's initialization.
|
||||||
|
* @see #registerHandlers
|
||||||
|
*/
|
||||||
|
public void initApplicationContext() throws BeansException {
|
||||||
|
super.initApplicationContext();
|
||||||
|
registerHandlers(this.parameterMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register all handlers specified in the Portlet mode map for the corresponding modes.
|
||||||
|
* @param parameterMap Map with parameter names as keys and handler beans or bean names as values
|
||||||
|
* @throws BeansException if the handler couldn't be registered
|
||||||
|
*/
|
||||||
|
protected void registerHandlers(Map parameterMap) throws BeansException {
|
||||||
|
if (CollectionUtils.isEmpty(parameterMap)) {
|
||||||
|
logger.warn("'parameterMap' is empty on ParameterHandlerMapping");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.registerHandlers(parameterMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the value of the specified parameter as lookup key.
|
||||||
|
* @see #setParameterName
|
||||||
|
*/
|
||||||
|
protected Object getLookupKey(PortletRequest request) throws Exception {
|
||||||
|
return request.getParameter(this.parameterName);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2007 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.handler;
|
||||||
|
|
||||||
|
import javax.portlet.ActionRequest;
|
||||||
|
import javax.portlet.ActionResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interceptor to forward a request parameter from the <code>ActionRequest</code> to the
|
||||||
|
* <code>RenderRequest</code>.
|
||||||
|
*
|
||||||
|
* <p>This can be useful when using {@link ParameterHandlerMapping ParameterHandlerMapping}
|
||||||
|
* or {@link PortletModeParameterHandlerMapping PortletModeParameterHandlerMapping}.
|
||||||
|
* It will ensure that the parameter that was used to map the <code>ActionRequest</code>
|
||||||
|
* to a handler will be forwarded to the <code>RenderRequest</code> so that it will also be
|
||||||
|
* mapped the same way.
|
||||||
|
*
|
||||||
|
* <p>When using this Interceptor, you can still change the value of the mapping parameter
|
||||||
|
* in your handler in order to change where the render request will get mapped.
|
||||||
|
*
|
||||||
|
* <p>Be aware that this Interceptor does call <code>ActionResponse.setRenderParameter</code>,
|
||||||
|
* which means that you will not be able to call <code>ActionResponse.sendRedirect</code> in
|
||||||
|
* your handler. If you may need to issue a redirect, then you should avoid this Interceptor
|
||||||
|
* and either write a different one that does this in a different way, or manually forward
|
||||||
|
* the parameter from within your handler(s).
|
||||||
|
*
|
||||||
|
* <p>Thanks to Rainer Schmitz for suggesting this mapping strategy!
|
||||||
|
*
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @since 2.0
|
||||||
|
* @see ParameterHandlerMapping
|
||||||
|
* @see PortletModeParameterHandlerMapping
|
||||||
|
*/
|
||||||
|
public class ParameterMappingInterceptor extends HandlerInterceptorAdapter {
|
||||||
|
|
||||||
|
/** Request parameter name to use for mapping to handlers */
|
||||||
|
public final static String DEFAULT_PARAMETER_NAME = "action";
|
||||||
|
|
||||||
|
private String parameterName = DEFAULT_PARAMETER_NAME;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the name of the parameter used for mapping.
|
||||||
|
*/
|
||||||
|
public void setParameterName(String parameterName) {
|
||||||
|
this.parameterName = (parameterName != null ? parameterName : DEFAULT_PARAMETER_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If request is an {@link javax.portlet.ActionRequest ActionRequest},
|
||||||
|
* get handler mapping parameter and add it to the ActionResponse.
|
||||||
|
*/
|
||||||
|
public boolean preHandleAction(ActionRequest request, ActionResponse response, Object handler) {
|
||||||
|
String mappingParameter = request.getParameter(this.parameterName);
|
||||||
|
if (mappingParameter != null) {
|
||||||
|
response.setRenderParameter(parameterName, mappingParameter);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,162 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2007 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.handler;
|
||||||
|
|
||||||
|
import javax.portlet.PortletException;
|
||||||
|
import javax.portlet.PortletRequest;
|
||||||
|
import javax.portlet.PortletResponse;
|
||||||
|
import javax.portlet.RenderRequest;
|
||||||
|
import javax.portlet.RenderResponse;
|
||||||
|
|
||||||
|
import org.springframework.web.portlet.context.PortletApplicationObjectSupport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenient superclass for any kind of web content generator,
|
||||||
|
* like {@link org.springframework.web.portlet.mvc.AbstractController}.
|
||||||
|
* Can also be used for custom handlers that have their own
|
||||||
|
* {@link org.springframework.web.portlet.HandlerAdapter}.
|
||||||
|
*
|
||||||
|
* <p>Supports portlet cache control options.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @since 2.0
|
||||||
|
* @see #setCacheSeconds
|
||||||
|
* @see #setRequireSession
|
||||||
|
*/
|
||||||
|
public abstract class PortletContentGenerator extends PortletApplicationObjectSupport {
|
||||||
|
|
||||||
|
private boolean requireSession = false;
|
||||||
|
|
||||||
|
private int cacheSeconds = -1;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether a session should be required to handle requests.
|
||||||
|
*/
|
||||||
|
public final void setRequireSession(boolean requireSession) {
|
||||||
|
this.requireSession = requireSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether a session is required to handle requests.
|
||||||
|
*/
|
||||||
|
public final boolean isRequireSession() {
|
||||||
|
return this.requireSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache content for the given number of seconds. Default is -1,
|
||||||
|
* indicating no override of portlet content caching.
|
||||||
|
* <p>Only if this is set to 0 (no cache) or a positive value (cache for
|
||||||
|
* this many seconds) will this class override the portlet settings.
|
||||||
|
* <p>The cache setting can be overwritten by subclasses, before content is generated.
|
||||||
|
*/
|
||||||
|
public final void setCacheSeconds(int seconds) {
|
||||||
|
this.cacheSeconds = seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the number of seconds that content is cached.
|
||||||
|
*/
|
||||||
|
public final int getCacheSeconds() {
|
||||||
|
return this.cacheSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check and prepare the given request and response according to the settings
|
||||||
|
* of this generator. Checks for a required session, and applies the number of
|
||||||
|
* cache seconds configured for this generator (if it is a render request/response).
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param response current portlet response
|
||||||
|
* @throws PortletException if the request cannot be handled because a check failed
|
||||||
|
*/
|
||||||
|
protected final void check(PortletRequest request, PortletResponse response) throws PortletException {
|
||||||
|
if (this.requireSession) {
|
||||||
|
if (request.getPortletSession(false) == null) {
|
||||||
|
throw new PortletSessionRequiredException("Pre-existing session required but none found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check and prepare the given request and response according to the settings
|
||||||
|
* of this generator. Checks for a required session, and applies the number of
|
||||||
|
* cache seconds configured for this generator (if it is a render request/response).
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param response current portlet response
|
||||||
|
* @throws PortletException if the request cannot be handled because a check failed
|
||||||
|
*/
|
||||||
|
protected final void checkAndPrepare(RenderRequest request, RenderResponse response)
|
||||||
|
throws PortletException {
|
||||||
|
|
||||||
|
checkAndPrepare(request, response, this.cacheSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check and prepare the given request and response according to the settings
|
||||||
|
* of this generator. Checks for a required session, and applies the given
|
||||||
|
* number of cache seconds (if it is a render request/response).
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param response current portlet response
|
||||||
|
* @param cacheSeconds positive number of seconds into the future that the
|
||||||
|
* response should be cacheable for, 0 to prevent caching
|
||||||
|
* @throws PortletException if the request cannot be handled because a check failed
|
||||||
|
*/
|
||||||
|
protected final void checkAndPrepare(
|
||||||
|
RenderRequest request, RenderResponse response, int cacheSeconds)
|
||||||
|
throws PortletException {
|
||||||
|
|
||||||
|
check(request, response);
|
||||||
|
applyCacheSeconds(response, cacheSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevent the render response from being cached.
|
||||||
|
*/
|
||||||
|
protected final void preventCaching(RenderResponse response) {
|
||||||
|
cacheForSeconds(response, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set portlet response to allow caching for the given number of seconds.
|
||||||
|
* @param response current portlet render response
|
||||||
|
* @param seconds number of seconds into the future that the response
|
||||||
|
* should be cacheable for
|
||||||
|
*/
|
||||||
|
protected final void cacheForSeconds(RenderResponse response, int seconds) {
|
||||||
|
response.setProperty(RenderResponse.EXPIRATION_CACHE, Integer.toString(seconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply the given cache seconds to the render response
|
||||||
|
* @param response current portlet render response
|
||||||
|
* @param seconds positive number of seconds into the future that the
|
||||||
|
* response should be cacheable for, 0 to prevent caching
|
||||||
|
*/
|
||||||
|
protected final void applyCacheSeconds(RenderResponse response, int seconds) {
|
||||||
|
if (seconds > 0) {
|
||||||
|
cacheForSeconds(response, seconds);
|
||||||
|
}
|
||||||
|
else if (seconds == 0) {
|
||||||
|
preventCaching(response);
|
||||||
|
}
|
||||||
|
// Leave caching to the portlet configuration otherwise.
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2007 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.handler;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import javax.portlet.PortletMode;
|
||||||
|
import javax.portlet.PortletRequest;
|
||||||
|
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of the {@link org.springframework.web.portlet.HandlerMapping}
|
||||||
|
* interface to map from the current PortletMode to request handler beans.
|
||||||
|
*
|
||||||
|
* <p>The bean configuration for this mapping will look something like this:
|
||||||
|
* <pre>
|
||||||
|
* <bean id="portletModeHandlerMapping" class="org.springframework.web.portlet.handler.PortletModeHandlerMapping">
|
||||||
|
* <property name="portletModeMap">
|
||||||
|
* <map>
|
||||||
|
* <entry key="view"><ref bean="viewHandler"/></entry>
|
||||||
|
* <entry key="edit"><ref bean="editHandler"/></entry>
|
||||||
|
* <entry key="help"><ref bean="helpHandler"/></entry>
|
||||||
|
* </map>
|
||||||
|
* </property>
|
||||||
|
* </bean>
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @author William G. Thompson, Jr.
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public class PortletModeHandlerMapping extends AbstractMapBasedHandlerMapping {
|
||||||
|
|
||||||
|
private final Map portletModeMap = new HashMap();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set PortletMode to handler bean name mappings from a Properties object.
|
||||||
|
* @param mappings properties with PortletMode names as keys and bean names as values
|
||||||
|
*/
|
||||||
|
public void setMappings(Properties mappings) {
|
||||||
|
this.portletModeMap.putAll(mappings);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a Map with PortletModes as keys and handler beans as values.
|
||||||
|
* Convenient for population with bean references.
|
||||||
|
* @param portletModeMap map with PortletMode names as keys and beans or bean names as values
|
||||||
|
*/
|
||||||
|
public void setPortletModeMap(Map portletModeMap) {
|
||||||
|
this.portletModeMap.putAll(portletModeMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls the <code>registerHandlers</code> method in addition
|
||||||
|
* to the superclass's initialization.
|
||||||
|
* @see #registerHandlers
|
||||||
|
*/
|
||||||
|
public void initApplicationContext() throws BeansException {
|
||||||
|
super.initApplicationContext();
|
||||||
|
registerHandlers(this.portletModeMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register all handlers specified in the Portlet mode map for the corresponding modes.
|
||||||
|
* @param portletModeMap Map with mode names as keys and handler beans or bean names as values
|
||||||
|
* @throws BeansException if the handler couldn't be registered
|
||||||
|
*/
|
||||||
|
protected void registerHandlers(Map portletModeMap) throws BeansException {
|
||||||
|
if (CollectionUtils.isEmpty(portletModeMap)) {
|
||||||
|
logger.warn("Neither 'portletModeMap' nor 'mappings' set on PortletModeHandlerMapping");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (Iterator it = portletModeMap.entrySet().iterator(); it.hasNext();) {
|
||||||
|
Map.Entry entry = (Map.Entry) it.next();
|
||||||
|
String modeKey = (String) entry.getKey();
|
||||||
|
PortletMode mode = new PortletMode(modeKey);
|
||||||
|
Object handler = entry.getValue();
|
||||||
|
registerHandler(mode, handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the current PortletMode as lookup key.
|
||||||
|
*/
|
||||||
|
protected Object getLookupKey(PortletRequest request) throws Exception {
|
||||||
|
return request.getPortletMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,255 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2008 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.handler;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.portlet.PortletMode;
|
||||||
|
import javax.portlet.PortletRequest;
|
||||||
|
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
import org.springframework.util.ObjectUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of the {@link org.springframework.web.portlet.HandlerMapping}
|
||||||
|
* interface to map from the current PortletMode and a request parameter to
|
||||||
|
* request handler beans. The mapping consists of two levels: first the
|
||||||
|
* PortletMode and then the parameter value. In order to be mapped,
|
||||||
|
* both elements must match the mapping definition.
|
||||||
|
*
|
||||||
|
* <p>This is a combination of the methods used in {@link PortletModeHandlerMapping PortletModeHandlerMapping}
|
||||||
|
* and {@link ParameterHandlerMapping ParameterHandlerMapping}. Unlike
|
||||||
|
* those two classes, this mapping cannot be initialized with properties since it
|
||||||
|
* requires a two-level map.
|
||||||
|
*
|
||||||
|
* <p>The default name of the parameter is "action", but can be changed using
|
||||||
|
* {@link #setParameterName setParameterName()}.
|
||||||
|
*
|
||||||
|
* <p>By default, the same parameter value may not be used in two different portlet
|
||||||
|
* modes. This is so that if the portal itself changes the portlet mode, the request
|
||||||
|
* will no longer be valid in the mapping. This behavior can be changed with
|
||||||
|
* {@link #setAllowDuplicateParameters setAllowDupParameters()}.
|
||||||
|
*
|
||||||
|
* <p>The bean configuration for this mapping will look somthing like this:
|
||||||
|
*
|
||||||
|
* <pre class="code">
|
||||||
|
* <bean id="portletModeParameterHandlerMapping" class="org.springframework.web.portlet.handler.PortletModeParameterHandlerMapping">
|
||||||
|
* <property name="portletModeParameterMap">
|
||||||
|
* <map>
|
||||||
|
* <entry key="view"> <!-- portlet mode: view -->
|
||||||
|
* <map>
|
||||||
|
* <entry key="add"><ref bean="addItemHandler"/></entry>
|
||||||
|
* <entry key="edit"><ref bean="editItemHandler"/></entry>
|
||||||
|
* <entry key="delete"><ref bean="deleteItemHandler"/></entry>
|
||||||
|
* </map>
|
||||||
|
* </entry>
|
||||||
|
* <entry key="edit"> <!-- portlet mode: edit -->
|
||||||
|
* <map>
|
||||||
|
* <entry key="prefs"><ref bean="preferencesHandler"/></entry>
|
||||||
|
* <entry key="resetPrefs"><ref bean="resetPreferencesHandler"/></entry>
|
||||||
|
* </map>
|
||||||
|
* </entry>
|
||||||
|
* </map>
|
||||||
|
* </property>
|
||||||
|
* </bean></pre>
|
||||||
|
*
|
||||||
|
* <p>This mapping can be chained ahead of a {@link PortletModeHandlerMapping PortletModeHandlerMapping},
|
||||||
|
* which can then provide defaults for each mode and an overall default as well.
|
||||||
|
*
|
||||||
|
* <p>Thanks to Rainer Schmitz and Yujin Kim for suggesting this mapping strategy!
|
||||||
|
*
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 2.0
|
||||||
|
* @see ParameterMappingInterceptor
|
||||||
|
*/
|
||||||
|
public class PortletModeParameterHandlerMapping extends AbstractMapBasedHandlerMapping {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default request parameter name to use for mapping to handlers: "action".
|
||||||
|
*/
|
||||||
|
public final static String DEFAULT_PARAMETER_NAME = "action";
|
||||||
|
|
||||||
|
|
||||||
|
private String parameterName = DEFAULT_PARAMETER_NAME;
|
||||||
|
|
||||||
|
private Map portletModeParameterMap;
|
||||||
|
|
||||||
|
private boolean allowDuplicateParameters = false;
|
||||||
|
|
||||||
|
private final Set parametersUsed = new HashSet();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the name of the parameter used for mapping to handlers.
|
||||||
|
* <p>Default is "action".
|
||||||
|
*/
|
||||||
|
public void setParameterName(String parameterName) {
|
||||||
|
Assert.hasText(parameterName, "'parameterName' must not be empty");
|
||||||
|
this.parameterName = parameterName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a Map with portlet mode names as keys and another Map as values.
|
||||||
|
* The sub-map has parameter names as keys and handler bean or bean names as values.
|
||||||
|
* <p>Convenient for population with bean references.
|
||||||
|
* @param portletModeParameterMap two-level map of portlet modes and parameters to handler beans
|
||||||
|
*/
|
||||||
|
public void setPortletModeParameterMap(Map portletModeParameterMap) {
|
||||||
|
this.portletModeParameterMap = portletModeParameterMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether to allow duplicate parameter values across different portlet modes.
|
||||||
|
* Default is "false".
|
||||||
|
* <p>Doing this is dangerous because the portlet mode can be changed by the
|
||||||
|
* portal itself and the only way to see that is a rerender of the portlet.
|
||||||
|
* If the same parameter value is legal in multiple modes, then a change in
|
||||||
|
* mode could result in a matched mapping that is not intended and the user
|
||||||
|
* could end up in a strange place in the application.
|
||||||
|
*/
|
||||||
|
public void setAllowDuplicateParameters(boolean allowDuplicateParameters) {
|
||||||
|
this.allowDuplicateParameters = allowDuplicateParameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls the <code>registerHandlers</code> method in addition
|
||||||
|
* to the superclass's initialization.
|
||||||
|
* @see #registerHandlers
|
||||||
|
*/
|
||||||
|
public void initApplicationContext() throws BeansException {
|
||||||
|
super.initApplicationContext();
|
||||||
|
registerHandlers(this.portletModeParameterMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register all handlers specified in the Portlet mode map for the corresponding modes.
|
||||||
|
* @param portletModeParameterMap Map with mode names as keys and parameter Maps as values
|
||||||
|
* @throws BeansException if the handler couldn't be registered
|
||||||
|
*/
|
||||||
|
protected void registerHandlers(Map portletModeParameterMap) throws BeansException {
|
||||||
|
if (CollectionUtils.isEmpty(portletModeParameterMap)) {
|
||||||
|
logger.warn("'portletModeParameterMap' not set on PortletModeParameterHandlerMapping");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (Iterator it = portletModeParameterMap.entrySet().iterator(); it.hasNext();) {
|
||||||
|
Map.Entry entry = (Map.Entry) it.next();
|
||||||
|
String modeKey = (String) entry.getKey();
|
||||||
|
PortletMode mode = new PortletMode(modeKey);
|
||||||
|
Object parameterMap = entry.getValue();
|
||||||
|
if (!(parameterMap instanceof Map)) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"The value for the portlet mode must be a Map of parameter Strings to handler Objects");
|
||||||
|
}
|
||||||
|
registerHandler(mode, (Map) parameterMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register all handlers specified in the given parameter map.
|
||||||
|
* @param parameterMap Map with parameter names as keys and handler beans or bean names as values
|
||||||
|
* @throws BeansException if the handler couldn't be registered
|
||||||
|
*/
|
||||||
|
protected void registerHandler(PortletMode mode, Map parameterMap) throws BeansException {
|
||||||
|
for (Iterator it = parameterMap.entrySet().iterator(); it.hasNext();) {
|
||||||
|
Map.Entry entry = (Map.Entry) it.next();
|
||||||
|
String parameter = (String) entry.getKey();
|
||||||
|
Object handler = entry.getValue();
|
||||||
|
registerHandler(mode, parameter, handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the given handler instance for the given PortletMode and parameter value,
|
||||||
|
* under an appropriate lookup key.
|
||||||
|
* @param mode the PortletMode for which this mapping is valid
|
||||||
|
* @param parameter the parameter value to which this handler is mapped
|
||||||
|
* @param handler the handler instance bean
|
||||||
|
* @throws BeansException if the handler couldn't be registered
|
||||||
|
* @throws IllegalStateException if there is a conflicting handler registered
|
||||||
|
* @see #registerHandler(Object, Object)
|
||||||
|
*/
|
||||||
|
protected void registerHandler(PortletMode mode, String parameter, Object handler)
|
||||||
|
throws BeansException, IllegalStateException {
|
||||||
|
|
||||||
|
// Check for duplicate parameter values across all portlet modes.
|
||||||
|
if (!this.allowDuplicateParameters && this.parametersUsed.contains(parameter)) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Duplicate entries for parameter [" + parameter + "] in different Portlet modes");
|
||||||
|
}
|
||||||
|
this.parametersUsed.add(parameter);
|
||||||
|
|
||||||
|
registerHandler(new LookupKey(mode, parameter), handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a lookup key that combines the current PortletMode and the current
|
||||||
|
* value of the specified parameter.
|
||||||
|
* @see javax.portlet.PortletRequest#getPortletMode()
|
||||||
|
* @see #setParameterName
|
||||||
|
*/
|
||||||
|
protected Object getLookupKey(PortletRequest request) throws Exception {
|
||||||
|
PortletMode mode = request.getPortletMode();
|
||||||
|
String parameter = request.getParameter(this.parameterName);
|
||||||
|
return new LookupKey(mode, parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal class used as lookup key, combining PortletMode and parameter value.
|
||||||
|
*/
|
||||||
|
private static class LookupKey {
|
||||||
|
|
||||||
|
private final PortletMode mode;
|
||||||
|
|
||||||
|
private final String parameter;
|
||||||
|
|
||||||
|
public LookupKey(PortletMode portletMode, String parameter) {
|
||||||
|
this.mode = portletMode;
|
||||||
|
this.parameter = parameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
if (this == other) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(other instanceof LookupKey)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LookupKey otherKey = (LookupKey) other;
|
||||||
|
return (this.mode.equals(otherKey.mode) &&
|
||||||
|
ObjectUtils.nullSafeEquals(this.parameter, otherKey.parameter));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int hashCode() {
|
||||||
|
return (this.mode.hashCode() * 29 + ObjectUtils.nullSafeHashCode(this.parameter));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return "Portlet mode '" + this.mode + "', parameter '" + this.parameter + "'";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2006 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.handler;
|
||||||
|
|
||||||
|
import javax.portlet.PortletException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception thrown when a portlet content generator requires a pre-existing session.
|
||||||
|
*
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @since 2.0
|
||||||
|
* @see org.springframework.web.portlet.handler.PortletContentGenerator
|
||||||
|
*/
|
||||||
|
public class PortletSessionRequiredException extends PortletException {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new PortletSessionRequiredException.
|
||||||
|
* @param msg the detail message
|
||||||
|
*/
|
||||||
|
public PortletSessionRequiredException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,396 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2007 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.handler;
|
||||||
|
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.portlet.RenderRequest;
|
||||||
|
import javax.portlet.RenderResponse;
|
||||||
|
import javax.portlet.WindowState;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.web.portlet.HandlerExceptionResolver;
|
||||||
|
import org.springframework.web.portlet.ModelAndView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link org.springframework.web.portlet.HandlerExceptionResolver} implementation
|
||||||
|
* that allows for mapping exception class names to view names, either for a
|
||||||
|
* set of given handlers or for all handlers in the DispatcherPortlet.
|
||||||
|
*
|
||||||
|
* <p>Error views are analogous to error page JSPs, but can be used with any
|
||||||
|
* kind of exception including any checked one, with fine-granular mappings for
|
||||||
|
* specific handlers.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public class SimpleMappingExceptionResolver implements HandlerExceptionResolver, Ordered {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default name of the exception attribute: "exception".
|
||||||
|
*/
|
||||||
|
public static final String DEFAULT_EXCEPTION_ATTRIBUTE = "exception";
|
||||||
|
|
||||||
|
|
||||||
|
/** Logger available to subclasses */
|
||||||
|
protected final Log logger = LogFactory.getLog(getClass());
|
||||||
|
|
||||||
|
private int order = Integer.MAX_VALUE; // default: same as non-Ordered
|
||||||
|
|
||||||
|
private Set mappedHandlers;
|
||||||
|
|
||||||
|
private Class[] mappedHandlerClasses;
|
||||||
|
|
||||||
|
private boolean renderWhenMinimized = false;
|
||||||
|
|
||||||
|
private Log warnLogger;
|
||||||
|
|
||||||
|
private Properties exceptionMappings;
|
||||||
|
|
||||||
|
private String defaultErrorView;
|
||||||
|
|
||||||
|
private String exceptionAttribute = DEFAULT_EXCEPTION_ATTRIBUTE;
|
||||||
|
|
||||||
|
|
||||||
|
public void setOrder(int order) {
|
||||||
|
this.order = order;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOrder() {
|
||||||
|
return this.order;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify the set of handlers that this exception resolver should map.
|
||||||
|
* The exception mappings and the default error view will only apply
|
||||||
|
* to the specified handlers.
|
||||||
|
* <p>If no handlers set, both the exception mappings and the default error
|
||||||
|
* view will apply to all handlers. This means that a specified default
|
||||||
|
* error view will be used as fallback for all exceptions; any further
|
||||||
|
* HandlerExceptionResolvers in the chain will be ignored in this case.
|
||||||
|
*/
|
||||||
|
public void setMappedHandlers(Set mappedHandlers) {
|
||||||
|
this.mappedHandlers = mappedHandlers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify the set of classes that this exception resolver should apply to.
|
||||||
|
* The exception mappings and the default error view will only apply
|
||||||
|
* to handlers of the specified type; the specified types may be interfaces
|
||||||
|
* and superclasses of handlers as well.
|
||||||
|
* <p>If no handlers and handler classes are set, the exception mappings
|
||||||
|
* and the default error view will apply to all handlers. This means that
|
||||||
|
* a specified default error view will be used as fallback for all exceptions;
|
||||||
|
* any further HandlerExceptionResolvers in the chain will be ignored in
|
||||||
|
* this case.
|
||||||
|
*/
|
||||||
|
public void setMappedHandlerClasses(Class[] mappedHandlerClasses) {
|
||||||
|
this.mappedHandlerClasses = mappedHandlerClasses;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set if the resolver should render a view when the portlet is in
|
||||||
|
* a minimized window. The default is "false".
|
||||||
|
* @see javax.portlet.RenderRequest#getWindowState()
|
||||||
|
* @see javax.portlet.WindowState#MINIMIZED
|
||||||
|
*/
|
||||||
|
public void setRenderWhenMinimized(boolean renderWhenMinimized) {
|
||||||
|
this.renderWhenMinimized = renderWhenMinimized;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the log category for warn logging. The name will be passed to the
|
||||||
|
* underlying logger implementation through Commons Logging, getting
|
||||||
|
* interpreted as log category according to the logger's configuration.
|
||||||
|
* <p>Default is no warn logging. Specify this setting to activate
|
||||||
|
* warn logging into a specific category. Alternatively, override
|
||||||
|
* the {@link #logException} method for custom logging.
|
||||||
|
* @see org.apache.commons.logging.LogFactory#getLog(String)
|
||||||
|
* @see org.apache.log4j.Logger#getLogger(String)
|
||||||
|
* @see java.util.logging.Logger#getLogger(String)
|
||||||
|
*/
|
||||||
|
public void setWarnLogCategory(String loggerName) {
|
||||||
|
this.warnLogger = LogFactory.getLog(loggerName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the mappings between exception class names and error view names.
|
||||||
|
* The exception class name can be a substring, with no wildcard support
|
||||||
|
* at present. A value of "PortletException" would match
|
||||||
|
* <code>javax.portet.PortletException</code> and subclasses, for example.
|
||||||
|
* <p><b>NB:</b> Consider carefully how specific the pattern is, and whether
|
||||||
|
* to include package information (which isn't mandatory). For example,
|
||||||
|
* "Exception" will match nearly anything, and will probably hide other rules.
|
||||||
|
* "java.lang.Exception" would be correct if "Exception" was meant to define
|
||||||
|
* a rule for all checked exceptions. With more unusual exception names such
|
||||||
|
* as "BaseBusinessException" there's no need to use a FQN.
|
||||||
|
* <p>Follows the same matching algorithm as RuleBasedTransactionAttribute
|
||||||
|
* and RollbackRuleAttribute.
|
||||||
|
* @param mappings exception patterns (can also be fully qualified class names)
|
||||||
|
* as keys, and error view names as values
|
||||||
|
* @see org.springframework.transaction.interceptor.RuleBasedTransactionAttribute
|
||||||
|
* @see org.springframework.transaction.interceptor.RollbackRuleAttribute
|
||||||
|
*/
|
||||||
|
public void setExceptionMappings(Properties mappings) {
|
||||||
|
this.exceptionMappings = mappings;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the name of the default error view.
|
||||||
|
* This view will be returned if no specific mapping was found.
|
||||||
|
* <p>Default is none.
|
||||||
|
*/
|
||||||
|
public void setDefaultErrorView(String defaultErrorView) {
|
||||||
|
this.defaultErrorView = defaultErrorView;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the name of the model attribute as which the exception should
|
||||||
|
* be exposed. Default is "exception".
|
||||||
|
* @see #DEFAULT_EXCEPTION_ATTRIBUTE
|
||||||
|
*/
|
||||||
|
public void setExceptionAttribute(String exceptionAttribute) {
|
||||||
|
this.exceptionAttribute = exceptionAttribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether this resolver is supposed to apply (i.e. the handler
|
||||||
|
* matches in case of "mappedHandlers" having been specified), then
|
||||||
|
* delegates to the {@link #doResolveException} template method.
|
||||||
|
*/
|
||||||
|
public ModelAndView resolveException(
|
||||||
|
RenderRequest request, RenderResponse response, Object handler, Exception ex) {
|
||||||
|
|
||||||
|
if (shouldApplyTo(request, handler)) {
|
||||||
|
return doResolveException(request, response, handler, ex);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether this resolver is supposed to apply to the given handler.
|
||||||
|
* <p>The default implementation checks against the specified mapped handlers
|
||||||
|
* and handler classes, if any, and alspo checks the window state (according
|
||||||
|
* to the "renderWhenMinimize" property).
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param handler the executed handler, or <code>null</code> if none chosen at the
|
||||||
|
* time of the exception (for example, if multipart resolution failed)
|
||||||
|
* @return whether this resolved should proceed with resolving the exception
|
||||||
|
* for the given request and handler
|
||||||
|
* @see #setMappedHandlers
|
||||||
|
* @see #setMappedHandlerClasses
|
||||||
|
*/
|
||||||
|
protected boolean shouldApplyTo(RenderRequest request, Object handler) {
|
||||||
|
// If the portlet is minimized and we don't want to render then return null.
|
||||||
|
if (WindowState.MINIMIZED.equals(request.getWindowState()) && !this.renderWhenMinimized) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handler != null) {
|
||||||
|
if (this.mappedHandlers != null && this.mappedHandlers.contains(handler)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (this.mappedHandlerClasses != null) {
|
||||||
|
for (int i = 0; i < this.mappedHandlerClasses.length; i++) {
|
||||||
|
if (this.mappedHandlerClasses[i].isInstance(handler)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Else only apply if there are no explicit handler mappings.
|
||||||
|
return (this.mappedHandlers == null && this.mappedHandlerClasses == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actually resolve the given exception that got thrown during on handler execution,
|
||||||
|
* returning a ModelAndView that represents a specific error page if appropriate.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param response current portlet response
|
||||||
|
* @param handler the executed handler, or null if none chosen at the time of
|
||||||
|
* the exception (for example, if multipart resolution failed)
|
||||||
|
* @param ex the exception that got thrown during handler execution
|
||||||
|
* @return a corresponding ModelAndView to forward to, or null for default processing
|
||||||
|
*/
|
||||||
|
protected ModelAndView doResolveException(
|
||||||
|
RenderRequest request, RenderResponse response, Object handler, Exception ex) {
|
||||||
|
|
||||||
|
// Log exception, both at debug log level and at warn level, if desired.
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Resolving exception from handler [" + handler + "]: " + ex);
|
||||||
|
}
|
||||||
|
logException(ex, request);
|
||||||
|
|
||||||
|
// Expose ModelAndView for chosen error view.
|
||||||
|
String viewName = determineViewName(ex, request);
|
||||||
|
if (viewName != null) {
|
||||||
|
return getModelAndView(viewName, ex, request);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log the given exception at warn level, provided that warn logging has been
|
||||||
|
* activated through the {@link #setWarnLogCategory "warnLogCategory"} property.
|
||||||
|
* <p>Calls {@link #buildLogMessage} in order to determine the concrete message
|
||||||
|
* to log. Always passes the full exception to the logger.
|
||||||
|
* @param ex the exception that got thrown during handler execution
|
||||||
|
* @param request current portlet request (useful for obtaining metadata)
|
||||||
|
* @see #setWarnLogCategory
|
||||||
|
* @see #buildLogMessage
|
||||||
|
* @see org.apache.commons.logging.Log#warn(Object, Throwable)
|
||||||
|
*/
|
||||||
|
protected void logException(Exception ex, RenderRequest request) {
|
||||||
|
if (this.warnLogger != null && this.warnLogger.isWarnEnabled()) {
|
||||||
|
this.warnLogger.warn(buildLogMessage(ex, request), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a log message for the given exception, occured during processing
|
||||||
|
* the given request.
|
||||||
|
* @param ex the exception that got thrown during handler execution
|
||||||
|
* @param request current portlet request (useful for obtaining metadata)
|
||||||
|
* @return the log message to use
|
||||||
|
*/
|
||||||
|
protected String buildLogMessage(Exception ex, RenderRequest request) {
|
||||||
|
return "Handler execution resulted in exception";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the view name for the given exception, searching the
|
||||||
|
* {@link #setExceptionMappings "exceptionMappings"}, using the
|
||||||
|
* {@link #setDefaultErrorView "defaultErrorView"} as fallback.
|
||||||
|
* @param ex the exception that got thrown during handler execution
|
||||||
|
* @param request current portlet request (useful for obtaining metadata)
|
||||||
|
* @return the resolved view name, or <code>null</code> if none found
|
||||||
|
*/
|
||||||
|
protected String determineViewName(Exception ex, RenderRequest request) {
|
||||||
|
String viewName = null;
|
||||||
|
// Check for specific exception mappings.
|
||||||
|
if (this.exceptionMappings != null) {
|
||||||
|
viewName = findMatchingViewName(this.exceptionMappings, ex);
|
||||||
|
}
|
||||||
|
// Return default error view else, if defined.
|
||||||
|
if (viewName == null && this.defaultErrorView != null) {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Resolving to default view '" + this.defaultErrorView +
|
||||||
|
"' for exception of type [" + ex.getClass().getName() + "]");
|
||||||
|
}
|
||||||
|
viewName = this.defaultErrorView;
|
||||||
|
}
|
||||||
|
return viewName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a matching view name in the given exception mappings
|
||||||
|
* @param exceptionMappings mappings between exception class names and error view names
|
||||||
|
* @param ex the exception that got thrown during handler execution
|
||||||
|
* @return the view name, or <code>null</code> if none found
|
||||||
|
* @see #setExceptionMappings
|
||||||
|
*/
|
||||||
|
protected String findMatchingViewName(Properties exceptionMappings, Exception ex) {
|
||||||
|
String viewName = null;
|
||||||
|
String dominantMapping = null;
|
||||||
|
int deepest = Integer.MAX_VALUE;
|
||||||
|
for (Enumeration names = exceptionMappings.propertyNames(); names.hasMoreElements();) {
|
||||||
|
String exceptionMapping = (String) names.nextElement();
|
||||||
|
int depth = getDepth(exceptionMapping, ex);
|
||||||
|
if (depth >= 0 && depth < deepest) {
|
||||||
|
deepest = depth;
|
||||||
|
dominantMapping = exceptionMapping;
|
||||||
|
viewName = exceptionMappings.getProperty(exceptionMapping);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (viewName != null && logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Resolving to view '" + viewName + "' for exception of type [" + ex.getClass().getName() +
|
||||||
|
"], based on exception mapping [" + dominantMapping + "]");
|
||||||
|
}
|
||||||
|
return viewName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the depth to the superclass matching.
|
||||||
|
* <p>0 means ex matches exactly. Returns -1 if there's no match.
|
||||||
|
* Otherwise, returns depth. Lowest depth wins.
|
||||||
|
* <p>Follows the same algorithm as
|
||||||
|
* {@link org.springframework.transaction.interceptor.RollbackRuleAttribute}.
|
||||||
|
*/
|
||||||
|
protected int getDepth(String exceptionMapping, Exception ex) {
|
||||||
|
return getDepth(exceptionMapping, ex.getClass(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getDepth(String exceptionMapping, Class exceptionClass, int depth) {
|
||||||
|
if (exceptionClass.getName().indexOf(exceptionMapping) != -1) {
|
||||||
|
// Found it!
|
||||||
|
return depth;
|
||||||
|
}
|
||||||
|
// If we've gone as far as we can go and haven't found it...
|
||||||
|
if (exceptionClass.equals(Throwable.class)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return getDepth(exceptionMapping, exceptionClass.getSuperclass(), depth + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a ModelAndView for the given request, view name and exception.
|
||||||
|
* Default implementation delegates to <code>getModelAndView(viewName, ex)</code>.
|
||||||
|
* @param viewName the name of the error view
|
||||||
|
* @param ex the exception that got thrown during handler execution
|
||||||
|
* @param request current portlet request (useful for obtaining metadata)
|
||||||
|
* @return the ModelAndView instance
|
||||||
|
* @see #getModelAndView(String, Exception)
|
||||||
|
*/
|
||||||
|
protected ModelAndView getModelAndView(String viewName, Exception ex, RenderRequest request) {
|
||||||
|
return getModelAndView(viewName, ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a ModelAndView for the given view name and exception.
|
||||||
|
* Default implementation adds the specified exception attribute.
|
||||||
|
* Can be overridden in subclasses.
|
||||||
|
* @param viewName the name of the error view
|
||||||
|
* @param ex the exception that got thrown during handler execution
|
||||||
|
* @return the ModelAndView instance
|
||||||
|
* @see #setExceptionAttribute
|
||||||
|
*/
|
||||||
|
protected ModelAndView getModelAndView(String viewName, Exception ex) {
|
||||||
|
ModelAndView mv = new ModelAndView(viewName);
|
||||||
|
if (this.exceptionAttribute != null) {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Exposing Exception as model attribute '" + this.exceptionAttribute + "'");
|
||||||
|
}
|
||||||
|
mv.addObject(this.exceptionAttribute, ex);
|
||||||
|
}
|
||||||
|
return mv;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2006 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.handler;
|
||||||
|
|
||||||
|
import javax.portlet.ActionRequest;
|
||||||
|
import javax.portlet.ActionResponse;
|
||||||
|
import javax.portlet.Portlet;
|
||||||
|
import javax.portlet.RenderRequest;
|
||||||
|
import javax.portlet.RenderResponse;
|
||||||
|
|
||||||
|
import org.springframework.web.portlet.HandlerAdapter;
|
||||||
|
import org.springframework.web.portlet.ModelAndView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter to use the Portlet interface with the generic DispatcherPortlet.
|
||||||
|
* Calls the Portlet's <code>render</code> and <code>processAction</code>
|
||||||
|
* methods to handle a request.
|
||||||
|
*
|
||||||
|
* <p>This adapter is not activated by default; it needs to be defined as a
|
||||||
|
* bean in the DispatcherPortlet context. It will automatically apply to
|
||||||
|
* mapped handler beans that implement the Portlet interface then.
|
||||||
|
*
|
||||||
|
* <p>Note that Portlet instances defined as bean will not receive initialization
|
||||||
|
* and destruction callbacks, unless a special post-processor such as
|
||||||
|
* SimplePortletPostProcessor is defined in the DispatcherPortlet context.
|
||||||
|
*
|
||||||
|
* <p><b>Alternatively, consider wrapping a Portlet with Spring's
|
||||||
|
* PortletWrappingController.</b> This is particularly appropriate for
|
||||||
|
* existing Portlet classes, allowing to specify Portlet initialization
|
||||||
|
* parameters, etc.
|
||||||
|
*
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @since 2.0
|
||||||
|
* @see javax.portlet.Portlet
|
||||||
|
* @see SimplePortletPostProcessor
|
||||||
|
* @see org.springframework.web.portlet.mvc.PortletWrappingController
|
||||||
|
*/
|
||||||
|
public class SimplePortletHandlerAdapter implements HandlerAdapter {
|
||||||
|
|
||||||
|
public boolean supports(Object handler) {
|
||||||
|
return (handler instanceof Portlet);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleAction(ActionRequest request, ActionResponse response, Object handler)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
((Portlet) handler).processAction(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModelAndView handleRender(RenderRequest request, RenderResponse response, Object handler)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
((Portlet) handler).render(request, response);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,167 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2006 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.handler;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
import javax.portlet.Portlet;
|
||||||
|
import javax.portlet.PortletConfig;
|
||||||
|
import javax.portlet.PortletContext;
|
||||||
|
import javax.portlet.PortletException;
|
||||||
|
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.beans.factory.BeanInitializationException;
|
||||||
|
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
|
||||||
|
import org.springframework.web.portlet.context.PortletConfigAware;
|
||||||
|
import org.springframework.web.portlet.context.PortletContextAware;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bean post-processor that applies initialization and destruction callbacks
|
||||||
|
* to beans that implement the Portlet interface.
|
||||||
|
*
|
||||||
|
* <p>After initialization of the bean instance, the Portlet <code>init</code>
|
||||||
|
* method will be called with a PortletConfig that contains the bean name
|
||||||
|
* of the Portlet and the PortletContext that it is running in.
|
||||||
|
*
|
||||||
|
* <p>Before destruction of the bean instance, the Portlet <code>destroy</code>
|
||||||
|
* will be called.
|
||||||
|
*
|
||||||
|
* <p><b>Note that this post-processor does not support Portlet initialization
|
||||||
|
* parameters.</b> Bean instances that implement the Portlet interface are
|
||||||
|
* supposed to be configured like any other Spring bean, that is, through
|
||||||
|
* constructor arguments or bean properties.
|
||||||
|
*
|
||||||
|
* <p>For reuse of a Portlet implementation in a plain Portlet container and as
|
||||||
|
* a bean in a Spring context, consider deriving from Spring's GenericPortletBean
|
||||||
|
* base class that applies Portlet initialization parameters as bean properties,
|
||||||
|
* supporting both initialization styles.
|
||||||
|
*
|
||||||
|
* <p><b>Alternatively, consider wrapping a Portlet with Spring's
|
||||||
|
* PortletWrappingController.</b> This is particularly appropriate for
|
||||||
|
* existing Portlet classes, allowing to specify Portlet initialization
|
||||||
|
* parameters etc.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @since 2.0
|
||||||
|
* @see javax.portlet.Portlet
|
||||||
|
* @see javax.portlet.PortletConfig
|
||||||
|
* @see SimplePortletHandlerAdapter
|
||||||
|
* @see org.springframework.web.portlet.GenericPortletBean
|
||||||
|
* @see org.springframework.web.portlet.mvc.PortletWrappingController
|
||||||
|
*/
|
||||||
|
public class SimplePortletPostProcessor
|
||||||
|
implements DestructionAwareBeanPostProcessor, PortletContextAware, PortletConfigAware {
|
||||||
|
|
||||||
|
private boolean useSharedPortletConfig = true;
|
||||||
|
|
||||||
|
private PortletContext portletContext;
|
||||||
|
|
||||||
|
private PortletConfig portletConfig;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether to use the shared PortletConfig object passed in
|
||||||
|
* through <code>setPortletConfig</code>, if available.
|
||||||
|
* <p>Default is "true". Turn this setting to "false" to pass in
|
||||||
|
* a mock PortletConfig object with the bean name as portlet name,
|
||||||
|
* holding the current PortletContext.
|
||||||
|
* @see #setPortletConfig
|
||||||
|
*/
|
||||||
|
public void setUseSharedPortletConfig(boolean useSharedPortletConfig) {
|
||||||
|
this.useSharedPortletConfig = useSharedPortletConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPortletContext(PortletContext portletContext) {
|
||||||
|
this.portletContext = portletContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPortletConfig(PortletConfig portletConfig) {
|
||||||
|
this.portletConfig = portletConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||||
|
return bean;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||||
|
if (bean instanceof Portlet) {
|
||||||
|
PortletConfig config = this.portletConfig;
|
||||||
|
if (config == null || !this.useSharedPortletConfig) {
|
||||||
|
config = new DelegatingPortletConfig(beanName, this.portletContext, this.portletConfig);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
((Portlet) bean).init(config);
|
||||||
|
}
|
||||||
|
catch (PortletException ex) {
|
||||||
|
throw new BeanInitializationException("Portlet.init threw exception", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bean;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
|
||||||
|
if (bean instanceof Portlet) {
|
||||||
|
((Portlet) bean).destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal implementation of the PortletConfig interface, to be passed
|
||||||
|
* to the wrapped servlet.
|
||||||
|
*/
|
||||||
|
private static class DelegatingPortletConfig implements PortletConfig {
|
||||||
|
|
||||||
|
private final String portletName;
|
||||||
|
|
||||||
|
private final PortletContext portletContext;
|
||||||
|
|
||||||
|
private final PortletConfig portletConfig;
|
||||||
|
|
||||||
|
public DelegatingPortletConfig(String portletName, PortletContext portletContext, PortletConfig portletConfig) {
|
||||||
|
this.portletName = portletName;
|
||||||
|
this.portletContext = portletContext;
|
||||||
|
this.portletConfig = portletConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPortletName() {
|
||||||
|
return portletName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PortletContext getPortletContext() {
|
||||||
|
return portletContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getInitParameter(String paramName) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Enumeration getInitParameterNames() {
|
||||||
|
return Collections.enumeration(Collections.EMPTY_SET);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourceBundle getResourceBundle(Locale locale) {
|
||||||
|
return portletConfig == null ? null : portletConfig.getResourceBundle(locale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2006 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.handler;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.portlet.PortletException;
|
||||||
|
import javax.portlet.PortletRequest;
|
||||||
|
import javax.portlet.PortletResponse;
|
||||||
|
import javax.portlet.PortletSecurityException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interceptor that checks the authorization of the current user via the
|
||||||
|
* user's roles, as evaluated by PortletRequest's isUserInRole method.
|
||||||
|
*
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 2.0
|
||||||
|
* @see javax.portlet.PortletRequest#isUserInRole
|
||||||
|
*/
|
||||||
|
public class UserRoleAuthorizationInterceptor extends HandlerInterceptorAdapter {
|
||||||
|
|
||||||
|
private String[] authorizedRoles;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the roles that this interceptor should treat as authorized.
|
||||||
|
* @param authorizedRoles array of role names
|
||||||
|
*/
|
||||||
|
public final void setAuthorizedRoles(String[] authorizedRoles) {
|
||||||
|
this.authorizedRoles = authorizedRoles;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public final boolean preHandle(PortletRequest request, PortletResponse response, Object handler)
|
||||||
|
throws PortletException, IOException {
|
||||||
|
|
||||||
|
if (this.authorizedRoles != null) {
|
||||||
|
for (int i = 0; i < this.authorizedRoles.length; i++) {
|
||||||
|
if (request.isUserInRole(this.authorizedRoles[i])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handleNotAuthorized(request, response, handler);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle a request that is not authorized according to this interceptor.
|
||||||
|
* Default implementation throws a new PortletSecurityException.
|
||||||
|
* <p>This method can be overridden to write a custom message, forward or
|
||||||
|
* redirect to some error page or login page, or throw a PortletException.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param response current portlet response
|
||||||
|
* @param handler chosen handler to execute, for type and/or instance evaluation
|
||||||
|
* @throws javax.portlet.PortletException if there is an internal error
|
||||||
|
* @throws java.io.IOException in case of an I/O error when writing the response
|
||||||
|
*/
|
||||||
|
protected void handleNotAuthorized(PortletRequest request, PortletResponse response, Object handler)
|
||||||
|
throws PortletException, IOException {
|
||||||
|
|
||||||
|
throw new PortletSecurityException("Request not authorized");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2006 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.handler;
|
||||||
|
|
||||||
|
import javax.portlet.ActionRequest;
|
||||||
|
import javax.portlet.ActionResponse;
|
||||||
|
import javax.portlet.RenderRequest;
|
||||||
|
import javax.portlet.RenderResponse;
|
||||||
|
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.web.context.request.WebRequestInterceptor;
|
||||||
|
import org.springframework.web.portlet.HandlerInterceptor;
|
||||||
|
import org.springframework.web.portlet.ModelAndView;
|
||||||
|
import org.springframework.web.portlet.context.PortletWebRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter that implements the Portlet HandlerInterceptor interface
|
||||||
|
* and wraps an underlying WebRequestInterceptor.
|
||||||
|
*
|
||||||
|
* <p><b>NOTE:</b> The WebRequestInterceptor is by default only applied to the Portlet
|
||||||
|
* <b>render</b> phase, which is dealing with preparing and rendering a Portlet view.
|
||||||
|
* The Portlet action phase will only be intercepted with WebRequestInterceptor calls
|
||||||
|
* if the <code>renderPhaseOnly</code> flag is explicitly set to <code>false</code>.
|
||||||
|
* In general, it is recommended to use the Portlet-specific HandlerInterceptor
|
||||||
|
* mechanism for differentiating between action and render interception.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 2.0
|
||||||
|
* @see org.springframework.web.context.request.WebRequestInterceptor
|
||||||
|
* @see org.springframework.web.portlet.HandlerInterceptor
|
||||||
|
*/
|
||||||
|
public class WebRequestHandlerInterceptorAdapter implements HandlerInterceptor {
|
||||||
|
|
||||||
|
private final WebRequestInterceptor requestInterceptor;
|
||||||
|
|
||||||
|
private final boolean renderPhaseOnly;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new WebRequestHandlerInterceptorAdapter for the given WebRequestInterceptor,
|
||||||
|
* applying to the render phase only.
|
||||||
|
* @param requestInterceptor the WebRequestInterceptor to wrap
|
||||||
|
*/
|
||||||
|
public WebRequestHandlerInterceptorAdapter(WebRequestInterceptor requestInterceptor) {
|
||||||
|
this(requestInterceptor, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new WebRequestHandlerInterceptorAdapter for the given WebRequestInterceptor.
|
||||||
|
* @param requestInterceptor the WebRequestInterceptor to wrap
|
||||||
|
* @param renderPhaseOnly whether to apply to the render phase only (<code>true</code>)
|
||||||
|
* or to the action phase as well (<code>false</code>)
|
||||||
|
*/
|
||||||
|
public WebRequestHandlerInterceptorAdapter(WebRequestInterceptor requestInterceptor, boolean renderPhaseOnly) {
|
||||||
|
Assert.notNull(requestInterceptor, "WebRequestInterceptor must not be null");
|
||||||
|
this.requestInterceptor = requestInterceptor;
|
||||||
|
this.renderPhaseOnly = renderPhaseOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean preHandleAction(ActionRequest request, ActionResponse response, Object handler) throws Exception {
|
||||||
|
if (!this.renderPhaseOnly) {
|
||||||
|
this.requestInterceptor.preHandle(new PortletWebRequest(request));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void afterActionCompletion(
|
||||||
|
ActionRequest request, ActionResponse response, Object handler, Exception ex) throws Exception {
|
||||||
|
|
||||||
|
if (!this.renderPhaseOnly) {
|
||||||
|
this.requestInterceptor.afterCompletion(new PortletWebRequest(request), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean preHandleRender(RenderRequest request, RenderResponse response, Object handler) throws Exception {
|
||||||
|
this.requestInterceptor.preHandle(new PortletWebRequest(request));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void postHandleRender(
|
||||||
|
RenderRequest request, RenderResponse response, Object handler, ModelAndView modelAndView) throws Exception {
|
||||||
|
|
||||||
|
this.requestInterceptor.postHandle(new PortletWebRequest(request),
|
||||||
|
(modelAndView != null ? modelAndView.getModelMap() : null));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void afterRenderCompletion(
|
||||||
|
RenderRequest request, RenderResponse response, Object handler, Exception ex) throws Exception {
|
||||||
|
|
||||||
|
this.requestInterceptor.afterCompletion(new PortletWebRequest(request), ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
Provides standard HandlerMapping implementations,
|
||||||
|
including abstract base classes for custom implementations.
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,200 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2008 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.multipart;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.portlet.ActionRequest;
|
||||||
|
import javax.portlet.PortletContext;
|
||||||
|
|
||||||
|
import org.apache.commons.fileupload.FileItemFactory;
|
||||||
|
import org.apache.commons.fileupload.FileUpload;
|
||||||
|
import org.apache.commons.fileupload.FileUploadBase;
|
||||||
|
import org.apache.commons.fileupload.FileUploadException;
|
||||||
|
import org.apache.commons.fileupload.portlet.PortletFileUpload;
|
||||||
|
import org.apache.commons.fileupload.portlet.PortletRequestContext;
|
||||||
|
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
|
import org.springframework.web.multipart.MaxUploadSizeExceededException;
|
||||||
|
import org.springframework.web.multipart.MultipartException;
|
||||||
|
import org.springframework.web.multipart.commons.CommonsFileUploadSupport;
|
||||||
|
import org.springframework.web.portlet.context.PortletContextAware;
|
||||||
|
import org.springframework.web.portlet.util.PortletUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link PortletMultipartResolver} implementation for
|
||||||
|
* <a href="http://jakarta.apache.org/commons/fileupload">Jakarta Commons FileUpload</a>
|
||||||
|
* 1.1 or above. Commons FileUpload 1.2 or above is recommended.
|
||||||
|
*
|
||||||
|
* <p>Provides "maxUploadSize", "maxInMemorySize" and "defaultEncoding" settings as
|
||||||
|
* bean properties (inherited from {@link CommonsFileUploadSupport}). See corresponding
|
||||||
|
* PortletFileUpload / DiskFileItemFactory properties ("sizeMax", "sizeThreshold",
|
||||||
|
* "headerEncoding") for details in terms of defaults and accepted values.
|
||||||
|
*
|
||||||
|
* <p>Saves temporary files to the portlet container's temporary directory.
|
||||||
|
* Needs to be initialized <i>either</i> by an application context <i>or</i>
|
||||||
|
* via the constructor that takes a PortletContext (for standalone usage).
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 2.0
|
||||||
|
* @see #CommonsPortletMultipartResolver(javax.portlet.PortletContext)
|
||||||
|
* @see #setResolveLazily
|
||||||
|
* @see org.springframework.web.multipart.commons.CommonsMultipartResolver
|
||||||
|
* @see org.apache.commons.fileupload.portlet.PortletFileUpload
|
||||||
|
* @see org.apache.commons.fileupload.disk.DiskFileItemFactory
|
||||||
|
*/
|
||||||
|
public class CommonsPortletMultipartResolver extends CommonsFileUploadSupport
|
||||||
|
implements PortletMultipartResolver, PortletContextAware {
|
||||||
|
|
||||||
|
private final boolean commonsFileUpload12Present =
|
||||||
|
ClassUtils.hasMethod(PortletFileUpload.class, "isMultipartContent", new Class[] {ActionRequest.class});
|
||||||
|
|
||||||
|
private boolean resolveLazily = false;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for use as bean. Determines the portlet container's
|
||||||
|
* temporary directory via the PortletContext passed in as through the
|
||||||
|
* PortletContextAware interface (typically by an ApplicationContext).
|
||||||
|
* @see #setPortletContext
|
||||||
|
* @see org.springframework.web.portlet.context.PortletContextAware
|
||||||
|
*/
|
||||||
|
public CommonsPortletMultipartResolver() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for standalone usage. Determines the portlet container's
|
||||||
|
* temporary directory via the given PortletContext.
|
||||||
|
* @param portletContext the PortletContext to use
|
||||||
|
*/
|
||||||
|
public CommonsPortletMultipartResolver(PortletContext portletContext) {
|
||||||
|
this();
|
||||||
|
setPortletContext(portletContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether to resolve the multipart request lazily at the time of
|
||||||
|
* file or parameter access.
|
||||||
|
* <p>Default is "false", resolving the multipart elements immediately, throwing
|
||||||
|
* corresponding exceptions at the time of the {@link #resolveMultipart} call.
|
||||||
|
* Switch this to "true" for lazy multipart parsing, throwing parse exceptions
|
||||||
|
* once the application attempts to obtain multipart files or parameters.
|
||||||
|
*/
|
||||||
|
public void setResolveLazily(boolean resolveLazily) {
|
||||||
|
this.resolveLazily = resolveLazily;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the underlying <code>org.apache.commons.fileupload.portlet.PortletFileUpload</code>
|
||||||
|
* instance. Can be overridden to use a custom subclass, e.g. for testing purposes.
|
||||||
|
* @return the new PortletFileUpload instance
|
||||||
|
*/
|
||||||
|
protected FileUpload newFileUpload(FileItemFactory fileItemFactory) {
|
||||||
|
return new PortletFileUpload(fileItemFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPortletContext(PortletContext portletContext) {
|
||||||
|
if (!isUploadTempDirSpecified()) {
|
||||||
|
getFileItemFactory().setRepository(PortletUtils.getTempDir(portletContext));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isMultipart(ActionRequest request) {
|
||||||
|
if (request == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (commonsFileUpload12Present) {
|
||||||
|
return PortletFileUpload.isMultipartContent(request);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return PortletFileUpload.isMultipartContent(new PortletRequestContext(request));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultipartActionRequest resolveMultipart(final ActionRequest request) throws MultipartException {
|
||||||
|
Assert.notNull(request, "Request must not be null");
|
||||||
|
if (this.resolveLazily) {
|
||||||
|
return new DefaultMultipartActionRequest(request) {
|
||||||
|
protected void initializeMultipart() {
|
||||||
|
MultipartParsingResult parsingResult = parseRequest(request);
|
||||||
|
setMultipartFiles(parsingResult.getMultipartFiles());
|
||||||
|
setMultipartParameters(parsingResult.getMultipartParameters());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
MultipartParsingResult parsingResult = parseRequest(request);
|
||||||
|
return new DefaultMultipartActionRequest(
|
||||||
|
request, parsingResult.getMultipartFiles(), parsingResult.getMultipartParameters());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the given portlet request, resolving its multipart elements.
|
||||||
|
* @param request the request to parse
|
||||||
|
* @return the parsing result
|
||||||
|
* @throws MultipartException if multipart resolution failed.
|
||||||
|
*/
|
||||||
|
protected MultipartParsingResult parseRequest(ActionRequest request) throws MultipartException {
|
||||||
|
String encoding = determineEncoding(request);
|
||||||
|
FileUpload fileUpload = prepareFileUpload(encoding);
|
||||||
|
try {
|
||||||
|
List fileItems = ((PortletFileUpload) fileUpload).parseRequest(request);
|
||||||
|
return parseFileItems(fileItems, encoding);
|
||||||
|
}
|
||||||
|
catch (FileUploadBase.SizeLimitExceededException ex) {
|
||||||
|
throw new MaxUploadSizeExceededException(fileUpload.getSizeMax(), ex);
|
||||||
|
}
|
||||||
|
catch (FileUploadException ex) {
|
||||||
|
throw new MultipartException("Could not parse multipart portlet request", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the encoding for the given request.
|
||||||
|
* Can be overridden in subclasses.
|
||||||
|
* <p>The default implementation checks the request encoding,
|
||||||
|
* falling back to the default encoding specified for this resolver.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @return the encoding for the request (never <code>null</code>)
|
||||||
|
* @see javax.portlet.ActionRequest#getCharacterEncoding
|
||||||
|
* @see #setDefaultEncoding
|
||||||
|
*/
|
||||||
|
protected String determineEncoding(ActionRequest request) {
|
||||||
|
String encoding = request.getCharacterEncoding();
|
||||||
|
if (encoding == null) {
|
||||||
|
encoding = getDefaultEncoding();
|
||||||
|
}
|
||||||
|
return encoding;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cleanupMultipart(MultipartActionRequest request) {
|
||||||
|
if (request != null) {
|
||||||
|
try {
|
||||||
|
cleanupFileItems(request.getFileMap().values());
|
||||||
|
}
|
||||||
|
catch (Throwable ex) {
|
||||||
|
logger.warn("Failed to perform multipart cleanup for portlet request", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,164 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2007 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.multipart;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.portlet.ActionRequest;
|
||||||
|
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
import org.springframework.web.portlet.util.ActionRequestWrapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default implementation of the {@link MultipartActionRequest} interface.
|
||||||
|
* Provides management of pre-generated parameter values.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 2.0
|
||||||
|
* @see PortletMultipartResolver
|
||||||
|
*/
|
||||||
|
public class DefaultMultipartActionRequest extends ActionRequestWrapper implements MultipartActionRequest {
|
||||||
|
|
||||||
|
private Map multipartFiles;
|
||||||
|
|
||||||
|
private Map multipartParameters;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap the given Portlet ActionRequest in a MultipartActionRequest.
|
||||||
|
* @param request the request to wrap
|
||||||
|
* @param multipartFiles a map of the multipart files
|
||||||
|
* @param multipartParameters a map of the parameters to expose,
|
||||||
|
* with Strings as keys and String arrays as values
|
||||||
|
*/
|
||||||
|
public DefaultMultipartActionRequest(ActionRequest request, Map multipartFiles, Map multipartParameters) {
|
||||||
|
super(request);
|
||||||
|
setMultipartFiles(multipartFiles);
|
||||||
|
setMultipartParameters(multipartParameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap the given Portlet ActionRequest in a MultipartActionRequest.
|
||||||
|
* @param request the request to wrap
|
||||||
|
*/
|
||||||
|
protected DefaultMultipartActionRequest(ActionRequest request) {
|
||||||
|
super(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Iterator getFileNames() {
|
||||||
|
return getMultipartFiles().keySet().iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultipartFile getFile(String name) {
|
||||||
|
return (MultipartFile) getMultipartFiles().get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map getFileMap() {
|
||||||
|
return getMultipartFiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Enumeration getParameterNames() {
|
||||||
|
Set paramNames = new HashSet();
|
||||||
|
Enumeration paramEnum = super.getParameterNames();
|
||||||
|
while (paramEnum.hasMoreElements()) {
|
||||||
|
paramNames.add(paramEnum.nextElement());
|
||||||
|
}
|
||||||
|
paramNames.addAll(getMultipartParameters().keySet());
|
||||||
|
return Collections.enumeration(paramNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getParameter(String name) {
|
||||||
|
String[] values = (String[]) getMultipartParameters().get(name);
|
||||||
|
if (values != null) {
|
||||||
|
return (values.length > 0 ? values[0] : null);
|
||||||
|
}
|
||||||
|
return super.getParameter(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getParameterValues(String name) {
|
||||||
|
String[] values = (String[]) getMultipartParameters().get(name);
|
||||||
|
if (values != null) {
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
return super.getParameterValues(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map getParameterMap() {
|
||||||
|
Map paramMap = new HashMap();
|
||||||
|
paramMap.putAll(super.getParameterMap());
|
||||||
|
paramMap.putAll(getMultipartParameters());
|
||||||
|
return paramMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a Map with parameter names as keys and MultipartFile objects as values.
|
||||||
|
* To be invoked by subclasses on initialization.
|
||||||
|
*/
|
||||||
|
protected final void setMultipartFiles(Map multipartFiles) {
|
||||||
|
this.multipartFiles = Collections.unmodifiableMap(multipartFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain the MultipartFile Map for retrieval,
|
||||||
|
* lazily initializing it if necessary.
|
||||||
|
* @see #initializeMultipart()
|
||||||
|
*/
|
||||||
|
protected Map getMultipartFiles() {
|
||||||
|
if (this.multipartFiles == null) {
|
||||||
|
initializeMultipart();
|
||||||
|
}
|
||||||
|
return this.multipartFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a Map with parameter names as keys and String array objects as values.
|
||||||
|
* To be invoked by subclasses on initialization.
|
||||||
|
*/
|
||||||
|
protected final void setMultipartParameters(Map multipartParameters) {
|
||||||
|
this.multipartParameters = multipartParameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain the multipart parameter Map for retrieval,
|
||||||
|
* lazily initializing it if necessary.
|
||||||
|
* @see #initializeMultipart()
|
||||||
|
*/
|
||||||
|
protected Map getMultipartParameters() {
|
||||||
|
if (this.multipartParameters == null) {
|
||||||
|
initializeMultipart();
|
||||||
|
}
|
||||||
|
return this.multipartParameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lazily initialize the multipart request, if possible.
|
||||||
|
* Only called if not already eagerly initialized.
|
||||||
|
*/
|
||||||
|
protected void initializeMultipart() {
|
||||||
|
throw new IllegalStateException("Multipart request not initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2008 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.multipart;
|
||||||
|
|
||||||
|
import javax.portlet.ActionRequest;
|
||||||
|
|
||||||
|
import org.springframework.web.multipart.MultipartRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface which provides additional methods for dealing with multipart
|
||||||
|
* content within a portlet request, allowing to access uploaded files.
|
||||||
|
* Implementations also need to override the standard ActionRequest
|
||||||
|
* methods for parameter access, making multipart parameters available.
|
||||||
|
*
|
||||||
|
* <p>A concrete implementation is {@link DefaultMultipartActionRequest}.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 2.0
|
||||||
|
* @see PortletMultipartResolver
|
||||||
|
* @see org.springframework.web.multipart.MultipartFile
|
||||||
|
* @see javax.portlet.ActionRequest#getParameter
|
||||||
|
* @see javax.portlet.ActionRequest#getParameterNames
|
||||||
|
* @see javax.portlet.ActionRequest#getParameterMap
|
||||||
|
*/
|
||||||
|
public interface MultipartActionRequest extends ActionRequest, MultipartRequest {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2007 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.multipart;
|
||||||
|
|
||||||
|
import javax.portlet.ActionRequest;
|
||||||
|
|
||||||
|
import org.springframework.web.multipart.MultipartException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Portlet version of Spring's multipart resolution strategy for file uploads
|
||||||
|
* as defined in <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>.
|
||||||
|
*
|
||||||
|
* <p>Implementations are typically usable both within any application context
|
||||||
|
* and standalone.
|
||||||
|
*
|
||||||
|
* <p>There is one concrete implementation included in Spring:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link org.springframework.web.multipart.commons.CommonsMultipartResolver}
|
||||||
|
* for Jakarta Commons FileUpload
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>There is no default resolver implementation used for Spring
|
||||||
|
* {@link org.springframework.web.portlet.DispatcherPortlet DispatcherPortlets},
|
||||||
|
* as an application might choose to parse its multipart requests itself. To
|
||||||
|
* define an implementation, create a bean with the id
|
||||||
|
* {@link org.springframework.web.portlet.DispatcherPortlet#MULTIPART_RESOLVER_BEAN_NAME "portletMultipartResolver"}
|
||||||
|
* in a <code>DispatcherPortlet's</code> application context. Such a resolver
|
||||||
|
* gets applied to all requests handled by that <code>DispatcherPortlet</code>.
|
||||||
|
*
|
||||||
|
* <p>If a <code>DispatcherPortlet</code> detects a multipart request, it will
|
||||||
|
* resolve it via the configured
|
||||||
|
* {@link org.springframework.web.multipart.MultipartResolver} and pass on a
|
||||||
|
* wrapped Portlet {@link ActionRequest}.
|
||||||
|
* {@link org.springframework.web.portlet.mvc.Controller Controllers} can then
|
||||||
|
* cast their given request to the {@link MultipartActionRequest} interface,
|
||||||
|
* being able to access <code>MultipartFiles</code>. Note that this cast is
|
||||||
|
* only supported in case of an actual multipart request.
|
||||||
|
*
|
||||||
|
* <pre class="code"> public void handleActionRequest(ActionRequest request, ActionResponse response) {
|
||||||
|
* MultipartActionRequest multipartRequest = (MultipartActionRequest) request;
|
||||||
|
* MultipartFile multipartFile = multipartRequest.getFile("image");
|
||||||
|
* ...
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* Instead of direct access, command or form controllers can register a
|
||||||
|
* {@link org.springframework.web.multipart.support.ByteArrayMultipartFileEditor}
|
||||||
|
* or {@link org.springframework.web.multipart.support.StringMultipartFileEditor}
|
||||||
|
* with their data binder, to automatically apply multipart content to command
|
||||||
|
* bean properties.
|
||||||
|
*
|
||||||
|
* <p>Note: There is hardly ever a need to access the
|
||||||
|
* <code>MultipartResolver</code> itself from application code. It will simply
|
||||||
|
* do its work behind the scenes, making <code>MultipartActionRequests</code>
|
||||||
|
* available to controllers.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 2.0
|
||||||
|
* @see MultipartActionRequest
|
||||||
|
* @see org.springframework.web.multipart.MultipartFile
|
||||||
|
* @see CommonsPortletMultipartResolver
|
||||||
|
* @see org.springframework.web.multipart.support.ByteArrayMultipartFileEditor
|
||||||
|
* @see org.springframework.web.multipart.support.StringMultipartFileEditor
|
||||||
|
* @see org.springframework.web.portlet.DispatcherPortlet
|
||||||
|
*/
|
||||||
|
public interface PortletMultipartResolver {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the given request contains multipart content.
|
||||||
|
* <p>Will typically check for content type
|
||||||
|
* "<code>multipart/form-data</code>", but the actually accepted requests
|
||||||
|
* might depend on the capabilities of the resolver implementation.
|
||||||
|
* @param request the portlet request to be evaluated
|
||||||
|
* @return whether the request contains multipart content
|
||||||
|
*/
|
||||||
|
boolean isMultipart(ActionRequest request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the given portlet request into multipart files and parameters,
|
||||||
|
* and wrap the request inside a MultipartActionRequest object
|
||||||
|
* that provides access to file descriptors and makes contained
|
||||||
|
* parameters accessible via the standard PortletRequest methods.
|
||||||
|
* @param request the portlet request to wrap (must be of a multipart content type)
|
||||||
|
* @return the wrapped portlet request
|
||||||
|
* @throws org.springframework.web.multipart.MultipartException if the portlet request
|
||||||
|
* is not multipart, or if implementation-specific problems are encountered
|
||||||
|
* (such as exceeding file size limits)
|
||||||
|
* @see org.springframework.web.portlet.multipart.MultipartActionRequest#getFile
|
||||||
|
* @see org.springframework.web.portlet.multipart.MultipartActionRequest#getFileNames
|
||||||
|
* @see org.springframework.web.portlet.multipart.MultipartActionRequest#getFileMap
|
||||||
|
* @see javax.portlet.ActionRequest#getParameter
|
||||||
|
* @see javax.portlet.ActionRequest#getParameterNames
|
||||||
|
* @see javax.portlet.ActionRequest#getParameterMap
|
||||||
|
*/
|
||||||
|
MultipartActionRequest resolveMultipart(ActionRequest request) throws MultipartException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleanup any resources used for the multipart handling,
|
||||||
|
* such as storage for any uploaded file(s).
|
||||||
|
* @param request the request to cleanup resources for
|
||||||
|
*/
|
||||||
|
void cleanupMultipart(MultipartActionRequest request);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
Multipart resolution framework for handling file uploads.
|
||||||
|
Provides a PortletMultipartResolver strategy interface,
|
||||||
|
and a generic extension of the ActionRequest interface
|
||||||
|
for accessing multipart files in web application code.
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,209 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2008 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.mvc;
|
||||||
|
|
||||||
|
import javax.portlet.ActionRequest;
|
||||||
|
import javax.portlet.ActionResponse;
|
||||||
|
import javax.portlet.RenderRequest;
|
||||||
|
import javax.portlet.RenderResponse;
|
||||||
|
|
||||||
|
import org.springframework.validation.BindException;
|
||||||
|
import org.springframework.web.portlet.ModelAndView;
|
||||||
|
import org.springframework.web.portlet.bind.PortletRequestDataBinder;
|
||||||
|
import org.springframework.web.portlet.util.PortletUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Abstract base class for custom command controllers. Autopopulates a
|
||||||
|
* command bean from the request. For command validation, a validator
|
||||||
|
* (property inherited from BaseCommandController) can be used.</p>
|
||||||
|
*
|
||||||
|
* <p>This command controller should preferrable not be used to handle form
|
||||||
|
* submission, because functionality for forms is more offered in more
|
||||||
|
* detail by the {@link org.springframework.web.portlet.mvc.AbstractFormController
|
||||||
|
* AbstractFormController} and its corresponding implementations.</p>
|
||||||
|
*
|
||||||
|
* <p><b><a name="config">Exposed configuration properties</a>
|
||||||
|
* (<a href="BaseCommandController.html#config">and those defined by superclass</a>):</b><br>
|
||||||
|
* <i>none</i> (so only those available in superclass).</p>
|
||||||
|
*
|
||||||
|
* <p><b><a name="workflow">Workflow
|
||||||
|
* (<a name="BaseCommandController.html#workflow">and that defined by superclass</a>):</b><br>
|
||||||
|
*
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 2.0
|
||||||
|
* @see #setCommandClass
|
||||||
|
* @see #setCommandName
|
||||||
|
* @see #setValidator
|
||||||
|
*/
|
||||||
|
public abstract class AbstractCommandController extends BaseCommandController {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This render parameter is used to indicate forward to the render phase
|
||||||
|
* that a valid command (and errors) object is in the session.
|
||||||
|
*/
|
||||||
|
private static final String COMMAND_IN_SESSION_PARAMETER = "command-in-session";
|
||||||
|
|
||||||
|
private static final String TRUE = Boolean.TRUE.toString();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new AbstractCommandController.
|
||||||
|
*/
|
||||||
|
public AbstractCommandController() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new AbstractCommandController.
|
||||||
|
* @param commandClass class of the command bean
|
||||||
|
*/
|
||||||
|
public AbstractCommandController(Class commandClass) {
|
||||||
|
setCommandClass(commandClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new AbstractCommandController.
|
||||||
|
* @param commandClass class of the command bean
|
||||||
|
* @param commandName name of the command bean
|
||||||
|
*/
|
||||||
|
public AbstractCommandController(Class commandClass, String commandName) {
|
||||||
|
setCommandClass(commandClass);
|
||||||
|
setCommandName(commandName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected final void handleActionRequestInternal(ActionRequest request, ActionResponse response)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
// Create the command object.
|
||||||
|
Object command = getCommand(request);
|
||||||
|
|
||||||
|
// Compute the errors object.
|
||||||
|
PortletRequestDataBinder binder = bindAndValidate(request, command);
|
||||||
|
BindException errors = new BindException(binder.getBindingResult());
|
||||||
|
|
||||||
|
// Actually handle the action.
|
||||||
|
handleAction(request, response, command, errors);
|
||||||
|
|
||||||
|
// Pass the command and errors forward to the render phase.
|
||||||
|
setRenderCommandAndErrors(request, command, errors);
|
||||||
|
setCommandInSession(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final ModelAndView handleRenderRequestInternal(
|
||||||
|
RenderRequest request, RenderResponse response) throws Exception {
|
||||||
|
|
||||||
|
Object command = null;
|
||||||
|
BindException errors = null;
|
||||||
|
|
||||||
|
// Get the command and errors objects from the session, if they exist.
|
||||||
|
if (isCommandInSession(request)) {
|
||||||
|
logger.debug("Render phase obtaining command and errors objects from session");
|
||||||
|
command = getRenderCommand(request);
|
||||||
|
errors = getRenderErrors(request);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logger.debug("Render phase creating new command and errors objects");
|
||||||
|
command = getCommand(request);
|
||||||
|
PortletRequestDataBinder binder = bindAndValidate(request, command);
|
||||||
|
errors = new BindException(binder.getBindingResult());
|
||||||
|
}
|
||||||
|
|
||||||
|
return handleRender(request, response, command, errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Template method for request handling, providing a populated and validated instance
|
||||||
|
* of the command class, and an Errors object containing binding and validation errors.
|
||||||
|
* <p>Call <code>errors.getModel()</code> to populate the ModelAndView model
|
||||||
|
* with the command and the Errors instance, under the specified command name,
|
||||||
|
* as expected by the "spring:bind" tag.
|
||||||
|
* @param request current action request
|
||||||
|
* @param response current action response
|
||||||
|
* @param command the populated command object
|
||||||
|
* @param errors validation errors holder
|
||||||
|
* @see org.springframework.validation.Errors
|
||||||
|
* @see org.springframework.validation.BindException#getModel
|
||||||
|
*/
|
||||||
|
protected abstract void handleAction(
|
||||||
|
ActionRequest request, ActionResponse response, Object command, BindException errors)
|
||||||
|
throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Template method for render request handling, providing a populated and validated instance
|
||||||
|
* of the command class, and an Errors object containing binding and validation errors.
|
||||||
|
* <p>Call <code>errors.getModel()</code> to populate the ModelAndView model
|
||||||
|
* with the command and the Errors instance, under the specified command name,
|
||||||
|
* as expected by the "spring:bind" tag.
|
||||||
|
* @param request current render request
|
||||||
|
* @param response current render response
|
||||||
|
* @param command the populated command object
|
||||||
|
* @param errors validation errors holder
|
||||||
|
* @return a ModelAndView to render, or null if handled directly
|
||||||
|
* @see org.springframework.validation.Errors
|
||||||
|
* @see org.springframework.validation.BindException#getModel
|
||||||
|
*/
|
||||||
|
protected abstract ModelAndView handleRender(
|
||||||
|
RenderRequest request, RenderResponse response, Object command, BindException errors)
|
||||||
|
throws Exception;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of the render parameter that indicates there
|
||||||
|
* is a valid command (and errors) object in the session.
|
||||||
|
* @return the name of the render parameter
|
||||||
|
* @see javax.portlet.RenderRequest#getParameter
|
||||||
|
*/
|
||||||
|
protected String getCommandInSessionParameterName() {
|
||||||
|
return COMMAND_IN_SESSION_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the action response parameter that indicates there is a
|
||||||
|
* command (and errors) object in the session for the render phase.
|
||||||
|
* @param response the current action response
|
||||||
|
* @see #getCommandInSessionParameterName
|
||||||
|
* @see #isCommandInSession
|
||||||
|
*/
|
||||||
|
protected final void setCommandInSession(ActionResponse response) {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Setting render parameter [" + getCommandInSessionParameterName() +
|
||||||
|
"] to indicate a valid command (and errors) object are in the session");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
response.setRenderParameter(getCommandInSessionParameterName(), TRUE);
|
||||||
|
}
|
||||||
|
catch (IllegalStateException ex) {
|
||||||
|
// Ignore in case sendRedirect was already set.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if there is a valid command (and errors) object in the
|
||||||
|
* session for this render request.
|
||||||
|
* @param request current render request
|
||||||
|
* @return if there is a valid command object in the session
|
||||||
|
* @see #getCommandInSessionParameterName
|
||||||
|
* @see #setCommandInSession
|
||||||
|
*/
|
||||||
|
protected boolean isCommandInSession(RenderRequest request) {
|
||||||
|
return (TRUE.equals(request.getParameter(getCommandInSessionParameterName())) &&
|
||||||
|
PortletUtils.getSessionAttribute(request, getRenderCommandSessionAttributeName()) != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,251 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2008 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.mvc;
|
||||||
|
|
||||||
|
import javax.portlet.ActionRequest;
|
||||||
|
import javax.portlet.ActionResponse;
|
||||||
|
import javax.portlet.PortletException;
|
||||||
|
import javax.portlet.PortletSession;
|
||||||
|
import javax.portlet.RenderRequest;
|
||||||
|
import javax.portlet.RenderResponse;
|
||||||
|
import javax.portlet.WindowState;
|
||||||
|
|
||||||
|
import org.springframework.web.portlet.ModelAndView;
|
||||||
|
import org.springframework.web.portlet.handler.PortletContentGenerator;
|
||||||
|
import org.springframework.web.portlet.util.PortletUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenient superclass for controller implementations, using the Template
|
||||||
|
* Method design pattern.
|
||||||
|
*
|
||||||
|
* <p>As stated in the {@link Controller Controller}
|
||||||
|
* interface, a lot of functionality is already provided by certain abstract
|
||||||
|
* base controllers. The AbstractController is one of the most important
|
||||||
|
* abstract base controller providing basic features such controlling if a
|
||||||
|
* session is required and render caching.
|
||||||
|
*
|
||||||
|
* <p><b><a name="workflow">Workflow
|
||||||
|
* (<a href="Controller.html#workflow">and that defined by interface</a>):</b><br>
|
||||||
|
* <ol>
|
||||||
|
* <li>If this is an action request, {@link #handleActionRequest handleActionRequest}
|
||||||
|
* will be called by the DispatcherPortlet once to perform the action defined by this
|
||||||
|
* controller.</li>
|
||||||
|
* <li>If a session is required, try to get it (PortletException if not found).</li>
|
||||||
|
* <li>Call method {@link #handleActionRequestInternal handleActionRequestInternal},
|
||||||
|
* (optionally synchronizing around the call on the PortletSession),
|
||||||
|
* which should be overridden by extending classes to provide actual functionality to
|
||||||
|
* perform the desired action of the controller. This will be executed only once.</li>
|
||||||
|
* <li>For a straight render request, or the render phase of an action request (assuming the
|
||||||
|
* same controller is called for the render phase -- see tip below),
|
||||||
|
* {@link #handleRenderRequest handleRenderRequest} will be called by the DispatcherPortlet
|
||||||
|
* repeatedly to render the display defined by this controller.</li>
|
||||||
|
* <li>If a session is required, try to get it (PortletException if none found).</li>
|
||||||
|
* <li>It will control caching as defined by the cacheSeconds property.</li>
|
||||||
|
* <li>Call method {@link #handleRenderRequestInternal handleRenderRequestInternal},
|
||||||
|
* (optionally synchronizing around the call on the PortletSession),
|
||||||
|
* which should be overridden by extending classes to provide actual functionality to
|
||||||
|
* return {@link org.springframework.web.portlet.ModelAndView ModelAndView} objects.
|
||||||
|
* This will be executed repeatedly as the portal updates the current displayed page.</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* <p><b><a name="config">Exposed configuration properties</a>
|
||||||
|
* (<a href="Controller.html#config">and those defined by interface</a>):</b><br>
|
||||||
|
* <table border="1">
|
||||||
|
* <tr>
|
||||||
|
* <td><b>name</b></th>
|
||||||
|
* <td><b>default</b></td>
|
||||||
|
* <td><b>description</b></td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>requireSession</td>
|
||||||
|
* <td>false</td>
|
||||||
|
* <td>whether a session should be required for requests to be able to
|
||||||
|
* be handled by this controller. This ensures, derived controller
|
||||||
|
* can - without fear of Nullpointers - call request.getSession() to
|
||||||
|
* retrieve a session. If no session can be found while processing
|
||||||
|
* the request, a PortletException will be thrown</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>synchronizeOnSession</td>
|
||||||
|
* <td>false</td>
|
||||||
|
* <td>whether the calls to <code>handleRenderRequestInternal</code> and
|
||||||
|
* <code>handleRenderRequestInternal</code> should be
|
||||||
|
* synchronized around the PortletSession, to serialize invocations
|
||||||
|
* from the same client. No effect if there is no PortletSession.
|
||||||
|
* </td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>cacheSeconds</td>
|
||||||
|
* <td>-1</td>
|
||||||
|
* <td>indicates the amount of seconds to specify caching is allowed in
|
||||||
|
* the render response generatedby this request. 0 (zero) will indicate
|
||||||
|
* no caching is allowed at all, -1 (the default) will not override the
|
||||||
|
* portlet configuration and any positive number will cause the render
|
||||||
|
* response to declare the amount indicated as seconds to cache the content</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>renderWhenMinimized</td>
|
||||||
|
* <td>false</td>
|
||||||
|
* <td>whether should be rendered when the portlet is in a minimized state --
|
||||||
|
* will return null for the ModelandView when the portlet is minimized
|
||||||
|
* and this is false</td>
|
||||||
|
* </tr>
|
||||||
|
* </table>
|
||||||
|
*
|
||||||
|
* <p><b>TIP:</b> The controller mapping will be run twice by the PortletDispatcher for
|
||||||
|
* action requests -- once for the action phase and again for the render phase. You can
|
||||||
|
* reach the render phase of a different controller by simply changing the values for the
|
||||||
|
* criteria your mapping is using, such as portlet mode or a request parameter, during the
|
||||||
|
* action phase of your controller. This is very handy since redirects within the portlet
|
||||||
|
* are apparently impossible. Before doing this, it is usually wise to call
|
||||||
|
* <code>clearAllRenderParameters</code> and then explicitly set all the parameters that
|
||||||
|
* you want the new controller to see. This avoids unexpected parameters from being passed
|
||||||
|
* to the render phase of the second controller, such as the parameter indicating a form
|
||||||
|
* submit ocurred in an <code>AbstractFormController</code>.
|
||||||
|
*
|
||||||
|
* <p>Thanks to Rainer Schmitz and Nick Lothian for their suggestions!
|
||||||
|
*
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public abstract class AbstractController extends PortletContentGenerator implements Controller {
|
||||||
|
|
||||||
|
private boolean synchronizeOnSession = false;
|
||||||
|
|
||||||
|
private boolean renderWhenMinimized = false;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set if controller execution should be synchronized on the session,
|
||||||
|
* to serialize parallel invocations from the same client.
|
||||||
|
* <p>More specifically, the execution of the <code>handleActionRequestInternal</code>
|
||||||
|
* method will get synchronized if this flag is "true". The best available
|
||||||
|
* session mutex will be used for the synchronization; ideally, this will
|
||||||
|
* be a mutex exposed by HttpSessionMutexListener.
|
||||||
|
* <p>The session mutex is guaranteed to be the same object during
|
||||||
|
* the entire lifetime of the session, available under the key defined
|
||||||
|
* by the <code>SESSION_MUTEX_ATTRIBUTE</code> constant. It serves as a
|
||||||
|
* safe reference to synchronize on for locking on the current session.
|
||||||
|
* <p>In many cases, the PortletSession reference itself is a safe mutex
|
||||||
|
* as well, since it will always be the same object reference for the
|
||||||
|
* same active logical session. However, this is not guaranteed across
|
||||||
|
* different servlet containers; the only 100% safe way is a session mutex.
|
||||||
|
* @see #handleActionRequestInternal
|
||||||
|
* @see org.springframework.web.util.HttpSessionMutexListener
|
||||||
|
* @see org.springframework.web.portlet.util.PortletUtils#getSessionMutex(javax.portlet.PortletSession)
|
||||||
|
*/
|
||||||
|
public final void setSynchronizeOnSession(boolean synchronizeOnSession) {
|
||||||
|
this.synchronizeOnSession = synchronizeOnSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether controller execution should be synchronized on the session.
|
||||||
|
*/
|
||||||
|
public final boolean isSynchronizeOnSession() {
|
||||||
|
return this.synchronizeOnSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set if the controller should render an view when the portlet is in
|
||||||
|
* a minimized window. The default is false.
|
||||||
|
* @see javax.portlet.RenderRequest#getWindowState
|
||||||
|
* @see javax.portlet.WindowState#MINIMIZED
|
||||||
|
*/
|
||||||
|
public final void setRenderWhenMinimized(boolean renderWhenMinimized) {
|
||||||
|
this.renderWhenMinimized = renderWhenMinimized;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether controller will render when portlet is minimized.
|
||||||
|
*/
|
||||||
|
public final boolean isRenderWhenMinimized() {
|
||||||
|
return this.renderWhenMinimized;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void handleActionRequest(ActionRequest request, ActionResponse response) throws Exception {
|
||||||
|
// Delegate to PortletContentGenerator for checking and preparing.
|
||||||
|
check(request, response);
|
||||||
|
|
||||||
|
// Execute in synchronized block if required.
|
||||||
|
if (this.synchronizeOnSession) {
|
||||||
|
PortletSession session = request.getPortletSession(false);
|
||||||
|
if (session != null) {
|
||||||
|
synchronized (session) {
|
||||||
|
handleActionRequestInternal(request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleActionRequestInternal(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModelAndView handleRenderRequest(RenderRequest request, RenderResponse response) throws Exception {
|
||||||
|
// If the portlet is minimized and we don't want to render then return null.
|
||||||
|
if (WindowState.MINIMIZED.equals(request.getWindowState()) && !this.renderWhenMinimized) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delegate to PortletContentGenerator for checking and preparing.
|
||||||
|
checkAndPrepare(request, response);
|
||||||
|
|
||||||
|
// Execute in synchronized block if required.
|
||||||
|
if (this.synchronizeOnSession) {
|
||||||
|
PortletSession session = request.getPortletSession(false);
|
||||||
|
if (session != null) {
|
||||||
|
Object mutex = PortletUtils.getSessionMutex(session);
|
||||||
|
synchronized (mutex) {
|
||||||
|
return handleRenderRequestInternal(request, response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return handleRenderRequestInternal(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses are meant to override this method if the controller
|
||||||
|
* is expected to handle action requests. The contract is the same as
|
||||||
|
* for <code>handleActionRequest</code>.
|
||||||
|
* <p>The default implementation throws a PortletException.
|
||||||
|
* @see #handleActionRequest
|
||||||
|
* @see #handleRenderRequestInternal
|
||||||
|
*/
|
||||||
|
protected void handleActionRequestInternal(ActionRequest request, ActionResponse response)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
throw new PortletException("[" + getClass().getName() + "] does not handle action requests");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses are meant to override this method if the controller
|
||||||
|
* is expected to handle render requests. The contract is the same as
|
||||||
|
* for <code>handleRenderRequest</code>.
|
||||||
|
* <p>The default implementation throws a PortletException.
|
||||||
|
* @see #handleRenderRequest
|
||||||
|
* @see #handleActionRequestInternal
|
||||||
|
*/
|
||||||
|
protected ModelAndView handleRenderRequestInternal(RenderRequest request, RenderResponse response)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
throw new PortletException("[" + getClass().getName() + "] does not handle render requests");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,969 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2008 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.mvc;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.portlet.ActionRequest;
|
||||||
|
import javax.portlet.ActionResponse;
|
||||||
|
import javax.portlet.PortletException;
|
||||||
|
import javax.portlet.PortletRequest;
|
||||||
|
import javax.portlet.PortletSession;
|
||||||
|
import javax.portlet.RenderRequest;
|
||||||
|
import javax.portlet.RenderResponse;
|
||||||
|
|
||||||
|
import org.springframework.validation.BindException;
|
||||||
|
import org.springframework.validation.Errors;
|
||||||
|
import org.springframework.web.portlet.ModelAndView;
|
||||||
|
import org.springframework.web.portlet.bind.PortletRequestDataBinder;
|
||||||
|
import org.springframework.web.portlet.handler.PortletSessionRequiredException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Form controller that auto-populates a form bean from the request.
|
||||||
|
* This, either using a new bean instance per request, or using the same bean
|
||||||
|
* when the <code>sessionForm</code> property has been set to
|
||||||
|
* <code>true</code>.</p>
|
||||||
|
*
|
||||||
|
* <p>This class is the base class for both framework subclasses such as
|
||||||
|
* {@link SimpleFormController} and {@link AbstractWizardFormController}
|
||||||
|
* and custom form controllers that you may provide yourself.</p>
|
||||||
|
*
|
||||||
|
* <p>A form-input view and an after-submission view have to be provided
|
||||||
|
* programmatically. To provide those views using configuration properties,
|
||||||
|
* use the {@link SimpleFormController}.</p>
|
||||||
|
*
|
||||||
|
* <p>Subclasses need to override <code>showForm</code> to prepare the form view,
|
||||||
|
* <code>processFormSubmission</code> to handle submit requests, and
|
||||||
|
* <code>renderFormSubmission</code> to display the results of the submit.
|
||||||
|
* For the latter two methods, binding errors like type mismatches will be
|
||||||
|
* reported via the given "errors" holder. For additional custom form validation,
|
||||||
|
* a validator (property inherited from BaseCommandController) can be used,
|
||||||
|
* reporting via the same "errors" instance.</p>
|
||||||
|
*
|
||||||
|
* <p>Comparing this Controller to the Struts notion of the <code>Action</code>
|
||||||
|
* shows us that with Spring, you can use any ordinary JavaBeans or database-
|
||||||
|
* backed JavaBeans without having to implement a framework-specific class
|
||||||
|
* (like Struts' <code>ActionForm</code>). More complex properties of JavaBeans
|
||||||
|
* (Dates, Locales, but also your own application-specific or compound types)
|
||||||
|
* can be represented and submitted to the controller, by using the notion of
|
||||||
|
* a <code>java.beans.PropertyEditors</code>. For more information on that
|
||||||
|
* subject, see the workflow of this controller and the explanation of the
|
||||||
|
* {@link BaseCommandController BaseCommandController}.</p>
|
||||||
|
*
|
||||||
|
* <p>This controller is different from it's servlet counterpart in that it must take
|
||||||
|
* into account the two phases of a portlet request: the action phase and the render
|
||||||
|
* phase. See the JSR-168 spec for more details on these two phases.
|
||||||
|
* Be especially aware that the action phase is called only once, but that the
|
||||||
|
* render phase will be called repeatedly by the portal; it does this every time
|
||||||
|
* the page containing the portlet is updated, even if the activity is in some other
|
||||||
|
* portlet. (This is not quite true, the portal can also be told to cache the results of
|
||||||
|
* the render for a period of time, but assume it is true for programming purposes.)</p>
|
||||||
|
*
|
||||||
|
* <p><b><a name="workflow">Workflow
|
||||||
|
* (<a href="BaseCommandController.html#workflow">and that defined by superclass</a>):</b><br>
|
||||||
|
* <ol>
|
||||||
|
* <li><b>The controller receives a request for a new form (typically a
|
||||||
|
* Render Request only).</b> The render phase will proceed to display
|
||||||
|
* the form as follows.</li>
|
||||||
|
* <li>Call to {@link #formBackingObject formBackingObject()} which by
|
||||||
|
* default, returns an instance of the commandClass that has been
|
||||||
|
* configured (see the properties the superclass exposes), but can also be
|
||||||
|
* overridden to e.g. retrieve an object from the database (that needs to
|
||||||
|
* be modified using the form).</li>
|
||||||
|
* <li>Call to {@link #initBinder initBinder()} which allows you to
|
||||||
|
* register custom editors for certain fields (often properties of non-
|
||||||
|
* primitive or non-String types) of the command class. This will render
|
||||||
|
* appropriate Strings for those property values, e.g. locale-specific
|
||||||
|
* date strings. </li>
|
||||||
|
* <li>The {@link PortletRequestDataBinder PortletRequestDataBinder}
|
||||||
|
* gets applied to populate the new form object with initial request parameters and the
|
||||||
|
* {@link #onBindOnNewForm(RenderRequest, Object, BindException)} callback method is invoked.
|
||||||
|
* (<i>only if <code>bindOnNewForm</code> is set to <code>true</code></i>)
|
||||||
|
* Make sure that the initial parameters do not include the parameter that indicates a
|
||||||
|
* form submission has occurred.</li>
|
||||||
|
* <li>Call to {@link #showForm(RenderRequest, RenderResponse,
|
||||||
|
* BindException) showForm} to return a View that should be rendered
|
||||||
|
* (typically the view that renders the form). This method has to be
|
||||||
|
* implemented in subclasses. </li>
|
||||||
|
* <li>The showForm() implementation will call {@link #referenceData referenceData},
|
||||||
|
* which you can implement to provide any relevant reference data you might need
|
||||||
|
* when editing a form (e.g. a List of Locale objects you're going to let the
|
||||||
|
* user select one from).</li>
|
||||||
|
* <li>Model gets exposed and view gets rendered, to let the user fill in
|
||||||
|
* the form.</li>
|
||||||
|
* <li><b>The controller receives a form submission (typically an Action
|
||||||
|
* Request).</b> To use a different way of detecting a form submission,
|
||||||
|
* override the {@link #isFormSubmission isFormSubmission} method.
|
||||||
|
* The action phase will proceed to process the form submission as follows.</li>
|
||||||
|
* <li>If <code>sessionForm</code> is not set, {@link #formBackingObject
|
||||||
|
* formBackingObject} is called to retrieve a form object. Otherwise,
|
||||||
|
* the controller tries to find the command object which is already bound
|
||||||
|
* in the session. If it cannot find the object, the action phase does a
|
||||||
|
* call to {@link #handleInvalidSubmit handleInvalidSubmit} which - by default -
|
||||||
|
* tries to create a new form object and resubmit the form. It then sets
|
||||||
|
* a render parameter that will indicate to the render phase that this was
|
||||||
|
* an invalid submit.</li>
|
||||||
|
* <li>Still in the action phase of a valid submit, the {@link
|
||||||
|
* PortletRequestDataBinder PortletRequestDataBinder} gets applied to populate
|
||||||
|
* the form object with current request parameters.</li>
|
||||||
|
* <li>Call to {@link #onBind onBind(PortletRequest, Object, Errors)}
|
||||||
|
* which allows you to do custom processing after binding but before
|
||||||
|
* validation (e.g. to manually bind request parameters to bean
|
||||||
|
* properties, to be seen by the Validator).</li>
|
||||||
|
* <li>If <code>validateOnBinding</code> is set, a registered Validator
|
||||||
|
* will be invoked. The Validator will check the form object properties,
|
||||||
|
* and register corresponding errors via the given {@link Errors Errors}
|
||||||
|
* object.</li>
|
||||||
|
* <li>Call to {@link #onBindAndValidate onBindAndValidate} which allows
|
||||||
|
* you to do custom processing after binding and validation (e.g. to
|
||||||
|
* manually bind request parameters, and to validate them outside a
|
||||||
|
* Validator).</li>
|
||||||
|
* <li>Call to {@link #processFormSubmission processFormSubmission}
|
||||||
|
* to process the submission, with or without binding errors.
|
||||||
|
* This method has to be implemented in subclasses and will be called
|
||||||
|
* only once per form submission.</li>
|
||||||
|
* <li>The portal will then call the render phase of processing the form
|
||||||
|
* submission. This phase will be called repeatedly by the portal every
|
||||||
|
* time the page is refreshed. All processing here should take this into
|
||||||
|
* account. Any one-time-only actions (such as modifying a database) must
|
||||||
|
* be done in the action phase.</li>
|
||||||
|
* <li>If the action phase indicated this is an invalid submit, the render
|
||||||
|
* phase calls {@link #renderInvalidSubmit renderInvalidSubmit} which –
|
||||||
|
* also by default – will render the results of the resubmitted
|
||||||
|
* form. Be sure to override both <code>handleInvalidSubmit</code> and
|
||||||
|
* <code>renderInvalidSubmit</code> if you want to change this overall
|
||||||
|
* behavior.</li>
|
||||||
|
* <li>Finally, call {@link #renderFormSubmission renderFormSubmission} to
|
||||||
|
* render the results of the submission, with or without binding errors.
|
||||||
|
* This method has to be implemented in subclasses and will be called
|
||||||
|
* repeatedly by the portal.</li>
|
||||||
|
* </ol>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>In session form mode, a submission without an existing form object in the
|
||||||
|
* session is considered invalid, like in the case of a resubmit/reload by the browser.
|
||||||
|
* The {@link #handleInvalidSubmit handleInvalidSubmit} /
|
||||||
|
* {@link #renderInvalidSubmit renderInvalidSubmit} methods are invoked then,
|
||||||
|
* by default trying to resubmit. This can be overridden in subclasses to show
|
||||||
|
* corresponding messages or to redirect to a new form, in order to avoid duplicate
|
||||||
|
* submissions. The form object in the session can be considered a transaction token
|
||||||
|
* in that case.</p>
|
||||||
|
*
|
||||||
|
* <p>Make sure that any URLs that take you to your form controller are Render URLs,
|
||||||
|
* so that it will not try to treat the initial call as a form submission.
|
||||||
|
* If you use action URLs to link to your controller, you will need to override the
|
||||||
|
* {@link #isFormSubmission isFormSubmission} method to use a different mechanism for
|
||||||
|
* determining whether a form has been submitted. Make sure this method will work for
|
||||||
|
* both the ActionRequest and the RenderRequest objects.</p>
|
||||||
|
*
|
||||||
|
* <p>Note that views should never retrieve form beans from the session but always
|
||||||
|
* from the request, as prepared by the form controller. Remember that some view
|
||||||
|
* technologies like Velocity cannot even access a HTTP session.</p>
|
||||||
|
*
|
||||||
|
* <p><b><a name="config">Exposed configuration properties</a>
|
||||||
|
* (<a href="BaseCommandController.html#config">and those defined by superclass</a>):</b><br>
|
||||||
|
* <table border="1">
|
||||||
|
* <tr>
|
||||||
|
* <td><b>name</b></td>
|
||||||
|
* <td><b>default</b></td>
|
||||||
|
* <td><b>description</b></td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>bindOnNewForm</td>
|
||||||
|
* <td>false</td>
|
||||||
|
* <td>Indicates whether to bind portlet request parameters when
|
||||||
|
* creating a new form. Otherwise, the parameters will only be
|
||||||
|
* bound on form submission attempts.</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>sessionForm</td>
|
||||||
|
* <td>false</td>
|
||||||
|
* <td>Indicates whether the form object should be kept in the session
|
||||||
|
* when a user asks for a new form. This allows you e.g. to retrieve
|
||||||
|
* an object from the database, let the user edit it, and then persist
|
||||||
|
* it again. Otherwise, a new command object will be created for each
|
||||||
|
* request (even when showing the form again after validation errors).</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>redirectAction</td>
|
||||||
|
* <td>false</td>
|
||||||
|
* <td>Specifies whether <code>processFormSubmission</code> is expected to call
|
||||||
|
* {@link ActionResponse#sendRedirect ActionResponse.sendRedirect}.
|
||||||
|
* This is important because some methods may not be called before
|
||||||
|
* {@link ActionResponse#sendRedirect ActionResponse.sendRedirect} (e.g.
|
||||||
|
* {@link ActionResponse#setRenderParameter ActionResponse.setRenderParameter}).
|
||||||
|
* Setting this flag will prevent AbstractFormController from setting render
|
||||||
|
* parameters that it normally needs for the render phase.
|
||||||
|
* If this is set true and <code>sendRedirect</code> is not called, then
|
||||||
|
* <code>processFormSubmission</code> must call
|
||||||
|
* {@link #setFormSubmit setFormSubmit}.
|
||||||
|
* Otherwise, the render phase will not realize the form was submitted
|
||||||
|
* and will simply display a new blank form.</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>renderParameters</td>
|
||||||
|
* <td>null</td>
|
||||||
|
* <td>An array of parameters that will be passed forward from the action
|
||||||
|
* phase to the render phase if the form needs to be displayed
|
||||||
|
* again. These can also be passed forward explicitly by calling
|
||||||
|
* the <code>passRenderParameters</code> method from any action
|
||||||
|
* phase method. Abstract descendants of this controller should follow
|
||||||
|
* similar behavior. If there are parameters you need in
|
||||||
|
* <code>renderFormSubmission</code>, then you need to pass those
|
||||||
|
* forward from <code>processFormSubmission</code>. If you override the
|
||||||
|
* default behavior of invalid submits and you set sessionForm to true,
|
||||||
|
* then you probably will not need to set this because your parameters
|
||||||
|
* are only going to be needed on the first request.</td>
|
||||||
|
* </tr>
|
||||||
|
* </table>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>Thanks to Rainer Schmitz and Nick Lothian for their suggestions!
|
||||||
|
*
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author Alef Arendsen
|
||||||
|
* @author Rob Harrop
|
||||||
|
* @since 2.0
|
||||||
|
* @see #showForm(RenderRequest, RenderResponse, BindException)
|
||||||
|
* @see SimpleFormController
|
||||||
|
* @see AbstractWizardFormController
|
||||||
|
*/
|
||||||
|
public abstract class AbstractFormController extends BaseCommandController {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These render parameters are used to indicate forward to the render phase
|
||||||
|
* if the form was submitted and if the submission was invalid.
|
||||||
|
*/
|
||||||
|
private static final String FORM_SUBMISSION_PARAMETER = "form-submit";
|
||||||
|
|
||||||
|
private static final String INVALID_SUBMISSION_PARAMETER = "invalid-submit";
|
||||||
|
|
||||||
|
private static final String TRUE = Boolean.TRUE.toString();
|
||||||
|
|
||||||
|
|
||||||
|
private boolean bindOnNewForm = false;
|
||||||
|
|
||||||
|
private boolean sessionForm = false;
|
||||||
|
|
||||||
|
private boolean redirectAction = false;
|
||||||
|
|
||||||
|
private String[] renderParameters = null;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new AbstractFormController.
|
||||||
|
* <p>Subclasses should set the following properties, either in the constructor
|
||||||
|
* or via a BeanFactory: commandName, commandClass, bindOnNewForm, sessionForm.
|
||||||
|
* Note that commandClass doesn't need to be set when overriding
|
||||||
|
* <code>formBackingObject</code>, as the latter determines the class anyway.
|
||||||
|
* <p>"cacheSeconds" is by default set to 0 (-> no caching for all form controllers).
|
||||||
|
* @see #setCommandName
|
||||||
|
* @see #setCommandClass
|
||||||
|
* @see #setBindOnNewForm
|
||||||
|
* @see #setSessionForm
|
||||||
|
* @see #formBackingObject
|
||||||
|
*/
|
||||||
|
public AbstractFormController() {
|
||||||
|
setCacheSeconds(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set if request parameters should be bound to the form object
|
||||||
|
* in case of a non-submitting request, i.e. a new form.
|
||||||
|
*/
|
||||||
|
public final void setBindOnNewForm(boolean bindOnNewForm) {
|
||||||
|
this.bindOnNewForm = bindOnNewForm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if request parameters should be bound in case of a new form.
|
||||||
|
*/
|
||||||
|
public final boolean isBindOnNewForm() {
|
||||||
|
return this.bindOnNewForm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activate/deactivate session form mode. In session form mode,
|
||||||
|
* the form is stored in the session to keep the form object instance
|
||||||
|
* between requests, instead of creating a new one on each request.
|
||||||
|
* <p>This is necessary for either wizard-style controllers that populate a
|
||||||
|
* single form object from multiple pages, or forms that populate a persistent
|
||||||
|
* object that needs to be identical to allow for tracking changes.
|
||||||
|
*/
|
||||||
|
public final void setSessionForm(boolean sessionForm) {
|
||||||
|
this.sessionForm = sessionForm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if session form mode is activated.
|
||||||
|
*/
|
||||||
|
public final boolean isSessionForm() {
|
||||||
|
return this.sessionForm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify whether the action phase is expected to call
|
||||||
|
* {@link ActionResponse#sendRedirect}.
|
||||||
|
* This information is important because some methods may not be called
|
||||||
|
* before {@link ActionResponse#sendRedirect}, e.g.
|
||||||
|
* {@link ActionResponse#setRenderParameter} and
|
||||||
|
* {@link ActionResponse#setRenderParameters}.
|
||||||
|
* <p><b>NOTE:</b> Call this at initialization time of your controller:
|
||||||
|
* either in the constructor or in the bean definition for your controller.
|
||||||
|
* @see ActionResponse#sendRedirect
|
||||||
|
*/
|
||||||
|
public void setRedirectAction(boolean redirectAction) {
|
||||||
|
this.redirectAction = redirectAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if {@link ActionResponse#sendRedirect} is
|
||||||
|
* expected to be called in the action phase.
|
||||||
|
*/
|
||||||
|
public boolean isRedirectAction() {
|
||||||
|
return this.redirectAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify the list of parameters that should be passed forward
|
||||||
|
* from the action phase to the render phase whenever the form is
|
||||||
|
* re-rendered or when {@link #passRenderParameters} is called.
|
||||||
|
* @see #passRenderParameters
|
||||||
|
*/
|
||||||
|
public void setRenderParameters(String[] parameters) {
|
||||||
|
this.renderParameters = parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of parameters that will be passed forward
|
||||||
|
* from the action phase to the render phase whenever the form is
|
||||||
|
* rerendered or when {@link #passRenderParameters} is called.
|
||||||
|
* @return the list of parameters
|
||||||
|
* @see #passRenderParameters
|
||||||
|
*/
|
||||||
|
public String[] getRenderParameters() {
|
||||||
|
return this.renderParameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles action phase of two cases: form submissions and showing a new form.
|
||||||
|
* Delegates the decision between the two to <code>isFormSubmission</code>,
|
||||||
|
* always treating requests without existing form session attribute
|
||||||
|
* as new form when using session form mode.
|
||||||
|
* @see #isFormSubmission
|
||||||
|
* @see #processFormSubmission
|
||||||
|
* @see #handleRenderRequestInternal
|
||||||
|
*/
|
||||||
|
protected void handleActionRequestInternal(ActionRequest request, ActionResponse response)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
// Form submission or new form to show?
|
||||||
|
if (isFormSubmission(request)) {
|
||||||
|
// Fetch form object, bind, validate, process submission.
|
||||||
|
try {
|
||||||
|
Object command = getCommand(request);
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Processing valid submit (redirectAction = " + isRedirectAction() + ")");
|
||||||
|
}
|
||||||
|
if (!isRedirectAction()) {
|
||||||
|
setFormSubmit(response);
|
||||||
|
}
|
||||||
|
PortletRequestDataBinder binder = bindAndValidate(request, command);
|
||||||
|
BindException errors = new BindException(binder.getBindingResult());
|
||||||
|
processFormSubmission(request, response, command, errors);
|
||||||
|
setRenderCommandAndErrors(request, command, errors);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (PortletSessionRequiredException ex) {
|
||||||
|
// Cannot submit a session form if no form object is in the session.
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Invalid submit detected: " + ex.getMessage());
|
||||||
|
}
|
||||||
|
setFormSubmit(response);
|
||||||
|
setInvalidSubmit(response);
|
||||||
|
handleInvalidSubmit(request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
logger.debug("Not a form submit - passing parameters to render phase");
|
||||||
|
passRenderParameters(request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles render phase of two cases: form submissions and showing a new form.
|
||||||
|
* Delegates the decision between the two to <code>isFormSubmission</code>,
|
||||||
|
* always treating requests without existing form session attribute
|
||||||
|
* as new form when using session form mode.
|
||||||
|
* @see #isFormSubmission
|
||||||
|
* @see #showNewForm
|
||||||
|
* @see #processFormSubmission
|
||||||
|
* @see #handleActionRequestInternal
|
||||||
|
*/
|
||||||
|
protected ModelAndView handleRenderRequestInternal(RenderRequest request, RenderResponse response)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
// Form submission or new form to show?
|
||||||
|
if (isFormSubmission(request)) {
|
||||||
|
|
||||||
|
// If it is an invalid submit then handle it.
|
||||||
|
if (isInvalidSubmission(request)) {
|
||||||
|
logger.debug("Invalid submit - calling renderInvalidSubmit");
|
||||||
|
return renderInvalidSubmit(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid submit -> render.
|
||||||
|
logger.debug("Valid submit - calling renderFormSubmission");
|
||||||
|
return renderFormSubmission(request, response, getRenderCommand(request), getRenderErrors(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
// New form to show: render form view.
|
||||||
|
return showNewForm(request, response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the given request represents a form submission.
|
||||||
|
* <p>The default implementation checks to see if this is an ActionRequest
|
||||||
|
* and treats all action requests as form submission. During the action
|
||||||
|
* phase it will pass forward a render parameter to indicate to the render
|
||||||
|
* phase that this is a form submission. This method can check both
|
||||||
|
* kinds of requests and indicate if this is a form submission.
|
||||||
|
* <p>Subclasses can override this to use a custom strategy, e.g. a specific
|
||||||
|
* request parameter (assumably a hidden field or submit button name). Make
|
||||||
|
* sure that the override can handle both ActionRequest and RenderRequest
|
||||||
|
* objects properly.
|
||||||
|
* @param request current request
|
||||||
|
* @return if the request represents a form submission
|
||||||
|
*/
|
||||||
|
protected boolean isFormSubmission(PortletRequest request) {
|
||||||
|
return (request instanceof ActionRequest || TRUE.equals(request.getParameter(getFormSubmitParameterName())));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the given request represents an invalid form submission.
|
||||||
|
*/
|
||||||
|
protected boolean isInvalidSubmission(PortletRequest request) {
|
||||||
|
return TRUE.equals(request.getParameter(getInvalidSubmitParameterName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of the render parameter that indicates this
|
||||||
|
* was a form submission.
|
||||||
|
* @return the name of the render parameter
|
||||||
|
* @see javax.portlet.RenderRequest#getParameter
|
||||||
|
*/
|
||||||
|
protected String getFormSubmitParameterName() {
|
||||||
|
return FORM_SUBMISSION_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of the render parameter that indicates this
|
||||||
|
* was an invalid form submission.
|
||||||
|
* @return the name of the render parameter
|
||||||
|
* @see javax.portlet.RenderRequest#getParameter
|
||||||
|
*/
|
||||||
|
protected String getInvalidSubmitParameterName() {
|
||||||
|
return INVALID_SUBMISSION_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the action response parameter that indicates this in a form submission.
|
||||||
|
* @param response the current action response
|
||||||
|
* @see #getFormSubmitParameterName()
|
||||||
|
*/
|
||||||
|
protected final void setFormSubmit(ActionResponse response) {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Setting render parameter [" + getFormSubmitParameterName() +
|
||||||
|
"] to indicate this is a form submission");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
response.setRenderParameter(getFormSubmitParameterName(), TRUE);
|
||||||
|
}
|
||||||
|
catch (IllegalStateException ex) {
|
||||||
|
// Ignore in case sendRedirect was already set.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the action response parameter that indicates this in an invalid submission.
|
||||||
|
* @param response the current action response
|
||||||
|
* @see #getInvalidSubmitParameterName()
|
||||||
|
*/
|
||||||
|
protected final void setInvalidSubmit(ActionResponse response) {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Setting render parameter [" + getInvalidSubmitParameterName() +
|
||||||
|
"] to indicate this is an invalid submission");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
response.setRenderParameter(getInvalidSubmitParameterName(), TRUE);
|
||||||
|
}
|
||||||
|
catch (IllegalStateException ex) {
|
||||||
|
// Ignore in case sendRedirect was already set.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of the PortletSession attribute that holds the form object
|
||||||
|
* for this form controller.
|
||||||
|
* <p>The default implementation delegates to the
|
||||||
|
* <code>getFormSessionAttributeName</code> version without arguments.
|
||||||
|
* @param request current HTTP request
|
||||||
|
* @return the name of the form session attribute,
|
||||||
|
* or <code>null</code> if not in session form mode
|
||||||
|
* @see #getFormSessionAttributeName()
|
||||||
|
* @see javax.portlet.PortletSession#getAttribute
|
||||||
|
*/
|
||||||
|
protected String getFormSessionAttributeName(PortletRequest request) {
|
||||||
|
return getFormSessionAttributeName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of the PortletSession attribute that holds the form object
|
||||||
|
* for this form controller.
|
||||||
|
* <p>Default is an internal name, of no relevance to applications, as the form
|
||||||
|
* session attribute is not usually accessed directly. Can be overridden to use
|
||||||
|
* an application-specific attribute name, which allows other code to access
|
||||||
|
* the session attribute directly.
|
||||||
|
* @return the name of the form session attribute
|
||||||
|
* @see javax.portlet.PortletSession#getAttribute
|
||||||
|
*/
|
||||||
|
protected String getFormSessionAttributeName() {
|
||||||
|
return getClass().getName() + ".FORM." + getCommandName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pass the specified list of action request parameters to the render phase
|
||||||
|
* by putting them into the action response object. This may not be called
|
||||||
|
* when the action will call will call
|
||||||
|
* {@link ActionResponse#sendRedirect sendRedirect}.
|
||||||
|
* @param request the current action request
|
||||||
|
* @param response the current action response
|
||||||
|
* @see ActionResponse#setRenderParameter
|
||||||
|
*/
|
||||||
|
protected void passRenderParameters(ActionRequest request, ActionResponse response) {
|
||||||
|
if (this.renderParameters == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
for (int i = 0; i < this.renderParameters.length; i++) {
|
||||||
|
String paramName = this.renderParameters[i];
|
||||||
|
String paramValues[] = request.getParameterValues(paramName);
|
||||||
|
if (paramValues != null) {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Passing parameter to render phase '" + paramName + "' = " +
|
||||||
|
(paramValues == null ? "NULL" : Arrays.asList(paramValues).toString()));
|
||||||
|
}
|
||||||
|
response.setRenderParameter(paramName, paramValues);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IllegalStateException ex) {
|
||||||
|
// Ignore in case sendRedirect was already set.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a new form. Prepares a backing object for the current form
|
||||||
|
* and the given request, including checking its validity.
|
||||||
|
* @param request current render request
|
||||||
|
* @param response current render response
|
||||||
|
* @return the prepared form view
|
||||||
|
* @throws Exception in case of an invalid new form object
|
||||||
|
* @see #getErrorsForNewForm
|
||||||
|
*/
|
||||||
|
protected final ModelAndView showNewForm(RenderRequest request, RenderResponse response)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
logger.debug("Displaying new form");
|
||||||
|
return showForm(request, response, getErrorsForNewForm(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a BindException instance for a new form.
|
||||||
|
* Called by <code>showNewForm</code>.
|
||||||
|
* <p>Can be used directly when intending to show a new form but with
|
||||||
|
* special errors registered on it (for example, on invalid submit).
|
||||||
|
* Usually, the resulting BindException will be passed to
|
||||||
|
* <code>showForm</code>, after registering the errors on it.
|
||||||
|
* @param request current render request
|
||||||
|
* @return the BindException instance
|
||||||
|
* @throws Exception in case of an invalid new form object
|
||||||
|
*/
|
||||||
|
protected final BindException getErrorsForNewForm(RenderRequest request) throws Exception {
|
||||||
|
// Create form-backing object for new form
|
||||||
|
Object command = formBackingObject(request);
|
||||||
|
if (command == null) {
|
||||||
|
throw new PortletException("Form object returned by formBackingObject() must not be null");
|
||||||
|
}
|
||||||
|
if (!checkCommand(command)) {
|
||||||
|
throw new PortletException("Form object returned by formBackingObject() must match commandClass");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind without validation, to allow for prepopulating a form, and for
|
||||||
|
// convenient error evaluation in views (on both first attempt and resubmit).
|
||||||
|
PortletRequestDataBinder binder = createBinder(request, command);
|
||||||
|
BindException errors = new BindException(binder.getBindingResult());
|
||||||
|
|
||||||
|
if (isBindOnNewForm()) {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Binding to new form");
|
||||||
|
}
|
||||||
|
binder.bind(request);
|
||||||
|
onBindOnNewForm(request, command, errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return BindException object that resulted from binding.
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback for custom post-processing in terms of binding for a new form.
|
||||||
|
* Called when preparing a new form if <code>bindOnNewForm</code> is <code>true</code>.
|
||||||
|
* <p>Default implementation delegates to <code>onBindOnNewForm(request, command)</code>.
|
||||||
|
* @param request current render request
|
||||||
|
* @param command the command object to perform further binding on
|
||||||
|
* @param errors validation errors holder, allowing for additional
|
||||||
|
* custom registration of binding errors
|
||||||
|
* @throws Exception in case of invalid state or arguments
|
||||||
|
* @see #onBindOnNewForm(RenderRequest, Object)
|
||||||
|
* @see #setBindOnNewForm
|
||||||
|
*/
|
||||||
|
protected void onBindOnNewForm(RenderRequest request, Object command, BindException errors)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
onBindOnNewForm(request, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback for custom post-processing in terms of binding for a new form.
|
||||||
|
* Called by the default implementation of the <code>onBindOnNewForm</code> version
|
||||||
|
* with all parameters, after standard binding when displaying the form view.
|
||||||
|
* Only called if <code>bindOnNewForm</code> is set to <code>true</code>.
|
||||||
|
* <p>The default implementation is empty.
|
||||||
|
* @param request current render request
|
||||||
|
* @param command the command object to perform further binding on
|
||||||
|
* @throws Exception in case of invalid state or arguments
|
||||||
|
* @see #onBindOnNewForm(RenderRequest, Object, BindException)
|
||||||
|
* @see #setBindOnNewForm(boolean)
|
||||||
|
*/
|
||||||
|
protected void onBindOnNewForm(RenderRequest request, Object command) throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the form object for the given request.
|
||||||
|
* <p>Calls <code>formBackingObject</code> if the object is not in the session
|
||||||
|
* @param request current request
|
||||||
|
* @return object form to bind onto
|
||||||
|
* @see #formBackingObject
|
||||||
|
*/
|
||||||
|
protected final Object getCommand(PortletRequest request) throws Exception {
|
||||||
|
// If not in session-form mode, create a new form-backing object.
|
||||||
|
if (!isSessionForm()) {
|
||||||
|
return formBackingObject(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Session-form mode: retrieve form object from portlet session attribute.
|
||||||
|
PortletSession session = request.getPortletSession(false);
|
||||||
|
if (session == null) {
|
||||||
|
throw new PortletSessionRequiredException("Must have session when trying to bind (in session-form mode)");
|
||||||
|
}
|
||||||
|
String formAttrName = getFormSessionAttributeName(request);
|
||||||
|
Object sessionFormObject = session.getAttribute(formAttrName);
|
||||||
|
if (sessionFormObject == null) {
|
||||||
|
throw new PortletSessionRequiredException("Form object not found in session (in session-form mode)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove form object from porlet session: we might finish the form workflow
|
||||||
|
// in this request. If it turns out that we need to show the form view again,
|
||||||
|
// we'll re-bind the form object to the portlet session.
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Removing form session attribute [" + formAttrName + "]");
|
||||||
|
}
|
||||||
|
session.removeAttribute(formAttrName);
|
||||||
|
|
||||||
|
// Check the command object to make sure its valid
|
||||||
|
if (!checkCommand(sessionFormObject)) {
|
||||||
|
throw new PortletSessionRequiredException("Object found in session does not match commandClass");
|
||||||
|
}
|
||||||
|
|
||||||
|
return sessionFormObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a backing object for the current form from the given request.
|
||||||
|
* <p>The properties of the form object will correspond to the form field values
|
||||||
|
* in your form view. This object will be exposed in the model under the specified
|
||||||
|
* command name, to be accessed under that name in the view: for example, with
|
||||||
|
* a "spring:bind" tag. The default command name is "command".
|
||||||
|
* <p>Note that you need to activate session form mode to reuse the form-backing
|
||||||
|
* object across the entire form workflow. Else, a new instance of the command
|
||||||
|
* class will be created for each submission attempt, just using this backing
|
||||||
|
* object as template for the initial form.
|
||||||
|
* <p>The default implementation calls <code>BaseCommandController.createCommand</code>,
|
||||||
|
* creating a new empty instance of the command class.
|
||||||
|
* Subclasses can override this to provide a preinitialized backing object.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @return the backing object
|
||||||
|
* @throws Exception in case of invalid state or arguments
|
||||||
|
* @see #setCommandName
|
||||||
|
* @see #setCommandClass
|
||||||
|
* @see #createCommand
|
||||||
|
*/
|
||||||
|
protected Object formBackingObject(PortletRequest request) throws Exception {
|
||||||
|
return createCommand();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare the form model and view, including reference and error data.
|
||||||
|
* Can show a configured form page, or generate a form view programmatically.
|
||||||
|
* <p>A typical implementation will call
|
||||||
|
* <code>showForm(request, errors, "myView")</code>
|
||||||
|
* to prepare the form view for a specific view name, returning the
|
||||||
|
* ModelAndView provided there.
|
||||||
|
* <p>For building a custom ModelAndView, call <code>errors.getModel()</code>
|
||||||
|
* to populate the ModelAndView model with the command and the Errors instance,
|
||||||
|
* under the specified command name, as expected by the "spring:bind" tag.
|
||||||
|
* You also need to include the model returned by <code>referenceData</code>.
|
||||||
|
* <p>Note: If you decide to have a "formView" property specifying the
|
||||||
|
* view name, consider using SimpleFormController.
|
||||||
|
* @param request current render request
|
||||||
|
* @param response current render response
|
||||||
|
* @param errors validation errors holder
|
||||||
|
* @return the prepared form view, or null if handled directly
|
||||||
|
* @throws Exception in case of invalid state or arguments
|
||||||
|
* @see #showForm(RenderRequest, BindException, String)
|
||||||
|
* @see org.springframework.validation.Errors
|
||||||
|
* @see org.springframework.validation.BindException#getModel
|
||||||
|
* @see #referenceData(PortletRequest, Object, Errors)
|
||||||
|
* @see SimpleFormController#setFormView
|
||||||
|
*/
|
||||||
|
protected abstract ModelAndView showForm(RenderRequest request, RenderResponse response, BindException errors) throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare model and view for the given form, including reference and errors.
|
||||||
|
* <p>In session form mode: Re-puts the form object in the session when
|
||||||
|
* returning to the form, as it has been removed by getCommand.
|
||||||
|
* <p>Can be used in subclasses to redirect back to a specific form page.
|
||||||
|
* @param request current render request
|
||||||
|
* @param errors validation errors holder
|
||||||
|
* @param viewName name of the form view
|
||||||
|
* @return the prepared form view
|
||||||
|
* @throws Exception in case of invalid state or arguments
|
||||||
|
* @see #showForm(RenderRequest, BindException, String, Map)
|
||||||
|
* @see #showForm(RenderRequest, RenderResponse, BindException)
|
||||||
|
*/
|
||||||
|
protected final ModelAndView showForm(RenderRequest request, BindException errors, String viewName)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
return showForm(request, errors, viewName, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare model and view for the given form, including reference and errors,
|
||||||
|
* adding a controller-specific control model.
|
||||||
|
* <p>In session form mode: Re-puts the form object in the session when returning
|
||||||
|
* to the form, as it has been removed by getCommand.
|
||||||
|
* <p>Can be used in subclasses to redirect back to a specific form page.
|
||||||
|
* @param request current render request
|
||||||
|
* @param errors validation errors holder
|
||||||
|
* @param viewName name of the form view
|
||||||
|
* @param controlModel model map containing controller-specific control data
|
||||||
|
* (e.g. current page in wizard-style controllers or special error message)
|
||||||
|
* @return the prepared form view
|
||||||
|
* @throws Exception in case of invalid state or arguments
|
||||||
|
* @see #showForm(RenderRequest, BindException, String)
|
||||||
|
* @see #showForm(RenderRequest, RenderResponse, BindException)
|
||||||
|
*/
|
||||||
|
protected final ModelAndView showForm(RenderRequest request, BindException errors, String viewName, Map controlModel)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
// In session form mode, re-expose form object as portlet session attribute.
|
||||||
|
// Re-binding is necessary for proper state handling in a cluster,
|
||||||
|
// to notify other nodes of changes in the form object.
|
||||||
|
if (isSessionForm()) {
|
||||||
|
String formAttrName = getFormSessionAttributeName(request);
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Setting form session attribute [" + formAttrName + "] to: " + errors.getTarget());
|
||||||
|
}
|
||||||
|
request.getPortletSession().setAttribute(formAttrName, errors.getTarget());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch errors model as starting point, containing form object under
|
||||||
|
// "commandName", and corresponding Errors instance under internal key.
|
||||||
|
Map model = errors.getModel();
|
||||||
|
|
||||||
|
// Merge reference data into model, if any.
|
||||||
|
Map referenceData = referenceData(request, errors.getTarget(), errors);
|
||||||
|
if (referenceData != null) {
|
||||||
|
model.putAll(referenceData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge control attributes into model, if any.
|
||||||
|
if (controlModel != null) {
|
||||||
|
model.putAll(controlModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger rendering of the specified view, using the final model.
|
||||||
|
return new ModelAndView(viewName, model);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a reference data map for the given request, consisting of
|
||||||
|
* bean name/bean instance pairs as expected by ModelAndView.
|
||||||
|
* <p>The default implementation returns <code>null</code>.
|
||||||
|
* Subclasses can override this to set reference data used in the view.
|
||||||
|
* @param request current render request
|
||||||
|
* @param command form object with request parameters bound onto it
|
||||||
|
* @param errors validation errors holder
|
||||||
|
* @return a Map with reference data entries, or null if none
|
||||||
|
* @throws Exception in case of invalid state or arguments
|
||||||
|
* @see ModelAndView
|
||||||
|
*/
|
||||||
|
protected Map referenceData(PortletRequest request, Object command, Errors errors) throws Exception {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process render phase of form submission request. Called by <code>handleRequestInternal</code>
|
||||||
|
* in case of a form submission, with or without binding errors. Implementations
|
||||||
|
* need to proceed properly, typically showing a form view in case of binding
|
||||||
|
* errors or rendering the result of a submit action else.
|
||||||
|
* <p>For a success view, call <code>errors.getModel()</code> to populate the
|
||||||
|
* ModelAndView model with the command and the Errors instance, under the
|
||||||
|
* specified command name, as expected by the "spring:bind" tag. For a form view,
|
||||||
|
* simply return the ModelAndView object privded by <code>showForm</code>.
|
||||||
|
* @param request current render request
|
||||||
|
* @param response current render response
|
||||||
|
* @param command form object with request parameters bound onto it
|
||||||
|
* @param errors errors holder
|
||||||
|
* @return the prepared model and view, or null
|
||||||
|
* @throws Exception in case of errors
|
||||||
|
* @see #handleRenderRequestInternal
|
||||||
|
* @see #processFormSubmission
|
||||||
|
* @see #isFormSubmission
|
||||||
|
* @see #showForm(RenderRequest, RenderResponse, BindException)
|
||||||
|
* @see org.springframework.validation.Errors
|
||||||
|
* @see org.springframework.validation.BindException#getModel
|
||||||
|
*/
|
||||||
|
protected abstract ModelAndView renderFormSubmission(RenderRequest request, RenderResponse response, Object command, BindException errors)
|
||||||
|
throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process action phase of form submission request. Called by <code>handleRequestInternal</code>
|
||||||
|
* in case of a form submission, with or without binding errors. Implementations
|
||||||
|
* need to proceed properly, typically performing a submit action if there are no binding errors.
|
||||||
|
* <p>Subclasses can implement this to provide custom submission handling
|
||||||
|
* like triggering a custom action. They can also provide custom validation
|
||||||
|
* or proceed with the submission accordingly.
|
||||||
|
* @param request current action request
|
||||||
|
* @param response current action response
|
||||||
|
* @param command form object with request parameters bound onto it
|
||||||
|
* @param errors errors holder (subclass can add errors if it wants to)
|
||||||
|
* @throws Exception in case of errors
|
||||||
|
* @see #handleActionRequestInternal
|
||||||
|
* @see #renderFormSubmission
|
||||||
|
* @see #isFormSubmission
|
||||||
|
* @see org.springframework.validation.Errors
|
||||||
|
*/
|
||||||
|
protected abstract void processFormSubmission(ActionRequest request, ActionResponse response, Object command, BindException errors)
|
||||||
|
throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle an invalid submit request, e.g. when in session form mode but no form object
|
||||||
|
* was found in the session (like in case of an invalid resubmit by the browser).
|
||||||
|
* <p>The default implementation simply tries to resubmit the form with a new form object.
|
||||||
|
* This should also work if the user hit the back button, changed some form data,
|
||||||
|
* and resubmitted the form.
|
||||||
|
* <p>Note: To avoid duplicate submissions, you need to override this method.
|
||||||
|
* Either show some "invalid submit" message, or call <code>showNewForm</code> for
|
||||||
|
* resetting the form (prepopulating it with the current values if "bindOnNewForm"
|
||||||
|
* is true). In this case, the form object in the session serves as transaction token.
|
||||||
|
* <pre class="code">
|
||||||
|
* protected ModelAndView handleInvalidSubmit(RenderRequest request, RenderResponse response) throws Exception {
|
||||||
|
* return showNewForm(request, response);
|
||||||
|
* }</pre>
|
||||||
|
* You can also show a new form but with special errors registered on it:
|
||||||
|
* <pre class="code">
|
||||||
|
* protected ModelAndView handleInvalidSubmit(RenderRequest request, RenderResponse response) throws Exception {
|
||||||
|
* BindException errors = getErrorsForNewForm(request);
|
||||||
|
* errors.reject("duplicateFormSubmission", "Duplicate form submission");
|
||||||
|
* return showForm(request, response, errors);
|
||||||
|
* }</pre>
|
||||||
|
* <p><b>WARNING:</b> If you override this method, be sure to also override the action
|
||||||
|
* phase version of this method so that it will not attempt to perform the resubmit
|
||||||
|
* action by default.
|
||||||
|
* @param request current render request
|
||||||
|
* @param response current render response
|
||||||
|
* @return a prepared view, or null if handled directly
|
||||||
|
* @throws Exception in case of errors
|
||||||
|
* @see #handleInvalidSubmit
|
||||||
|
*/
|
||||||
|
protected ModelAndView renderInvalidSubmit(RenderRequest request, RenderResponse response)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
return renderFormSubmission(request, response, getRenderCommand(request), getRenderErrors(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle an invalid submit request, e.g. when in session form mode but no form object
|
||||||
|
* was found in the session (like in case of an invalid resubmit by the browser).
|
||||||
|
* <p>The default implementation simply tries to resubmit the form with a new form object.
|
||||||
|
* This should also work if the user hit the back button, changed some form data,
|
||||||
|
* and resubmitted the form.
|
||||||
|
* <p>Note: To avoid duplicate submissions, you need to override this method.
|
||||||
|
* Most likely you will simply want it to do nothing here in the action phase
|
||||||
|
* and diplay an appropriate error and a new form in the render phase.
|
||||||
|
* <pre class="code">
|
||||||
|
* protected void handleInvalidSubmit(ActionRequest request, ActionResponse response) throws Exception {
|
||||||
|
* }</pre>
|
||||||
|
* <p>If you override this method but you do need a command object and bind errors
|
||||||
|
* in the render phase, be sure to call {@link #setRenderCommandAndErrors setRenderCommandAndErrors}
|
||||||
|
* from here.
|
||||||
|
* @param request current action request
|
||||||
|
* @param response current action response
|
||||||
|
* @throws Exception in case of errors
|
||||||
|
* @see #renderInvalidSubmit
|
||||||
|
* @see #setRenderCommandAndErrors
|
||||||
|
*/
|
||||||
|
protected void handleInvalidSubmit(ActionRequest request, ActionResponse response) throws Exception {
|
||||||
|
passRenderParameters(request, response);
|
||||||
|
Object command = formBackingObject(request);
|
||||||
|
if (command == null) {
|
||||||
|
throw new PortletException("Form object returned by formBackingObject() must not be null");
|
||||||
|
}
|
||||||
|
if (!checkCommand(command)) {
|
||||||
|
throw new PortletException("Form object returned by formBackingObject() must match commandClass");
|
||||||
|
}
|
||||||
|
PortletRequestDataBinder binder = bindAndValidate(request, command);
|
||||||
|
BindException errors = new BindException(binder.getBindingResult());
|
||||||
|
processFormSubmission(request, response, command, errors);
|
||||||
|
setRenderCommandAndErrors(request, command, errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,975 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2008 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.mvc;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.portlet.ActionRequest;
|
||||||
|
import javax.portlet.ActionResponse;
|
||||||
|
import javax.portlet.PortletException;
|
||||||
|
import javax.portlet.PortletRequest;
|
||||||
|
import javax.portlet.RenderRequest;
|
||||||
|
import javax.portlet.RenderResponse;
|
||||||
|
|
||||||
|
import org.springframework.validation.BindException;
|
||||||
|
import org.springframework.validation.Errors;
|
||||||
|
import org.springframework.web.portlet.ModelAndView;
|
||||||
|
import org.springframework.web.portlet.util.PortletUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form controller for typical wizard-style workflows.
|
||||||
|
*
|
||||||
|
* <p>In contrast to classic forms, wizards have more than one form view page.
|
||||||
|
* Therefore, there are various actions instead of one single submit action:
|
||||||
|
* <ul>
|
||||||
|
* <li>finish: trying to leave the wizard successfully, i.e. performing its
|
||||||
|
* final action, and thus needing a valid state;
|
||||||
|
* <li>cancel: leaving the wizard without performing its final action, and
|
||||||
|
* thus without regard to the validity of its current state;
|
||||||
|
* <li>page change: showing another wizard page, e.g. the next or previous
|
||||||
|
* one, with regard to "dirty back" and "dirty forward".
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>Finish and cancel actions can be triggered by request parameters, named
|
||||||
|
* PARAM_FINISH ("_finish") and PARAM_CANCEL ("_cancel"), ignoring parameter
|
||||||
|
* values to allow for HTML buttons. The target page for page changes can be
|
||||||
|
* specified by PARAM_TARGET, appending the page number to the parameter name
|
||||||
|
* (e.g. "_target1"). The action parameters are recognized when triggered by
|
||||||
|
* image buttons too (via "_finish.x", "_abort.x", or "_target1.x").
|
||||||
|
*
|
||||||
|
* <p>The current page number will be stored in the session. It can also be
|
||||||
|
* specified as request parameter PARAM_PAGE ("_page") in order to properly handle
|
||||||
|
* usage of the back button in a browser: In this case, a submission will always
|
||||||
|
* contain the correct page number, even if the user submitted from an old view.
|
||||||
|
*
|
||||||
|
* <p>The page can only be changed if it validates correctly, except if a
|
||||||
|
* "dirty back" or "dirty forward" is allowed. At finish, all pages get
|
||||||
|
* validated again to guarantee a consistent state.
|
||||||
|
*
|
||||||
|
* <p>Note that a validator's default validate method is not executed when using
|
||||||
|
* this class! Rather, the <code>validatePage</code> implementation should call
|
||||||
|
* special <code>validateXXX</code> methods that the validator needs to provide,
|
||||||
|
* validating certain pieces of the object. These can be combined to validate
|
||||||
|
* the elements of individual pages.
|
||||||
|
*
|
||||||
|
* <p>Note: Page numbering starts with 0, to be able to pass an array
|
||||||
|
* consisting of the corresponding view names to the "pages" bean property.
|
||||||
|
*
|
||||||
|
* <p>Parameters indicated with <code>setPassRenderParameters</code> will be present
|
||||||
|
* for each page. If there are render parameters you need in <code>renderFinish</code>
|
||||||
|
* or <code>renderCancel</code>, then you need to pass those forward from the
|
||||||
|
* <code>processFinish</code> or <code>processCancel</code> methods, respectively.
|
||||||
|
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @since 2.0
|
||||||
|
* @see #setPages
|
||||||
|
* @see #validatePage
|
||||||
|
* @see #processFinish
|
||||||
|
* @see #processCancel
|
||||||
|
*/
|
||||||
|
public abstract class AbstractWizardFormController extends AbstractFormController {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parameter triggering the finish action.
|
||||||
|
* Can be called from any wizard page!
|
||||||
|
*/
|
||||||
|
public static final String PARAM_FINISH = "_finish";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parameter triggering the cancel action.
|
||||||
|
* Can be called from any wizard page!
|
||||||
|
*/
|
||||||
|
public static final String PARAM_CANCEL = "_cancel";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parameter specifying the target page,
|
||||||
|
* appending the page number to the name.
|
||||||
|
*/
|
||||||
|
public static final String PARAM_TARGET = "_target";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parameter specifying the current page as value. Not necessary on
|
||||||
|
* form pages, but allows to properly handle usage of the back button.
|
||||||
|
* @see #setPageAttribute
|
||||||
|
*/
|
||||||
|
public static final String PARAM_PAGE = "_page";
|
||||||
|
|
||||||
|
|
||||||
|
private String[] pages;
|
||||||
|
|
||||||
|
private String pageAttribute;
|
||||||
|
|
||||||
|
private boolean allowDirtyBack = true;
|
||||||
|
|
||||||
|
private boolean allowDirtyForward = false;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new AbstractWizardFormController.
|
||||||
|
* <p>"sessionForm" is automatically turned on, "validateOnBinding"
|
||||||
|
* turned off, and "cacheSeconds" set to 0 by the base class
|
||||||
|
* (-> no caching for all form controllers).
|
||||||
|
*/
|
||||||
|
public AbstractWizardFormController() {
|
||||||
|
// AbstractFormController sets default cache seconds to 0.
|
||||||
|
super();
|
||||||
|
|
||||||
|
// Always needs session to keep data from all pages.
|
||||||
|
setSessionForm(true);
|
||||||
|
|
||||||
|
// Never validate everything on binding ->
|
||||||
|
// wizards validate individual pages.
|
||||||
|
setValidateOnBinding(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the wizard pages, i.e. the view names for the pages.
|
||||||
|
* The array index is interpreted as page number.
|
||||||
|
* @param pages view names for the pages
|
||||||
|
*/
|
||||||
|
public final void setPages(String[] pages) {
|
||||||
|
if (pages == null || pages.length == 0) {
|
||||||
|
throw new IllegalArgumentException("No wizard pages defined");
|
||||||
|
}
|
||||||
|
this.pages = pages;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the wizard pages, i.e. the view names for the pages.
|
||||||
|
* The array index corresponds to the page number.
|
||||||
|
* <p>Note that a concrete wizard form controller might override
|
||||||
|
* <code>getViewName(PortletRequest, Object, int)</code> to
|
||||||
|
* determine the view name for each page dynamically.
|
||||||
|
* @see #getViewName(PortletRequest, Object, int)
|
||||||
|
*/
|
||||||
|
public final String[] getPages() {
|
||||||
|
return this.pages;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the number of wizard pages.
|
||||||
|
* Useful to check whether the last page has been reached.
|
||||||
|
* <p>Note that a concrete wizard form controller might override
|
||||||
|
* <code>getPageCount(PortletRequest, Object)</code> to determine
|
||||||
|
* the page count dynamically.
|
||||||
|
* @see #getPageCount(PortletRequest, Object)
|
||||||
|
*/
|
||||||
|
protected final int getPageCount() {
|
||||||
|
return this.pages.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the name of the page attribute in the model, containing
|
||||||
|
* an Integer with the current page number.
|
||||||
|
* <p>This will be necessary for single views rendering multiple view pages.
|
||||||
|
* It also allows for specifying the optional "_page" parameter.
|
||||||
|
* @param pageAttribute name of the page attribute
|
||||||
|
* @see #PARAM_PAGE
|
||||||
|
*/
|
||||||
|
public final void setPageAttribute(String pageAttribute) {
|
||||||
|
this.pageAttribute = pageAttribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of the page attribute in the model.
|
||||||
|
*/
|
||||||
|
public final String getPageAttribute() {
|
||||||
|
return this.pageAttribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set if "dirty back" is allowed, i.e. if moving to a former wizard
|
||||||
|
* page is allowed in case of validation errors for the current page.
|
||||||
|
* @param allowDirtyBack if "dirty back" is allowed
|
||||||
|
*/
|
||||||
|
public final void setAllowDirtyBack(boolean allowDirtyBack) {
|
||||||
|
this.allowDirtyBack = allowDirtyBack;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether "dirty back" is allowed.
|
||||||
|
*/
|
||||||
|
public final boolean isAllowDirtyBack() {
|
||||||
|
return this.allowDirtyBack;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set if "dirty forward" is allowed, i.e. if moving to a later wizard
|
||||||
|
* page is allowed in case of validation errors for the current page.
|
||||||
|
* @param allowDirtyForward if "dirty forward" is allowed
|
||||||
|
*/
|
||||||
|
public final void setAllowDirtyForward(boolean allowDirtyForward) {
|
||||||
|
this.allowDirtyForward = allowDirtyForward;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether "dirty forward" is allowed.
|
||||||
|
*/
|
||||||
|
public final boolean isAllowDirtyForward() {
|
||||||
|
return this.allowDirtyForward;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls page-specific onBindAndValidate method.
|
||||||
|
*/
|
||||||
|
protected final void onBindAndValidate(PortletRequest request, Object command, BindException errors)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
onBindAndValidate(request, command, errors, getCurrentPage(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback for custom post-processing in terms of binding and validation.
|
||||||
|
* Called on each submit, after standard binding but before page-specific
|
||||||
|
* validation of this wizard form controller.
|
||||||
|
* <p>Note: AbstractWizardFormController does not perform standard
|
||||||
|
* validation on binding but rather applies page-specific validation
|
||||||
|
* on processing the form submission.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param command bound command
|
||||||
|
* @param errors Errors instance for additional custom validation
|
||||||
|
* @param page current wizard page
|
||||||
|
* @throws Exception in case of invalid state or arguments
|
||||||
|
* @see #bindAndValidate
|
||||||
|
* @see #processFormSubmission
|
||||||
|
* @see org.springframework.validation.Errors
|
||||||
|
*/
|
||||||
|
protected void onBindAndValidate(PortletRequest request, Object command, BindException errors, int page)
|
||||||
|
throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Consider an explicit finish or cancel request as a form submission too.
|
||||||
|
* @see #isFinishRequest(PortletRequest)
|
||||||
|
* @see #isCancelRequest(PortletRequest)
|
||||||
|
*/
|
||||||
|
protected boolean isFormSubmission(PortletRequest request) {
|
||||||
|
return super.isFormSubmission(request) || isFinishRequest(request) || isCancelRequest(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls page-specific referenceData method.
|
||||||
|
*/
|
||||||
|
protected final Map referenceData(PortletRequest request, Object command, Errors errors)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
return referenceData(request, command, errors, getCurrentPage(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a reference data map for the given request, consisting of
|
||||||
|
* bean name/bean instance pairs as expected by ModelAndView.
|
||||||
|
* <p>The default implementation delegates to {@link #referenceData(PortletRequest, int)}.
|
||||||
|
* Subclasses can override this to set reference data used in the view.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param command form object with request parameters bound onto it
|
||||||
|
* @param errors validation errors holder
|
||||||
|
* @param page current wizard page
|
||||||
|
* @return a Map with reference data entries, or null if none
|
||||||
|
* @throws Exception in case of invalid state or arguments
|
||||||
|
* @see #referenceData(PortletRequest, int)
|
||||||
|
* @see org.springframework.web.portlet.ModelAndView
|
||||||
|
*/
|
||||||
|
protected Map referenceData(PortletRequest request, Object command, Errors errors, int page)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
return referenceData(request, page);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a reference data map for the given request, consisting of
|
||||||
|
* bean name/bean instance pairs as expected by ModelAndView.
|
||||||
|
* <p>The default implementation returns <code>null</code>.
|
||||||
|
* Subclasses can override this to set reference data used in the view.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param page current wizard page
|
||||||
|
* @return a Map with reference data entries, or null if none
|
||||||
|
* @throws Exception in case of invalid state or arguments
|
||||||
|
* @see org.springframework.web.portlet.ModelAndView
|
||||||
|
*/
|
||||||
|
protected Map referenceData(PortletRequest request, int page) throws Exception {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the first page as form view.
|
||||||
|
* <p>This can be overridden in subclasses, e.g. to prepare wizard-specific
|
||||||
|
* error views in case of an Exception.
|
||||||
|
*/
|
||||||
|
protected ModelAndView showForm(
|
||||||
|
RenderRequest request, RenderResponse response, BindException errors) throws Exception {
|
||||||
|
|
||||||
|
return showPage(request, errors, getInitialPage(request, errors.getTarget()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare the form model and view, including reference and error data,
|
||||||
|
* for the given page. Can be used in <code>processFinish</code> implementations,
|
||||||
|
* to show the corresponding page in case of validation errors.
|
||||||
|
* @param request current portlet render request
|
||||||
|
* @param errors validation errors holder
|
||||||
|
* @param page number of page to show
|
||||||
|
* @return the prepared form view
|
||||||
|
* @throws Exception in case of invalid state or arguments
|
||||||
|
*/
|
||||||
|
protected final ModelAndView showPage(RenderRequest request, BindException errors, int page)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
if (page >= 0 && page < getPageCount(request, errors.getTarget())) {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Showing wizard page " + page + " for form bean '" + getCommandName() + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set page session attribute, expose overriding request attribute.
|
||||||
|
Integer pageInteger = new Integer(page);
|
||||||
|
String pageAttrName = getPageSessionAttributeName(request);
|
||||||
|
if (isSessionForm()) {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Setting page session attribute [" + pageAttrName + "] to: " + pageInteger);
|
||||||
|
}
|
||||||
|
request.getPortletSession().setAttribute(pageAttrName, pageInteger);
|
||||||
|
}
|
||||||
|
request.setAttribute(pageAttrName, pageInteger);
|
||||||
|
|
||||||
|
// Set page request attribute for evaluation by views.
|
||||||
|
Map controlModel = new HashMap();
|
||||||
|
if (this.pageAttribute != null) {
|
||||||
|
controlModel.put(this.pageAttribute, new Integer(page));
|
||||||
|
}
|
||||||
|
String viewName = getViewName(request, errors.getTarget(), page);
|
||||||
|
return showForm(request, errors, viewName, controlModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
throw new PortletException("Invalid wizard page number: " + page);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the page count for this wizard form controller.
|
||||||
|
* <p>The default implementation delegates to {@link #getPageCount()}.
|
||||||
|
* Can be overridden to dynamically adapt the page count.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param command the command object as returned by formBackingObject
|
||||||
|
* @return the current page count
|
||||||
|
* @see #getPageCount
|
||||||
|
*/
|
||||||
|
protected int getPageCount(PortletRequest request, Object command) {
|
||||||
|
return getPageCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of the view for the specified page of this wizard form controller.
|
||||||
|
* <p>The default implementation takes the view name from the {@link #getPages()} array.
|
||||||
|
* Can be overridden to dynamically switch the page view or to return view names
|
||||||
|
* for dynamically defined pages.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param command the command object as returned by <code>formBackingObject</code>
|
||||||
|
* @return the current page count
|
||||||
|
* @see #getPageCount
|
||||||
|
*/
|
||||||
|
protected String getViewName(PortletRequest request, Object command, int page) {
|
||||||
|
return getPages()[page];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the initial page of the wizard, i.e. the page shown at wizard startup.
|
||||||
|
* <p>The default implementation delegates to {@link #getInitialPage(PortletRequest)}.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param command the command object as returned by <code>formBackingObject</code>
|
||||||
|
* @return the initial page number
|
||||||
|
* @see #getInitialPage(PortletRequest)
|
||||||
|
* @see #formBackingObject
|
||||||
|
*/
|
||||||
|
protected int getInitialPage(PortletRequest request, Object command) {
|
||||||
|
return getInitialPage(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the initial page of the wizard, i.e. the page shown at wizard startup.
|
||||||
|
* <p>The default implementation returns 0 for first page.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @return the initial page number
|
||||||
|
*/
|
||||||
|
protected int getInitialPage(PortletRequest request) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of the PortletSession attribute that holds the page object
|
||||||
|
* for this wizard form controller.
|
||||||
|
* <p>The default implementation delegates to the <code>getPageSessionAttributeName</code>
|
||||||
|
* version without arguments.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @return the name of the form session attribute, or null if not in session form mode
|
||||||
|
* @see #getPageSessionAttributeName
|
||||||
|
* @see #getFormSessionAttributeName
|
||||||
|
* @see javax.portlet.PortletSession#getAttribute
|
||||||
|
*/
|
||||||
|
protected String getPageSessionAttributeName(PortletRequest request) {
|
||||||
|
return getPageSessionAttributeName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of the PortletSession attribute that holds the page object
|
||||||
|
* for this wizard form controller.
|
||||||
|
* <p>Default is an internal name, of no relevance to applications, as the form
|
||||||
|
* session attribute is not usually accessed directly. Can be overridden to use
|
||||||
|
* an application-specific attribute name, which allows other code to access
|
||||||
|
* the session attribute directly.
|
||||||
|
* @return the name of the page session attribute
|
||||||
|
* @see #getFormSessionAttributeName
|
||||||
|
* @see javax.portlet.PortletSession#getAttribute
|
||||||
|
*/
|
||||||
|
protected String getPageSessionAttributeName() {
|
||||||
|
return getClass().getName() + ".PAGE." + getCommandName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pass the page number to the render phase by setting a render parameter.
|
||||||
|
* This method may not be called when the action calls
|
||||||
|
* {@link javax.portlet.ActionResponse#sendRedirect(String)}.
|
||||||
|
* @param response the current action response
|
||||||
|
* @param page the page number
|
||||||
|
* @see ActionResponse#setRenderParameter
|
||||||
|
*/
|
||||||
|
protected void setPageRenderParameter(ActionResponse response, int page) {
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
logger.debug("Setting page number render parameter [" + PARAM_PAGE + "] to [" + page + "]");
|
||||||
|
try {
|
||||||
|
response.setRenderParameter(PARAM_PAGE, new Integer(page).toString());
|
||||||
|
}
|
||||||
|
catch (IllegalStateException ex) {
|
||||||
|
// ignore in case sendRedirect was already set
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pass the the parameter that indicates the target page of the request
|
||||||
|
* forward to the render phase. If the <code>getTargetPage<code> method
|
||||||
|
* was overridden, this may need to be overriden as well.
|
||||||
|
* @param request the current action request
|
||||||
|
* @param response the current action response
|
||||||
|
* @see #PARAM_TARGET
|
||||||
|
* @see #getTargetPage(PortletRequest, int)
|
||||||
|
* @see #getTargetPage(PortletRequest, Object, Errors, int)
|
||||||
|
* @see ActionResponse#setRenderParameter
|
||||||
|
*/
|
||||||
|
protected void setTargetRenderParameter(ActionRequest request, ActionResponse response) {
|
||||||
|
try {
|
||||||
|
Iterator it = PortletUtils.getParametersStartingWith(request, PARAM_TARGET).entrySet().iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
Map.Entry entry = (Map.Entry) it.next();
|
||||||
|
String param = PARAM_TARGET + (String) entry.getKey();
|
||||||
|
Object value = entry.getValue();
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Setting target render parameter [" + param + "]");
|
||||||
|
}
|
||||||
|
if (value instanceof String) {
|
||||||
|
response.setRenderParameter(param, (String) value);
|
||||||
|
}
|
||||||
|
else if (value instanceof String[]) {
|
||||||
|
response.setRenderParameter(param, (String[]) value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IllegalStateException ex) {
|
||||||
|
// ignore in case sendRedirect was already set
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pass the the parameter that indicates a finish request forward to the
|
||||||
|
* render phase. If the <code>isFinishRequest</code> method
|
||||||
|
* was overridden, this may need to be overriden as well.
|
||||||
|
* @param request the current action request
|
||||||
|
* @param response the current action response
|
||||||
|
* @see #PARAM_FINISH
|
||||||
|
* @see #isFinishRequest
|
||||||
|
* @see ActionResponse#setRenderParameter
|
||||||
|
*/
|
||||||
|
protected void setFinishRenderParameter(ActionRequest request, ActionResponse response) {
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
logger.debug("Setting cancel render parameter [" + PARAM_FINISH + "]");
|
||||||
|
try {
|
||||||
|
String name = PortletUtils.getSubmitParameter(request, PARAM_FINISH);
|
||||||
|
if (name != null)
|
||||||
|
response.setRenderParameter(name, request.getParameter(name));
|
||||||
|
}
|
||||||
|
catch (IllegalStateException ex) {
|
||||||
|
// ignore in case sendRedirect was already set
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pass the the parameter that indicates a cancel request forward to the
|
||||||
|
* render phase. If the <code>isCancelRequest</code> method
|
||||||
|
* was overridden, this may need to be overriden as well.
|
||||||
|
* @param request the current action request
|
||||||
|
* @param response the current action response
|
||||||
|
* @see #PARAM_CANCEL
|
||||||
|
* @see #isCancelRequest
|
||||||
|
* @see ActionResponse#setRenderParameter
|
||||||
|
*/
|
||||||
|
protected void setCancelRenderParameter(ActionRequest request, ActionResponse response) {
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
logger.debug("Setting cancel render parameter [" + PARAM_CANCEL + "]");
|
||||||
|
try {
|
||||||
|
String name = PortletUtils.getSubmitParameter(request, PARAM_CANCEL);
|
||||||
|
if (name != null)
|
||||||
|
response.setRenderParameter(name, request.getParameter(name));
|
||||||
|
}
|
||||||
|
catch (IllegalStateException ex) {
|
||||||
|
// ignore in case sendRedirect was already set
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle an invalid submit request, e.g. when in session form mode but no form object
|
||||||
|
* was found in the session (like in case of an invalid resubmit by the browser).
|
||||||
|
* <p>The default implementation for wizard form controllers simply shows the initial page
|
||||||
|
* of a new wizard form. If you want to show some "invalid submit" message, you need
|
||||||
|
* to override this method.
|
||||||
|
* @param request current portlet render request
|
||||||
|
* @param response current portlet render response
|
||||||
|
* @return a prepared view, or null if handled directly
|
||||||
|
* @throws Exception in case of errors
|
||||||
|
* @see #showNewForm
|
||||||
|
* @see #setBindOnNewForm
|
||||||
|
* @see #handleInvalidSubmit
|
||||||
|
*/
|
||||||
|
protected ModelAndView renderInvalidSubmit(RenderRequest request, RenderResponse response)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
return showNewForm(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle an invalid submit request, e.g. when in session form mode but no form object
|
||||||
|
* was found in the session (like in case of an invalid resubmit by the browser).
|
||||||
|
* <p>The default implementation for wizard form controllers simply shows the initial page
|
||||||
|
* of a new wizard form, so here in the action phase this method does nothing. If you
|
||||||
|
* want to take some action on an invalid submit, you need to override this method.
|
||||||
|
* @param request current portlet action request
|
||||||
|
* @param response current portlet action response
|
||||||
|
* @throws Exception in case of errors
|
||||||
|
* @see #renderInvalidSubmit
|
||||||
|
*/
|
||||||
|
protected void handleInvalidSubmit(ActionRequest request, ActionResponse response) throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply wizard workflow: finish, cancel, page change.
|
||||||
|
* @see #processFormSubmission
|
||||||
|
*/
|
||||||
|
protected final ModelAndView renderFormSubmission(RenderRequest request, RenderResponse response, Object command, BindException errors)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
int currentPage = getCurrentPage(request);
|
||||||
|
String pageAttrName = getPageSessionAttributeName(request);
|
||||||
|
request.setAttribute(pageAttrName, new Integer(currentPage));
|
||||||
|
|
||||||
|
// cancel?
|
||||||
|
if (isCancelRequest(request)) {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Cancelling wizard for form bean '" + getCommandName() + "'");
|
||||||
|
}
|
||||||
|
return renderCancel(request, response, command, errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
// finish?
|
||||||
|
if (isFinishRequest(request)) {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Finishing wizard for form bean '" + getCommandName() + "'");
|
||||||
|
}
|
||||||
|
return renderValidatePagesAndFinish(request, response, command, errors, currentPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normal submit: show specified target page.
|
||||||
|
int targetPage = getTargetPage(request, command, errors, currentPage);
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Target page " + targetPage + " requested");
|
||||||
|
}
|
||||||
|
if (targetPage != currentPage) {
|
||||||
|
if (!errors.hasErrors() || (this.allowDirtyBack && targetPage < currentPage) ||
|
||||||
|
(this.allowDirtyForward && targetPage > currentPage)) {
|
||||||
|
// Allowed to go to target page.
|
||||||
|
return showPage(request, errors, targetPage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show current page again
|
||||||
|
return showPage(request, errors, currentPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply wizard workflow: finish, cancel, page change.
|
||||||
|
* @see #renderFormSubmission
|
||||||
|
*/
|
||||||
|
protected final void processFormSubmission(
|
||||||
|
ActionRequest request, ActionResponse response, Object command, BindException errors)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
int currentPage = getCurrentPage(request);
|
||||||
|
// Remove page session attribute, provide copy as request attribute.
|
||||||
|
String pageAttrName = getPageSessionAttributeName(request);
|
||||||
|
if (isSessionForm()) {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Removing page session attribute [" + pageAttrName + "]");
|
||||||
|
}
|
||||||
|
request.getPortletSession().removeAttribute(pageAttrName);
|
||||||
|
}
|
||||||
|
request.setAttribute(pageAttrName, new Integer(currentPage));
|
||||||
|
|
||||||
|
// cancel?
|
||||||
|
if (isCancelRequest(request)) {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Cancelling wizard for form bean '" + getCommandName() + "'");
|
||||||
|
}
|
||||||
|
setPageRenderParameter(response, currentPage);
|
||||||
|
setCancelRenderParameter(request, response);
|
||||||
|
processCancel(request, response, command, errors);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// finish?
|
||||||
|
if (isFinishRequest(request)) {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Finishing wizard for form bean '" + getCommandName() + "'");
|
||||||
|
}
|
||||||
|
if (!isRedirectAction()) {
|
||||||
|
setPageRenderParameter(response, currentPage);
|
||||||
|
setFinishRenderParameter(request, response);
|
||||||
|
}
|
||||||
|
validatePagesAndFinish(request, response, command, errors, currentPage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normal submit: validate current page
|
||||||
|
if (!suppressValidation(request)) {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Validating wizard page " + currentPage + " for form bean '" + getCommandName() + "'");
|
||||||
|
}
|
||||||
|
validatePage(command, errors, currentPage, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
setPageRenderParameter(response, currentPage);
|
||||||
|
setTargetRenderParameter(request, response);
|
||||||
|
passRenderParameters(request, response);
|
||||||
|
|
||||||
|
// Give subclasses a change to perform custom post-procession
|
||||||
|
// of the current page and its command object.
|
||||||
|
postProcessPage(request, command, errors, currentPage);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the current page number. Used by {@link #processFormSubmission}.
|
||||||
|
* <p>The default implementation checks the page session attribute.
|
||||||
|
* Subclasses can override this for customized page determination.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @return the current page number
|
||||||
|
* @see #getPageSessionAttributeName()
|
||||||
|
*/
|
||||||
|
protected int getCurrentPage(PortletRequest request) {
|
||||||
|
// Check for overriding attribute in request.
|
||||||
|
String pageAttrName = getPageSessionAttributeName(request);
|
||||||
|
Integer pageAttr = (Integer) request.getAttribute(pageAttrName);
|
||||||
|
if (pageAttr != null) {
|
||||||
|
return pageAttr.intValue();
|
||||||
|
}
|
||||||
|
// Check for explicit request parameter.
|
||||||
|
String pageParam = request.getParameter(PARAM_PAGE);
|
||||||
|
if (pageParam != null) {
|
||||||
|
return Integer.parseInt(pageParam);
|
||||||
|
}
|
||||||
|
// Check for original attribute in session.
|
||||||
|
if (isSessionForm()) {
|
||||||
|
pageAttr = (Integer) request.getPortletSession().getAttribute(pageAttrName);
|
||||||
|
if (pageAttr != null) {
|
||||||
|
return pageAttr.intValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("Page attribute [" + pageAttrName + "] neither found in session nor in request");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the incoming request is a request to finish the
|
||||||
|
* processing of the current form.
|
||||||
|
* <p>By default, this method returns <code>true</code> if a parameter
|
||||||
|
* matching the "_finish" key is present in the request, otherwise it
|
||||||
|
* returns <code>false</code>. Subclasses may override this method
|
||||||
|
* to provide custom logic to detect a finish request.
|
||||||
|
* <p>The parameter is recognized both when sent as a plain parameter
|
||||||
|
* ("_finish") or when triggered by an image button ("_finish.x").
|
||||||
|
* @param request current portlet request
|
||||||
|
* @return whether the request indicates to finish form processing
|
||||||
|
* @see #PARAM_FINISH
|
||||||
|
*/
|
||||||
|
protected boolean isFinishRequest(PortletRequest request) {
|
||||||
|
return PortletUtils.hasSubmitParameter(request, PARAM_FINISH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the incoming request is a request to cancel the
|
||||||
|
* processing of the current form.
|
||||||
|
* <p>By default, this method returns <code>true</code> if a parameter
|
||||||
|
* matching the "_cancel" key is present in the request, otherwise it
|
||||||
|
* returns <code>false</code>. Subclasses may override this method
|
||||||
|
* to provide custom logic to detect a cancel request.
|
||||||
|
* <p>The parameter is recognized both when sent as a plain parameter
|
||||||
|
* ("_cancel") or when triggered by an image button ("_cancel.x").
|
||||||
|
* @param request current portlet request
|
||||||
|
* @return whether the request indicates to cancel form processing
|
||||||
|
* @see #PARAM_CANCEL
|
||||||
|
*/
|
||||||
|
protected boolean isCancelRequest(PortletRequest request) {
|
||||||
|
return PortletUtils.hasSubmitParameter(request, PARAM_CANCEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the target page specified in the request.
|
||||||
|
* <p>The default implementation delegates to {@link #getTargetPage(PortletRequest, int)}.
|
||||||
|
* Subclasses can override this for customized target page determination.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param command form object with request parameters bound onto it
|
||||||
|
* @param errors validation errors holder
|
||||||
|
* @param currentPage the current page, to be returned as fallback
|
||||||
|
* if no target page specified
|
||||||
|
* @return the page specified in the request, or current page if not found
|
||||||
|
* @see #getTargetPage(PortletRequest, int)
|
||||||
|
*/
|
||||||
|
protected int getTargetPage(PortletRequest request, Object command, Errors errors, int currentPage) {
|
||||||
|
return getTargetPage(request, currentPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the target page specified in the request.
|
||||||
|
* <p>The default implementation examines "_target" parameter (e.g. "_target1").
|
||||||
|
* Subclasses can override this for customized target page determination.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param currentPage the current page, to be returned as fallback
|
||||||
|
* if no target page specified
|
||||||
|
* @return the page specified in the request, or current page if not found
|
||||||
|
* @see #PARAM_TARGET
|
||||||
|
*/
|
||||||
|
protected int getTargetPage(PortletRequest request, int currentPage) {
|
||||||
|
return PortletUtils.getTargetPage(request, PARAM_TARGET, currentPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate all pages and process finish.
|
||||||
|
* If there are page validation errors, show the corresponding view page.
|
||||||
|
* @see #validatePagesAndFinish
|
||||||
|
*/
|
||||||
|
private ModelAndView renderValidatePagesAndFinish(
|
||||||
|
RenderRequest request, RenderResponse response, Object command, BindException errors, int currentPage)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
// In case of any errors -> show current page.
|
||||||
|
if (errors.hasErrors())
|
||||||
|
return showPage(request, errors, currentPage);
|
||||||
|
|
||||||
|
// No remaining errors -> proceed with finish.
|
||||||
|
return renderFinish(request, response, command, errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate all pages and process finish.
|
||||||
|
* If there are page validation errors, show the corresponding view page.
|
||||||
|
* @see #renderValidatePagesAndFinish
|
||||||
|
*/
|
||||||
|
private void validatePagesAndFinish(
|
||||||
|
ActionRequest request, ActionResponse response, Object command, BindException errors, int currentPage)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
// In case of binding errors -> show current page.
|
||||||
|
if (errors.hasErrors()) {
|
||||||
|
setPageRenderParameter(response, currentPage);
|
||||||
|
passRenderParameters(request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!suppressValidation(request)) {
|
||||||
|
// In case of remaining errors on a page -> show the page.
|
||||||
|
for (int page = 0; page < getPageCount(request, command); page++) {
|
||||||
|
validatePage(command, errors, page, true);
|
||||||
|
if (errors.hasErrors()) {
|
||||||
|
setPageRenderParameter(response, currentPage);
|
||||||
|
passRenderParameters(request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No remaining errors -> proceed with finish.
|
||||||
|
if (!isRedirectAction())
|
||||||
|
setPageRenderParameter(response, currentPage);
|
||||||
|
processFinish(request, response, command, errors);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Template method for custom validation logic for individual pages.
|
||||||
|
* The default implementation calls <code>validatePage(command, errors, page)</code>.
|
||||||
|
* <p>Implementations will typically call fine-granular <code>validateXXX</code>
|
||||||
|
* methods of this instance's Validator, combining them to validation of the
|
||||||
|
* corresponding pages. The Validator's default <code>validate</code> method
|
||||||
|
* will not be called by a wizard form controller!
|
||||||
|
* @param command form object with the current wizard state
|
||||||
|
* @param errors validation errors holder
|
||||||
|
* @param page number of page to validate
|
||||||
|
* @param finish whether this method is called during final revalidation on finish
|
||||||
|
* (else, it is called for validating the current page)
|
||||||
|
* @see #validatePage(Object, Errors, int)
|
||||||
|
* @see org.springframework.validation.Validator#validate
|
||||||
|
*/
|
||||||
|
protected void validatePage(Object command, Errors errors, int page, boolean finish) {
|
||||||
|
validatePage(command, errors, page);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Template method for custom validation logic for individual pages.
|
||||||
|
* The default implementation is empty.
|
||||||
|
* <p>Implementations will typically call fine-granular validateXXX methods of this
|
||||||
|
* instance's validator, combining them to validation of the corresponding pages.
|
||||||
|
* The validator's default <code>validate</code> method will not be called by a
|
||||||
|
* wizard form controller!
|
||||||
|
* @param command form object with the current wizard state
|
||||||
|
* @param errors validation errors holder
|
||||||
|
* @param page number of page to validate
|
||||||
|
* @see org.springframework.validation.Validator#validate
|
||||||
|
*/
|
||||||
|
protected void validatePage(Object command, Errors errors, int page) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post-process the given page after binding and validation, potentially
|
||||||
|
* updating its command object. The passed-in request might contain special
|
||||||
|
* parameters sent by the page.
|
||||||
|
* <p>Only invoked when displaying another page or the same page again,
|
||||||
|
* not when finishing or cancelling.
|
||||||
|
* @param request current action request
|
||||||
|
* @param command form object with request parameters bound onto it
|
||||||
|
* @param errors validation errors holder
|
||||||
|
* @param page number of page to post-process
|
||||||
|
* @throws Exception in case of invalid state or arguments
|
||||||
|
*/
|
||||||
|
protected void postProcessPage(ActionRequest request, Object command, Errors errors, int page)
|
||||||
|
throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Template method for the render phase of the finish action of this wizard.
|
||||||
|
* <p>The default implementation throws a PortletException, saying that a finish
|
||||||
|
* render request is not supported by this controller. Thus, you do not need to
|
||||||
|
* implement this template method if you do not need to render after a finish.
|
||||||
|
* <p>Call <code>errors.getModel()</code> to populate the ModelAndView model
|
||||||
|
* with the command and the Errors instance, under the specified command name,
|
||||||
|
* as expected by the "spring:bind" tag.
|
||||||
|
* @param request current portlet render request
|
||||||
|
* @param response current portlet render response
|
||||||
|
* @param command form object with the current wizard state
|
||||||
|
* @param errors validation errors holder
|
||||||
|
* @return the finish view
|
||||||
|
* @throws Exception in case of invalid state or arguments
|
||||||
|
* @see #processFinish
|
||||||
|
* @see org.springframework.validation.Errors
|
||||||
|
* @see org.springframework.validation.BindException#getModel
|
||||||
|
*/
|
||||||
|
protected ModelAndView renderFinish(
|
||||||
|
RenderRequest request, RenderResponse response, Object command, BindException errors)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
throw new PortletException("Wizard form controller class [" + getClass().getName() + "] does not support a finish render request");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Template method for the action phase of the finish action of this wizard.
|
||||||
|
* <p>The default implementation throws a PortletException, saying that a finish
|
||||||
|
* action request is not supported by this controller. You will almost certainly
|
||||||
|
* need to override this method.
|
||||||
|
* @param request current portlet action request
|
||||||
|
* @param response current portlet action response
|
||||||
|
* @param command form object with the current wizard state
|
||||||
|
* @param errors validation errors holder
|
||||||
|
* @throws Exception in case of invalid state or arguments
|
||||||
|
* @see #renderFinish
|
||||||
|
* @see org.springframework.validation.Errors
|
||||||
|
*/
|
||||||
|
protected void processFinish(
|
||||||
|
ActionRequest request, ActionResponse response, Object command, BindException errors)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
throw new PortletException(
|
||||||
|
"Wizard form controller class [" + getClass().getName() + "] does not support a finish action request");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Template method for the render phase of the cancel action of this wizard.
|
||||||
|
* <p>The default implementation throws a PortletException, saying that a cancel
|
||||||
|
* render request is not supported by this controller. Thus, you do not need to
|
||||||
|
* implement this template method if you do not support a cancel operation.
|
||||||
|
* <p>Call <code>errors.getModel()</code> to populate the ModelAndView model
|
||||||
|
* with the command and the Errors instance, under the specified command name,
|
||||||
|
* as expected by the "spring:bind" tag.
|
||||||
|
* @param request current portlet render request
|
||||||
|
* @param response current portlet render response
|
||||||
|
* @param command form object with the current wizard state
|
||||||
|
* @param errors Errors instance containing errors
|
||||||
|
* @return the cancellation view
|
||||||
|
* @throws Exception in case of invalid state or arguments
|
||||||
|
* @see #processCancel
|
||||||
|
* @see org.springframework.validation.Errors
|
||||||
|
* @see org.springframework.validation.BindException#getModel
|
||||||
|
*/
|
||||||
|
protected ModelAndView renderCancel(
|
||||||
|
RenderRequest request, RenderResponse response, Object command, BindException errors)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
throw new PortletException(
|
||||||
|
"Wizard form controller class [" + getClass().getName() + "] does not support a cancel render request");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Template method for the action phase of the cancel action of this wizard.
|
||||||
|
* <p>The default implementation throws a PortletException, saying that a cancel
|
||||||
|
* action request is not supported by this controller. Thus, you do not need to
|
||||||
|
* implement this template method if you do not support a cancel operation.
|
||||||
|
* @param request current portlet action request
|
||||||
|
* @param response current portlet action response
|
||||||
|
* @param command form object with the current wizard state
|
||||||
|
* @param errors Errors instance containing errors
|
||||||
|
* @throws Exception in case of invalid state or arguments
|
||||||
|
* @see #renderCancel
|
||||||
|
* @see org.springframework.validation.Errors
|
||||||
|
*/
|
||||||
|
protected void processCancel(
|
||||||
|
ActionRequest request, ActionResponse response, Object command, BindException errors)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
throw new PortletException(
|
||||||
|
"Wizard form controller class [" + getClass().getName() + "] does not support a cancel action request");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,663 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2008 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.mvc;
|
||||||
|
|
||||||
|
import javax.portlet.ActionRequest;
|
||||||
|
import javax.portlet.PortletException;
|
||||||
|
import javax.portlet.PortletRequest;
|
||||||
|
import javax.portlet.PortletSession;
|
||||||
|
import javax.portlet.RenderRequest;
|
||||||
|
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.beans.PropertyEditorRegistrar;
|
||||||
|
import org.springframework.validation.BindException;
|
||||||
|
import org.springframework.validation.BindingErrorProcessor;
|
||||||
|
import org.springframework.validation.MessageCodesResolver;
|
||||||
|
import org.springframework.validation.ValidationUtils;
|
||||||
|
import org.springframework.validation.Validator;
|
||||||
|
import org.springframework.web.bind.support.WebBindingInitializer;
|
||||||
|
import org.springframework.web.portlet.bind.PortletRequestDataBinder;
|
||||||
|
import org.springframework.web.portlet.context.PortletWebRequest;
|
||||||
|
import org.springframework.web.portlet.handler.PortletSessionRequiredException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Controller implementation which creates an object (the command object) on
|
||||||
|
* receipt of a request and attempts to populate this object with request parameters.</p>
|
||||||
|
*
|
||||||
|
* <p>This controller is the base for all controllers wishing to populate
|
||||||
|
* JavaBeans based on request parameters, validate the content of such
|
||||||
|
* JavaBeans using {@link Validator Validators} and use custom editors (in the form of
|
||||||
|
* {@link java.beans.PropertyEditor PropertyEditors}) to transform
|
||||||
|
* objects into strings and vice versa, for example. Three notions are mentioned here:</p>
|
||||||
|
*
|
||||||
|
* <p><b>Command class:</b><br>
|
||||||
|
* An instance of the command class will be created for each request and populated
|
||||||
|
* with request parameters. A command class can basically be any Java class; the only
|
||||||
|
* requirement is a no-arg constructor. The command class should preferably be a
|
||||||
|
* JavaBean in order to be able to populate bean properties with request parameters.</p>
|
||||||
|
*
|
||||||
|
* <p><b>Populating using request parameters and PropertyEditors:</b><br>
|
||||||
|
* Upon receiving a request, any BaseCommandController will attempt to fill the
|
||||||
|
* command object using the request parameters. This is done using the typical
|
||||||
|
* and well-known JavaBeans property notation. When a request parameter named
|
||||||
|
* <code>'firstName'</code> exists, the framework will attempt to call
|
||||||
|
* <code>setFirstName([value])</code> passing the value of the parameter. Nested properties
|
||||||
|
* are of course supported. For instance a parameter named <code>'address.city'</code>
|
||||||
|
* will result in a <code>getAddress().setCity([value])</code> call on the
|
||||||
|
* command class.</p>
|
||||||
|
*
|
||||||
|
* <p>It's important to realize that you are not limited to String arguments in
|
||||||
|
* your JavaBeans. Using the PropertyEditor-notion as supplied by the
|
||||||
|
* java.beans package, you will be able to transform Strings to Objects and
|
||||||
|
* the other way around. For instance <code>setLocale(Locale loc)</code> is
|
||||||
|
* perfectly possible for a request parameter named <code>locale</code> having
|
||||||
|
* a value of <code>en</code>, as long as you register the appropriate
|
||||||
|
* PropertyEditor in the Controller (see {@link #initBinder initBinder()}
|
||||||
|
* for more information on that matter).</p>
|
||||||
|
*
|
||||||
|
* <p><b>Validators:</b>
|
||||||
|
* After the controller has successfully populated the command object with
|
||||||
|
* parameters from the request, it will use any configured validators to
|
||||||
|
* validate the object. Validation results will be put in a
|
||||||
|
* {@link org.springframework.validation.Errors Errors} object which can be
|
||||||
|
* used in a View to render any input problems.</p>
|
||||||
|
*
|
||||||
|
* <p><b><a name="workflow">Workflow
|
||||||
|
* (<a href="AbstractController.html#workflow">and that defined by superclass</a>):</b><br>
|
||||||
|
* Since this class is an abstract base class for more specific implementation,
|
||||||
|
* it does not override the <code>handleRequestInternal()</code> methods and also has no
|
||||||
|
* actual workflow. Implementing classes like
|
||||||
|
* {@link AbstractFormController AbstractFormController},
|
||||||
|
* {@link AbstractCommandController AbstractCommandController},
|
||||||
|
* {@link SimpleFormController SimpleFormController} and
|
||||||
|
* {@link AbstractWizardFormController AbstractWizardFormController}
|
||||||
|
* provide actual functionality and workflow.
|
||||||
|
* More information on workflow performed by superclasses can be found
|
||||||
|
* <a href="AbstractController.html#workflow">here</a>.</p>
|
||||||
|
*
|
||||||
|
* <p><b><a name="config">Exposed configuration properties</a>
|
||||||
|
* (<a href="AbstractController.html#config">and those defined by superclass</a>):</b><br>
|
||||||
|
* <table border="1">
|
||||||
|
* <tr>
|
||||||
|
* <td><b>name</b></th>
|
||||||
|
* <td><b>default</b></td>
|
||||||
|
* <td><b>description</b></td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>commandName</td>
|
||||||
|
* <td>command</td>
|
||||||
|
* <td>the name to use when binding the instantiated command class
|
||||||
|
* to the request</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>commandClass</td>
|
||||||
|
* <td><i>null</i></td>
|
||||||
|
* <td>the class to use upon receiving a request and which to fill
|
||||||
|
* using the request parameters. What object is used and whether
|
||||||
|
* or not it should be created is defined by extending classes
|
||||||
|
* and their configuration properties and methods.</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>validators</td>
|
||||||
|
* <td><i>null</i></td>
|
||||||
|
* <td>Array of Validator beans. The validator will be called at appropriate
|
||||||
|
* places in the workflow of subclasses (have a look at those for more info)
|
||||||
|
* to validate the command object.</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>validator</td>
|
||||||
|
* <td><i>null</i></td>
|
||||||
|
* <td>Short-form property for setting only one Validator bean (usually passed in
|
||||||
|
* using a <ref bean="beanId"/> property.</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>validateOnBinding</td>
|
||||||
|
* <td>true</td>
|
||||||
|
* <td>Indicates whether or not to validate the command object after the
|
||||||
|
* object has been populated with request parameters.</td>
|
||||||
|
* </tr>
|
||||||
|
* </table>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>Thanks to Rainer Schmitz and Nick Lothian for their suggestions!
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public abstract class BaseCommandController extends AbstractController {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlike the servlet version of these classes, we have to deal with the
|
||||||
|
* two-phase nature of the portlet request. To do this, we need to pass
|
||||||
|
* forward the command object and the bind/validation errors that occured
|
||||||
|
* on the command object from the action phase to the render phase.
|
||||||
|
* The only direct way to pass things forward and preserve them for each
|
||||||
|
* render request is through render parameters, but these are limited to
|
||||||
|
* String objects and we need to pass more complicated objects. The only
|
||||||
|
* other way to do this is in the session. The bad thing about using the
|
||||||
|
* session is that we have no way of knowing when we are done re-rendering
|
||||||
|
* the request and so we don't know when we can remove the objects from
|
||||||
|
* the session. So we will end up polluting the session with old objects
|
||||||
|
* when we finally leave the render of this controller and move on to
|
||||||
|
* somthing else. To minimize the pollution, we will use a static string
|
||||||
|
* value as the session attribute name. At least this way we are only ever
|
||||||
|
* leaving one orphaned set behind. The methods that return these names
|
||||||
|
* can be overridden if you want to use a different method, but be aware
|
||||||
|
* of the session pollution that may occur.
|
||||||
|
*/
|
||||||
|
private static final String RENDER_COMMAND_SESSION_ATTRIBUTE =
|
||||||
|
"org.springframework.web.portlet.mvc.RenderCommand";
|
||||||
|
|
||||||
|
private static final String RENDER_ERRORS_SESSION_ATTRIBUTE =
|
||||||
|
"org.springframework.web.portlet.mvc.RenderErrors";
|
||||||
|
|
||||||
|
public static final String DEFAULT_COMMAND_NAME = "command";
|
||||||
|
|
||||||
|
|
||||||
|
private String commandName = DEFAULT_COMMAND_NAME;
|
||||||
|
|
||||||
|
private Class commandClass;
|
||||||
|
|
||||||
|
private Validator[] validators;
|
||||||
|
|
||||||
|
private boolean validateOnBinding = true;
|
||||||
|
|
||||||
|
private MessageCodesResolver messageCodesResolver;
|
||||||
|
|
||||||
|
private BindingErrorProcessor bindingErrorProcessor;
|
||||||
|
|
||||||
|
private PropertyEditorRegistrar[] propertyEditorRegistrars;
|
||||||
|
|
||||||
|
private WebBindingInitializer webBindingInitializer;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the name of the command in the model.
|
||||||
|
* The command object will be included in the model under this name.
|
||||||
|
*/
|
||||||
|
public final void setCommandName(String commandName) {
|
||||||
|
this.commandName = commandName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of the command in the model.
|
||||||
|
*/
|
||||||
|
public final String getCommandName() {
|
||||||
|
return this.commandName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the command class for this controller.
|
||||||
|
* An instance of this class gets populated and validated on each request.
|
||||||
|
*/
|
||||||
|
public final void setCommandClass(Class commandClass) {
|
||||||
|
this.commandClass = commandClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the command class for this controller.
|
||||||
|
*/
|
||||||
|
public final Class getCommandClass() {
|
||||||
|
return this.commandClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the primary Validator for this controller. The Validator
|
||||||
|
* must support the specified command class. If there are one
|
||||||
|
* or more existing validators set already when this method is
|
||||||
|
* called, only the specified validator will be kept. Use
|
||||||
|
* {@link #setValidators(Validator[])} to set multiple validators.
|
||||||
|
*/
|
||||||
|
public final void setValidator(Validator validator) {
|
||||||
|
this.validators = new Validator[] {validator};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the primary Validator for this controller.
|
||||||
|
*/
|
||||||
|
public final Validator getValidator() {
|
||||||
|
return (this.validators != null && this.validators.length > 0 ? this.validators[0] : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the Validators for this controller.
|
||||||
|
* The Validator must support the specified command class.
|
||||||
|
*/
|
||||||
|
public final void setValidators(Validator[] validators) {
|
||||||
|
this.validators = validators;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the Validators for this controller.
|
||||||
|
*/
|
||||||
|
public final Validator[] getValidators() {
|
||||||
|
return this.validators;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set if the Validator should get applied when binding.
|
||||||
|
*/
|
||||||
|
public final void setValidateOnBinding(boolean validateOnBinding) {
|
||||||
|
this.validateOnBinding = validateOnBinding;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return if the Validator should get applied when binding.
|
||||||
|
*/
|
||||||
|
public final boolean isValidateOnBinding() {
|
||||||
|
return this.validateOnBinding;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the strategy to use for resolving errors into message codes.
|
||||||
|
* Applies the given strategy to all data binders used by this controller.
|
||||||
|
* <p>Default is <code>null</code>, i.e. using the default strategy of the data binder.
|
||||||
|
* @see #createBinder
|
||||||
|
* @see org.springframework.validation.DataBinder#setMessageCodesResolver
|
||||||
|
*/
|
||||||
|
public final void setMessageCodesResolver(MessageCodesResolver messageCodesResolver) {
|
||||||
|
this.messageCodesResolver = messageCodesResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the strategy to use for resolving errors into message codes (if any).
|
||||||
|
*/
|
||||||
|
public final MessageCodesResolver getMessageCodesResolver() {
|
||||||
|
return this.messageCodesResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the strategy to use for processing binding errors, that is,
|
||||||
|
* required field errors and <code>PropertyAccessException</code>s.
|
||||||
|
* <p>Default is <code>null</code>, i.e. using the default strategy of
|
||||||
|
* the data binder.
|
||||||
|
* @see #createBinder
|
||||||
|
* @see org.springframework.validation.DataBinder#setBindingErrorProcessor
|
||||||
|
*/
|
||||||
|
public final void setBindingErrorProcessor(BindingErrorProcessor bindingErrorProcessor) {
|
||||||
|
this.bindingErrorProcessor = bindingErrorProcessor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the strategy to use for processing binding errors (if any).
|
||||||
|
*/
|
||||||
|
public final BindingErrorProcessor getBindingErrorProcessor() {
|
||||||
|
return this.bindingErrorProcessor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify a single PropertyEditorRegistrar to be applied
|
||||||
|
* to every DataBinder that this controller uses.
|
||||||
|
* <p>Allows for factoring out the registration of PropertyEditors
|
||||||
|
* to separate objects, as an alternative to <code>initBinder</code>.
|
||||||
|
* @see #initBinder
|
||||||
|
*/
|
||||||
|
public final void setPropertyEditorRegistrar(PropertyEditorRegistrar propertyEditorRegistrar) {
|
||||||
|
this.propertyEditorRegistrars = new PropertyEditorRegistrar[] {propertyEditorRegistrar};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify one or more PropertyEditorRegistrars to be applied
|
||||||
|
* to every DataBinder that this controller uses.
|
||||||
|
* <p>Allows for factoring out the registration of PropertyEditors
|
||||||
|
* to separate objects, as alternative to <code>initBinder</code>.
|
||||||
|
* @see #initBinder
|
||||||
|
*/
|
||||||
|
public final void setPropertyEditorRegistrars(PropertyEditorRegistrar[] propertyEditorRegistrars) {
|
||||||
|
this.propertyEditorRegistrars = propertyEditorRegistrars;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the PropertyEditorRegistrars (if any) to be applied
|
||||||
|
* to every DataBinder that this controller uses.
|
||||||
|
*/
|
||||||
|
public final PropertyEditorRegistrar[] getPropertyEditorRegistrars() {
|
||||||
|
return this.propertyEditorRegistrars;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify a WebBindingInitializer which will apply pre-configured
|
||||||
|
* configuration to every DataBinder that this controller uses.
|
||||||
|
* <p>Allows for factoring out the entire binder configuration
|
||||||
|
* to separate objects, as an alternative to {@link #initBinder}.
|
||||||
|
*/
|
||||||
|
public final void setWebBindingInitializer(WebBindingInitializer webBindingInitializer) {
|
||||||
|
this.webBindingInitializer = webBindingInitializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the WebBindingInitializer (if any) which will apply pre-configured
|
||||||
|
* configuration to every DataBinder that this controller uses.
|
||||||
|
*/
|
||||||
|
public final WebBindingInitializer getWebBindingInitializer() {
|
||||||
|
return this.webBindingInitializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected void initApplicationContext() {
|
||||||
|
if (this.validators != null) {
|
||||||
|
for (int i = 0; i < this.validators.length; i++) {
|
||||||
|
if (this.commandClass != null && !this.validators[i].supports(this.commandClass))
|
||||||
|
throw new IllegalArgumentException("Validator [" + this.validators[i] +
|
||||||
|
"] does not support command class [" +
|
||||||
|
this.commandClass.getName() + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a command object for the given request.
|
||||||
|
* <p>The default implementation calls {@link #createCommand()}.
|
||||||
|
* Subclasses can override this.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @return object command to bind onto
|
||||||
|
* @see #createCommand
|
||||||
|
*/
|
||||||
|
protected Object getCommand(PortletRequest request) throws Exception {
|
||||||
|
return createCommand();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new command instance for the command class of this controller.
|
||||||
|
* <p>This implementation uses <code>BeanUtils.instantiateClass</code>,
|
||||||
|
* so the command needs to have a no-arg constructor (supposed to be
|
||||||
|
* public, but not required to).
|
||||||
|
* @return the new command instance
|
||||||
|
* @throws Exception if the command object could not be instantiated
|
||||||
|
* @see org.springframework.beans.BeanUtils#instantiateClass(Class)
|
||||||
|
*/
|
||||||
|
protected final Object createCommand() throws Exception {
|
||||||
|
if (this.commandClass == null) {
|
||||||
|
throw new IllegalStateException("Cannot create command without commandClass being set - " +
|
||||||
|
"either set commandClass or (in a form controller) override formBackingObject");
|
||||||
|
}
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Creating new command of class [" + this.commandClass.getName() + "]");
|
||||||
|
}
|
||||||
|
return BeanUtils.instantiateClass(this.commandClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the given command object is a valid for this controller,
|
||||||
|
* i.e. its command class.
|
||||||
|
* @param command the command object to check
|
||||||
|
* @return if the command object is valid for this controller
|
||||||
|
*/
|
||||||
|
protected final boolean checkCommand(Object command) {
|
||||||
|
return (this.commandClass == null || this.commandClass.isInstance(command));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bind the parameters of the given request to the given command object.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param command the command to bind onto
|
||||||
|
* @return the PortletRequestDataBinder instance for additional custom validation
|
||||||
|
* @throws Exception in case of invalid state or arguments
|
||||||
|
*/
|
||||||
|
protected final PortletRequestDataBinder bindAndValidate(PortletRequest request, Object command)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
PortletRequestDataBinder binder = createBinder(request, command);
|
||||||
|
if (!suppressBinding(request)) {
|
||||||
|
binder.bind(request);
|
||||||
|
BindException errors = new BindException(binder.getBindingResult());
|
||||||
|
onBind(request, command, errors);
|
||||||
|
if (this.validators != null && isValidateOnBinding() && !suppressValidation(request)) {
|
||||||
|
for (int i = 0; i < this.validators.length; i++) {
|
||||||
|
ValidationUtils.invokeValidator(this.validators[i], command, errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onBindAndValidate(request, command, errors);
|
||||||
|
}
|
||||||
|
return binder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether to suppress binding for the given request.
|
||||||
|
* <p>The default implementation always returns <code>false</code>.
|
||||||
|
* Can be overridden in subclasses to suppress validation:
|
||||||
|
* for example, if a special request parameter is set.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @return whether to suppress binding for the given request
|
||||||
|
* @see #suppressValidation
|
||||||
|
*/
|
||||||
|
protected boolean suppressBinding(PortletRequest request) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new binder instance for the given command and request.
|
||||||
|
* <p>Called by <code>bindAndValidate</code>. Can be overridden to plug in
|
||||||
|
* custom PortletRequestDataBinder instances.
|
||||||
|
* <p>The default implementation creates a standard PortletRequestDataBinder and
|
||||||
|
* invokes <code>prepareBinder</code> and <code>initBinder</code>.
|
||||||
|
* <p>Note that neither <code>prepareBinder</code> nor <code>initBinder</code>
|
||||||
|
* will be invoked automatically if you override this method! Call those methods
|
||||||
|
* at appropriate points of your overridden method.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param command the command to bind onto
|
||||||
|
* @return the new binder instance
|
||||||
|
* @throws Exception in case of invalid state or arguments
|
||||||
|
* @see #bindAndValidate
|
||||||
|
* @see #prepareBinder
|
||||||
|
* @see #initBinder
|
||||||
|
*/
|
||||||
|
protected PortletRequestDataBinder createBinder(PortletRequest request, Object command)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
PortletRequestDataBinder binder = new PortletRequestDataBinder(command, getCommandName());
|
||||||
|
prepareBinder(binder);
|
||||||
|
initBinder(request, binder);
|
||||||
|
return binder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare the given binder, applying the specified MessageCodesResolver,
|
||||||
|
* BindingErrorProcessor and PropertyEditorRegistrars (if any).
|
||||||
|
* Called by <code>createBinder</code>.
|
||||||
|
* @param binder the new binder instance
|
||||||
|
* @see #createBinder
|
||||||
|
* @see #setMessageCodesResolver
|
||||||
|
* @see #setBindingErrorProcessor
|
||||||
|
*/
|
||||||
|
protected final void prepareBinder(PortletRequestDataBinder binder) {
|
||||||
|
if (useDirectFieldAccess()) {
|
||||||
|
binder.initDirectFieldAccess();
|
||||||
|
}
|
||||||
|
if (this.messageCodesResolver != null) {
|
||||||
|
binder.setMessageCodesResolver(this.messageCodesResolver);
|
||||||
|
}
|
||||||
|
if (this.bindingErrorProcessor != null) {
|
||||||
|
binder.setBindingErrorProcessor(this.bindingErrorProcessor);
|
||||||
|
}
|
||||||
|
if (this.propertyEditorRegistrars != null) {
|
||||||
|
for (int i = 0; i < this.propertyEditorRegistrars.length; i++) {
|
||||||
|
this.propertyEditorRegistrars[i].registerCustomEditors(binder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether to use direct field access instead of bean property access.
|
||||||
|
* Applied by <code>prepareBinder</code>.
|
||||||
|
* <p>The default is <code>false</code>. Can be overridden in subclasses.
|
||||||
|
* @see #prepareBinder
|
||||||
|
* @see org.springframework.validation.DataBinder#initDirectFieldAccess()
|
||||||
|
*/
|
||||||
|
protected boolean useDirectFieldAccess() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the given binder instance, for example with custom editors.
|
||||||
|
* Called by <code>createBinder</code>.
|
||||||
|
* <p>This method allows you to register custom editors for certain fields of your
|
||||||
|
* command class. For instance, you will be able to transform Date objects into a
|
||||||
|
* String pattern and back, in order to allow your JavaBeans to have Date properties
|
||||||
|
* and still be able to set and display them in an HTML interface.
|
||||||
|
* <p>The default implementation is empty.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param binder new binder instance
|
||||||
|
* @throws Exception in case of invalid state or arguments
|
||||||
|
* @see #createBinder
|
||||||
|
* @see org.springframework.validation.DataBinder#registerCustomEditor
|
||||||
|
* @see org.springframework.beans.propertyeditors.CustomDateEditor
|
||||||
|
*/
|
||||||
|
protected void initBinder(PortletRequest request, PortletRequestDataBinder binder) throws Exception {
|
||||||
|
if (this.webBindingInitializer != null) {
|
||||||
|
this.webBindingInitializer.initBinder(binder, new PortletWebRequest(request));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback for custom post-processing in terms of binding.
|
||||||
|
* Called on each submit, after standard binding but before validation.
|
||||||
|
* <p>The default implementation delegates to <code>onBind(request, command)</code>.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param command the command object to perform further binding on
|
||||||
|
* @param errors validation errors holder, allowing for additional
|
||||||
|
* custom registration of binding errors
|
||||||
|
* @throws Exception in case of invalid state or arguments
|
||||||
|
* @see #bindAndValidate
|
||||||
|
* @see #onBind(PortletRequest, Object)
|
||||||
|
*/
|
||||||
|
protected void onBind(PortletRequest request, Object command, BindException errors) throws Exception {
|
||||||
|
onBind(request, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback for custom post-processing in terms of binding.
|
||||||
|
* Called by the default implementation of the <code>onBind</code> version with
|
||||||
|
* all parameters, after standard binding but before validation.
|
||||||
|
* <p>The default implementation is empty.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param command the command object to perform further binding on
|
||||||
|
* @throws Exception in case of invalid state or arguments
|
||||||
|
* @see #onBind(PortletRequest, Object, BindException)
|
||||||
|
*/
|
||||||
|
protected void onBind(PortletRequest request, Object command) throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether to suppress validation for the given request.
|
||||||
|
* <p>The default implementation always returns <code>false</code>.
|
||||||
|
* Can be overridden in subclasses to suppress validation:
|
||||||
|
* for example, if a special request parameter is set.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @return whether to suppress validation for the given request
|
||||||
|
*/
|
||||||
|
protected boolean suppressValidation(PortletRequest request) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback for custom post-processing in terms of binding and validation.
|
||||||
|
* Called on each submit, after standard binding and validation,
|
||||||
|
* but before error evaluation.
|
||||||
|
* <p>The default implementation is empty.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param command the command object, still allowing for further binding
|
||||||
|
* @param errors validation errors holder, allowing for additional
|
||||||
|
* custom validation
|
||||||
|
* @throws Exception in case of invalid state or arguments
|
||||||
|
* @see #bindAndValidate
|
||||||
|
* @see org.springframework.validation.Errors
|
||||||
|
*/
|
||||||
|
protected void onBindAndValidate(PortletRequest request, Object command, BindException errors)
|
||||||
|
throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of the session attribute that holds
|
||||||
|
* the render phase command object for this form controller.
|
||||||
|
* @return the name of the render phase command object session attribute
|
||||||
|
* @see javax.portlet.PortletSession#getAttribute
|
||||||
|
*/
|
||||||
|
protected String getRenderCommandSessionAttributeName() {
|
||||||
|
return RENDER_COMMAND_SESSION_ATTRIBUTE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of the session attribute that holds
|
||||||
|
* the render phase command object for this form controller.
|
||||||
|
* @return the name of the render phase command object session attribute
|
||||||
|
* @see javax.portlet.PortletSession#getAttribute
|
||||||
|
*/
|
||||||
|
protected String getRenderErrorsSessionAttributeName() {
|
||||||
|
return RENDER_ERRORS_SESSION_ATTRIBUTE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the command object cached for the render phase.
|
||||||
|
* @see #getRenderErrors
|
||||||
|
* @see #getRenderCommandSessionAttributeName
|
||||||
|
* @see #setRenderCommandAndErrors
|
||||||
|
*/
|
||||||
|
protected final Object getRenderCommand(RenderRequest request) throws PortletException {
|
||||||
|
PortletSession session = request.getPortletSession(false);
|
||||||
|
if (session == null) {
|
||||||
|
throw new PortletSessionRequiredException("Could not obtain portlet session");
|
||||||
|
}
|
||||||
|
Object command = session.getAttribute(getRenderCommandSessionAttributeName());
|
||||||
|
if (command == null) {
|
||||||
|
throw new PortletSessionRequiredException("Could not obtain command object from portlet session");
|
||||||
|
}
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the bind and validation errors cached for the render phase.
|
||||||
|
* @see #getRenderCommand
|
||||||
|
* @see #getRenderErrorsSessionAttributeName
|
||||||
|
* @see #setRenderCommandAndErrors
|
||||||
|
*/
|
||||||
|
protected final BindException getRenderErrors(RenderRequest request) throws PortletException {
|
||||||
|
PortletSession session = request.getPortletSession(false);
|
||||||
|
if (session == null) {
|
||||||
|
throw new PortletSessionRequiredException("Could not obtain portlet session");
|
||||||
|
}
|
||||||
|
BindException errors = (BindException) session.getAttribute(getRenderErrorsSessionAttributeName());
|
||||||
|
if (errors == null) {
|
||||||
|
throw new PortletSessionRequiredException("Could not obtain errors object from portlet session");
|
||||||
|
}
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the command object and errors object for the render phase.
|
||||||
|
* @param request the current action request
|
||||||
|
* @param command the command object to preserve for the render phase
|
||||||
|
* @param errors the errors from binding and validation to preserve for the render phase
|
||||||
|
* @see #getRenderCommand
|
||||||
|
* @see #getRenderErrors
|
||||||
|
* @see #getRenderCommandSessionAttributeName
|
||||||
|
* @see #getRenderErrorsSessionAttributeName
|
||||||
|
*/
|
||||||
|
protected final void setRenderCommandAndErrors(
|
||||||
|
ActionRequest request, Object command, BindException errors) throws Exception {
|
||||||
|
|
||||||
|
logger.debug("Storing command and error objects in session for render phase");
|
||||||
|
PortletSession session = request.getPortletSession();
|
||||||
|
session.setAttribute(getRenderCommandSessionAttributeName(), command);
|
||||||
|
session.setAttribute(getRenderErrorsSessionAttributeName(), errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2006 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.mvc;
|
||||||
|
|
||||||
|
import javax.portlet.ActionRequest;
|
||||||
|
import javax.portlet.ActionResponse;
|
||||||
|
import javax.portlet.RenderRequest;
|
||||||
|
import javax.portlet.RenderResponse;
|
||||||
|
|
||||||
|
import org.springframework.web.portlet.ModelAndView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base portlet Controller interface, representing a component that receives
|
||||||
|
* RenderRequest/RenderResponse and ActionRequest/ActionResponse like a
|
||||||
|
* <code>Portlet</code> but is able to participate in an MVC workflow.
|
||||||
|
*
|
||||||
|
* <p>Any implementation of the portlet Controller interface should be a
|
||||||
|
* <i>reusable, threadsafe</i> class, capable of handling multiple
|
||||||
|
* portlet requests throughout the lifecycle of an application. To be able to
|
||||||
|
* configure Controller(s) in an easy way, Controllers are usually JavaBeans.</p>
|
||||||
|
*
|
||||||
|
* <p><b><a name="workflow">Workflow</a>:</b></p>
|
||||||
|
*
|
||||||
|
* <p>After the DispatcherPortlet has received a request and has done its work
|
||||||
|
* to resolve locales, themes and suchlike, it tries to resolve a
|
||||||
|
* Controller to handle that request, using a
|
||||||
|
* {@link org.springframework.web.portlet.HandlerMapping HandlerMapping}.
|
||||||
|
* When a Controller has been found, the
|
||||||
|
* {@link #handleRenderRequest handleRenderRequest} or {@link #handleActionRequest handleActionRequest}
|
||||||
|
* method will be invoked, which is responsible for handling the actual
|
||||||
|
* request and - if applicable - returning an appropriate ModelAndView.
|
||||||
|
* So actually, these method are the main entrypoint for the
|
||||||
|
* {@link org.springframework.web.portlet.DispatcherPortlet DispatcherPortlet}
|
||||||
|
* which delegates requests to controllers. These method - and also this interface -
|
||||||
|
* should preferrably not be implemented by custom controllers <i>directly</i>, since
|
||||||
|
* abstract controllers also provided by this package already provide a lot of
|
||||||
|
* functionality for typical use cases in portlet applications. A few examples of
|
||||||
|
* those controllers:
|
||||||
|
* {@link AbstractController AbstractController},
|
||||||
|
* {@link AbstractCommandController AbstractCommandController},
|
||||||
|
* {@link AbstractFormController AbstractFormController},
|
||||||
|
* {@link SimpleFormController SimpleFormController}.</p>
|
||||||
|
*
|
||||||
|
* <p>So basically any <i>direct</i> implementation of the Controller interface
|
||||||
|
* just handles RenderRequests/ActionRequests and should return a ModelAndView, to be
|
||||||
|
* further used by the DispatcherPortlet. Any additional functionality such as
|
||||||
|
* optional validation, form handling, etc should be obtained through extending
|
||||||
|
* one of the abstract controller classes mentioned above.</p>
|
||||||
|
*
|
||||||
|
* @author William G. Thompson, Jr.
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @since 2.0
|
||||||
|
* @see SimpleControllerHandlerAdapter
|
||||||
|
* @see AbstractController
|
||||||
|
* @see AbstractCommandController
|
||||||
|
* @see AbstractFormController
|
||||||
|
* @see SimpleFormController
|
||||||
|
* @see org.springframework.context.ApplicationContextAware
|
||||||
|
* @see org.springframework.context.ResourceLoaderAware
|
||||||
|
* @see org.springframework.web.portlet.context.PortletContextAware
|
||||||
|
*/
|
||||||
|
public interface Controller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process the action request. There is nothing to return.
|
||||||
|
* @param request current portlet action request
|
||||||
|
* @param response current portlet action response
|
||||||
|
* @throws Exception in case of errors
|
||||||
|
*/
|
||||||
|
void handleActionRequest(ActionRequest request, ActionResponse response) throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process the render request and return a ModelAndView object which the DispatcherPortlet
|
||||||
|
* will render. A <code>null</code> return value is not an error: It indicates that this
|
||||||
|
* object completed request processing itself, thus there is no ModelAndView to render.
|
||||||
|
* @param request current portlet render request
|
||||||
|
* @param response current portlet render response
|
||||||
|
* @return a ModelAndView to render, or null if handled directly
|
||||||
|
* @throws Exception in case of errors
|
||||||
|
*/
|
||||||
|
ModelAndView handleRenderRequest(RenderRequest request, RenderResponse response) throws Exception;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2007 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.mvc;
|
||||||
|
|
||||||
|
import javax.portlet.RenderRequest;
|
||||||
|
import javax.portlet.RenderResponse;
|
||||||
|
|
||||||
|
import org.springframework.web.portlet.ModelAndView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Trivial controller that always returns a named view. The view
|
||||||
|
* can be configured using an exposed configuration property. This
|
||||||
|
* controller offers an alternative to sending a request straight to a view
|
||||||
|
* such as a JSP. The advantage here is that the client is not exposed to
|
||||||
|
* the concrete view technology but rather just to the controller URL;
|
||||||
|
* the concrete view will be determined by the ViewResolver.</p>
|
||||||
|
*
|
||||||
|
* <p><b><a name="workflow">Workflow
|
||||||
|
* (<a href="AbstractController.html#workflow">and that defined by superclass</a>):</b><br>
|
||||||
|
* <ol>
|
||||||
|
* <li>Render request is received by the controller</li>
|
||||||
|
* <li>call to {@link #handleRenderRequestInternal handleRenderRequestInternal} which
|
||||||
|
* just returns the view, named by the configuration property
|
||||||
|
* <code>viewName</code>. Nothing more, nothing less</li>
|
||||||
|
* </ol>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>This controller does not handle action requests.</p>
|
||||||
|
*
|
||||||
|
* <p><b><a name="config">Exposed configuration properties</a>
|
||||||
|
* (<a href="AbstractController.html#config">and those defined by superclass</a>):</b><br>
|
||||||
|
* <table border="1">
|
||||||
|
* <tr>
|
||||||
|
* <td><b>name</b></td>
|
||||||
|
* <td><b>default</b></td>
|
||||||
|
* <td><b>description</b></td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>viewName</td>
|
||||||
|
* <td><i>null</i></td>
|
||||||
|
* <td>the name of the view the viewResolver will use to forward to
|
||||||
|
* (if this property is not set, an exception will be thrown during
|
||||||
|
* initialization)</td>
|
||||||
|
* </tr>
|
||||||
|
* </table>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public class ParameterizableViewController extends AbstractController {
|
||||||
|
|
||||||
|
private String viewName;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the name of the view to delegate to.
|
||||||
|
*/
|
||||||
|
public void setViewName(String viewName) {
|
||||||
|
this.viewName = viewName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of the view to delegate to.
|
||||||
|
*/
|
||||||
|
public String getViewName() {
|
||||||
|
return this.viewName;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void initApplicationContext() {
|
||||||
|
if (this.viewName == null) {
|
||||||
|
throw new IllegalArgumentException("Property 'viewName' is required");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a ModelAndView object with the specified view name.
|
||||||
|
*/
|
||||||
|
protected ModelAndView handleRenderRequestInternal(RenderRequest request, RenderResponse response)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
return new ModelAndView(getViewName());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2006 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.mvc;
|
||||||
|
|
||||||
|
import javax.portlet.ActionRequest;
|
||||||
|
import javax.portlet.ActionResponse;
|
||||||
|
import javax.portlet.PortletException;
|
||||||
|
import javax.portlet.RenderRequest;
|
||||||
|
import javax.portlet.RenderResponse;
|
||||||
|
|
||||||
|
import org.springframework.web.portlet.ModelAndView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Trivial controller that transforms the PortletMode to a view name.
|
||||||
|
* The advantage here is that the client is not exposed to
|
||||||
|
* the concrete view technology but rather just to the controller URL;
|
||||||
|
* the concrete view will be determined by the ViewResolver.</p>
|
||||||
|
*
|
||||||
|
* <p>Example: PortletMode.VIEW -> "view"</p>
|
||||||
|
*
|
||||||
|
* <p>This controller does not handle action requests.</p>
|
||||||
|
*
|
||||||
|
* @author William G. Thompson, Jr.
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public class PortletModeNameViewController implements Controller {
|
||||||
|
|
||||||
|
public void handleActionRequest(ActionRequest request, ActionResponse response) throws Exception {
|
||||||
|
throw new PortletException("PortletModeNameViewController does not handle action requests");
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModelAndView handleRenderRequest(RenderRequest request, RenderResponse response) {
|
||||||
|
return new ModelAndView(request.getPortletMode().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,208 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2006 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.mvc;
|
||||||
|
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
import javax.portlet.ActionRequest;
|
||||||
|
import javax.portlet.ActionResponse;
|
||||||
|
import javax.portlet.Portlet;
|
||||||
|
import javax.portlet.PortletConfig;
|
||||||
|
import javax.portlet.PortletContext;
|
||||||
|
import javax.portlet.RenderRequest;
|
||||||
|
import javax.portlet.RenderResponse;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.BeanNameAware;
|
||||||
|
import org.springframework.beans.factory.DisposableBean;
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
import org.springframework.web.portlet.ModelAndView;
|
||||||
|
import org.springframework.web.portlet.context.PortletConfigAware;
|
||||||
|
import org.springframework.web.portlet.context.PortletContextAware;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Controller} implementation that wraps a portlet instance which it manages
|
||||||
|
* internally. Such a wrapped portlet is not known outside of this controller;
|
||||||
|
* its entire lifecycle is covered here.
|
||||||
|
*
|
||||||
|
* <p>Useful to invoke an existing portlet via Spring's dispatching infrastructure,
|
||||||
|
* for example to apply Spring
|
||||||
|
* {@link org.springframework.web.portlet.HandlerInterceptor HandlerInterceptors}
|
||||||
|
* to its requests.
|
||||||
|
*
|
||||||
|
* <p><b>Example:</b>
|
||||||
|
*
|
||||||
|
* <pre><bean id="wrappingController" class="org.springframework.web.portlet.mvc.PortletWrappingController">
|
||||||
|
* <property name="portletClass">
|
||||||
|
* <value>org.springframework.web.portlet.sample.HelloWorldPortlet</value>
|
||||||
|
* </property>
|
||||||
|
* <property name="portletName">
|
||||||
|
* <value>hello-world</value>
|
||||||
|
* </property>
|
||||||
|
* <property name="initParameters">
|
||||||
|
* <props>
|
||||||
|
* <prop key="config">/WEB-INF/hello-world-portlet-config.xml</prop>
|
||||||
|
* </props>
|
||||||
|
* </property>
|
||||||
|
* </bean></pre>
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public class PortletWrappingController extends AbstractController
|
||||||
|
implements BeanNameAware, InitializingBean, DisposableBean, PortletContextAware, PortletConfigAware {
|
||||||
|
|
||||||
|
private boolean useSharedPortletConfig = true;
|
||||||
|
|
||||||
|
private PortletContext portletContext;
|
||||||
|
|
||||||
|
private PortletConfig portletConfig;
|
||||||
|
|
||||||
|
private Class portletClass;
|
||||||
|
|
||||||
|
private String portletName;
|
||||||
|
|
||||||
|
private Properties initParameters = new Properties();
|
||||||
|
|
||||||
|
private String beanName;
|
||||||
|
|
||||||
|
private Portlet portletInstance;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether to use the shared PortletConfig object passed in
|
||||||
|
* through <code>setPortletConfig</code>, if available.
|
||||||
|
* <p>Default is "true". Turn this setting to "false" to pass in
|
||||||
|
* a mock PortletConfig object with the bean name as portlet name,
|
||||||
|
* holding the current PortletContext.
|
||||||
|
* @see #setPortletConfig
|
||||||
|
*/
|
||||||
|
public void setUseSharedPortletConfig(boolean useSharedPortletConfig) {
|
||||||
|
this.useSharedPortletConfig = useSharedPortletConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPortletContext(PortletContext portletContext) {
|
||||||
|
this.portletContext = portletContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPortletConfig(PortletConfig portletConfig) {
|
||||||
|
this.portletConfig = portletConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the class of the Portlet to wrap.
|
||||||
|
* Needs to implement <code>javax.portlet.Portlet</code>.
|
||||||
|
* @see javax.portlet.Portlet
|
||||||
|
*/
|
||||||
|
public void setPortletClass(Class portletClass) {
|
||||||
|
this.portletClass = portletClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the name of the Portlet to wrap.
|
||||||
|
* Default is the bean name of this controller.
|
||||||
|
*/
|
||||||
|
public void setPortletName(String portletName) {
|
||||||
|
this.portletName = portletName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify init parameters for the portlet to wrap,
|
||||||
|
* as name-value pairs.
|
||||||
|
*/
|
||||||
|
public void setInitParameters(Properties initParameters) {
|
||||||
|
this.initParameters = initParameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBeanName(String name) {
|
||||||
|
this.beanName = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void afterPropertiesSet() throws Exception {
|
||||||
|
if (this.portletClass == null) {
|
||||||
|
throw new IllegalArgumentException("portletClass is required");
|
||||||
|
}
|
||||||
|
if (!Portlet.class.isAssignableFrom(this.portletClass)) {
|
||||||
|
throw new IllegalArgumentException("portletClass [" + this.portletClass.getName() +
|
||||||
|
"] needs to implement interface [javax.portlet.Portlet]");
|
||||||
|
}
|
||||||
|
if (this.portletName == null) {
|
||||||
|
this.portletName = this.beanName;
|
||||||
|
}
|
||||||
|
PortletConfig config = this.portletConfig;
|
||||||
|
if (config == null || !this.useSharedPortletConfig) {
|
||||||
|
config = new DelegatingPortletConfig();
|
||||||
|
}
|
||||||
|
this.portletInstance = (Portlet) this.portletClass.newInstance();
|
||||||
|
this.portletInstance.init(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected void handleActionRequestInternal(
|
||||||
|
ActionRequest request, ActionResponse response) throws Exception {
|
||||||
|
|
||||||
|
this.portletInstance.processAction(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ModelAndView handleRenderRequestInternal(
|
||||||
|
RenderRequest request, RenderResponse response) throws Exception {
|
||||||
|
|
||||||
|
this.portletInstance.render(request, response);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void destroy() {
|
||||||
|
this.portletInstance.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal implementation of the PortletConfig interface, to be passed
|
||||||
|
* to the wrapped portlet.
|
||||||
|
* <p>Delegates to {@link PortletWrappingController} fields
|
||||||
|
* and methods to provide init parameters and other environment info.
|
||||||
|
*/
|
||||||
|
private class DelegatingPortletConfig implements PortletConfig {
|
||||||
|
|
||||||
|
public String getPortletName() {
|
||||||
|
return portletName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PortletContext getPortletContext() {
|
||||||
|
return portletContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getInitParameter(String paramName) {
|
||||||
|
return initParameters.getProperty(paramName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Enumeration getInitParameterNames() {
|
||||||
|
return initParameters.keys();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourceBundle getResourceBundle(Locale locale) {
|
||||||
|
return (portletConfig != null ? portletConfig.getResourceBundle(locale) : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2006 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.mvc;
|
||||||
|
|
||||||
|
import javax.portlet.ActionRequest;
|
||||||
|
import javax.portlet.ActionResponse;
|
||||||
|
import javax.portlet.RenderRequest;
|
||||||
|
import javax.portlet.RenderResponse;
|
||||||
|
|
||||||
|
import org.springframework.web.portlet.HandlerAdapter;
|
||||||
|
import org.springframework.web.portlet.ModelAndView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter to use the Controller workflow interface with the generic DispatcherPortlet.
|
||||||
|
*
|
||||||
|
* <p>This is an SPI class, not used directly by application code.
|
||||||
|
*
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @since 2.0
|
||||||
|
* @see org.springframework.web.portlet.DispatcherPortlet
|
||||||
|
* @see Controller
|
||||||
|
*/
|
||||||
|
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
|
||||||
|
|
||||||
|
public boolean supports(Object handler) {
|
||||||
|
return (handler instanceof Controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleAction(ActionRequest request, ActionResponse response, Object handler)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
((Controller) handler).handleActionRequest(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModelAndView handleRender(RenderRequest request, RenderResponse response, Object handler)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
return ((Controller) handler).handleRenderRequest(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,555 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2008 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.mvc;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.portlet.ActionRequest;
|
||||||
|
import javax.portlet.ActionResponse;
|
||||||
|
import javax.portlet.PortletException;
|
||||||
|
import javax.portlet.PortletRequest;
|
||||||
|
import javax.portlet.RenderRequest;
|
||||||
|
import javax.portlet.RenderResponse;
|
||||||
|
|
||||||
|
import org.springframework.validation.BindException;
|
||||||
|
import org.springframework.validation.Errors;
|
||||||
|
import org.springframework.web.portlet.ModelAndView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Concrete FormController implementation that provides configurable
|
||||||
|
* form and success views, and an onSubmit chain for convenient overriding.
|
||||||
|
* Automatically resubmits to the form view in case of validation errors,
|
||||||
|
* and renders the success view in case of a valid submission.</p>
|
||||||
|
*
|
||||||
|
* <p>The workflow of this Controller does not differ much from the one described
|
||||||
|
* in the {@link AbstractFormController AbstractFormController}. The difference
|
||||||
|
* is that you do not need to implement {@link #showForm showForm},
|
||||||
|
* {@link #processFormSubmission processFormSubmission}, and
|
||||||
|
* {@link #renderFormSubmission renderFormSubmission}: A form view and a
|
||||||
|
* success view can be configured declaratively.</p>
|
||||||
|
*
|
||||||
|
* <p>This controller is different from it's servlet counterpart in that it must take
|
||||||
|
* into account the two phases of a portlet request: the action phase and the render
|
||||||
|
* phase. See the JSR-168 spec for more details on these two phases.
|
||||||
|
* Be especially aware that the action phase is called only once, but that the
|
||||||
|
* render phase will be called repeatedly by the portal -- it does this every time
|
||||||
|
* the page containing the portlet is updated, even if the activity is in some other
|
||||||
|
* portlet. The main difference in the methods in this class is that the
|
||||||
|
* <code>onSubmit</code> methods have all been split into <code>onSubmitAction</code>
|
||||||
|
* and <code>onSubmitRender</code> to account for the two phases.</p>
|
||||||
|
*
|
||||||
|
* <p><b><a name="workflow">Workflow
|
||||||
|
* (<a href="AbstractFormController.html#workflow">in addition to the superclass</a>):</b><br>
|
||||||
|
* <ol>
|
||||||
|
* <li>Call to {@link #processFormSubmission processFormSubmission} which inspects
|
||||||
|
* the {@link org.springframework.validation.Errors Errors} object to see if
|
||||||
|
* any errors have occurred during binding and validation.</li>
|
||||||
|
* <li>If errors occured, the controller will return the configured formView,
|
||||||
|
* showing the form again (possibly rendering according error messages).</li>
|
||||||
|
* <li>If {@link #isFormChangeRequest isFormChangeRequest} is overridden and returns
|
||||||
|
* true for the given request, the controller will return the formView too.
|
||||||
|
* In that case, the controller will also suppress validation. Before returning the formView,
|
||||||
|
* the controller will invoke {@link #onFormChange}, giving sub-classes a chance
|
||||||
|
* to make modification to the command object.
|
||||||
|
* This is intended for requests that change the structure of the form,
|
||||||
|
* which should not cause validation and show the form in any case.</li>
|
||||||
|
* <li>If no errors occurred, the controller will call
|
||||||
|
* {@link #onSubmitAction(ActionRequest, ActionResponse, Object, BindException) onSubmitAction}
|
||||||
|
* during the action phase and then {@link #onSubmitRender(RenderRequest, RenderResponse,
|
||||||
|
* Object, BindException) onSubmitRender} during the render phase, which in case of the
|
||||||
|
* default implementation delegate to {@link #onSubmitAction(Object, BindException)
|
||||||
|
* onSubmitAction} and {@link #onSubmitRender(Object, BindException) onSubmitRender}
|
||||||
|
* with just the command object.
|
||||||
|
* The default implementation of the latter method will return the configured
|
||||||
|
* <code>successView</code>. Consider just implementing {@link #doSubmitAction doSubmitAction}
|
||||||
|
* for simply performing a submit action during the action phase and then rendering
|
||||||
|
* the success view during the render phase.</li>
|
||||||
|
* </ol>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>The submit behavior can be customized by overriding one of the
|
||||||
|
* {@link #onSubmitAction onSubmitAction} or {@link #onSubmitRender onSubmitRender}
|
||||||
|
* methods. Submit actions can also perform custom validation if necessary
|
||||||
|
* (typically database-driven checks), calling {@link #showForm(RenderRequest,
|
||||||
|
* RenderResponse, BindException) showForm} in case of validation errors to show
|
||||||
|
* the form view again. You do not have to override both the <code>onSubmitAction</code> and
|
||||||
|
* <code>onSubmitRender</code> methods at a given level unless you truly have custom logic to
|
||||||
|
* perform in both.<p>
|
||||||
|
*
|
||||||
|
* <p><b>WARNING:</b> Make sure that any one-time system updates (such as database
|
||||||
|
* updates or file writes) are performed in either an {@link #onSubmitAction onSubmitAction}
|
||||||
|
* method or the {@link #doSubmitAction doSubmitAction} method. Logic in the
|
||||||
|
* {@link #onSubmitRender onSubmitRender} methods may be executed repeatedly by
|
||||||
|
* the portal whenever the page containing the portlet is updated.</p>
|
||||||
|
*
|
||||||
|
* <p><b><a name="config">Exposed configuration properties</a>
|
||||||
|
* (<a href="AbstractFormController.html#config">and those defined by superclass</a>):</b><br>
|
||||||
|
* <table border="1">
|
||||||
|
* <tr>
|
||||||
|
* <td><b>name</b></td>
|
||||||
|
* <td><b>default</b></td>
|
||||||
|
* <td><b>description</b></td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>formView</td>
|
||||||
|
* <td><i>null</i></td>
|
||||||
|
* <td>Indicates what view to use when the user asks for a new form
|
||||||
|
* or when validation errors have occurred on form submission.</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>successView</td>
|
||||||
|
* <td><i>null</i></td>
|
||||||
|
* <td>Indicates what view to use when successful form submissions have
|
||||||
|
* occurred. Such a success view could e.g. display a submission summary.
|
||||||
|
* More sophisticated actions can be implemented by overriding one of
|
||||||
|
* the {@link #onSubmitRender(Object) onSubmitRender()} methods.</td>
|
||||||
|
* </tr>
|
||||||
|
* <table>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>Parameters indicated with <code>setPassRenderParameters</code> will be
|
||||||
|
* preserved if the form has errors or if a form change request occurs.
|
||||||
|
* If there are render parameters you need in <code>onSubmitRender</code>,
|
||||||
|
* then you need to pass those forward from <code>onSubmitAction</code>.
|
||||||
|
*
|
||||||
|
* <p>Thanks to Rainer Schmitz and Nick Lothian for their suggestions!
|
||||||
|
*
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author Rob Harrop
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public class SimpleFormController extends AbstractFormController {
|
||||||
|
|
||||||
|
private String formView;
|
||||||
|
|
||||||
|
private String successView;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new SimpleFormController.
|
||||||
|
* <p>Subclasses should set the following properties, either in the constructor
|
||||||
|
* or via a BeanFactory: commandName, commandClass, sessionForm, formView,
|
||||||
|
* successView. Note that commandClass doesn't need to be set when overriding
|
||||||
|
* <code>formBackingObject</code>, as this determines the class anyway.
|
||||||
|
* @see #setCommandClass(Class)
|
||||||
|
* @see #setCommandName(String)
|
||||||
|
* @see #setSessionForm(boolean)
|
||||||
|
* @see #setFormView
|
||||||
|
* @see #setSuccessView
|
||||||
|
* @see #formBackingObject(PortletRequest)
|
||||||
|
*/
|
||||||
|
public SimpleFormController() {
|
||||||
|
// AbstractFormController sets default cache seconds to 0.
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the name of the view that should be used for form display.
|
||||||
|
*/
|
||||||
|
public final void setFormView(String formView) {
|
||||||
|
this.formView = formView;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of the view that should be used for form display.
|
||||||
|
*/
|
||||||
|
public final String getFormView() {
|
||||||
|
return this.formView;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the name of the view that should be shown on successful submit.
|
||||||
|
*/
|
||||||
|
public final void setSuccessView(String successView) {
|
||||||
|
this.successView = successView;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of the view that should be shown on successful submit.
|
||||||
|
*/
|
||||||
|
public final String getSuccessView() {
|
||||||
|
return this.successView;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This implementation shows the configured form view, delegating to the
|
||||||
|
* analogous showForm version with a controlModel argument.
|
||||||
|
* <p>Can be called within onSubmit implementations, to redirect back to the form
|
||||||
|
* in case of custom validation errors (i.e. not determined by the validator).
|
||||||
|
* <p>Can be overridden in subclasses to show a custom view, writing directly
|
||||||
|
* to the response or preparing the response before rendering a view.
|
||||||
|
* <p>If calling showForm with a custom control model in subclasses, it's preferable
|
||||||
|
* to override the analogous showForm version with a controlModel argument
|
||||||
|
* (which will handle both standard form showing and custom form showing then).
|
||||||
|
* @see #setFormView
|
||||||
|
* @see #showForm(RenderRequest, RenderResponse, BindException, Map)
|
||||||
|
*/
|
||||||
|
protected ModelAndView showForm(RenderRequest request, RenderResponse response, BindException errors)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
return showForm(request, response, errors, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This implementation shows the configured form view.
|
||||||
|
* <p>Can be called within onSubmit implementations, to redirect back to the form
|
||||||
|
* in case of custom validation errors (i.e. not determined by the validator).
|
||||||
|
* <p>Can be overridden in subclasses to show a custom view, writing directly
|
||||||
|
* to the response or preparing the response before rendering a view.
|
||||||
|
* @param request current render request
|
||||||
|
* @param errors validation errors holder
|
||||||
|
* @param controlModel model map containing controller-specific control data
|
||||||
|
* (e.g. current page in wizard-style controllers or special error message)
|
||||||
|
* @return the prepared form view
|
||||||
|
* @throws Exception in case of invalid state or arguments
|
||||||
|
* @see #setFormView
|
||||||
|
*/
|
||||||
|
protected ModelAndView showForm(RenderRequest request, RenderResponse response, BindException errors, Map controlModel)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
return showForm(request, errors, getFormView(), controlModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a reference data map for the given request and command,
|
||||||
|
* consisting of bean name/bean instance pairs as expected by ModelAndView.
|
||||||
|
* <p>The default implementation delegates to {@link #referenceData(PortletRequest)}.
|
||||||
|
* Subclasses can override this to set reference data used in the view.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param command form object with request parameters bound onto it
|
||||||
|
* @param errors validation errors holder
|
||||||
|
* @return a Map with reference data entries, or null if none
|
||||||
|
* @throws Exception in case of invalid state or arguments
|
||||||
|
* @see ModelAndView
|
||||||
|
*/
|
||||||
|
protected Map referenceData(PortletRequest request, Object command, Errors errors) throws Exception {
|
||||||
|
return referenceData(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a reference data map for the given request.
|
||||||
|
* Called by referenceData version with all parameters.
|
||||||
|
* <p>The default implementation returns <code>null</code>.
|
||||||
|
* Subclasses can override this to set reference data used in the view.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @return a Map with reference data entries, or null if none
|
||||||
|
* @throws Exception in case of invalid state or arguments
|
||||||
|
* @see #referenceData(PortletRequest, Object, Errors)
|
||||||
|
* @see ModelAndView
|
||||||
|
*/
|
||||||
|
protected Map referenceData(PortletRequest request) throws Exception {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This implementation calls <code>showForm</code> in case of errors,
|
||||||
|
* and delegates to <code>onSubmitRender<code>'s full version else.
|
||||||
|
* <p>This can only be overridden to check for an action that should be executed
|
||||||
|
* without respect to binding errors, like a cancel action. To just handle successful
|
||||||
|
* submissions without binding errors, override one of the <code>onSubmitRender</code>
|
||||||
|
* methods.
|
||||||
|
* @see #showForm(RenderRequest, RenderResponse, BindException)
|
||||||
|
* @see #onSubmitRender(RenderRequest, RenderResponse, Object, BindException)
|
||||||
|
* @see #onSubmitRender(Object, BindException)
|
||||||
|
* @see #onSubmitRender(Object)
|
||||||
|
* @see #processFormSubmission(ActionRequest, ActionResponse, Object, BindException)
|
||||||
|
*/
|
||||||
|
protected ModelAndView renderFormSubmission(RenderRequest request, RenderResponse response, Object command, BindException errors)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
if (errors.hasErrors() || isFormChangeRequest(request)) {
|
||||||
|
return showForm(request, response, errors);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return onSubmitRender(request, response, command, errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This implementation does nothing in case of errors,
|
||||||
|
* and delegates to <code>onSubmitAction</code>'s full version else.
|
||||||
|
* <p>This can only be overridden to check for an action that should be executed
|
||||||
|
* without respect to binding errors, like a cancel action. To just handle successful
|
||||||
|
* submissions without binding errors, override one of the <code>onSubmitAction</code>
|
||||||
|
* methods or <code>doSubmitAction</code>.
|
||||||
|
* @see #showForm
|
||||||
|
* @see #onSubmitAction(ActionRequest, ActionResponse, Object, BindException)
|
||||||
|
* @see #onSubmitAction(Object, BindException)
|
||||||
|
* @see #onSubmitAction(Object)
|
||||||
|
* @see #doSubmitAction(Object)
|
||||||
|
* @see #renderFormSubmission(RenderRequest, RenderResponse, Object, BindException)
|
||||||
|
*/
|
||||||
|
protected void processFormSubmission(
|
||||||
|
ActionRequest request, ActionResponse response, Object command, BindException errors)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
if (errors.hasErrors()) {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Data binding errors: " + errors.getErrorCount());
|
||||||
|
}
|
||||||
|
if (isRedirectAction()) {
|
||||||
|
setFormSubmit(response);
|
||||||
|
}
|
||||||
|
passRenderParameters(request, response);
|
||||||
|
}
|
||||||
|
else if (isFormChangeRequest(request)) {
|
||||||
|
logger.debug("Detected form change request -> routing request to onFormChange");
|
||||||
|
if (isRedirectAction()) {
|
||||||
|
setFormSubmit(response);
|
||||||
|
}
|
||||||
|
passRenderParameters(request, response);
|
||||||
|
onFormChange(request, response, command, errors);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logger.debug("No errors - processing submit");
|
||||||
|
onSubmitAction(request, response, command, errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This implementation delegates to {@link #isFormChangeRequest}:
|
||||||
|
* A form change request changes the appearance of the form
|
||||||
|
* and should not get validated but just show the new form.
|
||||||
|
* @see #isFormChangeRequest
|
||||||
|
*/
|
||||||
|
protected boolean suppressValidation(PortletRequest request) {
|
||||||
|
return isFormChangeRequest(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the given request is a form change request.
|
||||||
|
* A form change request changes the appearance of the form
|
||||||
|
* and should always show the new form, without validation.
|
||||||
|
* <p>Gets called by {@link #suppressValidation} and {@link #processFormSubmission}.
|
||||||
|
* Consequently, this single method determines to suppress validation
|
||||||
|
* <i>and</i> to show the form view in any case.
|
||||||
|
* <p>The default implementation returns <code>false</code.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @return whether the given request is a form change request
|
||||||
|
* @see #suppressValidation
|
||||||
|
* @see #processFormSubmission
|
||||||
|
*/
|
||||||
|
protected boolean isFormChangeRequest(PortletRequest request) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called during form submission if {@link #isFormChangeRequest(PortletRequest)}
|
||||||
|
* returns <code>true</code>. Allows subclasses to implement custom logic
|
||||||
|
* to modify the command object to directly modify data in the form.
|
||||||
|
* <p>The default implementation delegates to
|
||||||
|
* <code>onFormChange(request, response, command)</code>.
|
||||||
|
* @param request current action request
|
||||||
|
* @param response current action response
|
||||||
|
* @param command form object with request parameters bound onto it
|
||||||
|
* @param errors validation errors holder, allowing for additional
|
||||||
|
* custom validation
|
||||||
|
* @throws Exception in case of errors
|
||||||
|
* @see #isFormChangeRequest(PortletRequest)
|
||||||
|
* @see #onFormChange(ActionRequest, ActionResponse, Object)
|
||||||
|
*/
|
||||||
|
protected void onFormChange(ActionRequest request, ActionResponse response, Object command, BindException errors)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
onFormChange(request, response, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simpler <code>onFormChange</code> variant, called by the full version
|
||||||
|
* <code>onFormChange(request, response, command, errors)</code>.
|
||||||
|
* <p>The default implementation is empty.
|
||||||
|
* @param request current action request
|
||||||
|
* @param response current action response
|
||||||
|
* @param command form object with request parameters bound onto it
|
||||||
|
* @throws Exception in case of errors
|
||||||
|
* @see #onFormChange(ActionRequest, ActionResponse, Object, BindException)
|
||||||
|
*/
|
||||||
|
protected void onFormChange(ActionRequest request, ActionResponse response, Object command)
|
||||||
|
throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit render phase callback with all parameters. Called in case of submit without errors
|
||||||
|
* reported by the registered validator, or on every submit if no validator.
|
||||||
|
* <p>The default implementation delegates to {@link #onSubmitRender(Object, BindException)}.
|
||||||
|
* For simply performing a submit action and rendering the specified success view,
|
||||||
|
* do not implement an <code>onSubmitRender</code> at all.
|
||||||
|
* <p>Subclasses can override this to provide custom rendering to display results of
|
||||||
|
* the action phase. Implementations can also call <code>showForm</code> to return to the form
|
||||||
|
* if the <code>onSubmitAction</code> failed custom validation. Do <i>not</i> implement multiple
|
||||||
|
* <code>onSubmitRender</code> methods: In that case,
|
||||||
|
* just this method will be called by the controller.
|
||||||
|
* <p>Call <code>errors.getModel()</code> to populate the ModelAndView model
|
||||||
|
* with the command and the Errors instance, under the specified command name,
|
||||||
|
* as expected by the "spring:bind" tag.
|
||||||
|
* @param request current render request
|
||||||
|
* @param response current render response
|
||||||
|
* @param command form object with request parameters bound onto it
|
||||||
|
* @param errors Errors instance without errors (subclass can add errors if it wants to)
|
||||||
|
* @return the prepared model and view
|
||||||
|
* @throws Exception in case of errors
|
||||||
|
* @see #onSubmitAction(ActionRequest, ActionResponse, Object, BindException)
|
||||||
|
* @see #onSubmitRender(Object, BindException)
|
||||||
|
* @see #doSubmitAction
|
||||||
|
* @see #showForm
|
||||||
|
* @see org.springframework.validation.Errors
|
||||||
|
* @see org.springframework.validation.BindException#getModel
|
||||||
|
*/
|
||||||
|
protected ModelAndView onSubmitRender(RenderRequest request, RenderResponse response, Object command, BindException errors)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
return onSubmitRender(command, errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit action phase callback with all parameters. Called in case of submit without errors
|
||||||
|
* reported by the registered validator respectively on every submit if no validator.
|
||||||
|
* <p>The default implementation delegates to {@link #onSubmitAction(Object, BindException)}.
|
||||||
|
* For simply performing a submit action consider implementing <code>doSubmitAction</code>
|
||||||
|
* rather than an <code>onSubmitAction</code> version.
|
||||||
|
* <p>Subclasses can override this to provide custom submission handling like storing
|
||||||
|
* the object to the database. Implementations can also perform custom validation and
|
||||||
|
* signal the render phase to call <code>showForm</code> to return to the form. Do <i>not</i>
|
||||||
|
* implement multiple <code>onSubmitAction</code> methods: In that case,
|
||||||
|
* just this method will be called by the controller.
|
||||||
|
* @param request current action request
|
||||||
|
* @param response current action response
|
||||||
|
* @param command form object with request parameters bound onto it
|
||||||
|
* @param errors Errors instance without errors (subclass can add errors if it wants to)
|
||||||
|
* @throws Exception in case of errors
|
||||||
|
* @see #onSubmitRender(RenderRequest, RenderResponse, Object, BindException)
|
||||||
|
* @see #onSubmitAction(Object, BindException)
|
||||||
|
* @see #doSubmitAction
|
||||||
|
* @see org.springframework.validation.Errors
|
||||||
|
*/
|
||||||
|
protected void onSubmitAction(ActionRequest request, ActionResponse response, Object command, BindException errors)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
onSubmitAction(command, errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simpler <code>onSubmitRender</code> version. Called by the default implementation
|
||||||
|
* of the <code>onSubmitRender</code> version with all parameters.
|
||||||
|
* <p>The default implementation calls {@link #onSubmitRender(Object)}, using the
|
||||||
|
* returned ModelAndView if actually implemented in a subclass. Else, the
|
||||||
|
* default behavior will apply: rendering the success view with the command
|
||||||
|
* and Errors instance as model.
|
||||||
|
* <p>Subclasses can override this to provide custom submission handling that
|
||||||
|
* does not need request and response.
|
||||||
|
* <p>Call <code>errors.getModel()</code> to populate the ModelAndView model
|
||||||
|
* with the command and the Errors instance, under the specified command name,
|
||||||
|
* as expected by the "spring:bind" tag.
|
||||||
|
* @param command form object with request parameters bound onto it
|
||||||
|
* @param errors Errors instance without errors
|
||||||
|
* @return the prepared model and view, or null
|
||||||
|
* @throws Exception in case of errors
|
||||||
|
* @see #onSubmitRender(RenderRequest, RenderResponse, Object, BindException)
|
||||||
|
* @see #onSubmitRender(Object)
|
||||||
|
* @see #onSubmitAction(Object, BindException)
|
||||||
|
* @see #setSuccessView
|
||||||
|
* @see org.springframework.validation.Errors
|
||||||
|
* @see org.springframework.validation.BindException#getModel
|
||||||
|
*/
|
||||||
|
protected ModelAndView onSubmitRender(Object command, BindException errors) throws Exception {
|
||||||
|
ModelAndView mv = onSubmitRender(command);
|
||||||
|
if (mv != null) {
|
||||||
|
// simplest onSubmit version implemented in custom subclass
|
||||||
|
return mv;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// default behavior: render success view
|
||||||
|
if (getSuccessView() == null) {
|
||||||
|
throw new PortletException("successView isn't set");
|
||||||
|
}
|
||||||
|
return new ModelAndView(getSuccessView(), errors.getModel());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simpler <code>onSubmitAction</code> version. Called by the default implementation
|
||||||
|
* of the <code>onSubmitAction</code> version with all parameters.
|
||||||
|
* <p>The default implementation calls {@link #onSubmitAction(Object)}.
|
||||||
|
* <p>Subclasses can override this to provide custom submission handling that
|
||||||
|
* does not need request and response.
|
||||||
|
* @param command form object with request parameters bound onto it
|
||||||
|
* @param errors Errors instance without errors
|
||||||
|
* @throws Exception in case of errors
|
||||||
|
* @see #onSubmitAction(ActionRequest, ActionResponse, Object, BindException)
|
||||||
|
* @see #onSubmitAction(Object)
|
||||||
|
* @see #onSubmitRender(Object, BindException)
|
||||||
|
* @see org.springframework.validation.Errors
|
||||||
|
*/
|
||||||
|
protected void onSubmitAction(Object command, BindException errors) throws Exception {
|
||||||
|
onSubmitAction(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simplest <code>onSubmitRender</code> version. Called by the default implementation
|
||||||
|
* of the <code>onSubmitRender</code> version with command and BindException parameters.
|
||||||
|
* <p>This implementation returns null as ModelAndView, making the calling
|
||||||
|
* <code>onSubmitRender</code> method perform its default rendering of the success view.
|
||||||
|
* <p>Subclasses can override this to provide custom submission handling
|
||||||
|
* that just depends on the command object.
|
||||||
|
* @param command form object with request parameters bound onto it
|
||||||
|
* @return the prepared model and view, or null for default (i.e. successView)
|
||||||
|
* @throws Exception in case of errors
|
||||||
|
* @see #onSubmitRender(Object, BindException)
|
||||||
|
* @see #onSubmitAction(Object)
|
||||||
|
* @see #doSubmitAction
|
||||||
|
* @see #setSuccessView
|
||||||
|
*/
|
||||||
|
protected ModelAndView onSubmitRender(Object command) throws Exception {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simplest <code>onSubmitAction</code> version. Called by the default implementation
|
||||||
|
* of the <code>onSubmitAction</code> version with command and BindException parameters.
|
||||||
|
* <p>This implementation calls <code>doSubmitAction</code>.
|
||||||
|
* <p>Subclasses can override this to provide custom submission handling
|
||||||
|
* that just depends on the command object.
|
||||||
|
* @param command form object with request parameters bound onto it
|
||||||
|
* @throws Exception in case of errors
|
||||||
|
* @see #onSubmitAction(Object, BindException)
|
||||||
|
* @see #onSubmitRender(Object)
|
||||||
|
* @see #doSubmitAction
|
||||||
|
*/
|
||||||
|
protected void onSubmitAction(Object command) throws Exception {
|
||||||
|
doSubmitAction(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Template method for submit actions. Called by the default implementation
|
||||||
|
* of the simplest <code>onSubmitAction</code> version.
|
||||||
|
* <p><b>This is the preferred submit callback to implement if you want to
|
||||||
|
* perform an action (like storing changes to the database) and then render
|
||||||
|
* the success view with the command and Errors instance as model.</b>
|
||||||
|
* @param command form object with request parameters bound onto it
|
||||||
|
* @throws Exception in case of errors
|
||||||
|
* @see #onSubmitAction(Object)
|
||||||
|
* @see #onSubmitRender(Object)
|
||||||
|
* @see #setSuccessView
|
||||||
|
*/
|
||||||
|
protected void doSubmitAction(Object command) throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,606 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2008 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.mvc.annotation;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import javax.portlet.ActionRequest;
|
||||||
|
import javax.portlet.ActionResponse;
|
||||||
|
import javax.portlet.PortalContext;
|
||||||
|
import javax.portlet.PortletException;
|
||||||
|
import javax.portlet.PortletMode;
|
||||||
|
import javax.portlet.PortletPreferences;
|
||||||
|
import javax.portlet.PortletRequest;
|
||||||
|
import javax.portlet.PortletResponse;
|
||||||
|
import javax.portlet.PortletSession;
|
||||||
|
import javax.portlet.RenderRequest;
|
||||||
|
import javax.portlet.RenderResponse;
|
||||||
|
import javax.portlet.UnavailableException;
|
||||||
|
import javax.portlet.WindowState;
|
||||||
|
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.core.Conventions;
|
||||||
|
import org.springframework.core.GenericTypeResolver;
|
||||||
|
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||||
|
import org.springframework.core.MethodParameter;
|
||||||
|
import org.springframework.core.ParameterNameDiscoverer;
|
||||||
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
|
import org.springframework.core.style.StylerUtils;
|
||||||
|
import org.springframework.ui.ExtendedModelMap;
|
||||||
|
import org.springframework.ui.Model;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
|
import org.springframework.validation.support.BindingAwareModelMap;
|
||||||
|
import org.springframework.web.bind.WebDataBinder;
|
||||||
|
import org.springframework.web.bind.annotation.InitBinder;
|
||||||
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.SessionAttributes;
|
||||||
|
import org.springframework.web.bind.annotation.support.HandlerMethodInvoker;
|
||||||
|
import org.springframework.web.bind.annotation.support.HandlerMethodResolver;
|
||||||
|
import org.springframework.web.bind.support.DefaultSessionAttributeStore;
|
||||||
|
import org.springframework.web.bind.support.SessionAttributeStore;
|
||||||
|
import org.springframework.web.bind.support.WebArgumentResolver;
|
||||||
|
import org.springframework.web.bind.support.WebBindingInitializer;
|
||||||
|
import org.springframework.web.context.request.NativeWebRequest;
|
||||||
|
import org.springframework.web.portlet.HandlerAdapter;
|
||||||
|
import org.springframework.web.portlet.ModelAndView;
|
||||||
|
import org.springframework.web.portlet.bind.MissingPortletRequestParameterException;
|
||||||
|
import org.springframework.web.portlet.bind.PortletRequestDataBinder;
|
||||||
|
import org.springframework.web.portlet.context.PortletWebRequest;
|
||||||
|
import org.springframework.web.portlet.handler.PortletContentGenerator;
|
||||||
|
import org.springframework.web.portlet.handler.PortletSessionRequiredException;
|
||||||
|
import org.springframework.web.portlet.util.PortletUtils;
|
||||||
|
import org.springframework.web.servlet.View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of the {@link org.springframework.web.portlet.HandlerAdapter}
|
||||||
|
* interface that maps handler methods based on portlet modes, action/render phases
|
||||||
|
* and request parameters expressed through the {@link RequestMapping} annotation.
|
||||||
|
*
|
||||||
|
* <p>Supports request parameter binding through the {@link RequestParam} annotation.
|
||||||
|
* Also supports the {@link ModelAttribute} annotation for exposing model attribute
|
||||||
|
* values to the view, as well as {@link InitBinder} for binder initialization methods
|
||||||
|
* and {@link SessionAttributes} for automatic session management of specific attributes.
|
||||||
|
*
|
||||||
|
* <p>This adapter can be customized through various bean properties.
|
||||||
|
* A common use case is to apply shared binder initialization logic through
|
||||||
|
* a custom {@link #setWebBindingInitializer WebBindingInitializer}.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author Arjen Poutsma
|
||||||
|
* @since 2.5
|
||||||
|
* @see #setWebBindingInitializer
|
||||||
|
* @see #setSessionAttributeStore
|
||||||
|
*/
|
||||||
|
public class AnnotationMethodHandlerAdapter extends PortletContentGenerator implements HandlerAdapter {
|
||||||
|
|
||||||
|
private static final String IMPLICIT_MODEL_ATTRIBUTE = "org.springframework.web.portlet.mvc.ImplicitModel";
|
||||||
|
|
||||||
|
|
||||||
|
private WebBindingInitializer webBindingInitializer;
|
||||||
|
|
||||||
|
private SessionAttributeStore sessionAttributeStore = new DefaultSessionAttributeStore();
|
||||||
|
|
||||||
|
private int cacheSecondsForSessionAttributeHandlers = 0;
|
||||||
|
|
||||||
|
private boolean synchronizeOnSession = false;
|
||||||
|
|
||||||
|
private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
|
||||||
|
|
||||||
|
private WebArgumentResolver[] customArgumentResolvers;
|
||||||
|
|
||||||
|
private final Map<Class<?>, PortletHandlerMethodResolver> methodResolverCache =
|
||||||
|
new ConcurrentHashMap<Class<?>, PortletHandlerMethodResolver>();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify a WebBindingInitializer which will apply pre-configured
|
||||||
|
* configuration to every DataBinder that this controller uses.
|
||||||
|
*/
|
||||||
|
public void setWebBindingInitializer(WebBindingInitializer webBindingInitializer) {
|
||||||
|
this.webBindingInitializer = webBindingInitializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify the strategy to store session attributes with.
|
||||||
|
* <p>Default is {@link org.springframework.web.bind.support.DefaultSessionAttributeStore},
|
||||||
|
* storing session attributes in the PortletSession, using the same
|
||||||
|
* attribute name as in the model.
|
||||||
|
*/
|
||||||
|
public void setSessionAttributeStore(SessionAttributeStore sessionAttributeStore) {
|
||||||
|
Assert.notNull(sessionAttributeStore, "SessionAttributeStore must not be null");
|
||||||
|
this.sessionAttributeStore = sessionAttributeStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache content produced by <code>@SessionAttributes</code> annotated handlers
|
||||||
|
* for the given number of seconds. Default is 0, preventing caching completely.
|
||||||
|
* <p>In contrast to the "cacheSeconds" property which will apply to all general
|
||||||
|
* handlers (but not to <code>@SessionAttributes</code> annotated handlers), this
|
||||||
|
* setting will apply to <code>@SessionAttributes</code> annotated handlers only.
|
||||||
|
* @see #setCacheSeconds
|
||||||
|
* @see org.springframework.web.bind.annotation.SessionAttributes
|
||||||
|
*/
|
||||||
|
public void setCacheSecondsForSessionAttributeHandlers(int cacheSecondsForSessionAttributeHandlers) {
|
||||||
|
this.cacheSecondsForSessionAttributeHandlers = cacheSecondsForSessionAttributeHandlers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set if controller execution should be synchronized on the session,
|
||||||
|
* to serialize parallel invocations from the same client.
|
||||||
|
* <p>More specifically, the execution of each handler method will get
|
||||||
|
* synchronized if this flag is "true". The best available session mutex
|
||||||
|
* will be used for the synchronization; ideally, this will be a mutex
|
||||||
|
* exposed by HttpSessionMutexListener.
|
||||||
|
* <p>The session mutex is guaranteed to be the same object during
|
||||||
|
* the entire lifetime of the session, available under the key defined
|
||||||
|
* by the <code>SESSION_MUTEX_ATTRIBUTE</code> constant. It serves as a
|
||||||
|
* safe reference to synchronize on for locking on the current session.
|
||||||
|
* <p>In many cases, the PortletSession reference itself is a safe mutex
|
||||||
|
* as well, since it will always be the same object reference for the
|
||||||
|
* same active logical session. However, this is not guaranteed across
|
||||||
|
* different servlet containers; the only 100% safe way is a session mutex.
|
||||||
|
* @see org.springframework.web.util.HttpSessionMutexListener
|
||||||
|
* @see org.springframework.web.portlet.util.PortletUtils#getSessionMutex(javax.portlet.PortletSession)
|
||||||
|
*/
|
||||||
|
public void setSynchronizeOnSession(boolean synchronizeOnSession) {
|
||||||
|
this.synchronizeOnSession = synchronizeOnSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the ParameterNameDiscoverer to use for resolving method parameter
|
||||||
|
* names if needed (e.g. for default attribute names).
|
||||||
|
* <p>Default is a {@link org.springframework.core.LocalVariableTableParameterNameDiscoverer}.
|
||||||
|
*/
|
||||||
|
public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) {
|
||||||
|
this.parameterNameDiscoverer = parameterNameDiscoverer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a custom ArgumentResolvers to use for special method parameter types.
|
||||||
|
* Such a custom ArgumentResolver will kick in first, having a chance to
|
||||||
|
* resolve an argument value before the standard argument handling kicks in.
|
||||||
|
*/
|
||||||
|
public void setCustomArgumentResolver(WebArgumentResolver argumentResolver) {
|
||||||
|
this.customArgumentResolvers = new WebArgumentResolver[] {argumentResolver};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set one or more custom ArgumentResolvers to use for special method
|
||||||
|
* parameter types. Any such custom ArgumentResolver will kick in first,
|
||||||
|
* having a chance to resolve an argument value before the standard
|
||||||
|
* argument handling kicks in.
|
||||||
|
*/
|
||||||
|
public void setCustomArgumentResolvers(WebArgumentResolver[] argumentResolvers) {
|
||||||
|
this.customArgumentResolvers = argumentResolvers;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean supports(Object handler) {
|
||||||
|
return getMethodResolver(handler).hasHandlerMethods();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleAction(ActionRequest request, ActionResponse response, Object handler) throws Exception {
|
||||||
|
Object returnValue = doHandle(request, response, handler);
|
||||||
|
if (returnValue != null) {
|
||||||
|
throw new IllegalStateException("Invalid action method return value: " + returnValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModelAndView handleRender(RenderRequest request, RenderResponse response, Object handler) throws Exception {
|
||||||
|
checkAndPrepare(request, response);
|
||||||
|
return doHandle(request, response, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ModelAndView doHandle(PortletRequest request, PortletResponse response, Object handler) throws Exception {
|
||||||
|
ExtendedModelMap implicitModel = null;
|
||||||
|
|
||||||
|
if (request instanceof RenderRequest && response instanceof RenderResponse) {
|
||||||
|
RenderRequest renderRequest = (RenderRequest) request;
|
||||||
|
RenderResponse renderResponse = (RenderResponse) response;
|
||||||
|
// Detect implicit model from associated action phase.
|
||||||
|
if (renderRequest.getParameter(IMPLICIT_MODEL_ATTRIBUTE) != null) {
|
||||||
|
PortletSession session = request.getPortletSession(false);
|
||||||
|
if (session != null) {
|
||||||
|
implicitModel = (ExtendedModelMap) session.getAttribute(IMPLICIT_MODEL_ATTRIBUTE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (handler.getClass().getAnnotation(SessionAttributes.class) != null) {
|
||||||
|
// Always prevent caching in case of session attribute management.
|
||||||
|
checkAndPrepare(renderRequest, renderResponse, this.cacheSecondsForSessionAttributeHandlers);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Uses configured default cacheSeconds setting.
|
||||||
|
checkAndPrepare(renderRequest, renderResponse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (implicitModel == null) {
|
||||||
|
implicitModel = new BindingAwareModelMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute invokeHandlerMethod in synchronized block if required.
|
||||||
|
if (this.synchronizeOnSession) {
|
||||||
|
PortletSession session = request.getPortletSession(false);
|
||||||
|
if (session != null) {
|
||||||
|
Object mutex = PortletUtils.getSessionMutex(session);
|
||||||
|
synchronized (mutex) {
|
||||||
|
return invokeHandlerMethod(request, response, handler, implicitModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return invokeHandlerMethod(request, response, handler, implicitModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ModelAndView invokeHandlerMethod(
|
||||||
|
PortletRequest request, PortletResponse response, Object handler, ExtendedModelMap implicitModel)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
PortletWebRequest webRequest = new PortletWebRequest(request, response);
|
||||||
|
PortletHandlerMethodResolver methodResolver = getMethodResolver(handler);
|
||||||
|
Method handlerMethod = methodResolver.resolveHandlerMethod(request, response);
|
||||||
|
PortletHandlerMethodInvoker methodInvoker = new PortletHandlerMethodInvoker(methodResolver);
|
||||||
|
|
||||||
|
Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
|
||||||
|
ModelAndView mav = methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel);
|
||||||
|
methodInvoker.updateModelAttributes(
|
||||||
|
handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
|
||||||
|
|
||||||
|
// Expose implicit model for subsequent render phase.
|
||||||
|
if (response instanceof ActionResponse && !implicitModel.isEmpty()) {
|
||||||
|
ActionResponse actionResponse = (ActionResponse) response;
|
||||||
|
try {
|
||||||
|
actionResponse.setRenderParameter(IMPLICIT_MODEL_ATTRIBUTE, Boolean.TRUE.toString());
|
||||||
|
request.getPortletSession().setAttribute(IMPLICIT_MODEL_ATTRIBUTE, implicitModel);
|
||||||
|
}
|
||||||
|
catch (IllegalStateException ex) {
|
||||||
|
// Probably sendRedirect called... no need to expose model to render phase.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mav;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Template method for creating a new PortletRequestDataBinder instance.
|
||||||
|
* <p>The default implementation creates a standard PortletRequestDataBinder.
|
||||||
|
* This can be overridden for custom PortletRequestDataBinder subclasses.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param target the target object to bind onto (or <code>null</code>
|
||||||
|
* if the binder is just used to convert a plain parameter value)
|
||||||
|
* @param objectName the objectName of the target object
|
||||||
|
* @return the PortletRequestDataBinder instance to use
|
||||||
|
* @throws Exception in case of invalid state or arguments
|
||||||
|
* @see PortletRequestDataBinder#bind(javax.portlet.PortletRequest)
|
||||||
|
* @see PortletRequestDataBinder#convertIfNecessary(Object, Class, MethodParameter)
|
||||||
|
*/
|
||||||
|
protected PortletRequestDataBinder createBinder(
|
||||||
|
PortletRequest request, Object target, String objectName) throws Exception {
|
||||||
|
|
||||||
|
return new PortletRequestDataBinder(target, objectName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a HandlerMethodResolver for the given handler type.
|
||||||
|
*/
|
||||||
|
private PortletHandlerMethodResolver getMethodResolver(Object handler) {
|
||||||
|
Class handlerClass = ClassUtils.getUserClass(handler);
|
||||||
|
PortletHandlerMethodResolver resolver = this.methodResolverCache.get(handlerClass);
|
||||||
|
if (resolver == null) {
|
||||||
|
resolver = new PortletHandlerMethodResolver(handlerClass);
|
||||||
|
this.methodResolverCache.put(handlerClass, resolver);
|
||||||
|
}
|
||||||
|
return resolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class PortletHandlerMethodResolver extends HandlerMethodResolver {
|
||||||
|
|
||||||
|
public PortletHandlerMethodResolver(Class<?> handlerType) {
|
||||||
|
super(handlerType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Method resolveHandlerMethod(PortletRequest request, PortletResponse response) throws PortletException {
|
||||||
|
String lookupMode = request.getPortletMode().toString();
|
||||||
|
Map<RequestMappingInfo, Method> targetHandlerMethods = new LinkedHashMap<RequestMappingInfo, Method>();
|
||||||
|
for (Method handlerMethod : getHandlerMethods()) {
|
||||||
|
RequestMapping mapping = AnnotationUtils.findAnnotation(handlerMethod, RequestMapping.class);
|
||||||
|
RequestMappingInfo mappingInfo = new RequestMappingInfo();
|
||||||
|
mappingInfo.modes = mapping.value();
|
||||||
|
mappingInfo.params = mapping.params();
|
||||||
|
mappingInfo.action = isActionMethod(handlerMethod);
|
||||||
|
mappingInfo.render = isRenderMethod(handlerMethod);
|
||||||
|
boolean match = false;
|
||||||
|
if (mappingInfo.modes.length > 0) {
|
||||||
|
for (String mappedMode : mappingInfo.modes) {
|
||||||
|
if (mappedMode.equalsIgnoreCase(lookupMode)) {
|
||||||
|
if (checkParameters(request, response, mappingInfo)) {
|
||||||
|
match = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// No modes specified: parameter match sufficient.
|
||||||
|
match = checkParameters(request, response, mappingInfo);
|
||||||
|
}
|
||||||
|
if (match) {
|
||||||
|
Method oldMappedMethod = targetHandlerMethods.put(mappingInfo, handlerMethod);
|
||||||
|
if (oldMappedMethod != null && oldMappedMethod != handlerMethod) {
|
||||||
|
throw new IllegalStateException("Ambiguous handler methods mapped for portlet mode '" +
|
||||||
|
lookupMode + "': {" + oldMappedMethod + ", " + handlerMethod +
|
||||||
|
"}. If you intend to handle the same mode in multiple methods, then factor " +
|
||||||
|
"them out into a dedicated handler class with that mode mapped at the type level!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!targetHandlerMethods.isEmpty()) {
|
||||||
|
if (targetHandlerMethods.size() == 1) {
|
||||||
|
return targetHandlerMethods.values().iterator().next();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
RequestMappingInfo bestMappingMatch = null;
|
||||||
|
for (RequestMappingInfo mapping : targetHandlerMethods.keySet()) {
|
||||||
|
if (bestMappingMatch == null) {
|
||||||
|
bestMappingMatch = mapping;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ((bestMappingMatch.modes.length == 0 && mapping.modes.length > 0) ||
|
||||||
|
bestMappingMatch.params.length < mapping.params.length) {
|
||||||
|
bestMappingMatch = mapping;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return targetHandlerMethods.get(bestMappingMatch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new UnavailableException("No matching handler method found for portlet request: mode '" +
|
||||||
|
request.getPortletMode() + "', type '" + (response instanceof ActionResponse ? "action" : "render") +
|
||||||
|
"', parameters " + StylerUtils.style(request.getParameterMap()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkParameters(PortletRequest request, PortletResponse response, RequestMappingInfo mapping) {
|
||||||
|
if (response instanceof RenderResponse) {
|
||||||
|
if (mapping.action) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (response instanceof ActionResponse) {
|
||||||
|
if (mapping.render) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return PortletAnnotationMappingUtils.checkParameters(mapping.params, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isActionMethod(Method handlerMethod) {
|
||||||
|
if (!void.class.equals(handlerMethod.getReturnType())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (Class<?> argType : handlerMethod.getParameterTypes()) {
|
||||||
|
if (ActionRequest.class.isAssignableFrom(argType) || ActionResponse.class.isAssignableFrom(argType) ||
|
||||||
|
InputStream.class.isAssignableFrom(argType) || Reader.class.isAssignableFrom(argType)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isRenderMethod(Method handlerMethod) {
|
||||||
|
if (!void.class.equals(handlerMethod.getReturnType())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (Class<?> argType : handlerMethod.getParameterTypes()) {
|
||||||
|
if (RenderRequest.class.isAssignableFrom(argType) || RenderResponse.class.isAssignableFrom(argType) ||
|
||||||
|
OutputStream.class.isAssignableFrom(argType) || Writer.class.isAssignableFrom(argType)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private class PortletHandlerMethodInvoker extends HandlerMethodInvoker {
|
||||||
|
|
||||||
|
public PortletHandlerMethodInvoker(HandlerMethodResolver resolver) {
|
||||||
|
super(resolver, webBindingInitializer, sessionAttributeStore,
|
||||||
|
parameterNameDiscoverer, customArgumentResolvers);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void raiseMissingParameterException(String paramName, Class paramType) throws Exception {
|
||||||
|
throw new MissingPortletRequestParameterException(paramName, paramType.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void raiseSessionRequiredException(String message) throws Exception {
|
||||||
|
throw new PortletSessionRequiredException(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
return AnnotationMethodHandlerAdapter.this.createBinder(
|
||||||
|
(PortletRequest) webRequest.getNativeRequest(), target, objectName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doBind(NativeWebRequest webRequest, WebDataBinder binder, boolean failOnErrors)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
PortletRequestDataBinder servletBinder = (PortletRequestDataBinder) binder;
|
||||||
|
servletBinder.bind((PortletRequest) webRequest.getNativeRequest());
|
||||||
|
if (failOnErrors) {
|
||||||
|
servletBinder.closeNoCatch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object resolveStandardArgument(Class parameterType, NativeWebRequest webRequest)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
PortletRequest request = (PortletRequest) webRequest.getNativeRequest();
|
||||||
|
PortletResponse response = (PortletResponse) webRequest.getNativeResponse();
|
||||||
|
|
||||||
|
if (PortletRequest.class.isAssignableFrom(parameterType)) {
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
else if (PortletResponse.class.isAssignableFrom(parameterType)) {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
else if (PortletSession.class.isAssignableFrom(parameterType)) {
|
||||||
|
return request.getPortletSession();
|
||||||
|
}
|
||||||
|
else if (PortletPreferences.class.isAssignableFrom(parameterType)) {
|
||||||
|
return request.getPreferences();
|
||||||
|
}
|
||||||
|
else if (PortletMode.class.isAssignableFrom(parameterType)) {
|
||||||
|
return request.getPortletMode();
|
||||||
|
}
|
||||||
|
else if (WindowState.class.isAssignableFrom(parameterType)) {
|
||||||
|
return request.getWindowState();
|
||||||
|
}
|
||||||
|
else if (PortalContext.class.isAssignableFrom(parameterType)) {
|
||||||
|
return request.getPortalContext();
|
||||||
|
}
|
||||||
|
else if (Principal.class.isAssignableFrom(parameterType)) {
|
||||||
|
return request.getUserPrincipal();
|
||||||
|
}
|
||||||
|
else if (Locale.class.equals(parameterType)) {
|
||||||
|
return request.getLocale();
|
||||||
|
}
|
||||||
|
else if (InputStream.class.isAssignableFrom(parameterType)) {
|
||||||
|
if (!(request instanceof ActionRequest)) {
|
||||||
|
throw new IllegalStateException("InputStream can only get obtained for ActionRequest");
|
||||||
|
}
|
||||||
|
return ((ActionRequest) request).getPortletInputStream();
|
||||||
|
}
|
||||||
|
else if (Reader.class.isAssignableFrom(parameterType)) {
|
||||||
|
if (!(request instanceof ActionRequest)) {
|
||||||
|
throw new IllegalStateException("Reader can only get obtained for ActionRequest");
|
||||||
|
}
|
||||||
|
return ((ActionRequest) request).getReader();
|
||||||
|
}
|
||||||
|
else if (OutputStream.class.isAssignableFrom(parameterType)) {
|
||||||
|
if (!(response instanceof RenderResponse)) {
|
||||||
|
throw new IllegalStateException("OutputStream can only get obtained for RenderResponse");
|
||||||
|
}
|
||||||
|
return ((RenderResponse) response).getPortletOutputStream();
|
||||||
|
}
|
||||||
|
else if (Writer.class.isAssignableFrom(parameterType)) {
|
||||||
|
if (!(response instanceof RenderResponse)) {
|
||||||
|
throw new IllegalStateException("Writer can only get obtained for RenderResponse");
|
||||||
|
}
|
||||||
|
return ((RenderResponse) response).getWriter();
|
||||||
|
}
|
||||||
|
return super.resolveStandardArgument(parameterType, webRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public ModelAndView getModelAndView(
|
||||||
|
Method handlerMethod, Class handlerType, Object returnValue, ExtendedModelMap implicitModel) {
|
||||||
|
|
||||||
|
if (returnValue instanceof ModelAndView) {
|
||||||
|
ModelAndView mav = (ModelAndView) returnValue;
|
||||||
|
mav.getModelMap().mergeAttributes(implicitModel);
|
||||||
|
return mav;
|
||||||
|
}
|
||||||
|
else if (returnValue instanceof org.springframework.web.servlet.ModelAndView) {
|
||||||
|
org.springframework.web.servlet.ModelAndView smav = (org.springframework.web.servlet.ModelAndView) returnValue;
|
||||||
|
ModelAndView mav = (smav.isReference() ?
|
||||||
|
new ModelAndView(smav.getViewName(), smav.getModelMap()) :
|
||||||
|
new ModelAndView(smav.getView(), smav.getModelMap()));
|
||||||
|
mav.getModelMap().mergeAttributes(implicitModel);
|
||||||
|
return mav;
|
||||||
|
}
|
||||||
|
else if (returnValue instanceof Model) {
|
||||||
|
return new ModelAndView().addAllObjects(implicitModel).addAllObjects(((Model) returnValue).asMap());
|
||||||
|
}
|
||||||
|
else if (returnValue instanceof Map) {
|
||||||
|
return new ModelAndView().addAllObjects(implicitModel).addAllObjects((Map) returnValue);
|
||||||
|
}
|
||||||
|
else if (returnValue instanceof View) {
|
||||||
|
return new ModelAndView(returnValue).addAllObjects(implicitModel);
|
||||||
|
}
|
||||||
|
else if (returnValue instanceof String) {
|
||||||
|
return new ModelAndView((String) returnValue).addAllObjects(implicitModel);
|
||||||
|
}
|
||||||
|
else if (returnValue == null) {
|
||||||
|
// Either returned null or was 'void' return.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else if (!BeanUtils.isSimpleProperty(returnValue.getClass())) {
|
||||||
|
// Assume a single model attribute...
|
||||||
|
ModelAttribute attr = AnnotationUtils.findAnnotation(handlerMethod, ModelAttribute.class);
|
||||||
|
String attrName = (attr != null ? attr.value() : "");
|
||||||
|
ModelAndView mav = new ModelAndView().addAllObjects(implicitModel);
|
||||||
|
if ("".equals(attrName)) {
|
||||||
|
Class resolvedType = GenericTypeResolver.resolveReturnType(handlerMethod, handlerType);
|
||||||
|
attrName = Conventions.getVariableNameForReturnType(handlerMethod, resolvedType, returnValue);
|
||||||
|
}
|
||||||
|
return mav.addObject(attrName, returnValue);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new IllegalArgumentException("Invalid handler method return value: " + returnValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class RequestMappingInfo {
|
||||||
|
|
||||||
|
public String[] modes = new String[0];
|
||||||
|
|
||||||
|
public String[] params = new String[0];
|
||||||
|
|
||||||
|
private boolean action = false;
|
||||||
|
|
||||||
|
private boolean render = false;
|
||||||
|
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
RequestMappingInfo other = (RequestMappingInfo) obj;
|
||||||
|
return (this.action == other.action && this.render == other.render &&
|
||||||
|
Arrays.equals(this.modes, other.modes) && Arrays.equals(this.params, other.params));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int hashCode() {
|
||||||
|
return (Arrays.hashCode(this.modes) * 29 + Arrays.hashCode(this.params));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,202 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2008 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.mvc.annotation;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.portlet.PortletMode;
|
||||||
|
import javax.portlet.PortletRequest;
|
||||||
|
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.beans.factory.ListableBeanFactory;
|
||||||
|
import org.springframework.beans.factory.generic.GenericBeanFactoryAccessor;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.portlet.handler.AbstractMapBasedHandlerMapping;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of the {@link org.springframework.web.portlet.HandlerMapping}
|
||||||
|
* interface that maps handlers based on portlet modes expressed through the
|
||||||
|
* {@link RequestMapping} annotation at the type or method level.
|
||||||
|
*
|
||||||
|
* <p>Registered by default in {@link org.springframework.web.portlet.DispatcherPortlet}
|
||||||
|
* on Java 5+. <b>NOTE:</b> If you define custom HandlerMapping beans in your
|
||||||
|
* DispatcherPortlet context, you need to add a DefaultAnnotationHandlerMapping bean
|
||||||
|
* explicitly, since custom HandlerMapping beans replace the default mapping strategies.
|
||||||
|
* Defining a DefaultAnnotationHandlerMapping also allows for registering custom
|
||||||
|
* interceptors:
|
||||||
|
*
|
||||||
|
* <pre class="code">
|
||||||
|
* <bean class="org.springframework.web.portlet.mvc.annotation.DefaultAnnotationHandlerMapping">
|
||||||
|
* <property name="interceptors">
|
||||||
|
* ...
|
||||||
|
* </property>
|
||||||
|
* </bean></pre>
|
||||||
|
*
|
||||||
|
* Annotated controllers are usually marked with the {@link Controller} stereotype
|
||||||
|
* at the type level. This is not strictly necessary when {@link RequestMapping} is
|
||||||
|
* applied at the type level (since such a handler usually implements the
|
||||||
|
* {@link org.springframework.web.portlet.mvc.Controller} interface). However,
|
||||||
|
* {@link Controller} is required for detecting {@link RequestMapping} annotations
|
||||||
|
* at the method level.
|
||||||
|
*
|
||||||
|
* <p><b>NOTE:</b> Method-level mappings are only allowed to narrow the mapping
|
||||||
|
* expressed at the class level (if any). Portlet modes need to uniquely map onto
|
||||||
|
* specific handler beans, with any given portlet mode only allowed to be mapped
|
||||||
|
* onto one specific handler bean (not spread across multiple handler beans).
|
||||||
|
* It is strongly recommended to co-locate related handler methods into the same bean.
|
||||||
|
*
|
||||||
|
* <p>The {@link AnnotationMethodHandlerAdapter} is responsible for processing
|
||||||
|
* annotated handler methods, as mapped by this HandlerMapping. For
|
||||||
|
* {@link RequestMapping} at the type level, specific HandlerAdapters such as
|
||||||
|
* {@link org.springframework.web.portlet.mvc.SimpleControllerHandlerAdapter} apply.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 2.5
|
||||||
|
* @see RequestMapping
|
||||||
|
* @see AnnotationMethodHandlerAdapter
|
||||||
|
*/
|
||||||
|
public class DefaultAnnotationHandlerMapping extends AbstractMapBasedHandlerMapping {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls the <code>registerHandlers</code> method in addition
|
||||||
|
* to the superclass's initialization.
|
||||||
|
* @see #detectHandlers
|
||||||
|
*/
|
||||||
|
public void initApplicationContext() throws BeansException {
|
||||||
|
super.initApplicationContext();
|
||||||
|
detectHandlers();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register all handlers specified in the Portlet mode map for the corresponding modes.
|
||||||
|
* @throws org.springframework.beans.BeansException if the handler couldn't be registered
|
||||||
|
*/
|
||||||
|
protected void detectHandlers() throws BeansException {
|
||||||
|
ApplicationContext context = getApplicationContext();
|
||||||
|
String[] beanNames = context.getBeanNamesForType(Object.class);
|
||||||
|
for (String beanName : beanNames) {
|
||||||
|
Class<?> handlerType = context.getType(beanName);
|
||||||
|
ListableBeanFactory bf = (context instanceof ConfigurableApplicationContext ?
|
||||||
|
((ConfigurableApplicationContext) context).getBeanFactory() : context);
|
||||||
|
GenericBeanFactoryAccessor bfa = new GenericBeanFactoryAccessor(bf);
|
||||||
|
RequestMapping mapping = bfa.findAnnotationOnBean(beanName, RequestMapping.class);
|
||||||
|
if (mapping != null) {
|
||||||
|
String[] modeKeys = mapping.value();
|
||||||
|
String[] params = mapping.params();
|
||||||
|
boolean registerHandlerType = true;
|
||||||
|
if (modeKeys.length == 0 || params.length == 0) {
|
||||||
|
registerHandlerType = !detectHandlerMethods(handlerType, beanName, mapping);
|
||||||
|
}
|
||||||
|
if (registerHandlerType) {
|
||||||
|
ParameterMappingPredicate predicate = new ParameterMappingPredicate(params);
|
||||||
|
for (String modeKey : modeKeys) {
|
||||||
|
registerHandler(new PortletMode(modeKey), beanName, predicate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (AnnotationUtils.findAnnotation(handlerType, Controller.class) != null) {
|
||||||
|
detectHandlerMethods(handlerType, beanName, mapping);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derive portlet mode mappings from the handler's method-level mappings.
|
||||||
|
* @param handlerType the handler type to introspect
|
||||||
|
* @param beanName the name of the bean introspected
|
||||||
|
* @param typeMapping the type level mapping (if any)
|
||||||
|
* @return <code>true</code> if at least 1 handler method has been registered;
|
||||||
|
* <code>false</code> otherwise
|
||||||
|
*/
|
||||||
|
protected boolean detectHandlerMethods(Class handlerType, final String beanName, final RequestMapping typeMapping) {
|
||||||
|
final Set<Boolean> handlersRegistered = new HashSet<Boolean>(1);
|
||||||
|
ReflectionUtils.doWithMethods(handlerType, new ReflectionUtils.MethodCallback() {
|
||||||
|
public void doWith(Method method) {
|
||||||
|
RequestMapping mapping = method.getAnnotation(RequestMapping.class);
|
||||||
|
if (mapping != null) {
|
||||||
|
String[] modeKeys = mapping.value();
|
||||||
|
if (modeKeys.length == 0) {
|
||||||
|
if (typeMapping != null) {
|
||||||
|
modeKeys = typeMapping.value();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"No portlet mode mappings specified - neither at type nor method level");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String[] params = mapping.params();
|
||||||
|
if (typeMapping != null) {
|
||||||
|
PortletAnnotationMappingUtils.validateModeMapping(modeKeys, typeMapping.value());
|
||||||
|
params = StringUtils.mergeStringArrays(typeMapping.params(), params);
|
||||||
|
}
|
||||||
|
ParameterMappingPredicate predicate = new ParameterMappingPredicate(params);
|
||||||
|
for (String modeKey : modeKeys) {
|
||||||
|
registerHandler(new PortletMode(modeKey), beanName, predicate);
|
||||||
|
handlersRegistered.add(Boolean.TRUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return !handlersRegistered.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the current PortletMode as lookup key.
|
||||||
|
*/
|
||||||
|
protected Object getLookupKey(PortletRequest request) throws Exception {
|
||||||
|
return request.getPortletMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Predicate that matches against parameter conditions.
|
||||||
|
*/
|
||||||
|
private static class ParameterMappingPredicate implements PortletRequestMappingPredicate {
|
||||||
|
|
||||||
|
private final String[] params;
|
||||||
|
|
||||||
|
private ParameterMappingPredicate(String[] params) {
|
||||||
|
this.params = params;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean match(PortletRequest request) {
|
||||||
|
return PortletAnnotationMappingUtils.checkParameters(this.params, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int compareTo(Object other) {
|
||||||
|
if (other instanceof PortletRequestMappingPredicate) {
|
||||||
|
return new Integer(((ParameterMappingPredicate) other).params.length).compareTo(this.params.length);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return StringUtils.arrayToCommaDelimitedString(this.params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2008 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.mvc.annotation;
|
||||||
|
|
||||||
|
import javax.portlet.PortletRequest;
|
||||||
|
|
||||||
|
import org.springframework.util.ObjectUtils;
|
||||||
|
import org.springframework.web.portlet.util.PortletUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class for annotation-based request mapping.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 2.5.2
|
||||||
|
*/
|
||||||
|
abstract class PortletAnnotationMappingUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the given request matches the specified request methods.
|
||||||
|
* @param modes the mapped portlet modes to check
|
||||||
|
* @param typeLevelModes the type-level mode mappings to check against
|
||||||
|
*/
|
||||||
|
public static boolean validateModeMapping(String[] modes, String[] typeLevelModes) {
|
||||||
|
if (!ObjectUtils.isEmpty(modes)) {
|
||||||
|
for (String mode : modes) {
|
||||||
|
boolean match = false;
|
||||||
|
for (String typeLevelMode : typeLevelModes) {
|
||||||
|
if (mode.equalsIgnoreCase(typeLevelMode)) {
|
||||||
|
match = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!match) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the given request matches the specified parameter conditions.
|
||||||
|
* @param params the parameter conditions, following
|
||||||
|
* {@link org.springframework.web.bind.annotation.RequestMapping#params()}
|
||||||
|
* @param request the current HTTP request to check
|
||||||
|
*/
|
||||||
|
public static boolean checkParameters(String[] params, PortletRequest request) {
|
||||||
|
if (!ObjectUtils.isEmpty(params)) {
|
||||||
|
for (String param : params) {
|
||||||
|
int separator = param.indexOf('=');
|
||||||
|
if (separator == -1) {
|
||||||
|
if (param.startsWith("!")) {
|
||||||
|
if (PortletUtils.hasSubmitParameter(request, param.substring(1))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!PortletUtils.hasSubmitParameter(request, param)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
String key = param.substring(0, separator);
|
||||||
|
String value = param.substring(separator + 1);
|
||||||
|
if (!value.equals(request.getParameter(key))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
Support package for annotation-based Portlet MVC controllers.
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,33 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Standard controller implementations for the portlet MVC framework that
|
||||||
|
comes with Spring. Provides both abstract base classes and concrete
|
||||||
|
implementations for often seen use cases.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
A <code>Controller</code> - as defined in this package - is analogous to a Struts
|
||||||
|
<code>Action</code>. Usually <code>Controllers</code> are JavaBeans
|
||||||
|
to allow easy configuration using the {@link org.springframework.beans org.springframework.beans}
|
||||||
|
package. Controllers define the <code>C</code> from so-called MVC paradigm
|
||||||
|
and can be used in conjunction with the {@link org.springframework.web.portlet.ModelAndView ModelAndView}
|
||||||
|
to achieve interactive applications. The view might be represented by a
|
||||||
|
HTML interface, but, because of model and the controller being completely
|
||||||
|
independent of the view, PDF views are possible, as well as for instance Excel
|
||||||
|
views.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Especially useful to read, while getting into the Spring MVC framework
|
||||||
|
are the following:
|
||||||
|
<ul>
|
||||||
|
<li><a href="Controller.html">Controller</a></li>
|
||||||
|
<li><a href="SimpleFormController.html">BaseCommandController</a></li>
|
||||||
|
<li><a href="ParameterizableViewController.html">ParameterizableViewController</a></li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
Provides JSR-168 portlets that integrate with the application context
|
||||||
|
infrastructure, and the core interfaces and classes for the Portlet
|
||||||
|
variant of Spring's web MVC framework.
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2006 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.util;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
|
||||||
|
import javax.portlet.ActionRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple wrapper for a Portlet {@link javax.portlet.ActionRequest},
|
||||||
|
* delegating all calls to the underlying request.
|
||||||
|
*
|
||||||
|
* <p>(In the style of the Servlet API's {@link javax.servlet.http.HttpServletRequestWrapper}.)
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 2.0
|
||||||
|
* @see ActionRequestWrapper
|
||||||
|
* @see javax.servlet.http.HttpServletRequestWrapper
|
||||||
|
*/
|
||||||
|
public class ActionRequestWrapper extends PortletRequestWrapper implements ActionRequest {
|
||||||
|
|
||||||
|
/** Original request that we're delegating to */
|
||||||
|
private final ActionRequest actionRequest;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a ActionRequestWrapper for the given request.
|
||||||
|
* @param request the original request to wrap
|
||||||
|
* @throws IllegalArgumentException if the supplied <code>request</code> is <code>null</code>
|
||||||
|
*/
|
||||||
|
public ActionRequestWrapper(ActionRequest request) {
|
||||||
|
super(request);
|
||||||
|
this.actionRequest = request;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public InputStream getPortletInputStream() throws IOException {
|
||||||
|
return this.actionRequest.getPortletInputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCharacterEncoding(String enc) throws UnsupportedEncodingException {
|
||||||
|
this.actionRequest.setCharacterEncoding(enc);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BufferedReader getReader() throws IOException {
|
||||||
|
return this.actionRequest.getReader();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCharacterEncoding() {
|
||||||
|
return this.actionRequest.getCharacterEncoding();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContentType() {
|
||||||
|
return this.actionRequest.getContentType();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getContentLength() {
|
||||||
|
return this.actionRequest.getContentLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,197 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2006 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.util;
|
||||||
|
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.security.Principal;
|
||||||
|
|
||||||
|
import javax.portlet.PortalContext;
|
||||||
|
import javax.portlet.PortletMode;
|
||||||
|
import javax.portlet.PortletPreferences;
|
||||||
|
import javax.portlet.PortletRequest;
|
||||||
|
import javax.portlet.PortletSession;
|
||||||
|
import javax.portlet.WindowState;
|
||||||
|
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple wrapper for a {@link javax.portlet.PortletRequest}, delegating all
|
||||||
|
* calls to the underlying request.
|
||||||
|
*
|
||||||
|
* <p>(In the style of the Servlet API's {@link javax.servlet.ServletRequestWrapper}.)
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 2.0
|
||||||
|
* @see ActionRequestWrapper
|
||||||
|
* @see javax.servlet.ServletRequestWrapper
|
||||||
|
*/
|
||||||
|
public class PortletRequestWrapper implements PortletRequest {
|
||||||
|
|
||||||
|
/** Original request that we're delegating to */
|
||||||
|
private final PortletRequest portletRequest;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a PortletRequestWrapper for the given {@link javax.portlet.PortletRequest}.
|
||||||
|
* @param request the original {@link javax.portlet.PortletRequest} to wrap
|
||||||
|
* @throws IllegalArgumentException if the supplied <code>request</code> is <code>null</code>
|
||||||
|
*/
|
||||||
|
public PortletRequestWrapper(PortletRequest request) {
|
||||||
|
Assert.notNull(request, "Request is required");
|
||||||
|
this.portletRequest = request;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isWindowStateAllowed(WindowState state) {
|
||||||
|
return this.portletRequest.isWindowStateAllowed(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPortletModeAllowed(PortletMode mode) {
|
||||||
|
return this.portletRequest.isPortletModeAllowed(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PortletMode getPortletMode() {
|
||||||
|
return this.portletRequest.getPortletMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public WindowState getWindowState() {
|
||||||
|
return this.portletRequest.getWindowState();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PortletPreferences getPreferences() {
|
||||||
|
return this.portletRequest.getPreferences();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PortletSession getPortletSession() {
|
||||||
|
return this.portletRequest.getPortletSession();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PortletSession getPortletSession(boolean create) {
|
||||||
|
return this.portletRequest.getPortletSession(create);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProperty(String name) {
|
||||||
|
return this.portletRequest.getProperty(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Enumeration getProperties(String name) {
|
||||||
|
return this.portletRequest.getProperties(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Enumeration getPropertyNames() {
|
||||||
|
return this.portletRequest.getPropertyNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PortalContext getPortalContext() {
|
||||||
|
return this.portletRequest.getPortalContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAuthType() {
|
||||||
|
return this.portletRequest.getAuthType();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContextPath() {
|
||||||
|
return this.portletRequest.getContextPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRemoteUser() {
|
||||||
|
return this.portletRequest.getRemoteUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Principal getUserPrincipal() {
|
||||||
|
return this.portletRequest.getUserPrincipal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUserInRole(String role) {
|
||||||
|
return this.portletRequest.isUserInRole(role);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getAttribute(String name) {
|
||||||
|
return this.portletRequest.getAttribute(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Enumeration getAttributeNames() {
|
||||||
|
return this.portletRequest.getAttributeNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getParameter(String name) {
|
||||||
|
return this.portletRequest.getParameter(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Enumeration getParameterNames() {
|
||||||
|
return this.portletRequest.getParameterNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getParameterValues(String name) {
|
||||||
|
return this.portletRequest.getParameterValues(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map getParameterMap() {
|
||||||
|
return this.portletRequest.getParameterMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSecure() {
|
||||||
|
return this.portletRequest.isSecure();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttribute(String name, Object value) {
|
||||||
|
this.portletRequest.setAttribute(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeAttribute(String name) {
|
||||||
|
this.portletRequest.removeAttribute(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRequestedSessionId() {
|
||||||
|
return this.portletRequest.getRequestedSessionId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRequestedSessionIdValid() {
|
||||||
|
return this.portletRequest.isRequestedSessionIdValid();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getResponseContentType() {
|
||||||
|
return this.portletRequest.getResponseContentType();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Enumeration getResponseContentTypes() {
|
||||||
|
return this.portletRequest.getResponseContentTypes();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Locale getLocale() {
|
||||||
|
return this.portletRequest.getLocale();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Enumeration getLocales() {
|
||||||
|
return this.portletRequest.getLocales();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getScheme() {
|
||||||
|
return this.portletRequest.getScheme();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getServerName() {
|
||||||
|
return this.portletRequest.getServerName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getServerPort() {
|
||||||
|
return this.portletRequest.getServerPort();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,439 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2007 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed 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.springframework.web.portlet.util;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import javax.portlet.ActionRequest;
|
||||||
|
import javax.portlet.ActionResponse;
|
||||||
|
import javax.portlet.PortletContext;
|
||||||
|
import javax.portlet.PortletRequest;
|
||||||
|
import javax.portlet.PortletSession;
|
||||||
|
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.web.util.WebUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Miscellaneous utilities for portlet applications.
|
||||||
|
* Used by various framework classes.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @author William G. Thompson, Jr.
|
||||||
|
* @author John A. Lewis
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public abstract class PortletUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the temporary directory for the current web application,
|
||||||
|
* as provided by the portlet container.
|
||||||
|
* @param portletContext the portlet context of the web application
|
||||||
|
* @return the File representing the temporary directory
|
||||||
|
*/
|
||||||
|
public static File getTempDir(PortletContext portletContext) {
|
||||||
|
Assert.notNull(portletContext, "PortletContext must not be null");
|
||||||
|
return (File) portletContext.getAttribute(WebUtils.TEMP_DIR_CONTEXT_ATTRIBUTE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the real path of the given path within the web application,
|
||||||
|
* as provided by the portlet container.
|
||||||
|
* <p>Prepends a slash if the path does not already start with a slash,
|
||||||
|
* and throws a {@link java.io.FileNotFoundException} if the path cannot
|
||||||
|
* be resolved to a resource (in contrast to
|
||||||
|
* {@link javax.portlet.PortletContext#getRealPath PortletContext's <code>getRealPath</code>},
|
||||||
|
* which simply returns <code>null</code>).
|
||||||
|
* @param portletContext the portlet context of the web application
|
||||||
|
* @param path the relative path within the web application
|
||||||
|
* @return the corresponding real path
|
||||||
|
* @throws FileNotFoundException if the path cannot be resolved to a resource
|
||||||
|
* @see javax.portlet.PortletContext#getRealPath
|
||||||
|
*/
|
||||||
|
public static String getRealPath(PortletContext portletContext, String path) throws FileNotFoundException {
|
||||||
|
Assert.notNull(portletContext, "PortletContext must not be null");
|
||||||
|
// Interpret location as relative to the web application root directory.
|
||||||
|
if (!path.startsWith("/")) {
|
||||||
|
path = "/" + path;
|
||||||
|
}
|
||||||
|
String realPath = portletContext.getRealPath(path);
|
||||||
|
if (realPath == null) {
|
||||||
|
throw new FileNotFoundException(
|
||||||
|
"PortletContext resource [" + path + "] cannot be resolved to absolute file path - " +
|
||||||
|
"web application archive not expanded?");
|
||||||
|
}
|
||||||
|
return realPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the given request for a session attribute of the given name under the
|
||||||
|
* {@link javax.portlet.PortletSession#PORTLET_SCOPE}.
|
||||||
|
* Returns <code>null</code> if there is no session or if the session has no such attribute in that scope.
|
||||||
|
* Does not create a new session if none has existed before!
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the session attribute
|
||||||
|
* @return the value of the session attribute, or <code>null</code> if not found
|
||||||
|
*/
|
||||||
|
public static Object getSessionAttribute(PortletRequest request, String name) {
|
||||||
|
return getSessionAttribute(request, name, PortletSession.PORTLET_SCOPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the given request for a session attribute of the given name in the given scope.
|
||||||
|
* Returns <code>null</code> if there is no session or if the session has no such attribute in that scope.
|
||||||
|
* Does not create a new session if none has existed before!
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the session attribute
|
||||||
|
* @param scope session scope of this attribute
|
||||||
|
* @return the value of the session attribute, or <code>null</code> if not found
|
||||||
|
*/
|
||||||
|
public static Object getSessionAttribute(PortletRequest request, String name, int scope) {
|
||||||
|
Assert.notNull(request, "Request must not be null");
|
||||||
|
PortletSession session = request.getPortletSession(false);
|
||||||
|
return (session != null ? session.getAttribute(name, scope) : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the given request for a session attribute of the given name
|
||||||
|
* under the {@link javax.portlet.PortletSession#PORTLET_SCOPE}.
|
||||||
|
* Throws an exception if there is no session or if the session has
|
||||||
|
* no such attribute in that scope.
|
||||||
|
* <p>Does not create a new session if none has existed before!
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the session attribute
|
||||||
|
* @return the value of the session attribute
|
||||||
|
* @throws IllegalStateException if the session attribute could not be found
|
||||||
|
*/
|
||||||
|
public static Object getRequiredSessionAttribute(PortletRequest request, String name)
|
||||||
|
throws IllegalStateException {
|
||||||
|
|
||||||
|
return getRequiredSessionAttribute(request, name, PortletSession.PORTLET_SCOPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the given request for a session attribute of the given name in the given scope.
|
||||||
|
* Throws an exception if there is no session or if the session has no such attribute
|
||||||
|
* in that scope.
|
||||||
|
* <p>Does not create a new session if none has existed before!
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the session attribute
|
||||||
|
* @param scope session scope of this attribute
|
||||||
|
* @return the value of the session attribute
|
||||||
|
* @throws IllegalStateException if the session attribute could not be found
|
||||||
|
*/
|
||||||
|
public static Object getRequiredSessionAttribute(PortletRequest request, String name, int scope)
|
||||||
|
throws IllegalStateException {
|
||||||
|
Object attr = getSessionAttribute(request, name, scope);
|
||||||
|
if (attr == null) {
|
||||||
|
throw new IllegalStateException("No session attribute '" + name + "' found");
|
||||||
|
}
|
||||||
|
return attr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the session attribute with the given name to the given value under the {@link javax.portlet.PortletSession#PORTLET_SCOPE}.
|
||||||
|
* Removes the session attribute if value is <code>null</code>, if a session existed at all.
|
||||||
|
* Does not create a new session if not necessary!
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the session attribute
|
||||||
|
* @param value the value of the session attribute
|
||||||
|
*/
|
||||||
|
public static void setSessionAttribute(PortletRequest request, String name, Object value) {
|
||||||
|
setSessionAttribute(request, name, value, PortletSession.PORTLET_SCOPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the session attribute with the given name to the given value in the given scope.
|
||||||
|
* Removes the session attribute if value is <code>null</code>, if a session existed at all.
|
||||||
|
* Does not create a new session if not necessary!
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name the name of the session attribute
|
||||||
|
* @param value the value of the session attribute
|
||||||
|
* @param scope session scope of this attribute
|
||||||
|
*/
|
||||||
|
public static void setSessionAttribute(PortletRequest request, String name, Object value, int scope) {
|
||||||
|
Assert.notNull(request, "Request must not be null");
|
||||||
|
if (value != null) {
|
||||||
|
request.getPortletSession().setAttribute(name, value, scope);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
PortletSession session = request.getPortletSession(false);
|
||||||
|
if (session != null) {
|
||||||
|
session.removeAttribute(name, scope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the specified session attribute under the {@link javax.portlet.PortletSession#PORTLET_SCOPE},
|
||||||
|
* creating and setting a new attribute if no existing found. The given class
|
||||||
|
* needs to have a public no-arg constructor.
|
||||||
|
* Useful for on-demand state objects in a web tier, like shopping carts.
|
||||||
|
* @param session current portlet session
|
||||||
|
* @param name the name of the session attribute
|
||||||
|
* @param clazz the class to instantiate for a new attribute
|
||||||
|
* @return the value of the session attribute, newly created if not found
|
||||||
|
* @throws IllegalArgumentException if the session attribute could not be instantiated
|
||||||
|
*/
|
||||||
|
public static Object getOrCreateSessionAttribute(PortletSession session, String name, Class clazz)
|
||||||
|
throws IllegalArgumentException {
|
||||||
|
|
||||||
|
return getOrCreateSessionAttribute(session, name, clazz, PortletSession.PORTLET_SCOPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the specified session attribute in the given scope,
|
||||||
|
* creating and setting a new attribute if no existing found. The given class
|
||||||
|
* needs to have a public no-arg constructor.
|
||||||
|
* Useful for on-demand state objects in a web tier, like shopping carts.
|
||||||
|
* @param session current portlet session
|
||||||
|
* @param name the name of the session attribute
|
||||||
|
* @param clazz the class to instantiate for a new attribute
|
||||||
|
* @param scope the session scope of this attribute
|
||||||
|
* @return the value of the session attribute, newly created if not found
|
||||||
|
* @throws IllegalArgumentException if the session attribute could not be instantiated
|
||||||
|
*/
|
||||||
|
public static Object getOrCreateSessionAttribute(PortletSession session, String name, Class clazz, int scope)
|
||||||
|
throws IllegalArgumentException {
|
||||||
|
|
||||||
|
Assert.notNull(session, "Session must not be null");
|
||||||
|
Object sessionObject = session.getAttribute(name, scope);
|
||||||
|
if (sessionObject == null) {
|
||||||
|
Assert.notNull(clazz, "Class must not be null if attribute value is to be instantiated");
|
||||||
|
try {
|
||||||
|
sessionObject = clazz.newInstance();
|
||||||
|
}
|
||||||
|
catch (InstantiationException ex) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Could not instantiate class [" + clazz.getName() +
|
||||||
|
"] for session attribute '" + name + "': " + ex.getMessage());
|
||||||
|
}
|
||||||
|
catch (IllegalAccessException ex) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Could not access default constructor of class [" + clazz.getName() +
|
||||||
|
"] for session attribute '" + name + "': " + ex.getMessage());
|
||||||
|
}
|
||||||
|
session.setAttribute(name, sessionObject, scope);
|
||||||
|
}
|
||||||
|
return sessionObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the best available mutex for the given session:
|
||||||
|
* that is, an object to synchronize on for the given session.
|
||||||
|
* <p>Returns the session mutex attribute if available; usually,
|
||||||
|
* this means that the
|
||||||
|
* {@link org.springframework.web.util.HttpSessionMutexListener}
|
||||||
|
* needs to be defined in <code>web.xml</code>. Falls back to the
|
||||||
|
* {@link javax.portlet.PortletSession} itself if no mutex attribute found.
|
||||||
|
* <p>The session mutex is guaranteed to be the same object during
|
||||||
|
* the entire lifetime of the session, available under the key defined
|
||||||
|
* by the {@link org.springframework.web.util.WebUtils#SESSION_MUTEX_ATTRIBUTE}
|
||||||
|
* constant. It serves as a safe reference to synchronize on for locking
|
||||||
|
* on the current session.
|
||||||
|
* <p>In many cases, the {@link javax.portlet.PortletSession} reference
|
||||||
|
* itself is a safe mutex as well, since it will always be the same
|
||||||
|
* object reference for the same active logical session. However, this is
|
||||||
|
* not guaranteed across different servlet containers; the only 100% safe
|
||||||
|
* way is a session mutex.
|
||||||
|
* @param session the HttpSession to find a mutex for
|
||||||
|
* @return the mutex object (never <code>null</code>)
|
||||||
|
* @see org.springframework.web.util.WebUtils#SESSION_MUTEX_ATTRIBUTE
|
||||||
|
* @see org.springframework.web.util.HttpSessionMutexListener
|
||||||
|
*/
|
||||||
|
public static Object getSessionMutex(PortletSession session) {
|
||||||
|
Assert.notNull(session, "Session must not be null");
|
||||||
|
Object mutex = session.getAttribute(WebUtils.SESSION_MUTEX_ATTRIBUTE);
|
||||||
|
if (mutex == null) {
|
||||||
|
mutex = session;
|
||||||
|
}
|
||||||
|
return mutex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expose the given Map as request attributes, using the keys as attribute names
|
||||||
|
* and the values as corresponding attribute values. Keys must be Strings.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param attributes the attributes Map
|
||||||
|
*/
|
||||||
|
public static void exposeRequestAttributes(PortletRequest request, Map attributes) {
|
||||||
|
Assert.notNull(request, "Request must not be null");
|
||||||
|
Assert.notNull(attributes, "attributes Map must not be null");
|
||||||
|
Iterator it = attributes.entrySet().iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
Map.Entry entry = (Map.Entry) it.next();
|
||||||
|
if (!(entry.getKey() instanceof String)) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Invalid key [" + entry.getKey() + "] in attributes Map - only Strings allowed as attribute keys");
|
||||||
|
}
|
||||||
|
request.setAttribute((String) entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a specific input type="submit" parameter was sent in the request,
|
||||||
|
* either via a button (directly with name) or via an image (name + ".x" or
|
||||||
|
* name + ".y").
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name name of the parameter
|
||||||
|
* @return if the parameter was sent
|
||||||
|
* @see org.springframework.web.util.WebUtils#SUBMIT_IMAGE_SUFFIXES
|
||||||
|
*/
|
||||||
|
public static boolean hasSubmitParameter(PortletRequest request, String name) {
|
||||||
|
return getSubmitParameter(request, name) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the full name of a specific input type="submit" parameter
|
||||||
|
* if it was sent in the request, either via a button (directly with name)
|
||||||
|
* or via an image (name + ".x" or name + ".y").
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param name name of the parameter
|
||||||
|
* @return the actual parameter name with suffix if needed - null if not present
|
||||||
|
* @see org.springframework.web.util.WebUtils#SUBMIT_IMAGE_SUFFIXES
|
||||||
|
*/
|
||||||
|
public static String getSubmitParameter(PortletRequest request, String name) {
|
||||||
|
Assert.notNull(request, "Request must not be null");
|
||||||
|
if (request.getParameter(name) != null) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < WebUtils.SUBMIT_IMAGE_SUFFIXES.length; i++) {
|
||||||
|
String suffix = WebUtils.SUBMIT_IMAGE_SUFFIXES[i];
|
||||||
|
String parameter = name + suffix;
|
||||||
|
if (request.getParameter(parameter) != null) {
|
||||||
|
return parameter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a map containing all parameters with the given prefix.
|
||||||
|
* Maps single values to String and multiple values to String array.
|
||||||
|
* <p>For example, with a prefix of "spring_", "spring_param1" and
|
||||||
|
* "spring_param2" result in a Map with "param1" and "param2" as keys.
|
||||||
|
* <p>Similar to portlet
|
||||||
|
* {@link javax.portlet.PortletRequest#getParameterMap()},
|
||||||
|
* but more flexible.
|
||||||
|
* @param request portlet request in which to look for parameters
|
||||||
|
* @param prefix the beginning of parameter names
|
||||||
|
* (if this is <code>null</code> or the empty string, all parameters will match)
|
||||||
|
* @return map containing request parameters <b>without the prefix</b>,
|
||||||
|
* containing either a String or a String array as values
|
||||||
|
* @see javax.portlet.PortletRequest#getParameterNames
|
||||||
|
* @see javax.portlet.PortletRequest#getParameterValues
|
||||||
|
* @see javax.portlet.PortletRequest#getParameterMap
|
||||||
|
*/
|
||||||
|
public static Map getParametersStartingWith(PortletRequest request, String prefix) {
|
||||||
|
Assert.notNull(request, "Request must not be null");
|
||||||
|
Enumeration paramNames = request.getParameterNames();
|
||||||
|
Map params = new TreeMap();
|
||||||
|
if (prefix == null) {
|
||||||
|
prefix = "";
|
||||||
|
}
|
||||||
|
while (paramNames != null && paramNames.hasMoreElements()) {
|
||||||
|
String paramName = (String) paramNames.nextElement();
|
||||||
|
if ("".equals(prefix) || paramName.startsWith(prefix)) {
|
||||||
|
String unprefixed = paramName.substring(prefix.length());
|
||||||
|
String[] values = request.getParameterValues(paramName);
|
||||||
|
if (values == null || values.length == 0) {
|
||||||
|
// Do nothing, no values found at all.
|
||||||
|
}
|
||||||
|
else if (values.length > 1) {
|
||||||
|
params.put(unprefixed, values);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
params.put(unprefixed, values[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the target page specified in the request.
|
||||||
|
* @param request current portlet request
|
||||||
|
* @param paramPrefix the parameter prefix to check for
|
||||||
|
* (e.g. "_target" for parameters like "_target1" or "_target2")
|
||||||
|
* @param currentPage the current page, to be returned as fallback
|
||||||
|
* if no target page specified
|
||||||
|
* @return the page specified in the request, or current page if not found
|
||||||
|
*/
|
||||||
|
public static int getTargetPage(PortletRequest request, String paramPrefix, int currentPage) {
|
||||||
|
Enumeration paramNames = request.getParameterNames();
|
||||||
|
while (paramNames.hasMoreElements()) {
|
||||||
|
String paramName = (String) paramNames.nextElement();
|
||||||
|
if (paramName.startsWith(paramPrefix)) {
|
||||||
|
for (int i = 0; i < WebUtils.SUBMIT_IMAGE_SUFFIXES.length; i++) {
|
||||||
|
String suffix = WebUtils.SUBMIT_IMAGE_SUFFIXES[i];
|
||||||
|
if (paramName.endsWith(suffix)) {
|
||||||
|
paramName = paramName.substring(0, paramName.length() - suffix.length());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Integer.parseInt(paramName.substring(paramPrefix.length()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return currentPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pass all the action request parameters to the render phase by putting them into
|
||||||
|
* the action response object. This may not be called when the action will call
|
||||||
|
* {@link javax.portlet.ActionResponse#sendRedirect sendRedirect}.
|
||||||
|
* @param request the current action request
|
||||||
|
* @param response the current action response
|
||||||
|
* @see javax.portlet.ActionResponse#setRenderParameter
|
||||||
|
*/
|
||||||
|
public static void passAllParametersToRenderPhase(ActionRequest request, ActionResponse response) {
|
||||||
|
try {
|
||||||
|
Enumeration en = request.getParameterNames();
|
||||||
|
while (en.hasMoreElements()) {
|
||||||
|
String param = (String) en.nextElement();
|
||||||
|
String values[] = request.getParameterValues(param);
|
||||||
|
response.setRenderParameter(param, values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IllegalStateException ex) {
|
||||||
|
// Ignore in case sendRedirect was already set.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear all the render parameters from the {@link javax.portlet.ActionResponse}.
|
||||||
|
* This may not be called when the action will call
|
||||||
|
* {@link ActionResponse#sendRedirect sendRedirect}.
|
||||||
|
* @param response the current action response
|
||||||
|
* @see ActionResponse#setRenderParameters
|
||||||
|
*/
|
||||||
|
public static void clearAllRenderParameters(ActionResponse response) {
|
||||||
|
try {
|
||||||
|
response.setRenderParameters(new HashMap());
|
||||||
|
}
|
||||||
|
catch (IllegalStateException ex) {
|
||||||
|
// Ignore in case sendRedirect was already set.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
Miscellaneous portlet utility classes.
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,7 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
The Spring Data Binding framework, an internal library used by Spring Web Flow.
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
|
||||||
|
|
||||||
|
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
|
||||||
|
|
||||||
|
<!-- Appenders -->
|
||||||
|
<appender name="console" class="org.apache.log4j.ConsoleAppender">
|
||||||
|
<param name="Target" value="System.out" />
|
||||||
|
<layout class="org.apache.log4j.PatternLayout">
|
||||||
|
<param name="ConversionPattern" value="%-5p: %c - %m%n" />
|
||||||
|
</layout>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<logger name="org.springframework.beans">
|
||||||
|
<level value="warn" />
|
||||||
|
</logger>
|
||||||
|
|
||||||
|
<logger name="org.springframework.binding">
|
||||||
|
<level value="debug" />
|
||||||
|
</logger>
|
||||||
|
|
||||||
|
<!-- Root Logger -->
|
||||||
|
<root>
|
||||||
|
<priority value="warn" />
|
||||||
|
<appender-ref ref="console" />
|
||||||
|
</root>
|
||||||
|
|
||||||
|
</log4j:configuration>
|
|
@ -0,0 +1,24 @@
|
||||||
|
Bundle-SymbolicName: org.springframework.web.portlet
|
||||||
|
Bundle-Name: Spring Web Portlet
|
||||||
|
Bundle-Vendor: SpringSource
|
||||||
|
Bundle-ManifestVersion: 2
|
||||||
|
Import-Template:
|
||||||
|
javax.portlet.*;version="[1.0.0, 2.0.0)",
|
||||||
|
javax.servlet.*;version="[2.4.0, 3.0.0)",
|
||||||
|
org.apache.commons.fileupload.*;version="[1.2.0, 2.0.0)";resolution:=optional,
|
||||||
|
org.apache.commons.logging.*;version="[1.1.1, 2.0.0)",
|
||||||
|
org.springframework.beans.*;version="[2.5.5.A, 2.5.5.A]",
|
||||||
|
org.springframework.context.*;version="[2.5.5.A, 2.5.5.A]",
|
||||||
|
org.springframework.core.*;version="[2.5.5.A, 2.5.5.A]",
|
||||||
|
org.springframework.stereotype;version="[2.5.5.A, 2.5.5.A]",
|
||||||
|
org.springframework.ui.*;version="[2.5.5.A, 2.5.5.A]",
|
||||||
|
org.springframework.util.*;version="[2.5.5.A, 2.5.5.A]",
|
||||||
|
org.springframework.validation.*;version="[2.5.5.A, 2.5.5.A]",
|
||||||
|
org.springframework.web.*;version="[2.5.5.A, 2.5.5.A]"
|
||||||
|
Unversioned-Imports:
|
||||||
|
org.xml.sax.*
|
||||||
|
Ignored-Existing-Headers:
|
||||||
|
Bnd-LastModified,
|
||||||
|
Import-Package,
|
||||||
|
Export-Package,
|
||||||
|
Tool
|
Loading…
Reference in New Issue