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 {
if (!this.connectLazily) {
try {
establishSharedConnection(); establishSharedConnection();
}
catch (JMSException ex) {
logger.debug("Could not connect on initialization - registering message consumers lazily", ex);
return;
}
initializeConsumers(); 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:";
@ -131,6 +133,8 @@ public class Jaxb2Marshaller
private Class<?>[] classesToBeBound; private Class<?>[] classesToBeBound;
private String[] packagesToScan;
private Map<String, ?> jaxbContextProperties; private Map<String, ?> jaxbContextProperties;
private Map<String, ?> marshallerProperties; private Map<String, ?> marshallerProperties;
@ -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;
@ -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>.
@ -291,6 +317,7 @@ public class Jaxb2Marshaller
/** /**
* 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 SchemaFactory#setResourceResolver(org.w3c.dom.ls.LSResourceResolver)
* @see #setSchema(Resource) * @see #setSchema(Resource)
* @see #setSchemas(Resource[]) * @see #setSchemas(Resource[])
@ -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));

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.
@ -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,10 +99,10 @@ 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);

View File

@ -46,10 +46,11 @@ public class ModelAndViewMethodReturnValueHandler implements HandlerMethodReturn
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;

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.
@ -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) {

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.
@ -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);

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.
@ -67,10 +67,10 @@ public class ServletRequestMethodArgumentResolver implements HandlerMethodArgume
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)) {

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.
@ -57,10 +57,10 @@ public class ServletResponseMethodArgumentResolver implements HandlerMethodArgum
* 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.
@ -41,10 +41,10 @@ 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.
@ -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;
} }

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.
@ -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;
} }

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,22 +222,33 @@ 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) {

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

@ -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,8 +407,14 @@ 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) {
try {
return new FeaturePortProvider().getPortStub(service, portQName, this.webServiceFeatures); 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()) :
service.getPort(getServiceInterface())); service.getPort(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.
@ -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);

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.
@ -91,10 +91,11 @@ public abstract class AbstractWebArgumentResolverAdapter implements HandlerMetho
* @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)) {

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.
@ -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;

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.
@ -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;
} }

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.
@ -93,10 +93,11 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
* 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);
@ -190,10 +191,11 @@ 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.
@ -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;
} }

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.
@ -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)) {

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.
@ -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();

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.
@ -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.
@ -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);

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.
@ -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);

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.
@ -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++) {

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,21 +40,68 @@ 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");
}
} }
/** /**
@ -60,16 +109,38 @@ public abstract class UriUtils {
* 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", statusCodes.put(200, "OK");
"REQUESTED_RANGE_NOT_SATISFIABLE", "EXPECTATION_FAILED", "UNPROCESSABLE_ENTITY", "LOCKED", statusCodes.put(201, "CREATED");
"FAILED_DEPENDENCY", "UPGRADE_REQUIRED", "INTERNAL_SERVER_ERROR", "NOT_IMPLEMENTED", "BAD_GATEWAY", statusCodes.put(202, "ACCEPTED");
"SERVICE_UNAVAILABLE", "GATEWAY_TIMEOUT", "HTTP_VERSION_NOT_SUPPORTED", "VARIANT_ALSO_NEGOTIATES", statusCodes.put(203, "NON_AUTHORITATIVE_INFORMATION");
"INSUFFICIENT_STORAGE", "LOOP_DETECTED", "NOT_EXTENDED",}; statusCodes.put(204, "NO_CONTENT");
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 @Test
public void registryValues() { public void fromMapToEnum() {
for (int i = 0; i < registryValues.length; i++) { for (Map.Entry<Integer, String> entry : statusCodes.entrySet()) {
HttpStatus status = HttpStatus.valueOf(registryValues[i]); int value = entry.getKey();
assertEquals("Invalid value", registryValues[i], status.value()); HttpStatus status = HttpStatus.valueOf(value);
assertEquals("Invalid descripion", registryDescriptions[i], status.name()); 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