@ -18,12 +18,18 @@ package org.springframework.web.portlet.mvc.annotation;
import java.lang.annotation.Annotation ;
import java.lang.annotation.Annotation ;
import java.lang.reflect.Method ;
import java.lang.reflect.Method ;
import java.util.Arrays ;
import java.util.HashMap ;
import java.util.HashSet ;
import java.util.HashSet ;
import java.util.Map ;
import java.util.Set ;
import java.util.Set ;
import javax.portlet.ClientDataRequest ;
import javax.portlet.ClientDataRequest ;
import javax.portlet.Event ;
import javax.portlet.EventRequest ;
import javax.portlet.PortletException ;
import javax.portlet.PortletException ;
import javax.portlet.PortletMode ;
import javax.portlet.PortletMode ;
import javax.portlet.PortletRequest ;
import javax.portlet.PortletRequest ;
import javax.portlet.ResourceRequest ;
import org.springframework.beans.BeansException ;
import org.springframework.beans.BeansException ;
import org.springframework.context.ApplicationContext ;
import org.springframework.context.ApplicationContext ;
@ -34,6 +40,9 @@ import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.Mapping ;
import org.springframework.web.bind.annotation.Mapping ;
import org.springframework.web.bind.annotation.RequestMapping ;
import org.springframework.web.bind.annotation.RequestMapping ;
import org.springframework.web.bind.annotation.RequestMethod ;
import org.springframework.web.bind.annotation.RequestMethod ;
import org.springframework.web.portlet.bind.PortletRequestBindingException ;
import org.springframework.web.portlet.bind.annotation.EventMapping ;
import org.springframework.web.portlet.bind.annotation.ResourceMapping ;
import org.springframework.web.portlet.handler.AbstractMapBasedHandlerMapping ;
import org.springframework.web.portlet.handler.AbstractMapBasedHandlerMapping ;
import org.springframework.web.portlet.handler.PortletRequestMethodNotSupportedException ;
import org.springframework.web.portlet.handler.PortletRequestMethodNotSupportedException ;
@ -64,10 +73,10 @@ import org.springframework.web.portlet.handler.PortletRequestMethodNotSupportedE
* at the method level .
* at the method level .
*
*
* < p > < b > NOTE : < / b > Method - level mappings are only allowed to narrow the mapping
* < 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
* expressed at the class level ( if any ) . A portlet mode in combination with specific
* specific handler beans , with any given portlet mode only allowed to be mapped
* parameter conditions needs to uniquely map onto one specific handler bean ,
* onto one specific handler bean ( not spread across multiple handler beans ) .
* not spread across multiple handler beans . It is strongly recommended to
* It is strongly recommended to co - locate related handler methods into the same bean .
* co - locate related handler methods into the same bean .
*
*
* < p > The { @link AnnotationMethodHandlerAdapter } is responsible for processing
* < p > The { @link AnnotationMethodHandlerAdapter } is responsible for processing
* annotated handler methods , as mapped by this HandlerMapping . For
* annotated handler methods , as mapped by this HandlerMapping . For
@ -81,6 +90,9 @@ import org.springframework.web.portlet.handler.PortletRequestMethodNotSupportedE
* /
* /
public class DefaultAnnotationHandlerMapping extends AbstractMapBasedHandlerMapping < PortletMode > {
public class DefaultAnnotationHandlerMapping extends AbstractMapBasedHandlerMapping < PortletMode > {
private final Map < Class , RequestMapping > cachedMappings = new HashMap < Class , RequestMapping > ( ) ;
/ * *
/ * *
* Calls the < code > registerHandlers < / code > method in addition
* Calls the < code > registerHandlers < / code > method in addition
* to the superclass ' s initialization .
* to the superclass ' s initialization .
@ -103,15 +115,17 @@ public class DefaultAnnotationHandlerMapping extends AbstractMapBasedHandlerMapp
Class < ? > handlerType = context . getType ( beanName ) ;
Class < ? > handlerType = context . getType ( beanName ) ;
RequestMapping mapping = context . findAnnotationOnBean ( beanName , RequestMapping . class ) ;
RequestMapping mapping = context . findAnnotationOnBean ( beanName , RequestMapping . class ) ;
if ( mapping ! = null ) {
if ( mapping ! = null ) {
// @RequestMapping found at type level
this . cachedMappings . put ( handlerType , mapping ) ;
String [ ] modeKeys = mapping . value ( ) ;
String [ ] modeKeys = mapping . value ( ) ;
String [ ] params = mapping . params ( ) ;
String [ ] params = mapping . params ( ) ;
RequestMethod [ ] methods = mapping . method ( ) ;
boolean registerHandlerType = true ;
boolean registerHandlerType = true ;
if ( modeKeys . length = = 0 | | params . length = = 0 ) {
if ( modeKeys . length = = 0 | | params . length = = 0 ) {
registerHandlerType = ! detectHandlerMethods ( handlerType , beanName , mapping ) ;
registerHandlerType = ! detectHandlerMethods ( handlerType , beanName , mapping ) ;
}
}
if ( registerHandlerType ) {
if ( registerHandlerType ) {
ParameterMappingPredicate predicate = new ParameterMappingPredicate ( params , methods ) ;
ParameterMappingPredicate predicate = new ParameterMappingPredicate (
params , mapping . headers ( ) , mapping . method ( ) ) ;
for ( String modeKey : modeKeys ) {
for ( String modeKey : modeKeys ) {
registerHandler ( new PortletMode ( modeKey ) , beanName , predicate ) ;
registerHandler ( new PortletMode ( modeKey ) , beanName , predicate ) ;
}
}
@ -138,14 +152,28 @@ public class DefaultAnnotationHandlerMapping extends AbstractMapBasedHandlerMapp
boolean mappingFound = false ;
boolean mappingFound = false ;
String [ ] modeKeys = new String [ 0 ] ;
String [ ] modeKeys = new String [ 0 ] ;
String [ ] params = new String [ 0 ] ;
String [ ] params = new String [ 0 ] ;
String resourceId = null ;
String eventName = null ;
for ( Annotation ann : method . getAnnotations ( ) ) {
for ( Annotation ann : method . getAnnotations ( ) ) {
if ( AnnotationUtils . findAnnotation ( ann . getClass ( ) , Mapping . class ) ! = null ) {
if ( AnnotationUtils . findAnnotation ( ann . getClass ( ) , Mapping . class ) ! = null ) {
mappingFound = true ;
mappingFound = true ;
if ( ann instanceof RequestMapping ) {
if ( ann instanceof RequestMapping ) {
modeKeys = ( String [ ] ) AnnotationUtils . getValue ( ann ) ;
RequestMapping rm = ( RequestMapping ) ann ;
modeKeys = rm . value ( ) ;
params = StringUtils . mergeStringArrays ( params , rm . params ( ) ) ;
}
else if ( ann instanceof ResourceMapping ) {
ResourceMapping rm = ( ResourceMapping ) ann ;
resourceId = rm . value ( ) ;
}
else if ( ann instanceof EventMapping ) {
EventMapping em = ( EventMapping ) ann ;
eventName = em . value ( ) ;
}
else {
String [ ] specificParams = ( String [ ] ) AnnotationUtils . getValue ( ann , "params" ) ;
params = StringUtils . mergeStringArrays ( params , specificParams ) ;
}
}
String [ ] specificParams = ( String [ ] ) AnnotationUtils . getValue ( ann , "params" ) ;
params = StringUtils . mergeStringArrays ( params , specificParams ) ;
}
}
}
}
if ( mappingFound ) {
if ( mappingFound ) {
@ -155,14 +183,26 @@ public class DefaultAnnotationHandlerMapping extends AbstractMapBasedHandlerMapp
}
}
else {
else {
throw new IllegalStateException (
throw new IllegalStateException (
"No portlet mode mappings specified - neither at type nor method level" ) ;
"No portlet mode mappings specified - neither at type nor at method level" ) ;
}
}
}
}
if ( typeMapping ! = null ) {
if ( typeMapping ! = null ) {
PortletAnnotationMappingUtils . validateModeMapping ( modeKeys , typeMapping . value ( ) ) ;
if ( ! PortletAnnotationMappingUtils . validateModeMapping ( modeKeys , typeMapping . value ( ) ) ) {
throw new IllegalStateException ( "Mode mappings conflict between method and type level: " +
Arrays . asList ( modeKeys ) + " versus " + Arrays . asList ( typeMapping . value ( ) ) ) ;
}
params = StringUtils . mergeStringArrays ( typeMapping . params ( ) , params ) ;
params = StringUtils . mergeStringArrays ( typeMapping . params ( ) , params ) ;
}
}
ParameterMappingPredicate predicate = new ParameterMappingPredicate ( params ) ;
PortletRequestMappingPredicate predicate ;
if ( resourceId ! = null ) {
predicate = new ResourceMappingPredicate ( resourceId ) ;
}
else if ( eventName ! = null ) {
predicate = new EventMappingPredicate ( eventName ) ;
}
else {
predicate = new ParameterMappingPredicate ( params ) ;
}
for ( String modeKey : modeKeys ) {
for ( String modeKey : modeKeys ) {
registerHandler ( new PortletMode ( modeKey ) , beanName , predicate ) ;
registerHandler ( new PortletMode ( modeKey ) , beanName , predicate ) ;
handlersRegistered . add ( Boolean . TRUE ) ;
handlersRegistered . add ( Boolean . TRUE ) ;
@ -181,6 +221,50 @@ public class DefaultAnnotationHandlerMapping extends AbstractMapBasedHandlerMapp
return request . getPortletMode ( ) ;
return request . getPortletMode ( ) ;
}
}
/ * *
* Validate the given annotated handler against the current request .
* @see # validateMapping
* /
protected void validateHandler ( Object handler , PortletRequest request ) throws Exception {
RequestMapping mapping = this . cachedMappings . get ( handler . getClass ( ) ) ;
if ( mapping = = null ) {
mapping = AnnotationUtils . findAnnotation ( handler . getClass ( ) , RequestMapping . class ) ;
}
if ( mapping ! = null ) {
validateMapping ( mapping , request ) ;
}
}
/ * *
* Validate the given type - level mapping metadata against the current request ,
* checking HTTP request method and parameter conditions .
* @param mapping the mapping metadata to validate
* @param request current HTTP request
* @throws Exception if validation failed
* /
protected void validateMapping ( RequestMapping mapping , PortletRequest request ) throws Exception {
RequestMethod [ ] mappedMethods = mapping . method ( ) ;
if ( ! PortletAnnotationMappingUtils . checkRequestMethod ( mappedMethods , request ) ) {
String [ ] supportedMethods = new String [ mappedMethods . length ] ;
for ( int i = 0 ; i < mappedMethods . length ; i + + ) {
supportedMethods [ i ] = mappedMethods [ i ] . name ( ) ;
}
if ( request instanceof ClientDataRequest ) {
throw new PortletRequestMethodNotSupportedException ( ( ( ClientDataRequest ) request ) . getMethod ( ) , supportedMethods ) ;
}
else {
throw new PortletRequestMethodNotSupportedException ( supportedMethods ) ;
}
}
String [ ] mappedHeaders = mapping . headers ( ) ;
if ( ! PortletAnnotationMappingUtils . checkHeaders ( mappedHeaders , request ) ) {
throw new PortletRequestBindingException ( "Header conditions \"" +
StringUtils . arrayToDelimitedString ( mappedHeaders , ", " ) +
"\" not met for actual request" ) ;
}
}
/ * *
/ * *
* Predicate that matches against parameter conditions .
* Predicate that matches against parameter conditions .
@ -189,16 +273,21 @@ public class DefaultAnnotationHandlerMapping extends AbstractMapBasedHandlerMapp
private final String [ ] params ;
private final String [ ] params ;
private final String [ ] headers ;
private final Set < String > methods = new HashSet < String > ( ) ;
private final Set < String > methods = new HashSet < String > ( ) ;
public ParameterMappingPredicate ( String [ ] params ) {
public ParameterMappingPredicate ( String [ ] params ) {
this . params = params ;
this ( params , null , null ) ;
}
}
public ParameterMappingPredicate ( String [ ] params , RequestMethod [ ] methods ) {
public ParameterMappingPredicate ( String [ ] params , String [ ] headers , RequestMethod [ ] methods ) {
this . params = params ;
this . params = params ;
for ( RequestMethod method : methods ) {
this . headers = headers ;
this . methods . add ( method . name ( ) ) ;
if ( methods ! = null ) {
for ( RequestMethod method : methods ) {
this . methods . add ( method . name ( ) ) ;
}
}
}
}
}
@ -207,6 +296,12 @@ public class DefaultAnnotationHandlerMapping extends AbstractMapBasedHandlerMapp
}
}
public void validate ( PortletRequest request ) throws PortletException {
public void validate ( PortletRequest request ) throws PortletException {
if ( ! PortletAnnotationMappingUtils . checkHeaders ( this . headers , request ) ) {
throw new PortletRequestBindingException ( "Header conditions \"" +
StringUtils . arrayToDelimitedString ( this . headers , ", " ) +
"\" not met for actual request" ) ;
}
if ( ! this . methods . isEmpty ( ) ) {
if ( ! this . methods . isEmpty ( ) ) {
if ( ! ( request instanceof ClientDataRequest ) ) {
if ( ! ( request instanceof ClientDataRequest ) ) {
throw new PortletRequestMethodNotSupportedException ( StringUtils . toStringArray ( this . methods ) ) ;
throw new PortletRequestMethodNotSupportedException ( StringUtils . toStringArray ( this . methods ) ) ;
@ -219,11 +314,11 @@ public class DefaultAnnotationHandlerMapping extends AbstractMapBasedHandlerMapp
}
}
public int compareTo ( Object other ) {
public int compareTo ( Object other ) {
if ( other instanceof PortletRequest MappingPredicate ) {
if ( other instanceof Parameter MappingPredicate ) {
return new Integer ( ( ( ParameterMappingPredicate ) other ) . params . length ) . compareTo ( this . params . length ) ;
return new Integer ( ( ( ParameterMappingPredicate ) other ) . params . length ) . compareTo ( this . params . length ) ;
}
}
else {
else {
return 0 ;
return 1 ;
}
}
}
}
@ -233,4 +328,54 @@ public class DefaultAnnotationHandlerMapping extends AbstractMapBasedHandlerMapp
}
}
}
}
private static class ResourceMappingPredicate implements PortletRequestMappingPredicate {
private final String resourceId ;
public ResourceMappingPredicate ( String resourceId ) {
this . resourceId = resourceId ;
}
public boolean match ( PortletRequest request ) {
return ( PortletRequest . RESOURCE_PHASE . equals ( request . getAttribute ( PortletRequest . LIFECYCLE_PHASE ) ) & &
( "" . equals ( this . resourceId ) | | this . resourceId . equals ( ( ( ResourceRequest ) request ) . getResourceID ( ) ) ) ) ;
}
public void validate ( PortletRequest request ) {
}
public int compareTo ( Object o ) {
return - 1 ;
}
}
private static class EventMappingPredicate implements PortletRequestMappingPredicate {
private final String eventName ;
public EventMappingPredicate ( String eventName ) {
this . eventName = eventName ;
}
public boolean match ( PortletRequest request ) {
if ( ! PortletRequest . EVENT_PHASE . equals ( request . getAttribute ( PortletRequest . LIFECYCLE_PHASE ) ) ) {
return false ;
}
if ( "" . equals ( this . eventName ) ) {
return true ;
}
Event event = ( ( EventRequest ) request ) . getEvent ( ) ;
return ( this . eventName . equals ( event . getName ( ) ) | | this . eventName . equals ( event . getQName ( ) . toString ( ) ) ) ;
}
public void validate ( PortletRequest request ) {
}
public int compareTo ( Object o ) {
return - 1 ;
}
}
}
}