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.
## Documentation
See the current [Javadoc](http://static.springsource.org/spring/docs/current/javadoc-api)
and [Reference docs](http://static.springsource.org/spring/docs/current/spring-framework-reference).
See the current [Javadoc](http://static.springsource.org/spring-framework/docs/current/api)
and [Reference docs](http://static.springsource.org/spring-framework/docs/current/reference).
## Getting support
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)
-------------------------------------
* 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
* context:property-placeholder's "file-encoding" attribute value is being applied correctly
* 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 does not insist on a "dataSource" reference being set
* 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
* 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)
--------------------------------------

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");
* 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.QueueSession;
import javax.jms.Session;
import javax.jms.TemporaryQueue;
import javax.jms.TemporaryTopic;
import javax.jms.Topic;
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
if ((methodName.equals("createConsumer") || methodName.equals("createReceiver") ||
methodName.equals("createSubscriber"))) {
if (args[0] != null) {
return getCachedConsumer((Destination) args[0],
Destination dest = (Destination) args[0];
if (dest != null && !(dest instanceof TemporaryQueue || dest instanceof TemporaryTopic)) {
return getCachedConsumer(dest,
(args.length > 1 ? (String) args[1] : null),
(args.length > 2 && (Boolean) args[2]),
null);
}
}
else if (methodName.equals("createDurableSubscriber")) {
if (args[0] != null) {
return getCachedConsumer((Destination) args[0],
Destination dest = (Destination) args[0];
if (dest != null) {
return getCachedConsumer(dest,
(args.length > 2 ? (String) args[2] : null),
(args.length > 3 && (Boolean) args[3]),
(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");
* 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()}.
*/
@Override
public void afterPropertiesSet() {
super.afterPropertiesSet();
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");
* 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;
}
@Override
protected void validateConfiguration() {
if (this.destination == null) {
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");
* 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 #setTransactionManager
*/
public abstract class AbstractPollingMessageListenerContainer extends AbstractMessageListenerContainer
implements BeanNameAware {
public abstract class AbstractPollingMessageListenerContainer extends AbstractMessageListenerContainer {
/**
* The default receive timeout: 1000 ms = 1 second.
@ -100,6 +99,7 @@ public abstract class AbstractPollingMessageListenerContainer extends AbstractMe
private volatile Boolean commitAfterNoMessageReceived;
@Override
public void setSessionTransacted(boolean sessionTransacted) {
super.setSessionTransacted(sessionTransacted);
this.sessionTransactedCalled = true;
@ -188,6 +188,7 @@ public abstract class AbstractPollingMessageListenerContainer extends AbstractMe
}
@Override
public void initialize() {
// Set sessionTransacted=true in case of a non-JTA transaction manager.
if (!this.sessionTransactedCalled &&
@ -374,6 +375,7 @@ public abstract class AbstractPollingMessageListenerContainer extends AbstractMe
* container's "sessionTransacted" flag being set to "true".
* @see org.springframework.jms.connection.JmsResourceHolder
*/
@Override
protected boolean isSessionLocallyTransacted(Session session) {
if (!super.isSessionLocallyTransacted(session)) {
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");
* 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() {
super.validateConfiguration();
synchronized (this.lifecycleMonitor) {
@ -484,6 +485,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
// Implementation of AbstractMessageListenerContainer's template methods
//-------------------------------------------------------------------------
@Override
public void initialize() {
// Adapt default cache level.
if (this.cacheLevel == CACHE_AUTO) {
@ -516,6 +518,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
* @see #scheduleNewInvoker
* @see #setTaskExecutor
*/
@Override
protected void doInitialize() throws JMSException {
synchronized (this.lifecycleMonitor) {
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.
*/
@Override
protected void doShutdown() throws JMSException {
logger.debug("Waiting for shutdown of message listener invokers");
try {
@ -549,6 +553,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
/**
* Overridden to reset the stop callback, if any.
*/
@Override
public void start() throws JmsException {
synchronized (this.lifecycleMonitor) {
this.stopCallback = null;
@ -658,6 +663,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
* @see #setCacheLevel
* @see #CACHE_CONNECTION
*/
@Override
protected final boolean sharedConnectionEnabled() {
return (getCacheLevel() >= CACHE_CONNECTION);
}
@ -666,6 +672,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
* Re-executes the given task via this listener container's TaskExecutor.
* @see #setTaskExecutor
*/
@Override
protected void doRescheduleTask(Object 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...
* @see #scheduleNewInvokerIfAppropriate()
*/
@Override
protected void messageReceived(Object invoker, Session session) {
((AsyncMessageListenerInvoker) invoker).setIdle(false);
scheduleNewInvokerIfAppropriate();
@ -682,6 +690,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
/**
* Marks the affected invoker as idle.
*/
@Override
protected void noMessageReceived(Object invoker, Session session) {
((AsyncMessageListenerInvoker) invoker).setIdle(true);
}
@ -745,6 +754,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
* asynchronous invokers to establish the shared Connection on first access.
* @see #refreshConnectionUntilSuccessful()
*/
@Override
protected void establishSharedConnection() {
try {
super.establishSharedConnection();
@ -760,6 +770,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
* <code>Connection.start()</code>, relying on listeners to perform
* appropriate recovery.
*/
@Override
protected void startSharedConnection() {
try {
super.startSharedConnection();
@ -774,6 +785,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
* <code>Connection.stop()</code>, relying on listeners to perform
* appropriate recovery after a restart.
*/
@Override
protected void stopSharedConnection() {
try {
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");
* 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 connectLazily = false;
private int concurrentConsumers = 1;
private Executor taskExecutor;
@ -89,6 +91,20 @@ public class SimpleMessageListenerContainer extends AbstractMessageListenerConta
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
* upper limit String, e.g. "10".
@ -159,6 +175,7 @@ public class SimpleMessageListenerContainer extends AbstractMessageListenerConta
this.taskExecutor = taskExecutor;
}
@Override
protected void validateConfiguration() {
super.validateConfiguration();
if (isSubscriptionDurable() && this.concurrentConsumers != 1) {
@ -174,6 +191,7 @@ public class SimpleMessageListenerContainer extends AbstractMessageListenerConta
/**
* Always use a shared JMS Connection.
*/
@Override
protected final boolean sharedConnectionEnabled() {
return true;
}
@ -183,15 +201,25 @@ public class SimpleMessageListenerContainer extends AbstractMessageListenerConta
* in the form of a JMS Session plus associated MessageConsumer.
* @see #createListenerConsumer
*/
@Override
protected void doInitialize() throws JMSException {
establishSharedConnection();
initializeConsumers();
if (!this.connectLazily) {
try {
establishSharedConnection();
}
catch (JMSException ex) {
logger.debug("Could not connect on initialization - registering message consumers lazily", ex);
return;
}
initializeConsumers();
}
}
/**
* Re-initializes this container's JMS message consumers,
* if not initialized already.
*/
@Override
protected void doStart() throws JMSException {
super.doStart();
initializeConsumers();
@ -200,6 +228,7 @@ public class SimpleMessageListenerContainer extends AbstractMessageListenerConta
/**
* Registers this listener container as JMS ExceptionListener on the shared connection.
*/
@Override
protected void prepareSharedConnection(Connection connection) throws JMSException {
super.prepareSharedConnection(connection);
connection.setExceptionListener(this);
@ -320,6 +349,7 @@ public class SimpleMessageListenerContainer extends AbstractMessageListenerConta
/**
* Destroy the registered JMS Sessions and associated MessageConsumers.
*/
@Override
protected void doShutdown() throws JMSException {
logger.debug("Closing JMS MessageConsumers");
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");
* you may not use this file except in compliance with the License.
@ -787,7 +787,7 @@ public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implemen
configTimeTransactionManagerHolder.remove();
}
if (this.cacheRegionFactory != null) {
configTimeCacheProviderHolder.remove();
configTimeRegionFactoryHolder.remove();
}
if (this.cacheProvider != null) {
configTimeCacheProviderHolder.remove();
@ -862,7 +862,7 @@ public class LocalSessionFactoryBean extends AbstractSessionFactoryBean implemen
/**
* 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
*/
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");
* 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.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.NamingStrategy;
import org.springframework.beans.factory.DisposableBean;
@ -37,16 +38,15 @@ import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
/**
* {@link org.springframework.beans.factory.FactoryBean} that creates a
* Hibernate {@link org.hibernate.SessionFactory}. This is the usual way to
* set up a shared Hibernate SessionFactory in a Spring application context;
* the SessionFactory can then be passed to Hibernate-based DAOs via
* dependency injection.
* {@link org.springframework.beans.factory.FactoryBean} that creates a Hibernate
* {@link org.hibernate.SessionFactory}. This is the usual way to set up a shared
* Hibernate SessionFactory in a Spring application context; the SessionFactory can
* then be passed to Hibernate-based data access objects via dependency injection.
*
* <p><b>NOTE:</b> This variant of LocalSessionFactoryBean requires Hibernate 4.0
* or higher. It is similar in role to the same-named class in the <code>orm.hibernate3</code>
* package. However, in practice, it is closer to <code>AnnotationSessionFactoryBean</code>
* since its core purpose is to bootstrap a <code>SessionFactory</code> from annotation scanning.
* <p><b>NOTE:</b> This variant of LocalSessionFactoryBean requires Hibernate 4.0 or higher.
* It is similar in role to the same-named class in the <code>orm.hibernate3</code> package.
* However, in practice, it is closer to <code>AnnotationSessionFactoryBean</code> since
* its core purpose is to bootstrap a <code>SessionFactory</code> from annotation scanning.
*
* @author Juergen Hoeller
* @since 3.1
@ -84,6 +84,8 @@ public class LocalSessionFactoryBean extends HibernateExceptionTranslator
private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
private Configuration configuration;
private SessionFactory sessionFactory;
@ -328,7 +330,36 @@ public class LocalSessionFactoryBean extends HibernateExceptionTranslator
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");
* you may not use this file except in compliance with the License.
@ -16,7 +16,7 @@
package org.springframework.oxm.jaxb;
import java.awt.Image;
import java.awt.*;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
@ -65,10 +65,20 @@ import javax.xml.transform.sax.SAXSource;
import javax.xml.validation.Schema;
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.InitializingBean;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.oxm.GenericMarshaller;
import org.springframework.oxm.GenericUnmarshaller;
import org.springframework.oxm.MarshallingFailureException;
@ -87,14 +97,6 @@ import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
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.
*
@ -117,7 +119,7 @@ import org.xml.sax.helpers.XMLReaderFactory;
*/
public class Jaxb2Marshaller
implements MimeMarshaller, MimeUnmarshaller, GenericMarshaller, GenericUnmarshaller, BeanClassLoaderAware,
InitializingBean {
ResourceLoaderAware, InitializingBean {
private static final String CID = "cid:";
@ -130,6 +132,8 @@ public class Jaxb2Marshaller
private String contextPath;
private Class<?>[] classesToBeBound;
private String[] packagesToScan;
private Map<String, ?> jaxbContextProperties;
@ -153,6 +157,8 @@ public class Jaxb2Marshaller
private ClassLoader beanClassLoader;
private ResourceLoader resourceLoader;
private JAXBContext jaxbContext;
private Schema schema;
@ -161,7 +167,7 @@ public class Jaxb2Marshaller
private boolean supportJaxbElementClass = false;
private LSResourceResolver schemaResourceResolver;
private LSResourceResolver schemaResourceResolver;
/**
@ -175,6 +181,8 @@ public class Jaxb2Marshaller
/**
* Set a JAXB context path.
* <p>Setting this property, {@link #setClassesToBeBound "classesToBeBound"}, or
* {@link #setPackagesToScan "packagesToScan"} is required.
*/
public void setContextPath(String contextPath) {
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.
* 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) {
Assert.notEmpty(classesToBeBound, "'classesToBeBound' must not be empty");
@ -204,6 +213,23 @@ public class Jaxb2Marshaller
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
* properties will be set on the underlying <code>JAXBContext</code>.
@ -289,17 +315,18 @@ public class Jaxb2Marshaller
this.schemaLanguage = schemaLanguage;
}
/**
* Sets the resource resolver, as used to load the schema resources.
* @see SchemaFactory#setResourceResolver(org.w3c.dom.ls.LSResourceResolver)
* @see #setSchema(Resource)
* @see #setSchemas(Resource[])
*/
public void setSchemaResourceResolver(LSResourceResolver schemaResourceResolver) {
this.schemaResourceResolver = schemaResourceResolver;
}
/**
* Sets the resource resolver, as used to load the schema resources.
*
* @see SchemaFactory#setResourceResolver(org.w3c.dom.ls.LSResourceResolver)
* @see #setSchema(Resource)
* @see #setSchemas(Resource[])
*/
public void setSchemaResourceResolver(LSResourceResolver schemaResourceResolver) {
this.schemaResourceResolver = schemaResourceResolver;
}
/**
/**
* Specify whether MTOM support should be enabled or not.
* Default is <code>false</code>: marshalling using XOP/MTOM not being enabled.
*/
@ -336,13 +363,23 @@ public class Jaxb2Marshaller
this.beanClassLoader = classLoader;
}
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
public final void afterPropertiesSet() throws Exception {
if (StringUtils.hasLength(getContextPath()) && !ObjectUtils.isEmpty(getClassesToBeBound())) {
throw new IllegalArgumentException("Specify either 'contextPath' or 'classesToBeBound property'; not both");
boolean hasContextPath = StringUtils.hasLength(getContextPath());
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())) {
throw new IllegalArgumentException("Setting either 'contextPath' or 'classesToBeBound' is required");
if (!hasContextPath && !hasClassesToBeBound && !hasPackagesToScan) {
throw new IllegalArgumentException(
"Setting either 'contextPath', 'classesToBeBound', " + "or 'packagesToScan' is required");
}
if (!this.lazyInit) {
getJaxbContext();
@ -361,6 +398,9 @@ public class Jaxb2Marshaller
else if (!ObjectUtils.isEmpty(getClassesToBeBound())) {
this.jaxbContext = createJaxbContextFromClasses();
}
else if (!ObjectUtils.isEmpty(getPackagesToScan())) {
this.jaxbContext = createJaxbContextFromPackages();
}
}
catch (JAXBException 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 {
if (logger.isDebugEnabled()) {
logger.debug("Setting validation schema to " + StringUtils.arrayToCommaDelimitedString(this.schemaResources));
@ -420,9 +480,9 @@ public class Jaxb2Marshaller
schemaSources[i] = new SAXSource(xmlReader, inputSource);
}
SchemaFactory schemaFactory = SchemaFactory.newInstance(schemaLanguage);
if (schemaResourceResolver != null) {
schemaFactory.setResourceResolver(schemaResourceResolver);
}
if (schemaResourceResolver != null) {
schemaFactory.setResourceResolver(schemaResourceResolver);
}
return schemaFactory.newSchema(schemaSources);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 the original author or authors.
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* 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);
}
@Test
public void supportsPackagesToScan() throws Exception {
marshaller = new Jaxb2Marshaller();
marshaller.setPackagesToScan(new String[] {CONTEXT_PATH});
marshaller.afterPropertiesSet();
}
@XmlRootElement
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.jibx.runtime.*;version="[1.1.5, 2.0.0)";resolution:=optional,
org.springframework.beans.*;version=${spring.osgi.range},
org.springframework.context.*;version=${spring.osgi.range},
org.springframework.core.*;version=${spring.osgi.range},
org.springframework.util.*;version=${spring.osgi.range},
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.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
@ -827,7 +827,7 @@ public class DispatcherServlet extends FrameworkServlet {
doDispatch(request, response);
}
finally {
this.flashMapManager.requestCompleted(request);
this.flashMapManager.requestCompleted(request, response);
// Restore the original attribute snapshot, in case of an include.
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
* 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) {
this.createdBy = createdBy;

View File

@ -17,6 +17,7 @@
package org.springframework.web.servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* A strategy interface for storing, retrieving, and managing {@code FlashMap}
@ -64,8 +65,9 @@ public interface FlashMapManager {
* <li>Clean expired FlashMap instances.
* </ol>
* @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
@ -73,7 +75,8 @@ public interface FlashMapManager {
* <p>The "output" FlashMap should not be saved if it is empty or if it was
* not created by the current FlashMapManager instance.
* @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;
/**
* Abstract base class for {@link org.springframework.web.servlet.HandlerExceptionResolver HandlerExceptionResolver}
* implementations that support handling exceptions from {@link HandlerMethod}s rather than handlers.
* Abstract base class for
* {@link org.springframework.web.servlet.HandlerExceptionResolver HandlerExceptionResolver}
* implementations that support handling exceptions from handlers of type {@link HandlerMethod}.
*
* @author Rossen Stoyanchev
* @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
@ -52,10 +53,10 @@ public class AbstractHandlerMethodExceptionResolver extends AbstractHandlerExcep
}
@Override
protected final ModelAndView doResolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
protected final ModelAndView doResolveException(
HttpServletRequest request, HttpServletResponse response,
Object handler, Exception 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
* @return a corresponding ModelAndView to forward to, or <code>null</code> for default processing
*/
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
HttpServletResponse response,
HandlerMethod handlerMethod,
Exception ex) {
return null;
}
protected abstract ModelAndView doResolveHandlerMethodException(
HttpServletRequest request, HttpServletResponse response,
HandlerMethod handlerMethod, Exception ex);
}

View File

@ -21,6 +21,7 @@ import javax.servlet.http.HttpServletResponse;
import org.springframework.util.Assert;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.support.RequestContextUtils;
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
* {@link #getViewNameForRequest}.
* {@link #getViewNameForRequest}. Also adds the content of
* {@link RequestContextUtils#getInputFlashMap} to the model.
*/
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) {
@ -95,7 +97,7 @@ public abstract class AbstractUrlViewController extends AbstractController {
if (logger.isDebugEnabled()) {
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 org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.support.RequestContextUtils;
/**
* <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.
* The content of {@link RequestContextUtils#getInputFlashMap} is also added to the model.
* @see #getViewName()
*/
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
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>
* <li>Sort 'Accept' header media types by quality value via
* {@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
* then with {@link MediaType#includes(MediaType)}.
* <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) {
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;
}
}

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 the original author or authors.
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -30,8 +30,8 @@ import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.mvc.support.RedirectAttributesModelMap;
/**
* Resolves method arguments of type {@link RedirectAttributes}.
*
* Resolves method arguments of type {@link RedirectAttributes}.
*
* <p>This resolver must be listed ahead of {@link org.springframework.web.method.annotation.ModelMethodProcessor} and
* {@link org.springframework.web.method.annotation.MapMethodProcessor}, which support {@link Map} and {@link Model}
* arguments both of which are "super" types of {@code RedirectAttributes}
@ -46,10 +46,11 @@ public class RedirectAttributesMethodArgumentResolver implements HandlerMethodAr
return RedirectAttributes.class.isAssignableFrom(parameter.getParameterType());
}
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
public Object resolveArgument(
MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
throws Exception {
DataBinder dataBinder = binderFactory.createBinder(webRequest, null, null);
ModelMap redirectAttributes = new RedirectAttributesModelMap(dataBinder);
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");
* 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 providedArgs argument values to try to use without the need for view resolution
*/
public final void invokeAndHandle(NativeWebRequest request,
ModelAndViewContainer mavContainer,
Object...providedArgs) throws Exception {
public final void invokeAndHandle(
NativeWebRequest request, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(request, mavContainer, providedArgs);
@ -124,7 +124,7 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
sb.append("[value=" + returnValue + "]");
return getDetailedErrorMessage(sb.toString());
}
/**
* Set the response status according to the {@link ResponseStatus} annotation.
*/
@ -157,4 +157,4 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
private boolean hasResponseStatus() {
return responseStatus != null;
}
}
}

View File

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

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 the original author or authors.
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -47,20 +47,20 @@ public class ServletResponseMethodArgumentResolver implements HandlerMethodArgum
public boolean supportsParameter(MethodParameter parameter) {
Class<?> paramType = parameter.getParameterType();
return ServletResponse.class.isAssignableFrom(paramType)
|| OutputStream.class.isAssignableFrom(paramType)
|| OutputStream.class.isAssignableFrom(paramType)
|| Writer.class.isAssignableFrom(paramType);
}
/**
* Set {@link ModelAndViewContainer#setRequestHandled(boolean)} to
* {@code false} to indicate that the method signature provides access
* to the response. If subsequently the underlying method returns
* Set {@link ModelAndViewContainer#setRequestHandled(boolean)} to
* {@code false} to indicate that the method signature provides access
* to the response. If subsequently the underlying method returns
* {@code null}, the request is considered directly handled.
*/
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws IOException {
public Object resolveArgument(
MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
throws IOException {
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");
* you may not use this file except in compliance with the License.
@ -28,7 +28,7 @@ import org.springframework.web.util.UriComponentsBuilder;
/**
* Resolvers argument values of type {@link UriComponentsBuilder}.
*
*
* <p>The returned instance is initialized via
* {@link ServletUriComponentsBuilder#fromServletMapping(HttpServletRequest)}.
*
@ -41,11 +41,11 @@ public class UriComponentsBuilderMethodArgumentResolver implements HandlerMethod
return UriComponentsBuilder.class.isAssignableFrom(parameter.getParameterType());
}
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
public Object resolveArgument(
MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
throws Exception {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
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");
* you may not use this file except in compliance with the License.
@ -27,14 +27,14 @@ import org.springframework.web.servlet.View;
/**
* Handles return values that are of type {@link View}.
*
* <p>A {@code null} return value is left as-is leaving it to the configured
* {@link RequestToViewNameTranslator} to select a view name by convention.
* <p>A {@code null} return value is left as-is leaving it to the configured
* {@link RequestToViewNameTranslator} to select a view name by convention.
*
* <p>A {@link View} return type has a set purpose. Therefore this handler
* should be configured ahead of handlers that support any return value type
* <p>A {@link View} return type has a set purpose. Therefore this handler
* should be configured ahead of handlers that support any return value type
* annotated with {@code @ModelAttribute} or {@code @ResponseBody} to ensure
* they don't take over.
*
*
* @author Rossen Stoyanchev
* @since 3.1
*/
@ -44,10 +44,11 @@ public class ViewMethodReturnValueHandler implements HandlerMethodReturnValueHan
return View.class.isAssignableFrom(returnType.getParameterType());
}
public void handleReturnValue(Object returnValue,
MethodParameter returnType,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest) throws Exception {
public void handleReturnValue(
Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws Exception {
if (returnValue == null) {
return;
}
@ -62,7 +63,7 @@ public class ViewMethodReturnValueHandler implements HandlerMethodReturnValueHan
}
else {
// should not happen
throw new UnsupportedOperationException("Unexpected return type: " +
throw new UnsupportedOperationException("Unexpected return type: " +
returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
}
}

View File

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

View File

@ -22,10 +22,12 @@ import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ObjectUtils;
@ -47,24 +49,46 @@ public class DefaultFlashMapManager implements FlashMapManager {
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
* (at request completion) and before it expires.
* <p>The default value is 180 seconds.
*/
public void setFlashMapTimeout(int flashTimeout) {
this.flashTimeout = flashTimeout;
public void setFlashMapTimeout(int flashMapTimeout) {
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}
* <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) {
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);
if (CollectionUtils.isEmpty(allMaps)) {
return;
@ -189,7 +213,7 @@ public class DefaultFlashMapManager implements FlashMapManager {
* {@inheritDoc}
* <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);
if (flashMap == null) {
throw new IllegalStateException("requestCompleted called but \"output\" FlashMap was never created");
@ -198,24 +222,35 @@ public class DefaultFlashMapManager implements FlashMapManager {
if (logger.isDebugEnabled()) {
logger.debug("Saving FlashMap=" + flashMap);
}
onSaveFlashMap(flashMap, request);
retrieveFlashMaps(request, true).add(flashMap);
onSaveFlashMap(flashMap, request, response);
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
* target request path is decoded and normalized if it is relative.
* @param flashMap the flash map to be saved
* @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();
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) {
if (path != null) {
path = this.urlPathHelper.decodeRequestString(request, path);

View File

@ -0,0 +1,71 @@
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.servlet.mvc;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.ui.ModelMap;
import org.springframework.web.servlet.FlashMapManager;
import org.springframework.web.servlet.ModelAndView;
/**
* Test fixture with a ParameterizableViewController.
*
* @author Rossen Stoyanchev
* @since 3.1.1
*/
public class ParameterizableViewControllerTests {
private ParameterizableViewController controller;
private MockHttpServletRequest request;
@Before
public void setup() {
this.controller = new ParameterizableViewController();
this.request = new MockHttpServletRequest("GET", "/");
}
@Test
public void handleRequestWithViewName() throws Exception {
String viewName = "testView";
this.controller.setViewName(viewName);
ModelAndView mav = this.controller.handleRequest(this.request, new MockHttpServletResponse());
assertEquals(viewName, mav.getViewName());
assertTrue(mav.getModel().isEmpty());
}
@Test
public void handleRequestWithoutViewName() throws Exception {
ModelAndView mav = this.controller.handleRequest(this.request, new MockHttpServletResponse());
assertNull(mav.getViewName());
assertTrue(mav.getModel().isEmpty());
}
@Test
public void handleRequestWithFlashAttributes() throws Exception {
this.request.setAttribute(FlashMapManager.INPUT_FLASH_MAP_ATTRIBUTE, new ModelMap("name", "value"));
ModelAndView mav = this.controller.handleRequest(this.request, new MockHttpServletResponse());
assertEquals(1, mav.getModel().size());
assertEquals("value", mav.getModel().get("name"));
}
}

View File

@ -20,8 +20,10 @@ import junit.framework.TestCase;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.ui.ModelMap;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.servlet.FlashMapManager;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.ModelAndView;
@ -150,6 +152,17 @@ public class UrlFilenameViewControllerTests extends TestCase {
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) {
String pathInMapping = this.pathMatcher.extractPathWithinPattern(mapping, request.getRequestURI());
request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathInMapping);

View File

@ -218,6 +218,20 @@ public class ProducesRequestConditionTests {
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
public void compareToEqualMatch() {
MockHttpServletRequest request = new MockHttpServletRequest();

View File

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

View File

@ -32,6 +32,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
import org.junit.Before;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.servlet.FlashMap;
import org.springframework.web.util.WebUtils;
@ -46,15 +47,18 @@ public class DefaultFlashMapManagerTests {
private MockHttpServletRequest request;
private MockHttpServletResponse response;
@Before
public void setup() {
this.flashMapManager = new DefaultFlashMapManager();
this.request = new MockHttpServletRequest();
this.response = new MockHttpServletResponse();
}
@Test
public void requestStarted() {
this.flashMapManager.requestStarted(this.request);
this.flashMapManager.requestStarted(this.request, this.response);
FlashMap flashMap = RequestContextUtils.getOutputFlashMap(request);
assertNotNull("Current FlashMap not found", flashMap);
@ -64,7 +68,7 @@ public class DefaultFlashMapManagerTests {
public void requestStartedAlready() {
FlashMap flashMap = new 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));
}
@ -79,7 +83,7 @@ public class DefaultFlashMapManagerTests {
allMaps.add(flashMap);
this.request.setRequestURI("/path");
this.flashMapManager.requestStarted(this.request);
this.flashMapManager.requestStarted(this.request, this.response);
assertEquals(flashMap, RequestContextUtils.getInputFlashMap(this.request));
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.setRequestURI("/mvc/accounts");
this.flashMapManager.requestStarted(this.request);
this.flashMapManager.requestStarted(this.request, this.response);
assertEquals(flashMap, RequestContextUtils.getInputFlashMap(this.request));
assertEquals("Input FlashMap should have been removed", 0, getFlashMaps().size());
@ -114,7 +118,7 @@ public class DefaultFlashMapManagerTests {
allMaps.add(flashMap);
this.request.setRequestURI("/path/");
this.flashMapManager.requestStarted(this.request);
this.flashMapManager.requestStarted(this.request, this.response);
assertEquals(flashMap, RequestContextUtils.getInputFlashMap(this.request));
assertEquals("Input FlashMap should have been removed", 0, getFlashMaps().size());
@ -130,21 +134,21 @@ public class DefaultFlashMapManagerTests {
allMaps.add(flashMap);
this.request.setParameter("number", (String) null);
this.flashMapManager.requestStarted(this.request);
this.flashMapManager.requestStarted(this.request, this.response);
assertNull(RequestContextUtils.getInputFlashMap(this.request));
assertEquals("FlashMap should not have been removed", 1, getFlashMaps().size());
clearFlashMapRequestAttributes();
this.request.setParameter("number", "two");
this.flashMapManager.requestStarted(this.request);
this.flashMapManager.requestStarted(this.request, this.response);
assertNull(RequestContextUtils.getInputFlashMap(this.request));
assertEquals("FlashMap should not have been removed", 1, getFlashMaps().size());
clearFlashMapRequestAttributes();
this.request.setParameter("number", "one");
this.flashMapManager.requestStarted(this.request);
this.flashMapManager.requestStarted(this.request, this.response);
assertEquals(flashMap, RequestContextUtils.getInputFlashMap(this.request));
assertEquals("Input FlashMap should have been removed", 0, getFlashMaps().size());
@ -163,14 +167,14 @@ public class DefaultFlashMapManagerTests {
allMaps.add(flashMap);
this.request.setParameter("id", "1");
this.flashMapManager.requestStarted(this.request);
this.flashMapManager.requestStarted(this.request, this.response);
assertNull(RequestContextUtils.getInputFlashMap(this.request));
assertEquals("FlashMap should not have been removed", 1, getFlashMaps().size());
clearFlashMapRequestAttributes();
this.request.addParameter("id", "2");
this.flashMapManager.requestStarted(this.request);
this.flashMapManager.requestStarted(this.request, this.response);
assertEquals(flashMap, RequestContextUtils.getInputFlashMap(this.request));
assertEquals("Input FlashMap should have been removed", 0, getFlashMaps().size());
@ -196,7 +200,7 @@ public class DefaultFlashMapManagerTests {
Collections.shuffle(allMaps);
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));
}
@ -210,15 +214,15 @@ public class DefaultFlashMapManagerTests {
flashMap.startExpirationPeriod(0);
}
Thread.sleep(100);
this.flashMapManager.requestStarted(this.request);
this.flashMapManager.requestStarted(this.request, this.response);
assertEquals(0, allMaps.size());
}
@Test
public void saveFlashMapWithoutAttributes() throws InterruptedException {
this.flashMapManager.requestStarted(this.request);
this.flashMapManager.requestCompleted(this.request);
this.flashMapManager.requestStarted(this.request, this.response);
this.flashMapManager.requestCompleted(this.request, this.response);
assertNull(getFlashMaps());
}
@ -227,19 +231,19 @@ public class DefaultFlashMapManagerTests {
public void saveFlashMapNotCreatedByThisManager() throws InterruptedException {
FlashMap flashMap = new FlashMap();
this.request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, flashMap);
this.flashMapManager.requestCompleted(this.request);
this.flashMapManager.requestCompleted(this.request, this.response);
assertNull(getFlashMaps());
}
@Test
public void saveFlashMapWithAttributes() throws InterruptedException {
this.flashMapManager.requestStarted(this.request);
this.flashMapManager.requestStarted(this.request, this.response);
FlashMap flashMap = RequestContextUtils.getOutputFlashMap(this.request);
flashMap.put("name", "value");
this.flashMapManager.setFlashMapTimeout(0);
this.flashMapManager.requestCompleted(this.request);
this.flashMapManager.requestCompleted(this.request, this.response);
Thread.sleep(100);
@ -252,49 +256,49 @@ public class DefaultFlashMapManagerTests {
@Test
public void decodeTargetPath() throws InterruptedException {
this.flashMapManager.requestStarted(this.request);
this.flashMapManager.requestStarted(this.request, this.response);
FlashMap flashMap = RequestContextUtils.getOutputFlashMap(this.request);
flashMap.put("key", "value");
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());
}
@Test
public void normalizeTargetPath() throws InterruptedException {
this.flashMapManager.requestStarted(this.request);
this.flashMapManager.requestStarted(this.request, this.response);
FlashMap flashMap = RequestContextUtils.getOutputFlashMap(this.request);
flashMap.put("key", "value");
flashMap.setTargetRequestPath(".");
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());
flashMap.setTargetRequestPath("./");
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());
flashMap.setTargetRequestPath("..");
this.request.setRequestURI("/once/upon/a/time");
this.flashMapManager.requestCompleted(this.request);
this.flashMapManager.requestCompleted(this.request, this.response);
assertEquals("/once/upon", flashMap.getTargetRequestPath());
flashMap.setTargetRequestPath("../");
this.request.setRequestURI("/once/upon/a/time");
this.flashMapManager.requestCompleted(this.request);
this.flashMapManager.requestCompleted(this.request, this.response);
assertEquals("/once/upon/", flashMap.getTargetRequestPath());
flashMap.setTargetRequestPath("../../only");
this.request.setRequestURI("/once/upon/a/time");
this.flashMapManager.requestCompleted(this.request);
this.flashMapManager.requestCompleted(this.request, this.response);
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");
* you may not use this file except in compliance with the License.
@ -24,6 +24,7 @@ package org.springframework.http;
* @author Arjen Poutsma
* @see HttpStatus.Series
* @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 {
@ -44,6 +45,12 @@ public enum HttpStatus {
* @see <a href="http://tools.ietf.org/html/rfc2518#section-10.1">WebDAV</a>
*/
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
@ -140,6 +147,12 @@ public enum HttpStatus {
* @see <a href="http://tools.ietf.org/html/rfc2616#section-10.3.8">HTTP/1.1</a>
*/
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 ---
@ -187,7 +200,7 @@ public enum HttpStatus {
* {@code 408 Request Timeout}.
* @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}.
* @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}.
* @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}.
* @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>
*/
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}.
* @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>
*/
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 ---
@ -295,7 +331,7 @@ public enum HttpStatus {
* {@code 504 Gateway Timeout}.
* @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}.
* @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>
*/
LOOP_DETECTED(508, "Loop Detected"),
/**
* {@code 509 Bandwidth Limit Exceeded}
*/
BANDWIDTH_LIMIT_EXCEEDED(509, "Bandwidth Limit Exceeded"),
/**
* {@code 510 Not Extended}
* @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;

View File

@ -331,7 +331,7 @@ public class MediaType implements Comparable<MediaType> {
String attribute = entry.getKey();
String value = entry.getValue();
checkParameters(attribute, value);
m.put(attribute, unquote(value));
m.put(attribute, value);
}
this.parameters = Collections.unmodifiableMap(m);
}
@ -428,7 +428,7 @@ public class MediaType implements Comparable<MediaType> {
*/
public Charset getCharSet() {
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() {
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");
* 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();
}
public int getRawStatusCode() throws IOException {
return this.response.getRawStatusCode();
}
public String getStatusText() throws IOException {
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");
* 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;
/**
* 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

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");
* 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.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
/**
* {@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
final class CommonsClientHttpResponse implements ClientHttpResponse {
final class CommonsClientHttpResponse extends AbstractClientHttpResponse {
private final HttpMethod httpMethod;
@ -49,8 +48,8 @@ final class CommonsClientHttpResponse implements ClientHttpResponse {
}
public HttpStatus getStatusCode() {
return HttpStatus.valueOf(this.httpMethod.getStatusCode());
public int getRawStatusCode() {
return this.httpMethod.getStatusCode();
}
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");
* 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.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
/**
* {@link org.springframework.http.client.ClientHttpResponse} implementation that uses
@ -38,20 +37,20 @@ import org.springframework.http.HttpStatus;
* @since 3.1
* @see HttpComponentsClientHttpRequest#execute()
*/
final class HttpComponentsClientHttpResponse implements ClientHttpResponse {
final class HttpComponentsClientHttpResponse extends AbstractClientHttpResponse {
private final HttpResponse httpResponse;
private HttpHeaders headers;
public HttpComponentsClientHttpResponse(HttpResponse httpResponse) {
HttpComponentsClientHttpResponse(HttpResponse httpResponse) {
this.httpResponse = httpResponse;
}
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.valueOf(this.httpResponse.getStatusLine().getStatusCode());
public int getRawStatusCode() throws IOException {
return this.httpResponse.getStatusLine().getStatusCode();
}
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");
* 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 org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.util.StringUtils;
/**
@ -32,7 +31,7 @@ import org.springframework.util.StringUtils;
* @author Arjen Poutsma
* @since 3.0
*/
final class SimpleClientHttpResponse implements ClientHttpResponse {
final class SimpleClientHttpResponse extends AbstractClientHttpResponse {
private final HttpURLConnection connection;
@ -44,8 +43,8 @@ final class SimpleClientHttpResponse implements ClientHttpResponse {
}
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.valueOf(this.connection.getResponseCode());
public int getRawStatusCode() throws IOException {
return this.connection.getResponseCode();
}
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");
* you may not use this file except in compliance with the License.
@ -407,7 +407,13 @@ public class JaxWsPortClientInterceptor extends LocalJaxWsServiceFactory
*/
protected Object getPortStub(Service service, QName portQName) {
if (this.webServiceFeatures != null) {
return new FeaturePortProvider().getPortStub(service, portQName, this.webServiceFeatures);
try {
return new FeaturePortProvider().getPortStub(service, portQName, this.webServiceFeatures);
}
catch (LinkageError ex) {
throw new IllegalStateException(
"Specifying the 'webServiceFeatures' property requires JAX-WS 2.1 or higher at runtime", ex);
}
}
else {
return (portQName != null ? service.getPort(portQName, getServiceInterface()) :
@ -527,6 +533,7 @@ public class JaxWsPortClientInterceptor extends LocalJaxWsServiceFactory
}
}
/**
* Inner class in order to avoid a hard-coded JAX-WS 2.1 dependency.
* 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");
* you may not use this file except in compliance with the License.
@ -35,8 +35,8 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
/**
* Abstract base class for resolving method arguments from a named value. Request parameters, request headers, and
* path variables are examples of named values. Each may have a name, a required flag, and a default value.
* Abstract base class for resolving method arguments from a named value. Request parameters, request headers, and
* path variables are examples of named values. Each may have a name, a required flag, and a default value.
* <p>Subclasses define how to do the following:
* <ul>
* <li>Obtain named value information for a method parameter
@ -44,11 +44,11 @@ import org.springframework.web.method.support.ModelAndViewContainer;
* <li>Handle missing argument values when argument values are required
* <li>Optionally handle a resolved value
* </ul>
* <p>A default value string can contain ${...} placeholders and Spring Expression Language #{...} expressions.
* <p>A default value string can contain ${...} placeholders and Spring Expression Language #{...} expressions.
* For this to work a {@link ConfigurableBeanFactory} must be supplied to the class constructor.
* <p>A {@link WebDataBinder} is created to apply type conversion to the resolved argument value if it doesn't
* <p>A {@link WebDataBinder} is created to apply type conversion to the resolved argument value if it doesn't
* match the method parameter type.
*
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @since 3.1
@ -63,7 +63,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
new ConcurrentHashMap<MethodParameter, NamedValueInfo>();
/**
* @param beanFactory a bean factory to use for resolving ${...} placeholder and #{...} SpEL expressions
* @param beanFactory a bean factory to use for resolving ${...} placeholder and #{...} SpEL expressions
* in default values, or {@code null} if default values are not expected to contain expressions
*/
public AbstractNamedValueMethodArgumentResolver(ConfigurableBeanFactory beanFactory) {
@ -71,10 +71,11 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
this.expressionContext = (beanFactory != null) ? new BeanExpressionContext(beanFactory, new RequestScope()) : null;
}
public final Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
public final Object resolveArgument(
MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
throws Exception {
Class<?> paramType = parameter.getParameterType();
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
@ -95,7 +96,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
arg = binder.convertIfNecessary(arg, paramType, parameter);
}
handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
return arg;
@ -115,9 +116,9 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
}
/**
* Create the {@link NamedValueInfo} object for the given method parameter. Implementations typically
* Create the {@link NamedValueInfo} object for the given method parameter. Implementations typically
* retrieve the method annotation by means of {@link MethodParameter#getParameterAnnotation(Class)}.
*
*
* @param parameter the method parameter
* @return the named value information
*/
@ -136,7 +137,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
String defaultValue = (ValueConstants.DEFAULT_NONE.equals(info.defaultValue) ? null : info.defaultValue);
return new NamedValueInfo(name, info.required, defaultValue);
}
/**
* Resolves the given parameter type and value name into an argument value.
* @param name the name of the value being resolved
@ -165,7 +166,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
}
/**
* Invoked when a named value is required, but {@link #resolveName(String, MethodParameter, NativeWebRequest)}
* Invoked when a named value is required, but {@link #resolveName(String, MethodParameter, NativeWebRequest)}
* returned {@code null} and there is no default value. Subclasses typically throw an exception in this case.
* @param name the name for the value
* @param parameter the method parameter
@ -219,4 +220,4 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
this.defaultValue = defaultValue;
}
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -18,6 +18,8 @@ package org.springframework.web.util;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.springframework.util.Assert;
@ -38,38 +40,107 @@ import org.springframework.util.Assert;
*/
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
/**
* Encodes the given source URI into an encoded String. All various URI components are
* 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 encoding the character encoding to encode to
* @return the encoded URI
* @throws IllegalArgumentException when the given uri parameter is not a valid URI
* @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 {
UriComponents uriComponents = UriComponentsBuilder.fromUriString(uri).build();
UriComponents encoded = uriComponents.encode(encoding);
return encoded.toUriString();
}
Assert.notNull(uri, "'uri' must not be null");
Assert.hasLength(encoding, "'encoding' must not be empty");
Matcher m = URI_PATTERN.matcher(uri);
if (m.matches()) {
String scheme = m.group(2);
String authority = m.group(3);
String userinfo = m.group(5);
String host = m.group(6);
String port = m.group(8);
String path = m.group(9);
String query = m.group(11);
String fragment = m.group(13);
return encodeUriComponents(scheme, authority, userinfo, host, port, path, query, fragment, encoding);
}
else {
throw new IllegalArgumentException("[" + uri + "] is not a valid URI");
}
}
/**
* Encodes the given HTTP URI into an encoded String. All various URI components are
* encoded according to their respective valid character sets.
* <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.
* <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 encoding the character encoding to encode to
* @return the encoded URL
* @throws IllegalArgumentException when the given uri parameter is not a valid URI
* @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 {
UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl(httpUrl).build();
UriComponents encoded = uriComponents.encode(encoding);
return encoded.toUriString();
Assert.notNull(httpUrl, "'httpUrl' must not be null");
Assert.hasLength(encoding, "'encoding' must not be empty");
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
* @throws IllegalArgumentException when the given uri parameter is not a valid URI
* @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,
String host, String port, String path, String query, String fragment, String encoding)
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();
builder.scheme(scheme).userInfo(userInfo).host(host).port(portAsInt);
builder.path(path).query(query).fragment(fragment);
if (scheme != null) {
sb.append(encodeScheme(scheme, encoding));
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");
* you may not use this file except in compliance with the License.
@ -16,38 +16,110 @@
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 static org.junit.Assert.*;
/** @author Arjen Poutsma */
public class HttpStatusTests {
private int[] registryValues =
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 Map<Integer, String> statusCodes = new LinkedHashMap<Integer, String>();
private String[] registryDescriptions =
new String[]{"CONTINUE", "SWITCHING_PROTOCOLS", "PROCESSING", "OK", "CREATED", "ACCEPTED",
"NON_AUTHORITATIVE_INFORMATION", "NO_CONTENT", "RESET_CONTENT", "PARTIAL_CONTENT", "MULTI_STATUS",
"ALREADY_REPORTED", "IM_USED", "MULTIPLE_CHOICES", "MOVED_PERMANENTLY", "FOUND", "SEE_OTHER",
"NOT_MODIFIED", "USE_PROXY", "TEMPORARY_REDIRECT", "BAD_REQUEST", "UNAUTHORIZED",
"PAYMENT_REQUIRED", "FORBIDDEN", "NOT_FOUND", "METHOD_NOT_ALLOWED", "NOT_ACCEPTABLE",
"PROXY_AUTHENTICATION_REQUIRED", "REQUEST_TIMEOUT", "CONFLICT", "GONE", "LENGTH_REQUIRED",
"PRECONDITION_FAILED", "REQUEST_ENTITY_TOO_LARGE", "REQUEST_URI_TOO_LONG", "UNSUPPORTED_MEDIA_TYPE",
"REQUESTED_RANGE_NOT_SATISFIABLE", "EXPECTATION_FAILED", "UNPROCESSABLE_ENTITY", "LOCKED",
"FAILED_DEPENDENCY", "UPGRADE_REQUIRED", "INTERNAL_SERVER_ERROR", "NOT_IMPLEMENTED", "BAD_GATEWAY",
"SERVICE_UNAVAILABLE", "GATEWAY_TIMEOUT", "HTTP_VERSION_NOT_SUPPORTED", "VARIANT_ALSO_NEGOTIATES",
"INSUFFICIENT_STORAGE", "LOOP_DETECTED", "NOT_EXTENDED",};
@Before
public void createStatusCodes() {
statusCodes.put(100, "CONTINUE");
statusCodes.put(101, "SWITCHING_PROTOCOLS");
statusCodes.put(102, "PROCESSING");
statusCodes.put(103, "CHECKPOINT");
@Test
public void registryValues() {
for (int i = 0; i < registryValues.length; i++) {
HttpStatus status = HttpStatus.valueOf(registryValues[i]);
assertEquals("Invalid value", registryValues[i], status.value());
assertEquals("Invalid descripion", registryDescriptions[i], status.name());
}
statusCodes.put(200, "OK");
statusCodes.put(201, "CREATED");
statusCodes.put(202, "ACCEPTED");
statusCodes.put(203, "NON_AUTHORITATIVE_INFORMATION");
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
public void fromMapToEnum() {
for (Map.Entry<Integer, String> entry : statusCodes.entrySet()) {
int value = entry.getKey();
HttpStatus status = HttpStatus.valueOf(value);
assertEquals("Invalid value", value, status.value());
assertEquals("Invalid name for [" + value + "]", entry.getValue(), status.name());
}
}
@Test
public void fromEnumToMap() {
for (HttpStatus status : HttpStatus.values()) {
int value = status.value();
if (value == 302) {
continue;
}
assertTrue("Map has no value for [" + value + "]", statusCodes.containsKey(value));
assertEquals("Invalid name for [" + value + "]", statusCodes.get(value), status.name());
}
}
}

View File

@ -173,9 +173,12 @@ public class MediaTypeTests {
MediaType.parseMediaType("text/html; charset=foo-bar");
}
// SPR-8917
@Test
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)

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");
* you may not use this file except in compliance with the License.
@ -291,6 +291,10 @@ public class InterceptingClientHttpRequestFactoryTests {
return statusCode;
}
public int getRawStatusCode() throws IOException {
return statusCode.value();
}
public String getStatusText() throws IOException {
return statusText;
}
@ -300,7 +304,7 @@ public class InterceptingClientHttpRequestFactoryTests {
}
public InputStream getBody() throws IOException {
return null; //To change body of implemented methods use File | Settings | File Templates.
return null;
}
public void close() {

View File

@ -128,6 +128,9 @@ public class UriUtilsTests {
assertEquals("Invalid encoded URI", "http://example.com/query=foo@bar",
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