Merge branch '3.1.x'

This commit is contained in:
Chris Beams 2012-01-25 23:17:39 +01:00
commit 3798626a90
63 changed files with 1211 additions and 494 deletions

View File

@ -20,8 +20,8 @@ Instructions on
via Maven and other build systems are available via the project wiki. via Maven and other build systems are available via the project wiki.
## Documentation ## Documentation
See the current [Javadoc](http://static.springsource.org/spring/docs/current/javadoc-api) See the current [Javadoc](http://static.springsource.org/spring-framework/docs/current/api)
and [Reference docs](http://static.springsource.org/spring/docs/current/spring-framework-reference). and [Reference docs](http://static.springsource.org/spring-framework/docs/current/reference).
## Getting support ## Getting support
Check out the [Spring forums](http://forum.springsource.org) and the Check out the [Spring forums](http://forum.springsource.org) and the

View File

@ -6,7 +6,7 @@ http://www.springsource.org
Changes in version 3.1.1 (2012-02-06) Changes in version 3.1.1 (2012-02-06)
------------------------------------- -------------------------------------
* official support for Hibernate 4.0 GA * official support for Hibernate 4.0 GA (as released in the meantime)
* JBossNativeJdbcExtractor is compatible with JBoss AS 7 as well * JBossNativeJdbcExtractor is compatible with JBoss AS 7 as well
* context:property-placeholder's "file-encoding" attribute value is being applied correctly * context:property-placeholder's "file-encoding" attribute value is being applied correctly
* DataBinder correctly handles ParseException from Formatter for String->String case * DataBinder correctly handles ParseException from Formatter for String->String case
@ -19,8 +19,14 @@ Changes in version 3.1.1 (2012-02-06)
* Hibernate 4 LocalSessionFactoryBean implements PersistenceExceptionTranslator interface as well * Hibernate 4 LocalSessionFactoryBean implements PersistenceExceptionTranslator interface as well
* Hibernate 4 LocalSessionFactoryBean does not insist on a "dataSource" reference being set * Hibernate 4 LocalSessionFactoryBean does not insist on a "dataSource" reference being set
* added "entityInterceptor" property to Hibernate 4 LocalSessionFactoryBean * added "entityInterceptor" property to Hibernate 4 LocalSessionFactoryBean
* added "getConfiguration" accessor to Hibernate 4 LocalSessionFactoryBean
* corrected fix for QuartzJobBean to work with Quartz 2.0/2.1 * corrected fix for QuartzJobBean to work with Quartz 2.0/2.1
* JMS CachingConnectionFactory never caches consumers for temporary queues and topics
* JMS SimpleMessageListenerContainer silently falls back to lazy registration of consumers
* fix regresion in UriUtils
* allow adding flash attributes in methods with a ModelAndView return value
* preserve quotes in MediaType parameters
* make flash attributes available in the model of ParameterizableViewController and UrlFilenameViewController
Changes in version 3.1 GA (2011-12-12) Changes in version 3.1 GA (2011-12-12)
-------------------------------------- --------------------------------------

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -34,6 +34,8 @@ import javax.jms.MessageConsumer;
import javax.jms.MessageProducer; import javax.jms.MessageProducer;
import javax.jms.QueueSession; import javax.jms.QueueSession;
import javax.jms.Session; import javax.jms.Session;
import javax.jms.TemporaryQueue;
import javax.jms.TemporaryTopic;
import javax.jms.Topic; import javax.jms.Topic;
import javax.jms.TopicSession; import javax.jms.TopicSession;
@ -323,16 +325,18 @@ public class CachingConnectionFactory extends SingleConnectionFactory {
// let raw JMS invocation throw an exception if Destination (i.e. args[0]) is null // let raw JMS invocation throw an exception if Destination (i.e. args[0]) is null
if ((methodName.equals("createConsumer") || methodName.equals("createReceiver") || if ((methodName.equals("createConsumer") || methodName.equals("createReceiver") ||
methodName.equals("createSubscriber"))) { methodName.equals("createSubscriber"))) {
if (args[0] != null) { Destination dest = (Destination) args[0];
return getCachedConsumer((Destination) args[0], if (dest != null && !(dest instanceof TemporaryQueue || dest instanceof TemporaryTopic)) {
return getCachedConsumer(dest,
(args.length > 1 ? (String) args[1] : null), (args.length > 1 ? (String) args[1] : null),
(args.length > 2 && (Boolean) args[2]), (args.length > 2 && (Boolean) args[2]),
null); null);
} }
} }
else if (methodName.equals("createDurableSubscriber")) { else if (methodName.equals("createDurableSubscriber")) {
if (args[0] != null) { Destination dest = (Destination) args[0];
return getCachedConsumer((Destination) args[0], if (dest != null) {
return getCachedConsumer(dest,
(args.length > 2 ? (String) args[2] : null), (args.length > 2 ? (String) args[2] : null),
(args.length > 3 && (Boolean) args[3]), (args.length > 3 && (Boolean) args[3]),
(String) args[1]); (String) args[1]);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -151,6 +151,7 @@ public abstract class AbstractJmsListeningContainer extends JmsDestinationAccess
/** /**
* Delegates to {@link #validateConfiguration()} and {@link #initialize()}. * Delegates to {@link #validateConfiguration()} and {@link #initialize()}.
*/ */
@Override
public void afterPropertiesSet() { public void afterPropertiesSet() {
super.afterPropertiesSet(); super.afterPropertiesSet();
validateConfiguration(); validateConfiguration();

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -410,6 +410,7 @@ public abstract class AbstractMessageListenerContainer extends AbstractJmsListen
return this.acceptMessagesWhileStopping; return this.acceptMessagesWhileStopping;
} }
@Override
protected void validateConfiguration() { protected void validateConfiguration() {
if (this.destination == null) { if (this.destination == null) {
throw new IllegalArgumentException("Property 'destination' or 'destinationName' is required"); throw new IllegalArgumentException("Property 'destination' or 'destinationName' is required");

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2010 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -75,8 +75,7 @@ import org.springframework.transaction.support.TransactionSynchronizationUtils;
* @see #receiveAndExecute * @see #receiveAndExecute
* @see #setTransactionManager * @see #setTransactionManager
*/ */
public abstract class AbstractPollingMessageListenerContainer extends AbstractMessageListenerContainer public abstract class AbstractPollingMessageListenerContainer extends AbstractMessageListenerContainer {
implements BeanNameAware {
/** /**
* The default receive timeout: 1000 ms = 1 second. * The default receive timeout: 1000 ms = 1 second.
@ -100,6 +99,7 @@ public abstract class AbstractPollingMessageListenerContainer extends AbstractMe
private volatile Boolean commitAfterNoMessageReceived; private volatile Boolean commitAfterNoMessageReceived;
@Override
public void setSessionTransacted(boolean sessionTransacted) { public void setSessionTransacted(boolean sessionTransacted) {
super.setSessionTransacted(sessionTransacted); super.setSessionTransacted(sessionTransacted);
this.sessionTransactedCalled = true; this.sessionTransactedCalled = true;
@ -188,6 +188,7 @@ public abstract class AbstractPollingMessageListenerContainer extends AbstractMe
} }
@Override
public void initialize() { public void initialize() {
// Set sessionTransacted=true in case of a non-JTA transaction manager. // Set sessionTransacted=true in case of a non-JTA transaction manager.
if (!this.sessionTransactedCalled && if (!this.sessionTransactedCalled &&
@ -374,6 +375,7 @@ public abstract class AbstractPollingMessageListenerContainer extends AbstractMe
* container's "sessionTransacted" flag being set to "true". * container's "sessionTransacted" flag being set to "true".
* @see org.springframework.jms.connection.JmsResourceHolder * @see org.springframework.jms.connection.JmsResourceHolder
*/ */
@Override
protected boolean isSessionLocallyTransacted(Session session) { protected boolean isSessionLocallyTransacted(Session session) {
if (!super.isSessionLocallyTransacted(session)) { if (!super.isSessionLocallyTransacted(session)) {
return false; return false;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -470,6 +470,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
} }
} }
@Override
protected void validateConfiguration() { protected void validateConfiguration() {
super.validateConfiguration(); super.validateConfiguration();
synchronized (this.lifecycleMonitor) { synchronized (this.lifecycleMonitor) {
@ -484,6 +485,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
// Implementation of AbstractMessageListenerContainer's template methods // Implementation of AbstractMessageListenerContainer's template methods
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
@Override
public void initialize() { public void initialize() {
// Adapt default cache level. // Adapt default cache level.
if (this.cacheLevel == CACHE_AUTO) { if (this.cacheLevel == CACHE_AUTO) {
@ -516,6 +518,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
* @see #scheduleNewInvoker * @see #scheduleNewInvoker
* @see #setTaskExecutor * @see #setTaskExecutor
*/ */
@Override
protected void doInitialize() throws JMSException { protected void doInitialize() throws JMSException {
synchronized (this.lifecycleMonitor) { synchronized (this.lifecycleMonitor) {
for (int i = 0; i < this.concurrentConsumers; i++) { for (int i = 0; i < this.concurrentConsumers; i++) {
@ -527,6 +530,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
/** /**
* Destroy the registered JMS Sessions and associated MessageConsumers. * Destroy the registered JMS Sessions and associated MessageConsumers.
*/ */
@Override
protected void doShutdown() throws JMSException { protected void doShutdown() throws JMSException {
logger.debug("Waiting for shutdown of message listener invokers"); logger.debug("Waiting for shutdown of message listener invokers");
try { try {
@ -549,6 +553,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
/** /**
* Overridden to reset the stop callback, if any. * Overridden to reset the stop callback, if any.
*/ */
@Override
public void start() throws JmsException { public void start() throws JmsException {
synchronized (this.lifecycleMonitor) { synchronized (this.lifecycleMonitor) {
this.stopCallback = null; this.stopCallback = null;
@ -658,6 +663,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
* @see #setCacheLevel * @see #setCacheLevel
* @see #CACHE_CONNECTION * @see #CACHE_CONNECTION
*/ */
@Override
protected final boolean sharedConnectionEnabled() { protected final boolean sharedConnectionEnabled() {
return (getCacheLevel() >= CACHE_CONNECTION); return (getCacheLevel() >= CACHE_CONNECTION);
} }
@ -666,6 +672,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
* Re-executes the given task via this listener container's TaskExecutor. * Re-executes the given task via this listener container's TaskExecutor.
* @see #setTaskExecutor * @see #setTaskExecutor
*/ */
@Override
protected void doRescheduleTask(Object task) { protected void doRescheduleTask(Object task) {
this.taskExecutor.execute((Runnable) task); this.taskExecutor.execute((Runnable) task);
} }
@ -674,6 +681,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
* Tries scheduling a new invoker, since we know messages are coming in... * Tries scheduling a new invoker, since we know messages are coming in...
* @see #scheduleNewInvokerIfAppropriate() * @see #scheduleNewInvokerIfAppropriate()
*/ */
@Override
protected void messageReceived(Object invoker, Session session) { protected void messageReceived(Object invoker, Session session) {
((AsyncMessageListenerInvoker) invoker).setIdle(false); ((AsyncMessageListenerInvoker) invoker).setIdle(false);
scheduleNewInvokerIfAppropriate(); scheduleNewInvokerIfAppropriate();
@ -682,6 +690,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
/** /**
* Marks the affected invoker as idle. * Marks the affected invoker as idle.
*/ */
@Override
protected void noMessageReceived(Object invoker, Session session) { protected void noMessageReceived(Object invoker, Session session) {
((AsyncMessageListenerInvoker) invoker).setIdle(true); ((AsyncMessageListenerInvoker) invoker).setIdle(true);
} }
@ -745,6 +754,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
* asynchronous invokers to establish the shared Connection on first access. * asynchronous invokers to establish the shared Connection on first access.
* @see #refreshConnectionUntilSuccessful() * @see #refreshConnectionUntilSuccessful()
*/ */
@Override
protected void establishSharedConnection() { protected void establishSharedConnection() {
try { try {
super.establishSharedConnection(); super.establishSharedConnection();
@ -760,6 +770,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
* <code>Connection.start()</code>, relying on listeners to perform * <code>Connection.start()</code>, relying on listeners to perform
* appropriate recovery. * appropriate recovery.
*/ */
@Override
protected void startSharedConnection() { protected void startSharedConnection() {
try { try {
super.startSharedConnection(); super.startSharedConnection();
@ -774,6 +785,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
* <code>Connection.stop()</code>, relying on listeners to perform * <code>Connection.stop()</code>, relying on listeners to perform
* appropriate recovery after a restart. * appropriate recovery after a restart.
*/ */
@Override
protected void stopSharedConnection() { protected void stopSharedConnection() {
try { try {
super.stopSharedConnection(); super.stopSharedConnection();

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2010 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -62,6 +62,8 @@ public class SimpleMessageListenerContainer extends AbstractMessageListenerConta
private boolean pubSubNoLocal = false; private boolean pubSubNoLocal = false;
private boolean connectLazily = false;
private int concurrentConsumers = 1; private int concurrentConsumers = 1;
private Executor taskExecutor; private Executor taskExecutor;
@ -89,6 +91,20 @@ public class SimpleMessageListenerContainer extends AbstractMessageListenerConta
return this.pubSubNoLocal; return this.pubSubNoLocal;
} }
/**
* Specify whether to connect lazily, i.e. whether to establish the JMS Connection
* and the corresponding Sessions and MessageConsumers as late as possible -
* in the start phase of this container.
* <p>Default is "false": connecting early, i.e. during the bean initialization phase.
* Set this flag to "true" in order to switch to lazy connecting if your target broker
* is likely to not have started up yet and you prefer to not even try a connection.
* @see #start()
* @see #initialize()
*/
public void setConnectLazily(boolean connectLazily) {
this.connectLazily = connectLazily;
}
/** /**
* Specify concurrency limits via a "lower-upper" String, e.g. "5-10", or a simple * Specify concurrency limits via a "lower-upper" String, e.g. "5-10", or a simple
* upper limit String, e.g. "10". * upper limit String, e.g. "10".
@ -159,6 +175,7 @@ public class SimpleMessageListenerContainer extends AbstractMessageListenerConta
this.taskExecutor = taskExecutor; this.taskExecutor = taskExecutor;
} }
@Override
protected void validateConfiguration() { protected void validateConfiguration() {
super.validateConfiguration(); super.validateConfiguration();
if (isSubscriptionDurable() && this.concurrentConsumers != 1) { if (isSubscriptionDurable() && this.concurrentConsumers != 1) {
@ -174,6 +191,7 @@ public class SimpleMessageListenerContainer extends AbstractMessageListenerConta
/** /**
* Always use a shared JMS Connection. * Always use a shared JMS Connection.
*/ */
@Override
protected final boolean sharedConnectionEnabled() { protected final boolean sharedConnectionEnabled() {
return true; return true;
} }
@ -183,15 +201,25 @@ public class SimpleMessageListenerContainer extends AbstractMessageListenerConta
* in the form of a JMS Session plus associated MessageConsumer. * in the form of a JMS Session plus associated MessageConsumer.
* @see #createListenerConsumer * @see #createListenerConsumer
*/ */
@Override
protected void doInitialize() throws JMSException { protected void doInitialize() throws JMSException {
establishSharedConnection(); if (!this.connectLazily) {
initializeConsumers(); try {
establishSharedConnection();
}
catch (JMSException ex) {
logger.debug("Could not connect on initialization - registering message consumers lazily", ex);
return;
}
initializeConsumers();
}
} }
/** /**
* Re-initializes this container's JMS message consumers, * Re-initializes this container's JMS message consumers,
* if not initialized already. * if not initialized already.
*/ */
@Override
protected void doStart() throws JMSException { protected void doStart() throws JMSException {
super.doStart(); super.doStart();
initializeConsumers(); initializeConsumers();
@ -200,6 +228,7 @@ public class SimpleMessageListenerContainer extends AbstractMessageListenerConta
/** /**
* Registers this listener container as JMS ExceptionListener on the shared connection. * Registers this listener container as JMS ExceptionListener on the shared connection.
*/ */
@Override
protected void prepareSharedConnection(Connection connection) throws JMSException { protected void prepareSharedConnection(Connection connection) throws JMSException {
super.prepareSharedConnection(connection); super.prepareSharedConnection(connection);
connection.setExceptionListener(this); connection.setExceptionListener(this);
@ -320,6 +349,7 @@ public class SimpleMessageListenerContainer extends AbstractMessageListenerConta
/** /**
* Destroy the registered JMS Sessions and associated MessageConsumers. * Destroy the registered JMS Sessions and associated MessageConsumers.
*/ */
@Override
protected void doShutdown() throws JMSException { protected void doShutdown() throws JMSException {
logger.debug("Closing JMS MessageConsumers"); logger.debug("Closing JMS MessageConsumers");
for (MessageConsumer consumer : this.consumers) { for (MessageConsumer consumer : this.consumers) {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2010 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -787,7 +787,7 @@ public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implemen
configTimeTransactionManagerHolder.remove(); configTimeTransactionManagerHolder.remove();
} }
if (this.cacheRegionFactory != null) { if (this.cacheRegionFactory != null) {
configTimeCacheProviderHolder.remove(); configTimeRegionFactoryHolder.remove();
} }
if (this.cacheProvider != null) { if (this.cacheProvider != null) {
configTimeCacheProviderHolder.remove(); configTimeCacheProviderHolder.remove();
@ -862,7 +862,7 @@ public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implemen
/** /**
* Return the Configuration object used to build the SessionFactory. * Return the Configuration object used to build the SessionFactory.
* Allows access to configuration metadata stored there (rarely needed). * Allows for access to configuration metadata stored there (rarely needed).
* @throws IllegalStateException if the Configuration object has not been initialized yet * @throws IllegalStateException if the Configuration object has not been initialized yet
*/ */
public final Configuration getConfiguration() { public final Configuration getConfiguration() {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -23,6 +23,7 @@ import javax.sql.DataSource;
import org.hibernate.Interceptor; import org.hibernate.Interceptor;
import org.hibernate.SessionFactory; import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.NamingStrategy; import org.hibernate.cfg.NamingStrategy;
import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.DisposableBean;
@ -37,16 +38,15 @@ import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils; import org.springframework.core.io.support.ResourcePatternUtils;
/** /**
* {@link org.springframework.beans.factory.FactoryBean} that creates a * {@link org.springframework.beans.factory.FactoryBean} that creates a Hibernate
* Hibernate {@link org.hibernate.SessionFactory}. This is the usual way to * {@link org.hibernate.SessionFactory}. This is the usual way to set up a shared
* set up a shared Hibernate SessionFactory in a Spring application context; * Hibernate SessionFactory in a Spring application context; the SessionFactory can
* the SessionFactory can then be passed to Hibernate-based DAOs via * then be passed to Hibernate-based data access objects via dependency injection.
* dependency injection.
* *
* <p><b>NOTE:</b> This variant of LocalSessionFactoryBean requires Hibernate 4.0 * <p><b>NOTE:</b> This variant of LocalSessionFactoryBean requires Hibernate 4.0 or higher.
* or higher. It is similar in role to the same-named class in the <code>orm.hibernate3</code> * It is similar in role to the same-named class in the <code>orm.hibernate3</code> package.
* package. However, in practice, it is closer to <code>AnnotationSessionFactoryBean</code> * However, in practice, it is closer to <code>AnnotationSessionFactoryBean</code> since
* since its core purpose is to bootstrap a <code>SessionFactory</code> from annotation scanning. * its core purpose is to bootstrap a <code>SessionFactory</code> from annotation scanning.
* *
* @author Juergen Hoeller * @author Juergen Hoeller
* @since 3.1 * @since 3.1
@ -84,6 +84,8 @@ public class LocalSessionFactoryBean extends HibernateExceptionTranslator
private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
private Configuration configuration;
private SessionFactory sessionFactory; private SessionFactory sessionFactory;
@ -328,7 +330,36 @@ public class LocalSessionFactoryBean extends HibernateExceptionTranslator
sfb.scanPackages(this.packagesToScan); sfb.scanPackages(this.packagesToScan);
} }
this.sessionFactory = sfb.buildSessionFactory(); // Build SessionFactory instance.
this.configuration = sfb;
this.sessionFactory = buildSessionFactory(sfb);
}
/**
* Subclasses can override this method to perform custom initialization
* of the SessionFactory instance, creating it via the given Configuration
* object that got prepared by this LocalSessionFactoryBean.
* <p>The default implementation invokes LocalSessionFactoryBuilder's buildSessionFactory.
* A custom implementation could prepare the instance in a specific way (e.g. applying
* a custom ServiceRegistry) or use a custom SessionFactoryImpl subclass.
* @param sfb LocalSessionFactoryBuilder prepared by this LocalSessionFactoryBean
* @return the SessionFactory instance
* @see LocalSessionFactoryBuilder#buildSessionFactory
*/
protected SessionFactory buildSessionFactory(LocalSessionFactoryBuilder sfb) {
return sfb.buildSessionFactory();
}
/**
* Return the Hibernate Configuration object used to build the SessionFactory.
* Allows for access to configuration metadata stored there (rarely needed).
* @throws IllegalStateException if the Configuration object has not been initialized yet
*/
public final Configuration getConfiguration() {
if (this.configuration == null) {
throw new IllegalStateException("Configuration not initialized yet");
}
return this.configuration;
} }

View File

@ -0,0 +1,120 @@
/*
* Copyright 2002-2012 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.oxm.jaxb;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlEnum;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlType;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.oxm.UncategorizedMappingException;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Helper class for {@link Jaxb2Marshaller} that scans given packages for classes marked with JAXB2 annotations.
*
* @author Arjen Poutsma
* @author David Harrigan
* @see #scanPackages()
*/
class ClassPathJaxb2TypeScanner {
private static final String RESOURCE_PATTERN = "/**/*.class";
private final TypeFilter[] jaxb2TypeFilters =
new TypeFilter[]{new AnnotationTypeFilter(XmlRootElement.class, false),
new AnnotationTypeFilter(XmlType.class, false), new AnnotationTypeFilter(XmlSeeAlso.class, false),
new AnnotationTypeFilter(XmlEnum.class, false)};
private final String[] packagesToScan;
private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
private List<Class<?>> jaxb2Classes = new ArrayList<Class<?>>();
/** Constructs a new {@code ClassPathJaxb2TypeScanner} for the given packages. */
ClassPathJaxb2TypeScanner(String[] packagesToScan) {
Assert.notEmpty(packagesToScan, "'packagesToScan' must not be empty");
this.packagesToScan = packagesToScan;
}
void setResourceLoader(ResourceLoader resourceLoader) {
if (resourceLoader != null) {
this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
}
}
/** Returns the JAXB2 classes found in the specified packages. */
Class<?>[] getJaxb2Classes() {
return jaxb2Classes.toArray(new Class<?>[jaxb2Classes.size()]);
}
/**
* Scans the packages for classes marked with JAXB2 annotations.
*
* @throws UncategorizedMappingException in case of errors
*/
void scanPackages() throws UncategorizedMappingException {
try {
for (String packageToScan : packagesToScan) {
String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
ClassUtils.convertClassNameToResourcePath(packageToScan) + RESOURCE_PATTERN;
Resource[] resources = resourcePatternResolver.getResources(pattern);
MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
for (Resource resource : resources) {
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
if (isJaxb2Class(metadataReader, metadataReaderFactory)) {
String className = metadataReader.getClassMetadata().getClassName();
Class<?> jaxb2AnnotatedClass = resourcePatternResolver.getClassLoader().loadClass(className);
jaxb2Classes.add(jaxb2AnnotatedClass);
}
}
}
}
catch (IOException ex) {
throw new UncategorizedMappingException("Failed to scan classpath for unlisted classes", ex);
}
catch (ClassNotFoundException ex) {
throw new UncategorizedMappingException("Failed to load annotated classes from classpath", ex);
}
}
private boolean isJaxb2Class(MetadataReader reader, MetadataReaderFactory factory) throws IOException {
for (TypeFilter filter : jaxb2TypeFilters) {
if (filter.match(reader, factory)) {
return true;
}
}
return false;
}
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,7 +16,7 @@
package org.springframework.oxm.jaxb; package org.springframework.oxm.jaxb;
import java.awt.Image; import java.awt.*;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -65,10 +65,20 @@ import javax.xml.transform.sax.SAXSource;
import javax.xml.validation.Schema; import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory; import javax.xml.validation.SchemaFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.ls.LSResourceResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.oxm.GenericMarshaller; import org.springframework.oxm.GenericMarshaller;
import org.springframework.oxm.GenericUnmarshaller; import org.springframework.oxm.GenericUnmarshaller;
import org.springframework.oxm.MarshallingFailureException; import org.springframework.oxm.MarshallingFailureException;
@ -87,14 +97,6 @@ import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.util.xml.StaxUtils; import org.springframework.util.xml.StaxUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.ls.LSResourceResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
/** /**
* Implementation of the <code>Marshaller</code> interface for JAXB 2.0. * Implementation of the <code>Marshaller</code> interface for JAXB 2.0.
* *
@ -117,7 +119,7 @@ import org.xml.sax.helpers.XMLReaderFactory;
*/ */
public class Jaxb2Marshaller public class Jaxb2Marshaller
implements MimeMarshaller, MimeUnmarshaller, GenericMarshaller, GenericUnmarshaller, BeanClassLoaderAware, implements MimeMarshaller, MimeUnmarshaller, GenericMarshaller, GenericUnmarshaller, BeanClassLoaderAware,
InitializingBean { ResourceLoaderAware, InitializingBean {
private static final String CID = "cid:"; private static final String CID = "cid:";
@ -130,6 +132,8 @@ public class Jaxb2Marshaller
private String contextPath; private String contextPath;
private Class<?>[] classesToBeBound; private Class<?>[] classesToBeBound;
private String[] packagesToScan;
private Map<String, ?> jaxbContextProperties; private Map<String, ?> jaxbContextProperties;
@ -153,6 +157,8 @@ public class Jaxb2Marshaller
private ClassLoader beanClassLoader; private ClassLoader beanClassLoader;
private ResourceLoader resourceLoader;
private JAXBContext jaxbContext; private JAXBContext jaxbContext;
private Schema schema; private Schema schema;
@ -161,7 +167,7 @@ public class Jaxb2Marshaller
private boolean supportJaxbElementClass = false; private boolean supportJaxbElementClass = false;
private LSResourceResolver schemaResourceResolver; private LSResourceResolver schemaResourceResolver;
/** /**
@ -175,6 +181,8 @@ public class Jaxb2Marshaller
/** /**
* Set a JAXB context path. * Set a JAXB context path.
* <p>Setting this property, {@link #setClassesToBeBound "classesToBeBound"}, or
* {@link #setPackagesToScan "packagesToScan"} is required.
*/ */
public void setContextPath(String contextPath) { public void setContextPath(String contextPath) {
Assert.hasText(contextPath, "'contextPath' must not be null"); Assert.hasText(contextPath, "'contextPath' must not be null");
@ -190,7 +198,8 @@ public class Jaxb2Marshaller
/** /**
* Set the list of Java classes to be recognized by a newly created JAXBContext. * Set the list of Java classes to be recognized by a newly created JAXBContext.
* Setting this property or {@link #setContextPath "contextPath"} is required. * <p>Setting this property, {@link #setContextPath "contextPath"}, or
* {@link #setPackagesToScan "packagesToScan"} is required.
*/ */
public void setClassesToBeBound(Class<?>... classesToBeBound) { public void setClassesToBeBound(Class<?>... classesToBeBound) {
Assert.notEmpty(classesToBeBound, "'classesToBeBound' must not be empty"); Assert.notEmpty(classesToBeBound, "'classesToBeBound' must not be empty");
@ -204,6 +213,23 @@ public class Jaxb2Marshaller
return this.classesToBeBound; return this.classesToBeBound;
} }
/**
* Set the packages to search using Spring-based scanning for classes with JAXB2 annotations in the classpath.
* <p>Setting this property, {@link #setContextPath "contextPath"}, or
* {@link #setClassesToBeBound "classesToBeBound"} is required. This is analogous to Spring's component-scan feature
* ({@link org.springframework.context.annotation.ClassPathBeanDefinitionScanner}).
*/
public void setPackagesToScan(String[] packagesToScan) {
this.packagesToScan = packagesToScan;
}
/**
* Returns the packages to search for JAXB2 annotations.
*/
public String[] getPackagesToScan() {
return packagesToScan;
}
/** /**
* Set the <code>JAXBContext</code> properties. These implementation-specific * Set the <code>JAXBContext</code> properties. These implementation-specific
* properties will be set on the underlying <code>JAXBContext</code>. * properties will be set on the underlying <code>JAXBContext</code>.
@ -289,17 +315,18 @@ public class Jaxb2Marshaller
this.schemaLanguage = schemaLanguage; this.schemaLanguage = schemaLanguage;
} }
/** /**
* Sets the resource resolver, as used to load the schema resources. * Sets the resource resolver, as used to load the schema resources.
* @see SchemaFactory#setResourceResolver(org.w3c.dom.ls.LSResourceResolver) *
* @see #setSchema(Resource) * @see SchemaFactory#setResourceResolver(org.w3c.dom.ls.LSResourceResolver)
* @see #setSchemas(Resource[]) * @see #setSchema(Resource)
*/ * @see #setSchemas(Resource[])
public void setSchemaResourceResolver(LSResourceResolver schemaResourceResolver) { */
this.schemaResourceResolver = schemaResourceResolver; public void setSchemaResourceResolver(LSResourceResolver schemaResourceResolver) {
} this.schemaResourceResolver = schemaResourceResolver;
}
/** /**
* Specify whether MTOM support should be enabled or not. * Specify whether MTOM support should be enabled or not.
* Default is <code>false</code>: marshalling using XOP/MTOM not being enabled. * Default is <code>false</code>: marshalling using XOP/MTOM not being enabled.
*/ */
@ -336,13 +363,23 @@ public class Jaxb2Marshaller
this.beanClassLoader = classLoader; this.beanClassLoader = classLoader;
} }
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
public final void afterPropertiesSet() throws Exception { public final void afterPropertiesSet() throws Exception {
if (StringUtils.hasLength(getContextPath()) && !ObjectUtils.isEmpty(getClassesToBeBound())) { boolean hasContextPath = StringUtils.hasLength(getContextPath());
throw new IllegalArgumentException("Specify either 'contextPath' or 'classesToBeBound property'; not both"); boolean hasClassesToBeBound = !ObjectUtils.isEmpty(getClassesToBeBound());
boolean hasPackagesToScan = !ObjectUtils.isEmpty(getPackagesToScan());
if (hasContextPath && (hasClassesToBeBound || hasPackagesToScan) ||
(hasClassesToBeBound && hasPackagesToScan)) {
throw new IllegalArgumentException("Specify either 'contextPath', 'classesToBeBound', " +
"or 'packagesToScan'");
} }
else if (!StringUtils.hasLength(getContextPath()) && ObjectUtils.isEmpty(getClassesToBeBound())) { if (!hasContextPath && !hasClassesToBeBound && !hasPackagesToScan) {
throw new IllegalArgumentException("Setting either 'contextPath' or 'classesToBeBound' is required"); throw new IllegalArgumentException(
"Setting either 'contextPath', 'classesToBeBound', " + "or 'packagesToScan' is required");
} }
if (!this.lazyInit) { if (!this.lazyInit) {
getJaxbContext(); getJaxbContext();
@ -361,6 +398,9 @@ public class Jaxb2Marshaller
else if (!ObjectUtils.isEmpty(getClassesToBeBound())) { else if (!ObjectUtils.isEmpty(getClassesToBeBound())) {
this.jaxbContext = createJaxbContextFromClasses(); this.jaxbContext = createJaxbContextFromClasses();
} }
else if (!ObjectUtils.isEmpty(getPackagesToScan())) {
this.jaxbContext = createJaxbContextFromPackages();
}
} }
catch (JAXBException ex) { catch (JAXBException ex) {
throw convertJaxbException(ex); throw convertJaxbException(ex);
@ -404,6 +444,26 @@ public class Jaxb2Marshaller
} }
} }
private JAXBContext createJaxbContextFromPackages() throws JAXBException {
if (logger.isInfoEnabled()) {
logger.info("Creating JAXBContext by scanning packages [" +
StringUtils.arrayToCommaDelimitedString(getPackagesToScan()) + "]");
}
ClassPathJaxb2TypeScanner scanner = new ClassPathJaxb2TypeScanner(getPackagesToScan());
scanner.setResourceLoader(this.resourceLoader);
scanner.scanPackages();
Class<?>[] jaxb2Classes = scanner.getJaxb2Classes();
if (logger.isDebugEnabled()) {
logger.debug("Found JAXB2 classes: [" + StringUtils.arrayToCommaDelimitedString(jaxb2Classes) + "]");
}
if (this.jaxbContextProperties != null) {
return JAXBContext.newInstance(jaxb2Classes, this.jaxbContextProperties);
}
else {
return JAXBContext.newInstance(jaxb2Classes);
}
}
private Schema loadSchema(Resource[] resources, String schemaLanguage) throws IOException, SAXException { private Schema loadSchema(Resource[] resources, String schemaLanguage) throws IOException, SAXException {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Setting validation schema to " + StringUtils.arrayToCommaDelimitedString(this.schemaResources)); logger.debug("Setting validation schema to " + StringUtils.arrayToCommaDelimitedString(this.schemaResources));
@ -420,9 +480,9 @@ public class Jaxb2Marshaller
schemaSources[i] = new SAXSource(xmlReader, inputSource); schemaSources[i] = new SAXSource(xmlReader, inputSource);
} }
SchemaFactory schemaFactory = SchemaFactory.newInstance(schemaLanguage); SchemaFactory schemaFactory = SchemaFactory.newInstance(schemaLanguage);
if (schemaResourceResolver != null) { if (schemaResourceResolver != null) {
schemaFactory.setResourceResolver(schemaResourceResolver); schemaFactory.setResourceResolver(schemaResourceResolver);
} }
return schemaFactory.newSchema(schemaSources); return schemaFactory.newSchema(schemaSources);
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -279,6 +279,13 @@ public class Jaxb2MarshallerTests extends AbstractMarshallerTests {
assertTrue("No XML written", writer.toString().length() > 0); assertTrue("No XML written", writer.toString().length() > 0);
} }
@Test
public void supportsPackagesToScan() throws Exception {
marshaller = new Jaxb2Marshaller();
marshaller.setPackagesToScan(new String[] {CONTEXT_PATH});
marshaller.afterPropertiesSet();
}
@XmlRootElement @XmlRootElement
public static class DummyRootElement { public static class DummyRootElement {

View File

@ -12,6 +12,7 @@ Import-Template:
org.exolab.castor.*;version="[1.2.0, 2.0.0)";resolution:=optional, org.exolab.castor.*;version="[1.2.0, 2.0.0)";resolution:=optional,
org.jibx.runtime.*;version="[1.1.5, 2.0.0)";resolution:=optional, org.jibx.runtime.*;version="[1.1.5, 2.0.0)";resolution:=optional,
org.springframework.beans.*;version=${spring.osgi.range}, org.springframework.beans.*;version=${spring.osgi.range},
org.springframework.context.*;version=${spring.osgi.range},
org.springframework.core.*;version=${spring.osgi.range}, org.springframework.core.*;version=${spring.osgi.range},
org.springframework.util.*;version=${spring.osgi.range}, org.springframework.util.*;version=${spring.osgi.range},
org.w3c.dom.*;version="0", org.w3c.dom.*;version="0",

View File

@ -815,7 +815,7 @@ public class DispatcherServlet extends FrameworkServlet {
} }
} }
this.flashMapManager.requestStarted(request); this.flashMapManager.requestStarted(request, response);
// Make framework objects available to handlers and view objects. // Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
@ -827,7 +827,7 @@ public class DispatcherServlet extends FrameworkServlet {
doDispatch(request, response); doDispatch(request, response);
} }
finally { finally {
this.flashMapManager.requestCompleted(request); this.flashMapManager.requestCompleted(request, response);
// Restore the original attribute snapshot, in case of an include. // Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) { if (attributesSnapshot != null) {

View File

@ -63,6 +63,8 @@ public final class FlashMap extends HashMap<String, Object> implements Comparabl
/** /**
* Create a new instance with an id uniquely identifying the creator of * Create a new instance with an id uniquely identifying the creator of
* this FlashMap. * this FlashMap.
* @param createdBy identifies the FlashMapManager instance that created
* and will manage this FlashMap instance (e.g. via a hashCode)
*/ */
public FlashMap(int createdBy) { public FlashMap(int createdBy) {
this.createdBy = createdBy; this.createdBy = createdBy;

View File

@ -17,6 +17,7 @@
package org.springframework.web.servlet; package org.springframework.web.servlet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/** /**
* A strategy interface for storing, retrieving, and managing {@code FlashMap} * A strategy interface for storing, retrieving, and managing {@code FlashMap}
@ -64,8 +65,9 @@ public interface FlashMapManager {
* <li>Clean expired FlashMap instances. * <li>Clean expired FlashMap instances.
* </ol> * </ol>
* @param request the current request * @param request the current request
* @param response the current response
*/ */
void requestStarted(HttpServletRequest request); void requestStarted(HttpServletRequest request, HttpServletResponse response);
/** /**
* Start the expiration period of the "output" FlashMap save it in the * Start the expiration period of the "output" FlashMap save it in the
@ -73,7 +75,8 @@ public interface FlashMapManager {
* <p>The "output" FlashMap should not be saved if it is empty or if it was * <p>The "output" FlashMap should not be saved if it is empty or if it was
* not created by the current FlashMapManager instance. * not created by the current FlashMapManager instance.
* @param request the current request * @param request the current request
* @param response the current response
*/ */
void requestCompleted(HttpServletRequest request); void requestCompleted(HttpServletRequest request, HttpServletResponse response);
} }

View File

@ -23,13 +23,14 @@ import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.ModelAndView;
/** /**
* Abstract base class for {@link org.springframework.web.servlet.HandlerExceptionResolver HandlerExceptionResolver} * Abstract base class for
* implementations that support handling exceptions from {@link HandlerMethod}s rather than handlers. * {@link org.springframework.web.servlet.HandlerExceptionResolver HandlerExceptionResolver}
* implementations that support handling exceptions from handlers of type {@link HandlerMethod}.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 3.1 * @since 3.1
*/ */
public class AbstractHandlerMethodExceptionResolver extends AbstractHandlerExceptionResolver { public abstract class AbstractHandlerMethodExceptionResolver extends AbstractHandlerExceptionResolver {
/** /**
* Checks if the handler is a {@link HandlerMethod} instance and performs the check against the bean * Checks if the handler is a {@link HandlerMethod} instance and performs the check against the bean
@ -52,10 +53,10 @@ public class AbstractHandlerMethodExceptionResolver extends AbstractHandlerExcep
} }
@Override @Override
protected final ModelAndView doResolveException(HttpServletRequest request, protected final ModelAndView doResolveException(
HttpServletResponse response, HttpServletRequest request, HttpServletResponse response,
Object handler, Object handler, Exception ex) {
Exception ex) {
return doResolveHandlerMethodException(request, response, (HandlerMethod) handler, ex); return doResolveHandlerMethodException(request, response, (HandlerMethod) handler, ex);
} }
@ -73,11 +74,8 @@ public class AbstractHandlerMethodExceptionResolver extends AbstractHandlerExcep
* @param ex the exception that got thrown during handler execution * @param ex the exception that got thrown during handler execution
* @return a corresponding ModelAndView to forward to, or <code>null</code> for default processing * @return a corresponding ModelAndView to forward to, or <code>null</code> for default processing
*/ */
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request, protected abstract ModelAndView doResolveHandlerMethodException(
HttpServletResponse response, HttpServletRequest request, HttpServletResponse response,
HandlerMethod handlerMethod, HandlerMethod handlerMethod, Exception ex);
Exception ex) {
return null;
}
} }

View File

@ -21,6 +21,7 @@ import javax.servlet.http.HttpServletResponse;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.support.RequestContextUtils;
import org.springframework.web.util.UrlPathHelper; import org.springframework.web.util.UrlPathHelper;
/** /**
@ -86,7 +87,8 @@ public abstract class AbstractUrlViewController extends AbstractController {
/** /**
* Retrieves the URL path to use for lookup and delegates to * Retrieves the URL path to use for lookup and delegates to
* {@link #getViewNameForRequest}. * {@link #getViewNameForRequest}. Also adds the content of
* {@link RequestContextUtils#getInputFlashMap} to the model.
*/ */
@Override @Override
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) { protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) {
@ -95,7 +97,7 @@ public abstract class AbstractUrlViewController extends AbstractController {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Returning view name '" + viewName + "' for lookup path [" + lookupPath + "]"); logger.debug("Returning view name '" + viewName + "' for lookup path [" + lookupPath + "]");
} }
return new ModelAndView(viewName); return new ModelAndView(viewName, RequestContextUtils.getInputFlashMap(request));
} }
/** /**

View File

@ -20,6 +20,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.support.RequestContextUtils;
/** /**
* <p>Trivial controller that always returns a named view. The view * <p>Trivial controller that always returns a named view. The view
@ -87,12 +88,13 @@ public class ParameterizableViewController extends AbstractController {
/** /**
* Return a ModelAndView object with the specified view name. * Return a ModelAndView object with the specified view name.
* The content of {@link RequestContextUtils#getInputFlashMap} is also added to the model.
* @see #getViewName() * @see #getViewName()
*/ */
@Override @Override
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception { throws Exception {
return new ModelAndView(getViewName()); return new ModelAndView(getViewName(), RequestContextUtils.getInputFlashMap(request));
} }
} }

View File

@ -173,7 +173,7 @@ public final class ProducesRequestCondition extends AbstractRequestCondition<Pro
* <ol> * <ol>
* <li>Sort 'Accept' header media types by quality value via * <li>Sort 'Accept' header media types by quality value via
* {@link MediaType#sortByQualityValue(List)} and iterate the list. * {@link MediaType#sortByQualityValue(List)} and iterate the list.
* <li>Get the lowest index of matching media types from each "produces" * <li>Get the first index of matching media types in each "produces"
* condition first matching with {@link MediaType#equals(Object)} and * condition first matching with {@link MediaType#equals(Object)} and
* then with {@link MediaType#includes(MediaType)}. * then with {@link MediaType#includes(MediaType)}.
* <li>If a lower index is found, the condition at that index wins. * <li>If a lower index is found, the condition at that index wins.
@ -220,7 +220,9 @@ public final class ProducesRequestCondition extends AbstractRequestCondition<Pro
private int indexOfEqualMediaType(MediaType mediaType) { private int indexOfEqualMediaType(MediaType mediaType) {
for (int i = 0; i < getExpressionsToCompare().size(); i++) { for (int i = 0; i < getExpressionsToCompare().size(); i++) {
if (mediaType.equals(getExpressionsToCompare().get(i).getMediaType())) { MediaType currentMediaType = getExpressionsToCompare().get(i).getMediaType();
if (mediaType.getType().equalsIgnoreCase(currentMediaType.getType()) &&
mediaType.getSubtype().equalsIgnoreCase(currentMediaType.getSubtype())) {
return i; return i;
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -39,13 +39,13 @@ import org.springframework.web.method.support.ModelAndViewContainer;
/** /**
* Resolves {@link HttpEntity} method argument values and also handles * Resolves {@link HttpEntity} method argument values and also handles
* both {@link HttpEntity} and {@link ResponseEntity} return values. * both {@link HttpEntity} and {@link ResponseEntity} return values.
* *
* <p>An {@link HttpEntity} return type has a set purpose. Therefore this * <p>An {@link HttpEntity} return type has a set purpose. Therefore this
* handler should be configured ahead of handlers that support any return * handler should be configured ahead of handlers that support any return
* value type annotated with {@code @ModelAttribute} or {@code @ResponseBody} * value type annotated with {@code @ModelAttribute} or {@code @ResponseBody}
* to ensure they don't take over. * to ensure they don't take over.
* *
* @author Arjen Poutsma * @author Arjen Poutsma
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 3.1 * @since 3.1
@ -66,10 +66,9 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
return HttpEntity.class.equals(parameterType) || ResponseEntity.class.equals(parameterType); return HttpEntity.class.equals(parameterType) || ResponseEntity.class.equals(parameterType);
} }
public Object resolveArgument(MethodParameter parameter, public Object resolveArgument(
ModelAndViewContainer mavContainer, MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
WebDataBinderFactory binderFactory)
throws IOException, HttpMediaTypeNotSupportedException { throws IOException, HttpMediaTypeNotSupportedException {
HttpInputMessage inputMessage = createInputMessage(webRequest); HttpInputMessage inputMessage = createInputMessage(webRequest);
@ -100,11 +99,11 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
+ "in method " + parameter.getMethod() + "is not parameterized"); + "in method " + parameter.getMethod() + "is not parameterized");
} }
public void handleReturnValue(Object returnValue, public void handleReturnValue(
MethodParameter returnType, Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
NativeWebRequest webRequest) throws Exception { throws Exception {
mavContainer.setRequestHandled(true); mavContainer.setRequestHandled(true);
if (returnValue == null) { if (returnValue == null) {
@ -135,4 +134,4 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
} }
} }
} }

View File

@ -25,15 +25,15 @@ import org.springframework.web.servlet.SmartView;
import org.springframework.web.servlet.View; import org.springframework.web.servlet.View;
/** /**
* Handles return values of type {@link ModelAndView} copying view and model * Handles return values of type {@link ModelAndView} copying view and model
* information to the {@link ModelAndViewContainer}. * information to the {@link ModelAndViewContainer}.
* *
* <p>If the return value is {@code null}, the * <p>If the return value is {@code null}, the
* {@link ModelAndViewContainer#setRequestHandled(boolean)} flag is set to * {@link ModelAndViewContainer#setRequestHandled(boolean)} flag is set to
* {@code false} to indicate the request was handled directly. * {@code false} to indicate the request was handled directly.
* *
* <p>A {@link ModelAndView} return type has a set purpose. Therefore this * <p>A {@link ModelAndView} return type has a set purpose. Therefore this
* handler should be configured ahead of handlers that support any return * handler should be configured ahead of handlers that support any return
* value type annotated with {@code @ModelAttribute} or {@code @ResponseBody} * value type annotated with {@code @ModelAttribute} or {@code @ResponseBody}
* to ensure they don't take over. * to ensure they don't take over.
* *
@ -41,20 +41,21 @@ import org.springframework.web.servlet.View;
* @since 3.1 * @since 3.1
*/ */
public class ModelAndViewMethodReturnValueHandler implements HandlerMethodReturnValueHandler { public class ModelAndViewMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
public boolean supportsReturnType(MethodParameter returnType) { public boolean supportsReturnType(MethodParameter returnType) {
return ModelAndView.class.isAssignableFrom(returnType.getParameterType()); return ModelAndView.class.isAssignableFrom(returnType.getParameterType());
} }
public void handleReturnValue(Object returnValue, public void handleReturnValue(
MethodParameter returnType, Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
NativeWebRequest webRequest) throws Exception { throws Exception {
if (returnValue == null) { if (returnValue == null) {
mavContainer.setRequestHandled(true); mavContainer.setRequestHandled(true);
return; return;
} }
ModelAndView mav = (ModelAndView) returnValue; ModelAndView mav = (ModelAndView) returnValue;
if (mav.isReference()) { if (mav.isReference()) {
String viewName = mav.getViewName(); String viewName = mav.getViewName();
@ -75,4 +76,4 @@ public class ModelAndViewMethodReturnValueHandler implements HandlerMethodReturn
mavContainer.addAllAttributes(mav.getModel()); mavContainer.addAllAttributes(mav.getModel());
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -29,25 +29,25 @@ import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.annotation.ModelAndViewResolver; import org.springframework.web.servlet.mvc.annotation.ModelAndViewResolver;
/** /**
* This return value handler is intended to be ordered after all others as it * This return value handler is intended to be ordered after all others as it
* attempts to handle _any_ return value type (i.e. returns {@code true} for * attempts to handle _any_ return value type (i.e. returns {@code true} for
* all return types). * all return types).
* *
* <p>The return value is handled either with a {@link ModelAndViewResolver} * <p>The return value is handled either with a {@link ModelAndViewResolver}
* or otherwise by regarding it as a model attribute if it is a non-simple * or otherwise by regarding it as a model attribute if it is a non-simple
* type. If neither of these succeeds (essentially simple type other than * type. If neither of these succeeds (essentially simple type other than
* String), {@link UnsupportedOperationException} is raised. * String), {@link UnsupportedOperationException} is raised.
* *
* <p><strong>Note:</strong> This class is primarily needed to support * <p><strong>Note:</strong> This class is primarily needed to support
* {@link ModelAndViewResolver}, which unfortunately cannot be properly * {@link ModelAndViewResolver}, which unfortunately cannot be properly
* adapted to the {@link HandlerMethodReturnValueHandler} contract since the * adapted to the {@link HandlerMethodReturnValueHandler} contract since the
* {@link HandlerMethodReturnValueHandler#supportsReturnType} method * {@link HandlerMethodReturnValueHandler#supportsReturnType} method
* cannot be implemented. Hence {@code ModelAndViewResolver}s are limited * cannot be implemented. Hence {@code ModelAndViewResolver}s are limited
* to always being invoked at the end after all other return value * to always being invoked at the end after all other return value
* handlers have been given a chance. It is recommended to re-implement * handlers have been given a chance. It is recommended to re-implement
* a {@code ModelAndViewResolver} as {@code HandlerMethodReturnValueHandler}, * a {@code ModelAndViewResolver} as {@code HandlerMethodReturnValueHandler},
* which also provides better access to the return type and method information. * which also provides better access to the return type and method information.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 3.1 * @since 3.1
*/ */
@ -71,10 +71,10 @@ public class ModelAndViewResolverMethodReturnValueHandler implements HandlerMeth
return true; return true;
} }
public void handleReturnValue(Object returnValue, public void handleReturnValue(
MethodParameter returnType, Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, ModelAndViewContainer mavContainer, NativeWebRequest request)
NativeWebRequest request) throws Exception { throws Exception {
if (this.mavResolvers != null) { if (this.mavResolvers != null) {
for (ModelAndViewResolver mavResolver : this.mavResolvers) { for (ModelAndViewResolver mavResolver : this.mavResolvers) {
@ -93,7 +93,7 @@ public class ModelAndViewResolverMethodReturnValueHandler implements HandlerMeth
} }
} }
// No suitable ModelAndViewResolver.. // No suitable ModelAndViewResolver..
if (this.modelAttributeProcessor.supportsReturnType(returnType)) { if (this.modelAttributeProcessor.supportsReturnType(returnType)) {
this.modelAttributeProcessor.handleReturnValue(returnValue, returnType, mavContainer, request); this.modelAttributeProcessor.handleReturnValue(returnValue, returnType, mavContainer, request);
@ -104,4 +104,4 @@ public class ModelAndViewResolverMethodReturnValueHandler implements HandlerMeth
} }
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -30,8 +30,8 @@ import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.mvc.support.RedirectAttributesModelMap; import org.springframework.web.servlet.mvc.support.RedirectAttributesModelMap;
/** /**
* Resolves method arguments of type {@link RedirectAttributes}. * Resolves method arguments of type {@link RedirectAttributes}.
* *
* <p>This resolver must be listed ahead of {@link org.springframework.web.method.annotation.ModelMethodProcessor} and * <p>This resolver must be listed ahead of {@link org.springframework.web.method.annotation.ModelMethodProcessor} and
* {@link org.springframework.web.method.annotation.MapMethodProcessor}, which support {@link Map} and {@link Model} * {@link org.springframework.web.method.annotation.MapMethodProcessor}, which support {@link Map} and {@link Model}
* arguments both of which are "super" types of {@code RedirectAttributes} * arguments both of which are "super" types of {@code RedirectAttributes}
@ -46,10 +46,11 @@ public class RedirectAttributesMethodArgumentResolver implements HandlerMethodAr
return RedirectAttributes.class.isAssignableFrom(parameter.getParameterType()); return RedirectAttributes.class.isAssignableFrom(parameter.getParameterType());
} }
public Object resolveArgument(MethodParameter parameter, public Object resolveArgument(
ModelAndViewContainer mavContainer, MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
WebDataBinderFactory binderFactory) throws Exception { throws Exception {
DataBinder dataBinder = binderFactory.createBinder(webRequest, null, null); DataBinder dataBinder = binderFactory.createBinder(webRequest, null, null);
ModelMap redirectAttributes = new RedirectAttributesModelMap(dataBinder); ModelMap redirectAttributes = new RedirectAttributesModelMap(dataBinder);
mavContainer.setRedirectModel(redirectAttributes); mavContainer.setRedirectModel(redirectAttributes);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -89,9 +89,9 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
* @param mavContainer the {@link ModelAndViewContainer} for the current request * @param mavContainer the {@link ModelAndViewContainer} for the current request
* @param providedArgs argument values to try to use without the need for view resolution * @param providedArgs argument values to try to use without the need for view resolution
*/ */
public final void invokeAndHandle(NativeWebRequest request, public final void invokeAndHandle(
ModelAndViewContainer mavContainer, NativeWebRequest request, ModelAndViewContainer mavContainer,
Object...providedArgs) throws Exception { Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(request, mavContainer, providedArgs); Object returnValue = invokeForRequest(request, mavContainer, providedArgs);
@ -124,7 +124,7 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
sb.append("[value=" + returnValue + "]"); sb.append("[value=" + returnValue + "]");
return getDetailedErrorMessage(sb.toString()); return getDetailedErrorMessage(sb.toString());
} }
/** /**
* Set the response status according to the {@link ResponseStatus} annotation. * Set the response status according to the {@link ResponseStatus} annotation.
*/ */
@ -157,4 +157,4 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
private boolean hasResponseStatus() { private boolean hasResponseStatus() {
return responseStatus != null; return responseStatus != null;
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -37,7 +37,7 @@ import org.springframework.web.multipart.MultipartRequest;
import org.springframework.web.servlet.support.RequestContextUtils; import org.springframework.web.servlet.support.RequestContextUtils;
/** /**
* Resolves request-related method argument values of the following types: * Resolves request-related method argument values of the following types:
* <ul> * <ul>
* <li>{@link WebRequest} * <li>{@link WebRequest}
* <li>{@link ServletRequest} * <li>{@link ServletRequest}
@ -57,20 +57,20 @@ public class ServletRequestMethodArgumentResolver implements HandlerMethodArgume
public boolean supportsParameter(MethodParameter parameter) { public boolean supportsParameter(MethodParameter parameter) {
Class<?> paramType = parameter.getParameterType(); Class<?> paramType = parameter.getParameterType();
return WebRequest.class.isAssignableFrom(paramType) || return WebRequest.class.isAssignableFrom(paramType) ||
ServletRequest.class.isAssignableFrom(paramType) || ServletRequest.class.isAssignableFrom(paramType) ||
MultipartRequest.class.isAssignableFrom(paramType) || MultipartRequest.class.isAssignableFrom(paramType) ||
HttpSession.class.isAssignableFrom(paramType) || HttpSession.class.isAssignableFrom(paramType) ||
Principal.class.isAssignableFrom(paramType) || Principal.class.isAssignableFrom(paramType) ||
Locale.class.equals(paramType) || Locale.class.equals(paramType) ||
InputStream.class.isAssignableFrom(paramType) || InputStream.class.isAssignableFrom(paramType) ||
Reader.class.isAssignableFrom(paramType); Reader.class.isAssignableFrom(paramType);
} }
public Object resolveArgument(MethodParameter parameter, public Object resolveArgument(
ModelAndViewContainer mavContainer, MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
WebDataBinderFactory binderFactory) throws IOException { throws IOException {
Class<?> paramType = parameter.getParameterType(); Class<?> paramType = parameter.getParameterType();
if (WebRequest.class.isAssignableFrom(paramType)) { if (WebRequest.class.isAssignableFrom(paramType)) {
@ -108,4 +108,4 @@ public class ServletRequestMethodArgumentResolver implements HandlerMethodArgume
} }
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -47,20 +47,20 @@ public class ServletResponseMethodArgumentResolver implements HandlerMethodArgum
public boolean supportsParameter(MethodParameter parameter) { public boolean supportsParameter(MethodParameter parameter) {
Class<?> paramType = parameter.getParameterType(); Class<?> paramType = parameter.getParameterType();
return ServletResponse.class.isAssignableFrom(paramType) return ServletResponse.class.isAssignableFrom(paramType)
|| OutputStream.class.isAssignableFrom(paramType) || OutputStream.class.isAssignableFrom(paramType)
|| Writer.class.isAssignableFrom(paramType); || Writer.class.isAssignableFrom(paramType);
} }
/** /**
* Set {@link ModelAndViewContainer#setRequestHandled(boolean)} to * Set {@link ModelAndViewContainer#setRequestHandled(boolean)} to
* {@code false} to indicate that the method signature provides access * {@code false} to indicate that the method signature provides access
* to the response. If subsequently the underlying method returns * to the response. If subsequently the underlying method returns
* {@code null}, the request is considered directly handled. * {@code null}, the request is considered directly handled.
*/ */
public Object resolveArgument(MethodParameter parameter, public Object resolveArgument(
ModelAndViewContainer mavContainer, MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
WebDataBinderFactory binderFactory) throws IOException { throws IOException {
mavContainer.setRequestHandled(true); mavContainer.setRequestHandled(true);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -28,7 +28,7 @@ import org.springframework.web.util.UriComponentsBuilder;
/** /**
* Resolvers argument values of type {@link UriComponentsBuilder}. * Resolvers argument values of type {@link UriComponentsBuilder}.
* *
* <p>The returned instance is initialized via * <p>The returned instance is initialized via
* {@link ServletUriComponentsBuilder#fromServletMapping(HttpServletRequest)}. * {@link ServletUriComponentsBuilder#fromServletMapping(HttpServletRequest)}.
* *
@ -41,11 +41,11 @@ public class UriComponentsBuilderMethodArgumentResolver implements HandlerMethod
return UriComponentsBuilder.class.isAssignableFrom(parameter.getParameterType()); return UriComponentsBuilder.class.isAssignableFrom(parameter.getParameterType());
} }
public Object resolveArgument(MethodParameter parameter, public Object resolveArgument(
ModelAndViewContainer mavContainer, MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
WebDataBinderFactory binderFactory) throws Exception { throws Exception {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
return ServletUriComponentsBuilder.fromServletMapping(request); return ServletUriComponentsBuilder.fromServletMapping(request);
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -27,14 +27,14 @@ import org.springframework.web.servlet.View;
/** /**
* Handles return values that are of type {@link View}. * Handles return values that are of type {@link View}.
* *
* <p>A {@code null} return value is left as-is leaving it to the configured * <p>A {@code null} return value is left as-is leaving it to the configured
* {@link RequestToViewNameTranslator} to select a view name by convention. * {@link RequestToViewNameTranslator} to select a view name by convention.
* *
* <p>A {@link View} return type has a set purpose. Therefore this handler * <p>A {@link View} return type has a set purpose. Therefore this handler
* should be configured ahead of handlers that support any return value type * should be configured ahead of handlers that support any return value type
* annotated with {@code @ModelAttribute} or {@code @ResponseBody} to ensure * annotated with {@code @ModelAttribute} or {@code @ResponseBody} to ensure
* they don't take over. * they don't take over.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 3.1 * @since 3.1
*/ */
@ -44,10 +44,11 @@ public class ViewMethodReturnValueHandler implements HandlerMethodReturnValueHan
return View.class.isAssignableFrom(returnType.getParameterType()); return View.class.isAssignableFrom(returnType.getParameterType());
} }
public void handleReturnValue(Object returnValue, public void handleReturnValue(
MethodParameter returnType, Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
NativeWebRequest webRequest) throws Exception { throws Exception {
if (returnValue == null) { if (returnValue == null) {
return; return;
} }
@ -62,7 +63,7 @@ public class ViewMethodReturnValueHandler implements HandlerMethodReturnValueHan
} }
else { else {
// should not happen // should not happen
throw new UnsupportedOperationException("Unexpected return type: " + throw new UnsupportedOperationException("Unexpected return type: " +
returnType.getParameterType().getName() + " in method: " + returnType.getMethod()); returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -26,12 +26,12 @@ import org.springframework.web.servlet.RequestToViewNameTranslator;
* Handles return values of types {@code void} and {@code String} interpreting * Handles return values of types {@code void} and {@code String} interpreting
* them as view name reference. * them as view name reference.
* *
* <p>A {@code null} return value, either due to a {@code void} return type or * <p>A {@code null} return value, either due to a {@code void} return type or
* as the actual return value is left as-is allowing the configured * as the actual return value is left as-is allowing the configured
* {@link RequestToViewNameTranslator} to select a view name by convention. * {@link RequestToViewNameTranslator} to select a view name by convention.
* *
* <p>A String return value can be interpreted in more than one ways depending * <p>A String return value can be interpreted in more than one ways depending
* on the presence of annotations like {@code @ModelAttribute} or * on the presence of annotations like {@code @ModelAttribute} or
* {@code @ResponseBody}. Therefore this handler should be configured after * {@code @ResponseBody}. Therefore this handler should be configured after
* the handlers that support these annotations. * the handlers that support these annotations.
* *
@ -45,10 +45,11 @@ public class ViewNameMethodReturnValueHandler implements HandlerMethodReturnValu
return (void.class.equals(paramType) || String.class.equals(paramType)); return (void.class.equals(paramType) || String.class.equals(paramType));
} }
public void handleReturnValue(Object returnValue, public void handleReturnValue(
MethodParameter returnType, Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
NativeWebRequest webRequest) throws Exception { throws Exception {
if (returnValue == null) { if (returnValue == null) {
return; return;
} }
@ -61,7 +62,7 @@ public class ViewNameMethodReturnValueHandler implements HandlerMethodReturnValu
} }
else { else {
// should not happen // should not happen
throw new UnsupportedOperationException("Unexpected return type: " + throw new UnsupportedOperationException("Unexpected return type: " +
returnType.getParameterType().getName() + " in method: " + returnType.getMethod()); returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
} }
} }
@ -69,7 +70,7 @@ public class ViewNameMethodReturnValueHandler implements HandlerMethodReturnValu
/** /**
* Whether the given view name is a redirect view reference. * Whether the given view name is a redirect view reference.
* @param viewName the view name to check, never {@code null} * @param viewName the view name to check, never {@code null}
* @return "true" if the given view name is recognized as a redirect view * @return "true" if the given view name is recognized as a redirect view
* reference; "false" otherwise. * reference; "false" otherwise.
*/ */
protected boolean isRedirectViewName(String viewName) { protected boolean isRedirectViewName(String viewName) {

View File

@ -22,10 +22,12 @@ import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
@ -47,24 +49,46 @@ public class DefaultFlashMapManager implements FlashMapManager {
private static final Log logger = LogFactory.getLog(DefaultFlashMapManager.class); private static final Log logger = LogFactory.getLog(DefaultFlashMapManager.class);
private int flashTimeout = 180; private int flashMapTimeout = 180;
private final UrlPathHelper urlPathHelper = new UrlPathHelper(); private UrlPathHelper urlPathHelper = new UrlPathHelper();
/** /**
* Set the amount of time in seconds after a {@link FlashMap} is saved * Set the amount of time in seconds after a {@link FlashMap} is saved
* (at request completion) and before it expires. * (at request completion) and before it expires.
* <p>The default value is 180 seconds. * <p>The default value is 180 seconds.
*/ */
public void setFlashMapTimeout(int flashTimeout) { public void setFlashMapTimeout(int flashMapTimeout) {
this.flashTimeout = flashTimeout; this.flashMapTimeout = flashMapTimeout;
}
/**
* Return the amount of time in seconds before a FlashMap expires.
*/
public int getFlashMapTimeout() {
return flashMapTimeout;
}
/**
* Set the UrlPathHelper to use to obtain the request URI.
*/
public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
Assert.notNull(urlPathHelper, "UrlPathHelper must not be null");
this.urlPathHelper = urlPathHelper;
}
/**
* Return the UrlPathHelper implementation for the request URI.
*/
public UrlPathHelper getUrlPathHelper() {
return urlPathHelper;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
* <p>An HTTP session is never created by this method. * <p>An HTTP session is never created by this method.
*/ */
public void requestStarted(HttpServletRequest request) { public final void requestStarted(HttpServletRequest request, HttpServletResponse response) {
if (request.getAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE) != null) { if (request.getAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE) != null) {
return; return;
} }
@ -164,9 +188,9 @@ public class DefaultFlashMapManager implements FlashMapManager {
} }
/** /**
* Iterate all flash maps and remove expired ones. * Check and remove expired FlashMaps instances.
*/ */
private void removeExpiredFlashMaps(HttpServletRequest request) { protected void removeExpiredFlashMaps(HttpServletRequest request) {
List<FlashMap> allMaps = retrieveFlashMaps(request, false); List<FlashMap> allMaps = retrieveFlashMaps(request, false);
if (CollectionUtils.isEmpty(allMaps)) { if (CollectionUtils.isEmpty(allMaps)) {
return; return;
@ -189,7 +213,7 @@ public class DefaultFlashMapManager implements FlashMapManager {
* {@inheritDoc} * {@inheritDoc}
* <p>An HTTP session is never created if the "output" FlashMap is empty. * <p>An HTTP session is never created if the "output" FlashMap is empty.
*/ */
public void requestCompleted(HttpServletRequest request) { public void requestCompleted(HttpServletRequest request, HttpServletResponse response) {
FlashMap flashMap = (FlashMap) request.getAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE); FlashMap flashMap = (FlashMap) request.getAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE);
if (flashMap == null) { if (flashMap == null) {
throw new IllegalStateException("requestCompleted called but \"output\" FlashMap was never created"); throw new IllegalStateException("requestCompleted called but \"output\" FlashMap was never created");
@ -198,24 +222,35 @@ public class DefaultFlashMapManager implements FlashMapManager {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Saving FlashMap=" + flashMap); logger.debug("Saving FlashMap=" + flashMap);
} }
onSaveFlashMap(flashMap, request); onSaveFlashMap(flashMap, request, response);
retrieveFlashMaps(request, true).add(flashMap); saveFlashMap(flashMap, request, response);
} }
} }
/** /**
* Update a FlashMap before it is stored in the HTTP Session. * Update a FlashMap before it is stored in the underlying storage.
* <p>The default implementation starts the expiration period and ensures the * <p>The default implementation starts the expiration period and ensures the
* target request path is decoded and normalized if it is relative. * target request path is decoded and normalized if it is relative.
* @param flashMap the flash map to be saved * @param flashMap the flash map to be saved
* @param request the current request * @param request the current request
* @param response the current response
*/ */
protected void onSaveFlashMap(FlashMap flashMap, HttpServletRequest request) { protected void onSaveFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response) {
String targetPath = flashMap.getTargetRequestPath(); String targetPath = flashMap.getTargetRequestPath();
flashMap.setTargetRequestPath(decodeAndNormalizePath(targetPath, request)); flashMap.setTargetRequestPath(decodeAndNormalizePath(targetPath, request));
flashMap.startExpirationPeriod(this.flashTimeout); flashMap.startExpirationPeriod(this.flashMapTimeout);
} }
/**
* Save the FlashMap in the underlying storage.
* @param flashMap the FlashMap to save
* @param request the current request
* @param response the current response
*/
protected void saveFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response) {
retrieveFlashMaps(request, true).add(flashMap);
}
private String decodeAndNormalizePath(String path, HttpServletRequest request) { private String decodeAndNormalizePath(String path, HttpServletRequest request) {
if (path != null) { if (path != null) {
path = this.urlPathHelper.decodeRequestString(request, path); path = this.urlPathHelper.decodeRequestString(request, path);

View File

@ -0,0 +1,71 @@
/*
* Copyright 2002-2012 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.servlet.mvc;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.ui.ModelMap;
import org.springframework.web.servlet.FlashMapManager;
import org.springframework.web.servlet.ModelAndView;
/**
* Test fixture with a ParameterizableViewController.
*
* @author Rossen Stoyanchev
* @since 3.1.1
*/
public class ParameterizableViewControllerTests {
private ParameterizableViewController controller;
private MockHttpServletRequest request;
@Before
public void setup() {
this.controller = new ParameterizableViewController();
this.request = new MockHttpServletRequest("GET", "/");
}
@Test
public void handleRequestWithViewName() throws Exception {
String viewName = "testView";
this.controller.setViewName(viewName);
ModelAndView mav = this.controller.handleRequest(this.request, new MockHttpServletResponse());
assertEquals(viewName, mav.getViewName());
assertTrue(mav.getModel().isEmpty());
}
@Test
public void handleRequestWithoutViewName() throws Exception {
ModelAndView mav = this.controller.handleRequest(this.request, new MockHttpServletResponse());
assertNull(mav.getViewName());
assertTrue(mav.getModel().isEmpty());
}
@Test
public void handleRequestWithFlashAttributes() throws Exception {
this.request.setAttribute(FlashMapManager.INPUT_FLASH_MAP_ATTRIBUTE, new ModelMap("name", "value"));
ModelAndView mav = this.controller.handleRequest(this.request, new MockHttpServletResponse());
assertEquals(1, mav.getModel().size());
assertEquals("value", mav.getModel().get("name"));
}
}

View File

@ -20,8 +20,10 @@ import junit.framework.TestCase;
import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.ui.ModelMap;
import org.springframework.util.AntPathMatcher; import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher; import org.springframework.util.PathMatcher;
import org.springframework.web.servlet.FlashMapManager;
import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.ModelAndView;
@ -150,6 +152,17 @@ public class UrlFilenameViewControllerTests extends TestCase {
assertTrue(mv.getModel().isEmpty()); assertTrue(mv.getModel().isEmpty());
} }
public void testWithFlashAttributes() throws Exception {
UrlFilenameViewController ctrl = new UrlFilenameViewController();
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/index");
request.setAttribute(FlashMapManager.INPUT_FLASH_MAP_ATTRIBUTE, new ModelMap("name", "value"));
MockHttpServletResponse response = new MockHttpServletResponse();
ModelAndView mv = ctrl.handleRequest(request, response);
assertEquals("index", mv.getViewName());
assertEquals(1, mv.getModel().size());
assertEquals("value", mv.getModel().get("name"));
}
private void exposePathInMapping(MockHttpServletRequest request, String mapping) { private void exposePathInMapping(MockHttpServletRequest request, String mapping) {
String pathInMapping = this.pathMatcher.extractPathWithinPattern(mapping, request.getRequestURI()); String pathInMapping = this.pathMatcher.extractPathWithinPattern(mapping, request.getRequestURI());
request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathInMapping); request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathInMapping);

View File

@ -218,6 +218,20 @@ public class ProducesRequestConditionTests {
assertTrue(condition2.compareTo(condition1, request) > 0); assertTrue(condition2.compareTo(condition1, request) > 0);
} }
// SPR-9021
@Test
public void compareToMediaTypeAllWithParameter() {
MockHttpServletRequest request = new MockHttpServletRequest();
request.addHeader("Accept", "*/*;q=0.9");
ProducesRequestCondition condition1 = new ProducesRequestCondition();
ProducesRequestCondition condition2 = new ProducesRequestCondition("application/json");
assertTrue(condition1.compareTo(condition2, request) < 0);
assertTrue(condition2.compareTo(condition1, request) > 0);
}
@Test @Test
public void compareToEqualMatch() { public void compareToEqualMatch() {
MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletRequest request = new MockHttpServletRequest();

View File

@ -37,7 +37,7 @@ import org.springframework.web.servlet.view.RedirectView;
/** /**
* Test fixture with {@link ModelAndViewMethodReturnValueHandler}. * Test fixture with {@link ModelAndViewMethodReturnValueHandler}.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
*/ */
public class ModelAndViewMethodReturnValueHandlerTests { public class ModelAndViewMethodReturnValueHandlerTests {
@ -57,7 +57,7 @@ public class ModelAndViewMethodReturnValueHandlerTests {
this.webRequest = new ServletWebRequest(new MockHttpServletRequest()); this.webRequest = new ServletWebRequest(new MockHttpServletRequest());
this.returnParamModelAndView = getReturnValueParam("modelAndView"); this.returnParamModelAndView = getReturnValueParam("modelAndView");
} }
@Test @Test
public void supportsReturnType() throws Exception { public void supportsReturnType() throws Exception {
assertTrue(handler.supportsReturnType(returnParamModelAndView)); assertTrue(handler.supportsReturnType(returnParamModelAndView));
@ -68,7 +68,7 @@ public class ModelAndViewMethodReturnValueHandlerTests {
public void handleViewReference() throws Exception { public void handleViewReference() throws Exception {
ModelAndView mav = new ModelAndView("viewName", "attrName", "attrValue"); ModelAndView mav = new ModelAndView("viewName", "attrName", "attrValue");
handler.handleReturnValue(mav, returnParamModelAndView, mavContainer, webRequest); handler.handleReturnValue(mav, returnParamModelAndView, mavContainer, webRequest);
assertEquals("viewName", mavContainer.getView()); assertEquals("viewName", mavContainer.getView());
assertEquals("attrValue", mavContainer.getModel().get("attrName")); assertEquals("attrValue", mavContainer.getModel().get("attrName"));
} }
@ -77,7 +77,7 @@ public class ModelAndViewMethodReturnValueHandlerTests {
public void handleViewInstance() throws Exception { public void handleViewInstance() throws Exception {
ModelAndView mav = new ModelAndView(new RedirectView(), "attrName", "attrValue"); ModelAndView mav = new ModelAndView(new RedirectView(), "attrName", "attrValue");
handler.handleReturnValue(mav, returnParamModelAndView, mavContainer, webRequest); handler.handleReturnValue(mav, returnParamModelAndView, mavContainer, webRequest);
assertEquals(RedirectView.class, mavContainer.getView().getClass()); assertEquals(RedirectView.class, mavContainer.getView().getClass());
assertEquals("attrValue", mavContainer.getModel().get("attrName")); assertEquals("attrValue", mavContainer.getModel().get("attrName"));
} }
@ -85,7 +85,7 @@ public class ModelAndViewMethodReturnValueHandlerTests {
@Test @Test
public void handleNull() throws Exception { public void handleNull() throws Exception {
handler.handleReturnValue(null, returnParamModelAndView, mavContainer, webRequest); handler.handleReturnValue(null, returnParamModelAndView, mavContainer, webRequest);
assertTrue(mavContainer.isRequestHandled()); assertTrue(mavContainer.isRequestHandled());
} }
@ -93,10 +93,10 @@ public class ModelAndViewMethodReturnValueHandlerTests {
public void handleRedirectAttributesWithViewReference() throws Exception { public void handleRedirectAttributesWithViewReference() throws Exception {
RedirectAttributesModelMap redirectAttributes = new RedirectAttributesModelMap(); RedirectAttributesModelMap redirectAttributes = new RedirectAttributesModelMap();
mavContainer.setRedirectModel(redirectAttributes); mavContainer.setRedirectModel(redirectAttributes);
ModelAndView mav = new ModelAndView(new RedirectView(), "attrName", "attrValue"); ModelAndView mav = new ModelAndView(new RedirectView(), "attrName", "attrValue");
handler.handleReturnValue(mav, returnParamModelAndView, mavContainer, webRequest); handler.handleReturnValue(mav, returnParamModelAndView, mavContainer, webRequest);
assertEquals(RedirectView.class, mavContainer.getView().getClass()); assertEquals(RedirectView.class, mavContainer.getView().getClass());
assertEquals("attrValue", mavContainer.getModel().get("attrName")); assertEquals("attrValue", mavContainer.getModel().get("attrName"));
assertSame("RedirectAttributes should be used if controller redirects", redirectAttributes, assertSame("RedirectAttributes should be used if controller redirects", redirectAttributes,
@ -107,24 +107,24 @@ public class ModelAndViewMethodReturnValueHandlerTests {
public void handleRedirectAttributesWithViewInstance() throws Exception { public void handleRedirectAttributesWithViewInstance() throws Exception {
RedirectAttributesModelMap redirectAttributes = new RedirectAttributesModelMap(); RedirectAttributesModelMap redirectAttributes = new RedirectAttributesModelMap();
mavContainer.setRedirectModel(redirectAttributes); mavContainer.setRedirectModel(redirectAttributes);
ModelAndView mav = new ModelAndView("redirect:viewName", "attrName", "attrValue"); ModelAndView mav = new ModelAndView("redirect:viewName", "attrName", "attrValue");
handler.handleReturnValue(mav, returnParamModelAndView, mavContainer, webRequest); handler.handleReturnValue(mav, returnParamModelAndView, mavContainer, webRequest);
ModelMap model = mavContainer.getModel(); ModelMap model = mavContainer.getModel();
assertEquals("redirect:viewName", mavContainer.getViewName()); assertEquals("redirect:viewName", mavContainer.getViewName());
assertEquals("attrValue", model.get("attrName")); assertEquals("attrValue", model.get("attrName"));
assertSame("RedirectAttributes should be used if controller redirects", redirectAttributes, model); assertSame("RedirectAttributes should be used if controller redirects", redirectAttributes, model);
} }
@Test @Test
public void handleRedirectAttributesWithoutRedirect() throws Exception { public void handleRedirectAttributesWithoutRedirect() throws Exception {
RedirectAttributesModelMap redirectAttributes = new RedirectAttributesModelMap(); RedirectAttributesModelMap redirectAttributes = new RedirectAttributesModelMap();
mavContainer.setRedirectModel(redirectAttributes); mavContainer.setRedirectModel(redirectAttributes);
ModelAndView mav = new ModelAndView(); ModelAndView mav = new ModelAndView();
handler.handleReturnValue(mav, returnParamModelAndView, mavContainer, webRequest); handler.handleReturnValue(mav, returnParamModelAndView, mavContainer, webRequest);
ModelMap model = mavContainer.getModel(); ModelMap model = mavContainer.getModel();
assertEquals(null, mavContainer.getView()); assertEquals(null, mavContainer.getView());
assertTrue(mavContainer.getModel().isEmpty()); assertTrue(mavContainer.getModel().isEmpty());
@ -136,7 +136,7 @@ public class ModelAndViewMethodReturnValueHandlerTests {
Method method = getClass().getDeclaredMethod(methodName); Method method = getClass().getDeclaredMethod(methodName);
return new MethodParameter(method, -1); return new MethodParameter(method, -1);
} }
ModelAndView modelAndView() { ModelAndView modelAndView() {
return null; return null;
} }
@ -144,5 +144,5 @@ public class ModelAndViewMethodReturnValueHandlerTests {
String viewName() { String viewName() {
return null; return null;
} }
} }

View File

@ -32,6 +32,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.servlet.FlashMap; import org.springframework.web.servlet.FlashMap;
import org.springframework.web.util.WebUtils; import org.springframework.web.util.WebUtils;
@ -46,15 +47,18 @@ public class DefaultFlashMapManagerTests {
private MockHttpServletRequest request; private MockHttpServletRequest request;
private MockHttpServletResponse response;
@Before @Before
public void setup() { public void setup() {
this.flashMapManager = new DefaultFlashMapManager(); this.flashMapManager = new DefaultFlashMapManager();
this.request = new MockHttpServletRequest(); this.request = new MockHttpServletRequest();
this.response = new MockHttpServletResponse();
} }
@Test @Test
public void requestStarted() { public void requestStarted() {
this.flashMapManager.requestStarted(this.request); this.flashMapManager.requestStarted(this.request, this.response);
FlashMap flashMap = RequestContextUtils.getOutputFlashMap(request); FlashMap flashMap = RequestContextUtils.getOutputFlashMap(request);
assertNotNull("Current FlashMap not found", flashMap); assertNotNull("Current FlashMap not found", flashMap);
@ -64,7 +68,7 @@ public class DefaultFlashMapManagerTests {
public void requestStartedAlready() { public void requestStartedAlready() {
FlashMap flashMap = new FlashMap(); FlashMap flashMap = new FlashMap();
this.request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, flashMap); this.request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, flashMap);
this.flashMapManager.requestStarted(this.request); this.flashMapManager.requestStarted(this.request, this.response);
assertSame(flashMap, RequestContextUtils.getOutputFlashMap(request)); assertSame(flashMap, RequestContextUtils.getOutputFlashMap(request));
} }
@ -79,7 +83,7 @@ public class DefaultFlashMapManagerTests {
allMaps.add(flashMap); allMaps.add(flashMap);
this.request.setRequestURI("/path"); this.request.setRequestURI("/path");
this.flashMapManager.requestStarted(this.request); this.flashMapManager.requestStarted(this.request, this.response);
assertEquals(flashMap, RequestContextUtils.getInputFlashMap(this.request)); assertEquals(flashMap, RequestContextUtils.getInputFlashMap(this.request));
assertEquals("Input FlashMap should have been removed", 0, getFlashMaps().size()); assertEquals("Input FlashMap should have been removed", 0, getFlashMaps().size());
@ -98,7 +102,7 @@ public class DefaultFlashMapManagerTests {
this.request.setAttribute(WebUtils.FORWARD_REQUEST_URI_ATTRIBUTE, "/accounts"); this.request.setAttribute(WebUtils.FORWARD_REQUEST_URI_ATTRIBUTE, "/accounts");
this.request.setRequestURI("/mvc/accounts"); this.request.setRequestURI("/mvc/accounts");
this.flashMapManager.requestStarted(this.request); this.flashMapManager.requestStarted(this.request, this.response);
assertEquals(flashMap, RequestContextUtils.getInputFlashMap(this.request)); assertEquals(flashMap, RequestContextUtils.getInputFlashMap(this.request));
assertEquals("Input FlashMap should have been removed", 0, getFlashMaps().size()); assertEquals("Input FlashMap should have been removed", 0, getFlashMaps().size());
@ -114,7 +118,7 @@ public class DefaultFlashMapManagerTests {
allMaps.add(flashMap); allMaps.add(flashMap);
this.request.setRequestURI("/path/"); this.request.setRequestURI("/path/");
this.flashMapManager.requestStarted(this.request); this.flashMapManager.requestStarted(this.request, this.response);
assertEquals(flashMap, RequestContextUtils.getInputFlashMap(this.request)); assertEquals(flashMap, RequestContextUtils.getInputFlashMap(this.request));
assertEquals("Input FlashMap should have been removed", 0, getFlashMaps().size()); assertEquals("Input FlashMap should have been removed", 0, getFlashMaps().size());
@ -130,21 +134,21 @@ public class DefaultFlashMapManagerTests {
allMaps.add(flashMap); allMaps.add(flashMap);
this.request.setParameter("number", (String) null); this.request.setParameter("number", (String) null);
this.flashMapManager.requestStarted(this.request); this.flashMapManager.requestStarted(this.request, this.response);
assertNull(RequestContextUtils.getInputFlashMap(this.request)); assertNull(RequestContextUtils.getInputFlashMap(this.request));
assertEquals("FlashMap should not have been removed", 1, getFlashMaps().size()); assertEquals("FlashMap should not have been removed", 1, getFlashMaps().size());
clearFlashMapRequestAttributes(); clearFlashMapRequestAttributes();
this.request.setParameter("number", "two"); this.request.setParameter("number", "two");
this.flashMapManager.requestStarted(this.request); this.flashMapManager.requestStarted(this.request, this.response);
assertNull(RequestContextUtils.getInputFlashMap(this.request)); assertNull(RequestContextUtils.getInputFlashMap(this.request));
assertEquals("FlashMap should not have been removed", 1, getFlashMaps().size()); assertEquals("FlashMap should not have been removed", 1, getFlashMaps().size());
clearFlashMapRequestAttributes(); clearFlashMapRequestAttributes();
this.request.setParameter("number", "one"); this.request.setParameter("number", "one");
this.flashMapManager.requestStarted(this.request); this.flashMapManager.requestStarted(this.request, this.response);
assertEquals(flashMap, RequestContextUtils.getInputFlashMap(this.request)); assertEquals(flashMap, RequestContextUtils.getInputFlashMap(this.request));
assertEquals("Input FlashMap should have been removed", 0, getFlashMaps().size()); assertEquals("Input FlashMap should have been removed", 0, getFlashMaps().size());
@ -163,14 +167,14 @@ public class DefaultFlashMapManagerTests {
allMaps.add(flashMap); allMaps.add(flashMap);
this.request.setParameter("id", "1"); this.request.setParameter("id", "1");
this.flashMapManager.requestStarted(this.request); this.flashMapManager.requestStarted(this.request, this.response);
assertNull(RequestContextUtils.getInputFlashMap(this.request)); assertNull(RequestContextUtils.getInputFlashMap(this.request));
assertEquals("FlashMap should not have been removed", 1, getFlashMaps().size()); assertEquals("FlashMap should not have been removed", 1, getFlashMaps().size());
clearFlashMapRequestAttributes(); clearFlashMapRequestAttributes();
this.request.addParameter("id", "2"); this.request.addParameter("id", "2");
this.flashMapManager.requestStarted(this.request); this.flashMapManager.requestStarted(this.request, this.response);
assertEquals(flashMap, RequestContextUtils.getInputFlashMap(this.request)); assertEquals(flashMap, RequestContextUtils.getInputFlashMap(this.request));
assertEquals("Input FlashMap should have been removed", 0, getFlashMaps().size()); assertEquals("Input FlashMap should have been removed", 0, getFlashMaps().size());
@ -196,7 +200,7 @@ public class DefaultFlashMapManagerTests {
Collections.shuffle(allMaps); Collections.shuffle(allMaps);
this.request.setRequestURI("/one/two"); this.request.setRequestURI("/one/two");
this.flashMapManager.requestStarted(this.request); this.flashMapManager.requestStarted(this.request, this.response);
assertEquals(flashMapTwo, request.getAttribute(INPUT_FLASH_MAP_ATTRIBUTE)); assertEquals(flashMapTwo, request.getAttribute(INPUT_FLASH_MAP_ATTRIBUTE));
} }
@ -210,15 +214,15 @@ public class DefaultFlashMapManagerTests {
flashMap.startExpirationPeriod(0); flashMap.startExpirationPeriod(0);
} }
Thread.sleep(100); Thread.sleep(100);
this.flashMapManager.requestStarted(this.request); this.flashMapManager.requestStarted(this.request, this.response);
assertEquals(0, allMaps.size()); assertEquals(0, allMaps.size());
} }
@Test @Test
public void saveFlashMapWithoutAttributes() throws InterruptedException { public void saveFlashMapWithoutAttributes() throws InterruptedException {
this.flashMapManager.requestStarted(this.request); this.flashMapManager.requestStarted(this.request, this.response);
this.flashMapManager.requestCompleted(this.request); this.flashMapManager.requestCompleted(this.request, this.response);
assertNull(getFlashMaps()); assertNull(getFlashMaps());
} }
@ -227,19 +231,19 @@ public class DefaultFlashMapManagerTests {
public void saveFlashMapNotCreatedByThisManager() throws InterruptedException { public void saveFlashMapNotCreatedByThisManager() throws InterruptedException {
FlashMap flashMap = new FlashMap(); FlashMap flashMap = new FlashMap();
this.request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, flashMap); this.request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, flashMap);
this.flashMapManager.requestCompleted(this.request); this.flashMapManager.requestCompleted(this.request, this.response);
assertNull(getFlashMaps()); assertNull(getFlashMaps());
} }
@Test @Test
public void saveFlashMapWithAttributes() throws InterruptedException { public void saveFlashMapWithAttributes() throws InterruptedException {
this.flashMapManager.requestStarted(this.request); this.flashMapManager.requestStarted(this.request, this.response);
FlashMap flashMap = RequestContextUtils.getOutputFlashMap(this.request); FlashMap flashMap = RequestContextUtils.getOutputFlashMap(this.request);
flashMap.put("name", "value"); flashMap.put("name", "value");
this.flashMapManager.setFlashMapTimeout(0); this.flashMapManager.setFlashMapTimeout(0);
this.flashMapManager.requestCompleted(this.request); this.flashMapManager.requestCompleted(this.request, this.response);
Thread.sleep(100); Thread.sleep(100);
@ -252,49 +256,49 @@ public class DefaultFlashMapManagerTests {
@Test @Test
public void decodeTargetPath() throws InterruptedException { public void decodeTargetPath() throws InterruptedException {
this.flashMapManager.requestStarted(this.request); this.flashMapManager.requestStarted(this.request, this.response);
FlashMap flashMap = RequestContextUtils.getOutputFlashMap(this.request); FlashMap flashMap = RequestContextUtils.getOutputFlashMap(this.request);
flashMap.put("key", "value"); flashMap.put("key", "value");
flashMap.setTargetRequestPath("/once%20upon%20a%20time"); flashMap.setTargetRequestPath("/once%20upon%20a%20time");
this.flashMapManager.requestCompleted(this.request); this.flashMapManager.requestCompleted(this.request, this.response);
assertEquals("/once upon a time", flashMap.getTargetRequestPath()); assertEquals("/once upon a time", flashMap.getTargetRequestPath());
} }
@Test @Test
public void normalizeTargetPath() throws InterruptedException { public void normalizeTargetPath() throws InterruptedException {
this.flashMapManager.requestStarted(this.request); this.flashMapManager.requestStarted(this.request, this.response);
FlashMap flashMap = RequestContextUtils.getOutputFlashMap(this.request); FlashMap flashMap = RequestContextUtils.getOutputFlashMap(this.request);
flashMap.put("key", "value"); flashMap.put("key", "value");
flashMap.setTargetRequestPath("."); flashMap.setTargetRequestPath(".");
this.request.setRequestURI("/once/upon/a/time"); this.request.setRequestURI("/once/upon/a/time");
this.flashMapManager.requestCompleted(this.request); this.flashMapManager.requestCompleted(this.request, this.response);
assertEquals("/once/upon/a", flashMap.getTargetRequestPath()); assertEquals("/once/upon/a", flashMap.getTargetRequestPath());
flashMap.setTargetRequestPath("./"); flashMap.setTargetRequestPath("./");
this.request.setRequestURI("/once/upon/a/time"); this.request.setRequestURI("/once/upon/a/time");
this.flashMapManager.requestCompleted(this.request); this.flashMapManager.requestCompleted(this.request, this.response);
assertEquals("/once/upon/a/", flashMap.getTargetRequestPath()); assertEquals("/once/upon/a/", flashMap.getTargetRequestPath());
flashMap.setTargetRequestPath(".."); flashMap.setTargetRequestPath("..");
this.request.setRequestURI("/once/upon/a/time"); this.request.setRequestURI("/once/upon/a/time");
this.flashMapManager.requestCompleted(this.request); this.flashMapManager.requestCompleted(this.request, this.response);
assertEquals("/once/upon", flashMap.getTargetRequestPath()); assertEquals("/once/upon", flashMap.getTargetRequestPath());
flashMap.setTargetRequestPath("../"); flashMap.setTargetRequestPath("../");
this.request.setRequestURI("/once/upon/a/time"); this.request.setRequestURI("/once/upon/a/time");
this.flashMapManager.requestCompleted(this.request); this.flashMapManager.requestCompleted(this.request, this.response);
assertEquals("/once/upon/", flashMap.getTargetRequestPath()); assertEquals("/once/upon/", flashMap.getTargetRequestPath());
flashMap.setTargetRequestPath("../../only"); flashMap.setTargetRequestPath("../../only");
this.request.setRequestURI("/once/upon/a/time"); this.request.setRequestURI("/once/upon/a/time");
this.flashMapManager.requestCompleted(this.request); this.flashMapManager.requestCompleted(this.request, this.response);
assertEquals("/once/only", flashMap.getTargetRequestPath()); assertEquals("/once/only", flashMap.getTargetRequestPath());
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2010 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -24,6 +24,7 @@ package org.springframework.http;
* @author Arjen Poutsma * @author Arjen Poutsma
* @see HttpStatus.Series * @see HttpStatus.Series
* @see <a href="http://www.iana.org/assignments/http-status-codes">HTTP Status Code Registry</a> * @see <a href="http://www.iana.org/assignments/http-status-codes">HTTP Status Code Registry</a>
* @see <a href="http://en.wikipedia.org/wiki/List_of_HTTP_status_codes">List of HTTP status codes - Wikipedia</a>
*/ */
public enum HttpStatus { public enum HttpStatus {
@ -44,6 +45,12 @@ public enum HttpStatus {
* @see <a href="http://tools.ietf.org/html/rfc2518#section-10.1">WebDAV</a> * @see <a href="http://tools.ietf.org/html/rfc2518#section-10.1">WebDAV</a>
*/ */
PROCESSING(102, "Processing"), PROCESSING(102, "Processing"),
/**
* {@code 103 Checkpoint}.
* @see <a href="http://code.google.com/p/gears/wiki/ResumableHttpRequestsProposal">A proposal for supporting
* resumable POST/PUT HTTP requests in HTTP/1.0</a>
*/
CHECKPOINT(103, "Checkpoint"),
// 2xx Success // 2xx Success
@ -140,6 +147,12 @@ public enum HttpStatus {
* @see <a href="http://tools.ietf.org/html/rfc2616#section-10.3.8">HTTP/1.1</a> * @see <a href="http://tools.ietf.org/html/rfc2616#section-10.3.8">HTTP/1.1</a>
*/ */
TEMPORARY_REDIRECT(307, "Temporary Redirect"), TEMPORARY_REDIRECT(307, "Temporary Redirect"),
/**
* {@code 308 Resume Incomplete}.
* @see <a href="http://code.google.com/p/gears/wiki/ResumableHttpRequestsProposal">A proposal for supporting
* resumable POST/PUT HTTP requests in HTTP/1.0</a>
*/
RESUME_INCOMPLETE(308, "Resume Incomplete"),
// --- 4xx Client Error --- // --- 4xx Client Error ---
@ -187,7 +200,7 @@ public enum HttpStatus {
* {@code 408 Request Timeout}. * {@code 408 Request Timeout}.
* @see <a href="http://tools.ietf.org/html/rfc2616#section-10.4.9">HTTP/1.1</a> * @see <a href="http://tools.ietf.org/html/rfc2616#section-10.4.9">HTTP/1.1</a>
*/ */
REQUEST_TIMEOUT(408, "Request Time-out"), REQUEST_TIMEOUT(408, "Request Timeout"),
/** /**
* {@code 409 Conflict}. * {@code 409 Conflict}.
* @see <a href="http://tools.ietf.org/html/rfc2616#section-10.4.10">HTTP/1.1</a> * @see <a href="http://tools.ietf.org/html/rfc2616#section-10.4.10">HTTP/1.1</a>
@ -217,7 +230,7 @@ public enum HttpStatus {
* {@code 414 Request-URI Too Long}. * {@code 414 Request-URI Too Long}.
* @see <a href="http://tools.ietf.org/html/rfc2616#section-10.4.15">HTTP/1.1</a> * @see <a href="http://tools.ietf.org/html/rfc2616#section-10.4.15">HTTP/1.1</a>
*/ */
REQUEST_URI_TOO_LONG(414, "Request-URI Too Large"), REQUEST_URI_TOO_LONG(414, "Request-URI Too Long"),
/** /**
* {@code 415 Unsupported Media Type}. * {@code 415 Unsupported Media Type}.
* @see <a href="http://tools.ietf.org/html/rfc2616#section-10.4.16">HTTP/1.1</a> * @see <a href="http://tools.ietf.org/html/rfc2616#section-10.4.16">HTTP/1.1</a>
@ -233,6 +246,11 @@ public enum HttpStatus {
* @see <a href="http://tools.ietf.org/html/rfc2616#section-10.4.18">HTTP/1.1</a> * @see <a href="http://tools.ietf.org/html/rfc2616#section-10.4.18">HTTP/1.1</a>
*/ */
EXPECTATION_FAILED(417, "Expectation Failed"), EXPECTATION_FAILED(417, "Expectation Failed"),
/**
* {@code 418 I'm a teapot}.
* @see <a href="http://tools.ietf.org/html/rfc2324#section-2.3.2">HTCPCP/1.0</a>
*/
I_AM_A_TEAPOT(418, "I'm a teapot"),
/** /**
* {@code 419 Insufficient Space on Resource}. * {@code 419 Insufficient Space on Resource}.
* @see <a href="http://tools.ietf.org/html/draft-ietf-webdav-protocol-05#section-10.4">WebDAV Draft</a> * @see <a href="http://tools.ietf.org/html/draft-ietf-webdav-protocol-05#section-10.4">WebDAV Draft</a>
@ -268,6 +286,24 @@ public enum HttpStatus {
* @see <a href="http://tools.ietf.org/html/rfc2817#section-6">Upgrading to TLS Within HTTP/1.1</a> * @see <a href="http://tools.ietf.org/html/rfc2817#section-6">Upgrading to TLS Within HTTP/1.1</a>
*/ */
UPGRADE_REQUIRED(426, "Upgrade Required"), UPGRADE_REQUIRED(426, "Upgrade Required"),
/**
* {@code 428 Precondition Required}.
* @see <a href="http://tools.ietf.org/html/draft-nottingham-http-new-status-02#section-3">Additional HTTP Status
* Codes</a>
*/
PRECONDITION_REQUIRED(428, "Precondition Required"),
/**
* {@code 429 Too Many Requests}.
* @see <a href="http://tools.ietf.org/html/draft-nottingham-http-new-status-02#section-4">Additional HTTP Status
* Codes</a>
*/
TOO_MANY_REQUESTS(429, "Too Many Requests"),
/**
* {@code 431 Request Header Fields Too Large}.
* @see <a href="http://tools.ietf.org/html/draft-nottingham-http-new-status-02#section-5">Additional HTTP Status
* Codes</a>
*/
REQUEST_HEADER_FIELDS_TOO_LARGE(431, "Request Header Fields Too Large"),
// --- 5xx Server Error --- // --- 5xx Server Error ---
@ -295,7 +331,7 @@ public enum HttpStatus {
* {@code 504 Gateway Timeout}. * {@code 504 Gateway Timeout}.
* @see <a href="http://tools.ietf.org/html/rfc2616#section-10.5.5">HTTP/1.1</a> * @see <a href="http://tools.ietf.org/html/rfc2616#section-10.5.5">HTTP/1.1</a>
*/ */
GATEWAY_TIMEOUT(504, "Gateway Time-out"), GATEWAY_TIMEOUT(504, "Gateway Timeout"),
/** /**
* {@code 505 HTTP Version Not Supported}. * {@code 505 HTTP Version Not Supported}.
* @see <a href="http://tools.ietf.org/html/rfc2616#section-10.5.6">HTTP/1.1</a> * @see <a href="http://tools.ietf.org/html/rfc2616#section-10.5.6">HTTP/1.1</a>
@ -316,11 +352,22 @@ public enum HttpStatus {
* @see <a href="http://tools.ietf.org/html/draft-ietf-webdav-bind-27#section-7.2">WebDAV Binding Extensions</a> * @see <a href="http://tools.ietf.org/html/draft-ietf-webdav-bind-27#section-7.2">WebDAV Binding Extensions</a>
*/ */
LOOP_DETECTED(508, "Loop Detected"), LOOP_DETECTED(508, "Loop Detected"),
/**
* {@code 509 Bandwidth Limit Exceeded}
*/
BANDWIDTH_LIMIT_EXCEEDED(509, "Bandwidth Limit Exceeded"),
/** /**
* {@code 510 Not Extended} * {@code 510 Not Extended}
* @see <a href="http://tools.ietf.org/html/rfc2774#section-7">HTTP Extension Framework</a> * @see <a href="http://tools.ietf.org/html/rfc2774#section-7">HTTP Extension Framework</a>
*/ */
NOT_EXTENDED(510, "Not Extended"); NOT_EXTENDED(510, "Not Extended"),
/**
* {@code 511 Network Authentication Required}.
* @see <a href="http://tools.ietf.org/html/draft-nottingham-http-new-status-02#section-6">Additional HTTP Status
* Codes</a>
*/
NETWORK_AUTHENTICATION_REQUIRED(511, "Network Authentication Required");
private final int value; private final int value;

View File

@ -331,7 +331,7 @@ public class MediaType implements Comparable<MediaType> {
String attribute = entry.getKey(); String attribute = entry.getKey();
String value = entry.getValue(); String value = entry.getValue();
checkParameters(attribute, value); checkParameters(attribute, value);
m.put(attribute, unquote(value)); m.put(attribute, value);
} }
this.parameters = Collections.unmodifiableMap(m); this.parameters = Collections.unmodifiableMap(m);
} }
@ -428,7 +428,7 @@ public class MediaType implements Comparable<MediaType> {
*/ */
public Charset getCharSet() { public Charset getCharSet() {
String charSet = getParameter(PARAM_CHARSET); String charSet = getParameter(PARAM_CHARSET);
return (charSet != null ? Charset.forName(charSet) : null); return (charSet != null ? Charset.forName(unquote(charSet)) : null);
} }
/** /**
@ -438,7 +438,7 @@ public class MediaType implements Comparable<MediaType> {
*/ */
public double getQualityValue() { public double getQualityValue() {
String qualityFactory = getParameter(PARAM_QUALITY_FACTOR); String qualityFactory = getParameter(PARAM_QUALITY_FACTOR);
return (qualityFactory != null ? Double.parseDouble(qualityFactory) : 1D); return (qualityFactory != null ? Double.parseDouble(unquote(qualityFactory)) : 1D);
} }
/** /**

View File

@ -0,0 +1,35 @@
/*
* Copyright 2002-2012 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.http.client;
import java.io.IOException;
import org.springframework.http.HttpStatus;
/**
* Abstract base for {@link ClientHttpResponse}.
*
* @author Arjen Poutsma
* @since 3.1.1
*/
public abstract class AbstractClientHttpResponse implements ClientHttpResponse {
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.valueOf(getRawStatusCode());
}
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -47,6 +47,10 @@ final class BufferingClientHttpResponseWrapper implements ClientHttpResponse {
return this.response.getStatusCode(); return this.response.getStatusCode();
} }
public int getRawStatusCode() throws IOException {
return this.response.getRawStatusCode();
}
public String getStatusText() throws IOException { public String getStatusText() throws IOException {
return this.response.getStatusText(); return this.response.getStatusText();
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2009 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -39,6 +39,13 @@ public interface ClientHttpResponse extends HttpInputMessage {
*/ */
HttpStatus getStatusCode() throws IOException; HttpStatus getStatusCode() throws IOException;
/**
* Return the HTTP status code of the response as integer
* @return the HTTP status as an integer
* @throws IOException in case of I/O errors
*/
int getRawStatusCode() throws IOException;
/** /**
* Return the HTTP status text of the response. * Return the HTTP status text of the response.
* @return the HTTP status text * @return the HTTP status text

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -23,7 +23,6 @@ import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpMethod; import org.apache.commons.httpclient.HttpMethod;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
/** /**
* {@link org.springframework.http.client.ClientHttpResponse} implementation that uses * {@link org.springframework.http.client.ClientHttpResponse} implementation that uses
@ -37,7 +36,7 @@ import org.springframework.http.HttpStatus;
* @deprecated In favor of {@link HttpComponentsClientHttpResponse} * @deprecated In favor of {@link HttpComponentsClientHttpResponse}
*/ */
@Deprecated @Deprecated
final class CommonsClientHttpResponse implements ClientHttpResponse { final class CommonsClientHttpResponse extends AbstractClientHttpResponse {
private final HttpMethod httpMethod; private final HttpMethod httpMethod;
@ -49,8 +48,8 @@ final class CommonsClientHttpResponse implements ClientHttpResponse {
} }
public HttpStatus getStatusCode() { public int getRawStatusCode() {
return HttpStatus.valueOf(this.httpMethod.getStatusCode()); return this.httpMethod.getStatusCode();
} }
public String getStatusText() { public String getStatusText() {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -25,7 +25,6 @@ import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils; import org.apache.http.util.EntityUtils;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
/** /**
* {@link org.springframework.http.client.ClientHttpResponse} implementation that uses * {@link org.springframework.http.client.ClientHttpResponse} implementation that uses
@ -38,20 +37,20 @@ import org.springframework.http.HttpStatus;
* @since 3.1 * @since 3.1
* @see HttpComponentsClientHttpRequest#execute() * @see HttpComponentsClientHttpRequest#execute()
*/ */
final class HttpComponentsClientHttpResponse implements ClientHttpResponse { final class HttpComponentsClientHttpResponse extends AbstractClientHttpResponse {
private final HttpResponse httpResponse; private final HttpResponse httpResponse;
private HttpHeaders headers; private HttpHeaders headers;
public HttpComponentsClientHttpResponse(HttpResponse httpResponse) { HttpComponentsClientHttpResponse(HttpResponse httpResponse) {
this.httpResponse = httpResponse; this.httpResponse = httpResponse;
} }
public HttpStatus getStatusCode() throws IOException { public int getRawStatusCode() throws IOException {
return HttpStatus.valueOf(this.httpResponse.getStatusLine().getStatusCode()); return this.httpResponse.getStatusLine().getStatusCode();
} }
public String getStatusText() throws IOException { public String getStatusText() throws IOException {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -21,7 +21,6 @@ import java.io.InputStream;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
@ -32,7 +31,7 @@ import org.springframework.util.StringUtils;
* @author Arjen Poutsma * @author Arjen Poutsma
* @since 3.0 * @since 3.0
*/ */
final class SimpleClientHttpResponse implements ClientHttpResponse { final class SimpleClientHttpResponse extends AbstractClientHttpResponse {
private final HttpURLConnection connection; private final HttpURLConnection connection;
@ -44,8 +43,8 @@ final class SimpleClientHttpResponse implements ClientHttpResponse {
} }
public HttpStatus getStatusCode() throws IOException { public int getRawStatusCode() throws IOException {
return HttpStatus.valueOf(this.connection.getResponseCode()); return this.connection.getResponseCode();
} }
public String getStatusText() throws IOException { public String getStatusText() throws IOException {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2010 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -407,7 +407,13 @@ public class JaxWsPortClientInterceptor extends LocalJaxWsServiceFactory
*/ */
protected Object getPortStub(Service service, QName portQName) { protected Object getPortStub(Service service, QName portQName) {
if (this.webServiceFeatures != null) { if (this.webServiceFeatures != null) {
return new FeaturePortProvider().getPortStub(service, portQName, this.webServiceFeatures); try {
return new FeaturePortProvider().getPortStub(service, portQName, this.webServiceFeatures);
}
catch (LinkageError ex) {
throw new IllegalStateException(
"Specifying the 'webServiceFeatures' property requires JAX-WS 2.1 or higher at runtime", ex);
}
} }
else { else {
return (portQName != null ? service.getPort(portQName, getServiceInterface()) : return (portQName != null ? service.getPort(portQName, getServiceInterface()) :
@ -527,6 +533,7 @@ public class JaxWsPortClientInterceptor extends LocalJaxWsServiceFactory
} }
} }
/** /**
* Inner class in order to avoid a hard-coded JAX-WS 2.1 dependency. * Inner class in order to avoid a hard-coded JAX-WS 2.1 dependency.
* JAX-WS 2.0, as used in Java EE 5, didn't have WebServiceFeatures yet... * JAX-WS 2.0, as used in Java EE 5, didn't have WebServiceFeatures yet...

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -35,8 +35,8 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.method.support.ModelAndViewContainer;
/** /**
* Abstract base class for resolving method arguments from a named value. Request parameters, request headers, and * Abstract base class for resolving method arguments from a named value. Request parameters, request headers, and
* path variables are examples of named values. Each may have a name, a required flag, and a default value. * path variables are examples of named values. Each may have a name, a required flag, and a default value.
* <p>Subclasses define how to do the following: * <p>Subclasses define how to do the following:
* <ul> * <ul>
* <li>Obtain named value information for a method parameter * <li>Obtain named value information for a method parameter
@ -44,11 +44,11 @@ import org.springframework.web.method.support.ModelAndViewContainer;
* <li>Handle missing argument values when argument values are required * <li>Handle missing argument values when argument values are required
* <li>Optionally handle a resolved value * <li>Optionally handle a resolved value
* </ul> * </ul>
* <p>A default value string can contain ${...} placeholders and Spring Expression Language #{...} expressions. * <p>A default value string can contain ${...} placeholders and Spring Expression Language #{...} expressions.
* For this to work a {@link ConfigurableBeanFactory} must be supplied to the class constructor. * For this to work a {@link ConfigurableBeanFactory} must be supplied to the class constructor.
* <p>A {@link WebDataBinder} is created to apply type conversion to the resolved argument value if it doesn't * <p>A {@link WebDataBinder} is created to apply type conversion to the resolved argument value if it doesn't
* match the method parameter type. * match the method parameter type.
* *
* @author Arjen Poutsma * @author Arjen Poutsma
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 3.1 * @since 3.1
@ -63,7 +63,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
new ConcurrentHashMap<MethodParameter, NamedValueInfo>(); new ConcurrentHashMap<MethodParameter, NamedValueInfo>();
/** /**
* @param beanFactory a bean factory to use for resolving ${...} placeholder and #{...} SpEL expressions * @param beanFactory a bean factory to use for resolving ${...} placeholder and #{...} SpEL expressions
* in default values, or {@code null} if default values are not expected to contain expressions * in default values, or {@code null} if default values are not expected to contain expressions
*/ */
public AbstractNamedValueMethodArgumentResolver(ConfigurableBeanFactory beanFactory) { public AbstractNamedValueMethodArgumentResolver(ConfigurableBeanFactory beanFactory) {
@ -71,10 +71,11 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
this.expressionContext = (beanFactory != null) ? new BeanExpressionContext(beanFactory, new RequestScope()) : null; this.expressionContext = (beanFactory != null) ? new BeanExpressionContext(beanFactory, new RequestScope()) : null;
} }
public final Object resolveArgument(MethodParameter parameter, public final Object resolveArgument(
ModelAndViewContainer mavContainer, MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
WebDataBinderFactory binderFactory) throws Exception { throws Exception {
Class<?> paramType = parameter.getParameterType(); Class<?> paramType = parameter.getParameterType();
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter); NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
@ -95,7 +96,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name); WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
arg = binder.convertIfNecessary(arg, paramType, parameter); arg = binder.convertIfNecessary(arg, paramType, parameter);
} }
handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest); handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
return arg; return arg;
@ -115,9 +116,9 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
} }
/** /**
* Create the {@link NamedValueInfo} object for the given method parameter. Implementations typically * Create the {@link NamedValueInfo} object for the given method parameter. Implementations typically
* retrieve the method annotation by means of {@link MethodParameter#getParameterAnnotation(Class)}. * retrieve the method annotation by means of {@link MethodParameter#getParameterAnnotation(Class)}.
* *
* @param parameter the method parameter * @param parameter the method parameter
* @return the named value information * @return the named value information
*/ */
@ -136,7 +137,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
String defaultValue = (ValueConstants.DEFAULT_NONE.equals(info.defaultValue) ? null : info.defaultValue); String defaultValue = (ValueConstants.DEFAULT_NONE.equals(info.defaultValue) ? null : info.defaultValue);
return new NamedValueInfo(name, info.required, defaultValue); return new NamedValueInfo(name, info.required, defaultValue);
} }
/** /**
* Resolves the given parameter type and value name into an argument value. * Resolves the given parameter type and value name into an argument value.
* @param name the name of the value being resolved * @param name the name of the value being resolved
@ -165,7 +166,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
} }
/** /**
* Invoked when a named value is required, but {@link #resolveName(String, MethodParameter, NativeWebRequest)} * Invoked when a named value is required, but {@link #resolveName(String, MethodParameter, NativeWebRequest)}
* returned {@code null} and there is no default value. Subclasses typically throw an exception in this case. * returned {@code null} and there is no default value. Subclasses typically throw an exception in this case.
* @param name the name for the value * @param name the name for the value
* @param parameter the method parameter * @param parameter the method parameter
@ -219,4 +220,4 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
this.defaultValue = defaultValue; this.defaultValue = defaultValue;
} }
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -28,16 +28,16 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.method.support.ModelAndViewContainer;
/** /**
* An abstract base class adapting a {@link WebArgumentResolver} to the * An abstract base class adapting a {@link WebArgumentResolver} to the
* {@link HandlerMethodArgumentResolver} contract. * {@link HandlerMethodArgumentResolver} contract.
* *
* <p><strong>Note:</strong> This class is provided for backwards compatibility. * <p><strong>Note:</strong> This class is provided for backwards compatibility.
* However it is recommended to re-write a {@code WebArgumentResolver} as * However it is recommended to re-write a {@code WebArgumentResolver} as
* {@code HandlerMethodArgumentResolver}. Since {@link #supportsParameter} * {@code HandlerMethodArgumentResolver}. Since {@link #supportsParameter}
* can only be implemented by actually resolving the value and then checking * can only be implemented by actually resolving the value and then checking
* the result is not {@code WebArgumentResolver#UNRESOLVED} any exceptions * the result is not {@code WebArgumentResolver#UNRESOLVED} any exceptions
* raised must be absorbed and ignored since it's not clear whether the adapter * raised must be absorbed and ignored since it's not clear whether the adapter
* doesn't support the parameter or whether it failed for an internal reason. * doesn't support the parameter or whether it failed for an internal reason.
* The {@code HandlerMethodArgumentResolver} contract also provides access to * The {@code HandlerMethodArgumentResolver} contract also provides access to
* model attributes and to {@code WebDataBinderFactory} (for type conversion). * model attributes and to {@code WebDataBinderFactory} (for type conversion).
* *
@ -60,7 +60,7 @@ public abstract class AbstractWebArgumentResolverAdapter implements HandlerMetho
} }
/** /**
* Actually resolve the value and check the resolved value is not * Actually resolve the value and check the resolved value is not
* {@link WebArgumentResolver#UNRESOLVED} absorbing _any_ exceptions. * {@link WebArgumentResolver#UNRESOLVED} absorbing _any_ exceptions.
*/ */
public boolean supportsParameter(MethodParameter parameter) { public boolean supportsParameter(MethodParameter parameter) {
@ -88,21 +88,22 @@ public abstract class AbstractWebArgumentResolverAdapter implements HandlerMetho
/** /**
* Delegate to the {@link WebArgumentResolver} instance. * Delegate to the {@link WebArgumentResolver} instance.
* @exception IllegalStateException if the resolved value is not assignable * @exception IllegalStateException if the resolved value is not assignable
* to the method parameter. * to the method parameter.
*/ */
public Object resolveArgument(MethodParameter parameter, public Object resolveArgument(
ModelAndViewContainer mavContainer, MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
WebDataBinderFactory binderFactory) throws Exception { throws Exception {
Class<?> paramType = parameter.getParameterType(); Class<?> paramType = parameter.getParameterType();
Object result = this.adaptee.resolveArgument(parameter, webRequest); Object result = this.adaptee.resolveArgument(parameter, webRequest);
if (result == WebArgumentResolver.UNRESOLVED || !ClassUtils.isAssignableValue(paramType, result)) { if (result == WebArgumentResolver.UNRESOLVED || !ClassUtils.isAssignableValue(paramType, result)) {
throw new IllegalStateException( throw new IllegalStateException(
"Standard argument type [" + paramType.getName() + "] in method " + parameter.getMethod() + "Standard argument type [" + paramType.getName() + "] in method " + parameter.getMethod() +
"resolved to incompatible value of type [" + (result != null ? result.getClass() : null) + "resolved to incompatible value of type [" + (result != null ? result.getClass() : null) +
"]. Consider declaring the argument type in a less specific fashion."); "]. Consider declaring the argument type in a less specific fashion.");
} }
return result; return result;
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -29,12 +29,12 @@ import org.springframework.web.method.support.ModelAndViewContainer;
/** /**
* Resolves {@link Errors} method arguments. * Resolves {@link Errors} method arguments.
* *
* <p>An {@code Errors} method argument is expected to appear immediately after * <p>An {@code Errors} method argument is expected to appear immediately after
* the model attribute in the method signature. It is resolved by expecting the * the model attribute in the method signature. It is resolved by expecting the
* last two attributes added to the model to be the model attribute and its * last two attributes added to the model to be the model attribute and its
* {@link BindingResult}. * {@link BindingResult}.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 3.1 * @since 3.1
*/ */
@ -45,10 +45,11 @@ public class ErrorsMethodArgumentResolver implements HandlerMethodArgumentResolv
return Errors.class.isAssignableFrom(paramType); return Errors.class.isAssignableFrom(paramType);
} }
public Object resolveArgument(MethodParameter parameter, public Object resolveArgument(
ModelAndViewContainer mavContainer, MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
WebDataBinderFactory binderFactory) throws Exception { throws Exception {
ModelMap model = mavContainer.getModel(); ModelMap model = mavContainer.getModel();
if (model.size() > 0) { if (model.size() > 0) {
int lastIndex = model.size()-1; int lastIndex = model.size()-1;
@ -63,4 +64,4 @@ public class ErrorsMethodArgumentResolver implements HandlerMethodArgumentResolv
"argument in the controller method signature: " + parameter.getMethod()); "argument in the controller method signature: " + parameter.getMethod());
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -27,12 +27,12 @@ import org.springframework.web.method.support.ModelAndViewContainer;
/** /**
* Resolves {@link Map} method arguments and handles {@link Map} return values. * Resolves {@link Map} method arguments and handles {@link Map} return values.
* *
* <p>A Map return value can be interpreted in more than one ways depending * <p>A Map return value can be interpreted in more than one ways depending
* on the presence of annotations like {@code @ModelAttribute} or * on the presence of annotations like {@code @ModelAttribute} or
* {@code @ResponseBody}. Therefore this handler should be configured after * {@code @ResponseBody}. Therefore this handler should be configured after
* the handlers that support these annotations. * the handlers that support these annotations.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 3.1 * @since 3.1
*/ */
@ -42,10 +42,11 @@ public class MapMethodProcessor implements HandlerMethodArgumentResolver, Handle
return Map.class.isAssignableFrom(parameter.getParameterType()); return Map.class.isAssignableFrom(parameter.getParameterType());
} }
public Object resolveArgument(MethodParameter parameter, public Object resolveArgument(
ModelAndViewContainer mavContainer, MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
WebDataBinderFactory binderFactory) throws Exception { throws Exception {
return mavContainer.getModel(); return mavContainer.getModel();
} }
@ -54,10 +55,11 @@ public class MapMethodProcessor implements HandlerMethodArgumentResolver, Handle
} }
@SuppressWarnings({ "unchecked", "rawtypes" }) @SuppressWarnings({ "unchecked", "rawtypes" })
public void handleReturnValue(Object returnValue, public void handleReturnValue(
MethodParameter returnType, Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
NativeWebRequest webRequest) throws Exception { throws Exception {
if (returnValue == null) { if (returnValue == null) {
return; return;
} }
@ -66,8 +68,8 @@ public class MapMethodProcessor implements HandlerMethodArgumentResolver, Handle
} }
else { else {
// should not happen // should not happen
throw new UnsupportedOperationException("Unexpected return type: " + throw new UnsupportedOperationException("Unexpected return type: " +
returnType.getParameterType().getName() + " in method: " + returnType.getMethod()); returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
} }
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -39,15 +39,15 @@ import org.springframework.web.method.support.ModelAndViewContainer;
/** /**
* Resolves method arguments annotated with {@code @ModelAttribute} and handles * Resolves method arguments annotated with {@code @ModelAttribute} and handles
* return values from methods annotated with {@code @ModelAttribute}. * return values from methods annotated with {@code @ModelAttribute}.
* *
* <p>Model attributes are obtained from the model or if not found possibly * <p>Model attributes are obtained from the model or if not found possibly
* created with a default constructor if it is available. Once created, the * created with a default constructor if it is available. Once created, the
* attributed is populated with request data via data binding and also * attributed is populated with request data via data binding and also
* validation may be applied if the argument is annotated with * validation may be applied if the argument is annotated with
* {@code @javax.validation.Valid}. * {@code @javax.validation.Valid}.
* *
* <p>When this handler is created with {@code annotationNotRequired=true}, * <p>When this handler is created with {@code annotationNotRequired=true},
* any non-simple type argument and return value is regarded as a model * any non-simple type argument and return value is regarded as a model
* attribute with or without the presence of an {@code @ModelAttribute}. * attribute with or without the presence of an {@code @ModelAttribute}.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
@ -58,10 +58,10 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
protected Log logger = LogFactory.getLog(this.getClass()); protected Log logger = LogFactory.getLog(this.getClass());
private final boolean annotationNotRequired; private final boolean annotationNotRequired;
/** /**
* @param annotationNotRequired if "true", non-simple method arguments and * @param annotationNotRequired if "true", non-simple method arguments and
* return values are considered model attributes with or without a * return values are considered model attributes with or without a
* {@code @ModelAttribute} annotation. * {@code @ModelAttribute} annotation.
*/ */
public ModelAttributeMethodProcessor(boolean annotationNotRequired) { public ModelAttributeMethodProcessor(boolean annotationNotRequired) {
@ -85,18 +85,19 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
} }
/** /**
* Resolve the argument from the model or if not found instantiate it with * Resolve the argument from the model or if not found instantiate it with
* its default if it is available. The model attribute is then populated * its default if it is available. The model attribute is then populated
* with request values via data binding and optionally validated * with request values via data binding and optionally validated
* if {@code @java.validation.Valid} is present on the argument. * if {@code @java.validation.Valid} is present on the argument.
* @throws BindException if data binding and validation result in an error * @throws BindException if data binding and validation result in an error
* and the next method parameter is not of type {@link Errors}. * and the next method parameter is not of type {@link Errors}.
* @throws Exception if WebDataBinder initialization fails. * @throws Exception if WebDataBinder initialization fails.
*/ */
public final Object resolveArgument(MethodParameter parameter, public final Object resolveArgument(
ModelAndViewContainer mavContainer, MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest request, NativeWebRequest request, WebDataBinderFactory binderFactory)
WebDataBinderFactory binderFactory) throws Exception { throws Exception {
String name = ModelFactory.getNameForParameter(parameter); String name = ModelFactory.getNameForParameter(parameter);
Object target = (mavContainer.containsAttribute(name)) ? Object target = (mavContainer.containsAttribute(name)) ?
mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, request); mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, request);
@ -130,7 +131,7 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
return BeanUtils.instantiateClass(parameter.getParameterType()); return BeanUtils.instantiateClass(parameter.getParameterType());
} }
/** /**
* Extension point to bind the request to the target object. * Extension point to bind the request to the target object.
* @param binder the data binder instance to use for the binding * @param binder the data binder instance to use for the binding
@ -158,7 +159,7 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
/** /**
* Whether to raise a {@link BindException} on bind or validation errors. * Whether to raise a {@link BindException} on bind or validation errors.
* The default implementation returns {@code true} if the next method * The default implementation returns {@code true} if the next method
* argument is not of type {@link Errors}. * argument is not of type {@link Errors}.
* @param binder the data binder used to perform data binding * @param binder the data binder used to perform data binding
* @param parameter the method argument * @param parameter the method argument
@ -167,12 +168,12 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
int i = parameter.getParameterIndex(); int i = parameter.getParameterIndex();
Class<?>[] paramTypes = parameter.getMethod().getParameterTypes(); Class<?>[] paramTypes = parameter.getMethod().getParameterTypes();
boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1])); boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1]));
return !hasBindingResult; return !hasBindingResult;
} }
/** /**
* Return {@code true} if there is a method-level {@code @ModelAttribute} * Return {@code true} if there is a method-level {@code @ModelAttribute}
* or if it is a non-simple type when {@code annotationNotRequired=true}. * or if it is a non-simple type when {@code annotationNotRequired=true}.
*/ */
public boolean supportsReturnType(MethodParameter returnType) { public boolean supportsReturnType(MethodParameter returnType) {
@ -190,13 +191,14 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
/** /**
* Add non-null return values to the {@link ModelAndViewContainer}. * Add non-null return values to the {@link ModelAndViewContainer}.
*/ */
public void handleReturnValue(Object returnValue, public void handleReturnValue(
MethodParameter returnType, Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
NativeWebRequest webRequest) throws Exception { throws Exception {
if (returnValue != null) { if (returnValue != null) {
String name = ModelFactory.getNameForReturnValue(returnValue, returnType); String name = ModelFactory.getNameForReturnValue(returnValue, returnType);
mavContainer.addAttribute(name, returnValue); mavContainer.addAttribute(name, returnValue);
} }
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -25,13 +25,13 @@ import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.method.support.ModelAndViewContainer;
/** /**
* Resolves {@link Model} arguments and handles {@link Model} return values. * Resolves {@link Model} arguments and handles {@link Model} return values.
* *
* <p>A {@link Model} return type has a set purpose. Therefore this handler * <p>A {@link Model} return type has a set purpose. Therefore this handler
* should be configured ahead of handlers that support any return value type * should be configured ahead of handlers that support any return value type
* annotated with {@code @ModelAttribute} or {@code @ResponseBody} to ensure * annotated with {@code @ModelAttribute} or {@code @ResponseBody} to ensure
* they don't take over. * they don't take over.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 3.1 * @since 3.1
*/ */
@ -41,10 +41,11 @@ public class ModelMethodProcessor implements HandlerMethodArgumentResolver, Hand
return Model.class.isAssignableFrom(parameter.getParameterType()); return Model.class.isAssignableFrom(parameter.getParameterType());
} }
public Object resolveArgument(MethodParameter parameter, public Object resolveArgument(
ModelAndViewContainer mavContainer, MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
WebDataBinderFactory binderFactory) throws Exception { throws Exception {
return mavContainer.getModel(); return mavContainer.getModel();
} }
@ -52,10 +53,11 @@ public class ModelMethodProcessor implements HandlerMethodArgumentResolver, Hand
return Model.class.isAssignableFrom(returnType.getParameterType()); return Model.class.isAssignableFrom(returnType.getParameterType());
} }
public void handleReturnValue(Object returnValue, public void handleReturnValue(
MethodParameter returnType, Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
NativeWebRequest webRequest) throws Exception { throws Exception {
if (returnValue == null) { if (returnValue == null) {
return; return;
} }
@ -64,8 +66,8 @@ public class ModelMethodProcessor implements HandlerMethodArgumentResolver, Hand
} }
else { else {
// should not happen // should not happen
throw new UnsupportedOperationException("Unexpected return type: " + throw new UnsupportedOperationException("Unexpected return type: " +
returnType.getParameterType().getName() + " in method: " + returnType.getMethod()); returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
} }
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -31,13 +31,13 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.method.support.ModelAndViewContainer;
/** /**
* Resolves {@link Map} method arguments annotated with {@code @RequestHeader}. * Resolves {@link Map} method arguments annotated with {@code @RequestHeader}.
* For individual header values annotated with {@code @RequestHeader} see * For individual header values annotated with {@code @RequestHeader} see
* {@link RequestHeaderMethodArgumentResolver} instead. * {@link RequestHeaderMethodArgumentResolver} instead.
* *
* <p>The created {@link Map} contains all request header name/value pairs. * <p>The created {@link Map} contains all request header name/value pairs.
* The method parameter type may be a {@link MultiValueMap} to receive all * The method parameter type may be a {@link MultiValueMap} to receive all
* values for a header, not only the first one. * values for a header, not only the first one.
* *
* @author Arjen Poutsma * @author Arjen Poutsma
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
@ -50,10 +50,11 @@ public class RequestHeaderMapMethodArgumentResolver implements HandlerMethodArgu
&& Map.class.isAssignableFrom(parameter.getParameterType()); && Map.class.isAssignableFrom(parameter.getParameterType());
} }
public Object resolveArgument(MethodParameter parameter, public Object resolveArgument(
ModelAndViewContainer mavContainer, MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
WebDataBinderFactory binderFactory) throws Exception { throws Exception {
Class<?> paramType = parameter.getParameterType(); Class<?> paramType = parameter.getParameterType();
if (MultiValueMap.class.isAssignableFrom(paramType)) { if (MultiValueMap.class.isAssignableFrom(paramType)) {
@ -82,4 +83,4 @@ public class RequestHeaderMapMethodArgumentResolver implements HandlerMethodArgu
return result; return result;
} }
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -30,12 +30,12 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.method.support.ModelAndViewContainer;
/** /**
* Resolves {@link Map} method arguments annotated with an @{@link RequestParam} where the annotation does not * Resolves {@link Map} method arguments annotated with an @{@link RequestParam} where the annotation does not
* specify a request parameter name. See {@link RequestParamMethodArgumentResolver} for resolving {@link Map} * specify a request parameter name. See {@link RequestParamMethodArgumentResolver} for resolving {@link Map}
* method arguments with a request parameter name. * method arguments with a request parameter name.
* *
* <p>The created {@link Map} contains all request parameter name/value pairs. If the method parameter type * <p>The created {@link Map} contains all request parameter name/value pairs. If the method parameter type
* is {@link MultiValueMap} instead, the created map contains all request parameters and all there values for * is {@link MultiValueMap} instead, the created map contains all request parameters and all there values for
* cases where request parameters have multiple values. * cases where request parameters have multiple values.
* *
* @author Arjen Poutsma * @author Arjen Poutsma
@ -55,10 +55,11 @@ public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgum
return false; return false;
} }
public Object resolveArgument(MethodParameter parameter, public Object resolveArgument(
ModelAndViewContainer mavContainer, MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
WebDataBinderFactory binderFactory) throws Exception { throws Exception {
Class<?> paramType = parameter.getParameterType(); Class<?> paramType = parameter.getParameterType();
Map<String, String[]> parameterMap = webRequest.getParameterMap(); Map<String, String[]> parameterMap = webRequest.getParameterMap();
@ -81,4 +82,4 @@ public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgum
return result; return result;
} }
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -24,7 +24,7 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.method.support.ModelAndViewContainer;
/** /**
* Resolves a {@link SessionStatus} argument by obtaining it from * Resolves a {@link SessionStatus} argument by obtaining it from
* the {@link ModelAndViewContainer}. * the {@link ModelAndViewContainer}.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
@ -36,10 +36,11 @@ public class SessionStatusMethodArgumentResolver implements HandlerMethodArgumen
return SessionStatus.class.equals(parameter.getParameterType()); return SessionStatus.class.equals(parameter.getParameterType());
} }
public Object resolveArgument(MethodParameter parameter, public Object resolveArgument(
ModelAndViewContainer mavContainer, MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
WebDataBinderFactory binderFactory) throws Exception { throws Exception {
return mavContainer.getSessionStatus(); return mavContainer.getSessionStatus();
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -31,8 +31,8 @@ import org.springframework.web.context.request.NativeWebRequest;
/** /**
* Resolves method parameters by delegating to a list of registered {@link HandlerMethodArgumentResolver}s. * Resolves method parameters by delegating to a list of registered {@link HandlerMethodArgumentResolver}s.
* Previously resolved method parameters are cached for faster lookups. * Previously resolved method parameters are cached for faster lookups.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 3.1 * @since 3.1
*/ */
@ -40,12 +40,12 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu
protected final Log logger = LogFactory.getLog(getClass()); protected final Log logger = LogFactory.getLog(getClass());
private final List<HandlerMethodArgumentResolver> argumentResolvers = private final List<HandlerMethodArgumentResolver> argumentResolvers =
new ArrayList<HandlerMethodArgumentResolver>(); new ArrayList<HandlerMethodArgumentResolver>();
private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache = private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache =
new ConcurrentHashMap<MethodParameter, HandlerMethodArgumentResolver>(); new ConcurrentHashMap<MethodParameter, HandlerMethodArgumentResolver>();
/** /**
* Return a read-only list with the contained resolvers, or an empty list. * Return a read-only list with the contained resolvers, or an empty list.
*/ */
@ -54,7 +54,7 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu
} }
/** /**
* Whether the given {@linkplain MethodParameter method parameter} is supported by any registered * Whether the given {@linkplain MethodParameter method parameter} is supported by any registered
* {@link HandlerMethodArgumentResolver}. * {@link HandlerMethodArgumentResolver}.
*/ */
public boolean supportsParameter(MethodParameter parameter) { public boolean supportsParameter(MethodParameter parameter) {
@ -65,10 +65,11 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu
* Iterate over registered {@link HandlerMethodArgumentResolver}s and invoke the one that supports it. * Iterate over registered {@link HandlerMethodArgumentResolver}s and invoke the one that supports it.
* @exception IllegalStateException if no suitable {@link HandlerMethodArgumentResolver} is found. * @exception IllegalStateException if no suitable {@link HandlerMethodArgumentResolver} is found.
*/ */
public Object resolveArgument(MethodParameter parameter, public Object resolveArgument(
ModelAndViewContainer mavContainer, MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
WebDataBinderFactory binderFactory) throws Exception { throws Exception {
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter); HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
Assert.notNull(resolver, "Unknown parameter type [" + parameter.getParameterType().getName() + "]"); Assert.notNull(resolver, "Unknown parameter type [" + parameter.getParameterType().getName() + "]");
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory); return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
@ -94,7 +95,7 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu
} }
return result; return result;
} }
/** /**
* Add the given {@link HandlerMethodArgumentResolver}. * Add the given {@link HandlerMethodArgumentResolver}.
*/ */
@ -116,4 +117,4 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu
return this; return this;
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -29,17 +29,17 @@ import org.springframework.util.Assert;
import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.NativeWebRequest;
/** /**
* Handles method return values by delegating to a list of registered {@link HandlerMethodReturnValueHandler}s. * Handles method return values by delegating to a list of registered {@link HandlerMethodReturnValueHandler}s.
* Previously resolved return types are cached for faster lookups. * Previously resolved return types are cached for faster lookups.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 3.1 * @since 3.1
*/ */
public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler { public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler {
protected final Log logger = LogFactory.getLog(getClass()); protected final Log logger = LogFactory.getLog(getClass());
private final List<HandlerMethodReturnValueHandler> returnValueHandlers = private final List<HandlerMethodReturnValueHandler> returnValueHandlers =
new ArrayList<HandlerMethodReturnValueHandler>(); new ArrayList<HandlerMethodReturnValueHandler>();
private final Map<MethodParameter, HandlerMethodReturnValueHandler> returnValueHandlerCache = private final Map<MethodParameter, HandlerMethodReturnValueHandler> returnValueHandlerCache =
@ -53,7 +53,7 @@ public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodRe
} }
/** /**
* Whether the given {@linkplain MethodParameter method return type} is supported by any registered * Whether the given {@linkplain MethodParameter method return type} is supported by any registered
* {@link HandlerMethodReturnValueHandler}. * {@link HandlerMethodReturnValueHandler}.
*/ */
public boolean supportsReturnType(MethodParameter returnType) { public boolean supportsReturnType(MethodParameter returnType) {
@ -64,10 +64,11 @@ public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodRe
* Iterate over registered {@link HandlerMethodReturnValueHandler}s and invoke the one that supports it. * Iterate over registered {@link HandlerMethodReturnValueHandler}s and invoke the one that supports it.
* @exception IllegalStateException if no suitable {@link HandlerMethodReturnValueHandler} is found. * @exception IllegalStateException if no suitable {@link HandlerMethodReturnValueHandler} is found.
*/ */
public void handleReturnValue(Object returnValue, public void handleReturnValue(
MethodParameter returnType, Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
NativeWebRequest webRequest) throws Exception { throws Exception {
HandlerMethodReturnValueHandler handler = getReturnValueHandler(returnType); HandlerMethodReturnValueHandler handler = getReturnValueHandler(returnType);
Assert.notNull(handler, "Unknown return value type [" + returnType.getParameterType().getName() + "]"); Assert.notNull(handler, "Unknown return value type [" + returnType.getParameterType().getName() + "]");
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest); handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
@ -92,8 +93,8 @@ public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodRe
} }
} }
return result; return result;
} }
/** /**
* Add the given {@link HandlerMethodReturnValueHandler}. * Add the given {@link HandlerMethodReturnValueHandler}.
*/ */
@ -115,4 +116,4 @@ public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodRe
return this; return this;
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -32,16 +32,16 @@ import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.HandlerMethod; import org.springframework.web.method.HandlerMethod;
/** /**
* Provides a method for invoking the handler method for a given request after resolving its method argument * Provides a method for invoking the handler method for a given request after resolving its method argument
* values through registered {@link HandlerMethodArgumentResolver}s. * values through registered {@link HandlerMethodArgumentResolver}s.
* *
* <p>Argument resolution often requires a {@link WebDataBinder} for data binding or for type conversion. * <p>Argument resolution often requires a {@link WebDataBinder} for data binding or for type conversion.
* Use the {@link #setDataBinderFactory(WebDataBinderFactory)} property to supply a binder factory to pass to * Use the {@link #setDataBinderFactory(WebDataBinderFactory)} property to supply a binder factory to pass to
* argument resolvers. * argument resolvers.
* *
* <p>Use {@link #setHandlerMethodArgumentResolvers(HandlerMethodArgumentResolverComposite)} to customize * <p>Use {@link #setHandlerMethodArgumentResolvers(HandlerMethodArgumentResolverComposite)} to customize
* the list of argument resolvers. * the list of argument resolvers.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 3.1 * @since 3.1
*/ */
@ -99,20 +99,20 @@ public class InvocableHandlerMethod extends HandlerMethod {
} }
/** /**
* Invoke the method after resolving its argument values in the context of the given request. <p>Argument * Invoke the method after resolving its argument values in the context of the given request. <p>Argument
* values are commonly resolved through {@link HandlerMethodArgumentResolver}s. The {@code provideArgs} * values are commonly resolved through {@link HandlerMethodArgumentResolver}s. The {@code provideArgs}
* parameter however may supply argument values to be used directly, i.e. without argument resolution. * parameter however may supply argument values to be used directly, i.e. without argument resolution.
* Examples of provided argument values include a {@link WebDataBinder}, a {@link SessionStatus}, or * Examples of provided argument values include a {@link WebDataBinder}, a {@link SessionStatus}, or
* a thrown exception instance. Provided argument values are checked before argument resolvers. * a thrown exception instance. Provided argument values are checked before argument resolvers.
* *
* @param request the current request * @param request the current request
* @param mavContainer the {@link ModelAndViewContainer} for the current request * @param mavContainer the {@link ModelAndViewContainer} for the current request
* @param providedArgs argument values to try to use without view resolution * @param providedArgs argument values to try to use without view resolution
* @return the raw value returned by the invoked method * @return the raw value returned by the invoked method
* @exception Exception raised if no suitable argument resolver can be found, or the method raised an exception * @exception Exception raised if no suitable argument resolver can be found, or the method raised an exception
*/ */
public final Object invokeForRequest(NativeWebRequest request, public final Object invokeForRequest(NativeWebRequest request,
ModelAndViewContainer mavContainer, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception { Object... providedArgs) throws Exception {
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
@ -135,9 +135,10 @@ public class InvocableHandlerMethod extends HandlerMethod {
/** /**
* Get the method argument values for the current request. * Get the method argument values for the current request.
*/ */
private Object[] getMethodArgumentValues(NativeWebRequest request, private Object[] getMethodArgumentValues(
ModelAndViewContainer mavContainer, NativeWebRequest request, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception { Object... providedArgs) throws Exception {
MethodParameter[] parameters = getMethodParameters(); MethodParameter[] parameters = getMethodParameters();
Object[] args = new Object[parameters.length]; Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) { for (int i = 0; i < parameters.length; i++) {
@ -187,7 +188,7 @@ public class InvocableHandlerMethod extends HandlerMethod {
sb.append("Method [").append(getBridgedMethod().toGenericString()).append("]\n"); sb.append("Method [").append(getBridgedMethod().toGenericString()).append("]\n");
return sb.toString(); return sb.toString();
} }
/** /**
* Attempt to resolve a method parameter from the list of provided argument values. * Attempt to resolve a method parameter from the list of provided argument values.
*/ */
@ -202,7 +203,7 @@ public class InvocableHandlerMethod extends HandlerMethod {
} }
return null; return null;
} }
/** /**
* Invoke the handler method with the given argument values. * Invoke the handler method with the given argument values.
*/ */
@ -215,7 +216,7 @@ public class InvocableHandlerMethod extends HandlerMethod {
String msg = getInvocationErrorMessage(e.getMessage(), args); String msg = getInvocationErrorMessage(e.getMessage(), args);
throw new IllegalArgumentException(msg, e); throw new IllegalArgumentException(msg, e);
} }
catch (InvocationTargetException e) { catch (InvocationTargetException e) {
// Unwrap for HandlerExceptionResolvers ... // Unwrap for HandlerExceptionResolvers ...
Throwable targetException = e.getTargetException(); Throwable targetException = e.getTargetException();
if (targetException instanceof RuntimeException) { if (targetException instanceof RuntimeException) {
@ -233,7 +234,7 @@ public class InvocableHandlerMethod extends HandlerMethod {
} }
} }
} }
private String getInvocationErrorMessage(String message, Object[] resolvedArgs) { private String getInvocationErrorMessage(String message, Object[] resolvedArgs) {
StringBuilder sb = new StringBuilder(getDetailedErrorMessage(message)); StringBuilder sb = new StringBuilder(getDetailedErrorMessage(message));
sb.append("Resolved arguments: \n"); sb.append("Resolved arguments: \n");
@ -250,4 +251,4 @@ public class InvocableHandlerMethod extends HandlerMethod {
return sb.toString(); return sb.toString();
} }
} }

View File

@ -18,6 +18,8 @@ package org.springframework.web.util;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -38,38 +40,107 @@ import org.springframework.util.Assert;
*/ */
public abstract class UriUtils { public abstract class UriUtils {
private static final String SCHEME_PATTERN = "([^:/?#]+):";
private static final String HTTP_PATTERN = "(http|https):";
private static final String USERINFO_PATTERN = "([^@/]*)";
private static final String HOST_PATTERN = "([^/?#:]*)";
private static final String PORT_PATTERN = "(\\d*)";
private static final String PATH_PATTERN = "([^?#]*)";
private static final String QUERY_PATTERN = "([^#]*)";
private static final String LAST_PATTERN = "(.*)";
// Regex patterns that matches URIs. See RFC 3986, appendix B
private static final Pattern URI_PATTERN = Pattern.compile(
"^(" + SCHEME_PATTERN + ")?" + "(//(" + USERINFO_PATTERN + "@)?" + HOST_PATTERN + "(:" + PORT_PATTERN +
")?" + ")?" + PATH_PATTERN + "(\\?" + QUERY_PATTERN + ")?" + "(#" + LAST_PATTERN + ")?");
private static final Pattern HTTP_URL_PATTERN = Pattern.compile(
"^" + HTTP_PATTERN + "(//(" + USERINFO_PATTERN + "@)?" + HOST_PATTERN + "(:" + PORT_PATTERN + ")?" + ")?" +
PATH_PATTERN + "(\\?" + LAST_PATTERN + ")?");
// encoding // encoding
/** /**
* Encodes the given source URI into an encoded String. All various URI components are * Encodes the given source URI into an encoded String. All various URI components are
* encoded according to their respective valid character sets. * encoded according to their respective valid character sets.
* <p><strong>Note</strong> that this method does not attempt to encode "=" and "&"
* characters in query parameter names and query parameter values because they cannot
* be parsed in a reliable way. Instead use:
* <pre>
* UriComponents uriComponents = UriComponentsBuilder.fromUri("/path?name={value}").buildAndExpand("a=b");
* String encodedUri = uriComponents.encode().toUriString();
* </pre>
* @param uri the URI to be encoded * @param uri the URI to be encoded
* @param encoding the character encoding to encode to * @param encoding the character encoding to encode to
* @return the encoded URI * @return the encoded URI
* @throws IllegalArgumentException when the given uri parameter is not a valid URI * @throws IllegalArgumentException when the given uri parameter is not a valid URI
* @throws UnsupportedEncodingException when the given encoding parameter is not supported * @throws UnsupportedEncodingException when the given encoding parameter is not supported
* @deprecated in favor of {@link UriComponentsBuilder}; see note about query param encoding
*/ */
public static String encodeUri(String uri, String encoding) throws UnsupportedEncodingException { public static String encodeUri(String uri, String encoding) throws UnsupportedEncodingException {
UriComponents uriComponents = UriComponentsBuilder.fromUriString(uri).build(); Assert.notNull(uri, "'uri' must not be null");
UriComponents encoded = uriComponents.encode(encoding); Assert.hasLength(encoding, "'encoding' must not be empty");
return encoded.toUriString(); Matcher m = URI_PATTERN.matcher(uri);
} if (m.matches()) {
String scheme = m.group(2);
String authority = m.group(3);
String userinfo = m.group(5);
String host = m.group(6);
String port = m.group(8);
String path = m.group(9);
String query = m.group(11);
String fragment = m.group(13);
return encodeUriComponents(scheme, authority, userinfo, host, port, path, query, fragment, encoding);
}
else {
throw new IllegalArgumentException("[" + uri + "] is not a valid URI");
}
}
/** /**
* Encodes the given HTTP URI into an encoded String. All various URI components are * Encodes the given HTTP URI into an encoded String. All various URI components are
* encoded according to their respective valid character sets. * encoded according to their respective valid character sets.
* <p><strong>Note</strong> that this method does not support fragments ({@code #}), * <p><strong>Note</strong> that this method does not support fragments ({@code #}),
* as these are not supposed to be sent to the server, but retained by the client. * as these are not supposed to be sent to the server, but retained by the client.
* <p><strong>Note</strong> that this method does not attempt to encode "=" and "&"
* characters in query parameter names and query parameter values because they cannot
* be parsed in a reliable way. Instead use:
* <pre>
* UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl("/path?name={value}").buildAndExpand("a=b");
* String encodedUri = uriComponents.encode().toUriString();
* </pre>
* @param httpUrl the HTTP URL to be encoded * @param httpUrl the HTTP URL to be encoded
* @param encoding the character encoding to encode to * @param encoding the character encoding to encode to
* @return the encoded URL * @return the encoded URL
* @throws IllegalArgumentException when the given uri parameter is not a valid URI * @throws IllegalArgumentException when the given uri parameter is not a valid URI
* @throws UnsupportedEncodingException when the given encoding parameter is not supported * @throws UnsupportedEncodingException when the given encoding parameter is not supported
* @deprecated in favor of {@link UriComponentsBuilder}; see note about query param encoding
*/ */
public static String encodeHttpUrl(String httpUrl, String encoding) throws UnsupportedEncodingException { public static String encodeHttpUrl(String httpUrl, String encoding) throws UnsupportedEncodingException {
UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl(httpUrl).build(); Assert.notNull(httpUrl, "'httpUrl' must not be null");
UriComponents encoded = uriComponents.encode(encoding); Assert.hasLength(encoding, "'encoding' must not be empty");
return encoded.toUriString(); Matcher m = HTTP_URL_PATTERN.matcher(httpUrl);
if (m.matches()) {
String scheme = m.group(1);
String authority = m.group(2);
String userinfo = m.group(4);
String host = m.group(5);
String portString = m.group(7);
String path = m.group(8);
String query = m.group(10);
return encodeUriComponents(scheme, authority, userinfo, host, portString, path, query, null, encoding);
}
else {
throw new IllegalArgumentException("[" + httpUrl + "] is not a valid HTTP URL");
}
} }
/** /**
@ -87,20 +158,48 @@ public abstract class UriUtils {
* @return the encoded URI * @return the encoded URI
* @throws IllegalArgumentException when the given uri parameter is not a valid URI * @throws IllegalArgumentException when the given uri parameter is not a valid URI
* @throws UnsupportedEncodingException when the given encoding parameter is not supported * @throws UnsupportedEncodingException when the given encoding parameter is not supported
* @deprecated in favor of {@link UriComponentsBuilder}
*/ */
public static String encodeUriComponents(String scheme, String authority, String userInfo, public static String encodeUriComponents(String scheme, String authority, String userInfo,
String host, String port, String path, String query, String fragment, String encoding) String host, String port, String path, String query, String fragment, String encoding)
throws UnsupportedEncodingException { throws UnsupportedEncodingException {
int portAsInt = (port != null ? Integer.parseInt(port) : -1); Assert.hasLength(encoding, "'encoding' must not be empty");
StringBuilder sb = new StringBuilder();
UriComponentsBuilder builder = UriComponentsBuilder.newInstance(); if (scheme != null) {
builder.scheme(scheme).userInfo(userInfo).host(host).port(portAsInt); sb.append(encodeScheme(scheme, encoding));
builder.path(path).query(query).fragment(fragment); sb.append(':');
}
UriComponents encoded = builder.build().encode(encoding); if (authority != null) {
sb.append("//");
if (userInfo != null) {
sb.append(encodeUserInfo(userInfo, encoding));
sb.append('@');
}
if (host != null) {
sb.append(encodeHost(host, encoding));
}
if (port != null) {
sb.append(':');
sb.append(encodePort(port, encoding));
}
}
return encoded.toUriString(); sb.append(encodePath(path, encoding));
if (query != null) {
sb.append('?');
sb.append(encodeQuery(query, encoding));
}
if (fragment != null) {
sb.append('#');
sb.append(encodeFragment(fragment, encoding));
}
return sb.toString();
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2010 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,38 +16,110 @@
package org.springframework.http; package org.springframework.http;
import static org.junit.Assert.*; import java.util.LinkedHashMap;
import java.util.Map;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.*;
/** @author Arjen Poutsma */ /** @author Arjen Poutsma */
public class HttpStatusTests { public class HttpStatusTests {
private int[] registryValues = private Map<Integer, String> statusCodes = new LinkedHashMap<Integer, String>();
new int[]{100, 101, 102, 200, 201, 202, 203, 204, 205, 206, 207, 208, 226, 300, 301, 302, 303, 304, 305,
307, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 422,
423, 424, 426, 500, 501, 502, 503, 504, 505, 506, 507, 508, 510,};
private String[] registryDescriptions = @Before
new String[]{"CONTINUE", "SWITCHING_PROTOCOLS", "PROCESSING", "OK", "CREATED", "ACCEPTED", public void createStatusCodes() {
"NON_AUTHORITATIVE_INFORMATION", "NO_CONTENT", "RESET_CONTENT", "PARTIAL_CONTENT", "MULTI_STATUS", statusCodes.put(100, "CONTINUE");
"ALREADY_REPORTED", "IM_USED", "MULTIPLE_CHOICES", "MOVED_PERMANENTLY", "FOUND", "SEE_OTHER", statusCodes.put(101, "SWITCHING_PROTOCOLS");
"NOT_MODIFIED", "USE_PROXY", "TEMPORARY_REDIRECT", "BAD_REQUEST", "UNAUTHORIZED", statusCodes.put(102, "PROCESSING");
"PAYMENT_REQUIRED", "FORBIDDEN", "NOT_FOUND", "METHOD_NOT_ALLOWED", "NOT_ACCEPTABLE", statusCodes.put(103, "CHECKPOINT");
"PROXY_AUTHENTICATION_REQUIRED", "REQUEST_TIMEOUT", "CONFLICT", "GONE", "LENGTH_REQUIRED",
"PRECONDITION_FAILED", "REQUEST_ENTITY_TOO_LARGE", "REQUEST_URI_TOO_LONG", "UNSUPPORTED_MEDIA_TYPE",
"REQUESTED_RANGE_NOT_SATISFIABLE", "EXPECTATION_FAILED", "UNPROCESSABLE_ENTITY", "LOCKED",
"FAILED_DEPENDENCY", "UPGRADE_REQUIRED", "INTERNAL_SERVER_ERROR", "NOT_IMPLEMENTED", "BAD_GATEWAY",
"SERVICE_UNAVAILABLE", "GATEWAY_TIMEOUT", "HTTP_VERSION_NOT_SUPPORTED", "VARIANT_ALSO_NEGOTIATES",
"INSUFFICIENT_STORAGE", "LOOP_DETECTED", "NOT_EXTENDED",};
@Test statusCodes.put(200, "OK");
public void registryValues() { statusCodes.put(201, "CREATED");
for (int i = 0; i < registryValues.length; i++) { statusCodes.put(202, "ACCEPTED");
HttpStatus status = HttpStatus.valueOf(registryValues[i]); statusCodes.put(203, "NON_AUTHORITATIVE_INFORMATION");
assertEquals("Invalid value", registryValues[i], status.value()); statusCodes.put(204, "NO_CONTENT");
assertEquals("Invalid descripion", registryDescriptions[i], status.name()); statusCodes.put(205, "RESET_CONTENT");
} statusCodes.put(206, "PARTIAL_CONTENT");
statusCodes.put(207, "MULTI_STATUS");
statusCodes.put(208, "ALREADY_REPORTED");
statusCodes.put(226, "IM_USED");
statusCodes.put(300, "MULTIPLE_CHOICES");
statusCodes.put(301, "MOVED_PERMANENTLY");
statusCodes.put(302, "FOUND");
statusCodes.put(303, "SEE_OTHER");
statusCodes.put(304, "NOT_MODIFIED");
statusCodes.put(305, "USE_PROXY");
statusCodes.put(307, "TEMPORARY_REDIRECT");
statusCodes.put(308, "RESUME_INCOMPLETE");
statusCodes.put(400, "BAD_REQUEST");
statusCodes.put(401, "UNAUTHORIZED");
statusCodes.put(402, "PAYMENT_REQUIRED");
statusCodes.put(403, "FORBIDDEN");
statusCodes.put(404, "NOT_FOUND");
statusCodes.put(405, "METHOD_NOT_ALLOWED");
statusCodes.put(406, "NOT_ACCEPTABLE");
statusCodes.put(407, "PROXY_AUTHENTICATION_REQUIRED");
statusCodes.put(408, "REQUEST_TIMEOUT");
statusCodes.put(409, "CONFLICT");
statusCodes.put(410, "GONE");
statusCodes.put(411, "LENGTH_REQUIRED");
statusCodes.put(412, "PRECONDITION_FAILED");
statusCodes.put(413, "REQUEST_ENTITY_TOO_LARGE");
statusCodes.put(414, "REQUEST_URI_TOO_LONG");
statusCodes.put(415, "UNSUPPORTED_MEDIA_TYPE");
statusCodes.put(416, "REQUESTED_RANGE_NOT_SATISFIABLE");
statusCodes.put(417, "EXPECTATION_FAILED");
statusCodes.put(418, "I_AM_A_TEAPOT");
statusCodes.put(419, "INSUFFICIENT_SPACE_ON_RESOURCE");
statusCodes.put(420, "METHOD_FAILURE");
statusCodes.put(421, "DESTINATION_LOCKED");
statusCodes.put(422, "UNPROCESSABLE_ENTITY");
statusCodes.put(423, "LOCKED");
statusCodes.put(424, "FAILED_DEPENDENCY");
statusCodes.put(426, "UPGRADE_REQUIRED");
statusCodes.put(428, "PRECONDITION_REQUIRED");
statusCodes.put(429, "TOO_MANY_REQUESTS");
statusCodes.put(431, "REQUEST_HEADER_FIELDS_TOO_LARGE");
statusCodes.put(500, "INTERNAL_SERVER_ERROR");
statusCodes.put(501, "NOT_IMPLEMENTED");
statusCodes.put(502, "BAD_GATEWAY");
statusCodes.put(503, "SERVICE_UNAVAILABLE");
statusCodes.put(504, "GATEWAY_TIMEOUT");
statusCodes.put(505, "HTTP_VERSION_NOT_SUPPORTED");
statusCodes.put(506, "VARIANT_ALSO_NEGOTIATES");
statusCodes.put(507, "INSUFFICIENT_STORAGE");
statusCodes.put(508, "LOOP_DETECTED");
statusCodes.put(509, "BANDWIDTH_LIMIT_EXCEEDED");
statusCodes.put(510, "NOT_EXTENDED");
statusCodes.put(511, "NETWORK_AUTHENTICATION_REQUIRED");
} }
@Test
public void fromMapToEnum() {
for (Map.Entry<Integer, String> entry : statusCodes.entrySet()) {
int value = entry.getKey();
HttpStatus status = HttpStatus.valueOf(value);
assertEquals("Invalid value", value, status.value());
assertEquals("Invalid name for [" + value + "]", entry.getValue(), status.name());
}
}
@Test
public void fromEnumToMap() {
for (HttpStatus status : HttpStatus.values()) {
int value = status.value();
if (value == 302) {
continue;
}
assertTrue("Map has no value for [" + value + "]", statusCodes.containsKey(value));
assertEquals("Invalid name for [" + value + "]", statusCodes.get(value), status.name());
}
}
} }

View File

@ -173,9 +173,12 @@ public class MediaTypeTests {
MediaType.parseMediaType("text/html; charset=foo-bar"); MediaType.parseMediaType("text/html; charset=foo-bar");
} }
// SPR-8917
@Test @Test
public void parseMediaTypeQuotedParameterValue() { public void parseMediaTypeQuotedParameterValue() {
MediaType.parseMediaType("audio/*;attr=\"v>alue\""); MediaType mediaType = MediaType.parseMediaType("audio/*;attr=\"v>alue\"");
assertEquals("\"v>alue\"", mediaType.getParameter("attr"));
} }
@Test(expected = IllegalArgumentException.class) @Test(expected = IllegalArgumentException.class)

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2011 the original author or authors. * Copyright 2002-2012 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -291,6 +291,10 @@ public class InterceptingClientHttpRequestFactoryTests {
return statusCode; return statusCode;
} }
public int getRawStatusCode() throws IOException {
return statusCode.value();
}
public String getStatusText() throws IOException { public String getStatusText() throws IOException {
return statusText; return statusText;
} }
@ -300,7 +304,7 @@ public class InterceptingClientHttpRequestFactoryTests {
} }
public InputStream getBody() throws IOException { public InputStream getBody() throws IOException {
return null; //To change body of implemented methods use File | Settings | File Templates. return null;
} }
public void close() { public void close() {

View File

@ -128,6 +128,9 @@ public class UriUtilsTests {
assertEquals("Invalid encoded URI", "http://example.com/query=foo@bar", assertEquals("Invalid encoded URI", "http://example.com/query=foo@bar",
UriUtils.encodeUri("http://example.com/query=foo@bar", ENC)); UriUtils.encodeUri("http://example.com/query=foo@bar", ENC));
// SPR-8974
assertEquals("http://example.org?format=json&url=http://another.com?foo=bar",
UriUtils.encodeUri("http://example.org?format=json&url=http://another.com?foo=bar", ENC));
} }
@Test @Test