From d7eadd41d69143a71e0467a5b2e073d8537662d4 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Mon, 4 Jul 2016 12:28:13 +0200 Subject: [PATCH 0001/1274] Switch to 4.3.2.BUILD-SNAPSHOT --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 3e8083473ed..b61eae0b3b5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=4.3.1.BUILD-SNAPSHOT +version=4.3.2.BUILD-SNAPSHOT From 11cb109114568359a7cf214bbad3c51b6d660f75 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Mon, 4 Jul 2016 12:42:54 +0200 Subject: [PATCH 0002/1274] Upgrade copyright --- src/asciidoc/index-docinfo.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/asciidoc/index-docinfo.xml b/src/asciidoc/index-docinfo.xml index 78f1c77c897..57d964cffb6 100644 --- a/src/asciidoc/index-docinfo.xml +++ b/src/asciidoc/index-docinfo.xml @@ -1,7 +1,7 @@ Spring Framework {revnumber} - 2004-2015 + 2004-2016 Copies of this document may be made for your own use and for distribution to From 52065a736b6b140291c15f0c4caf051856aca6f7 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 6 Jul 2016 15:12:18 +0200 Subject: [PATCH 0003/1274] Avoid canonicalName call for already-seen bean name Issue: SPR-14433 (cherry picked from commit 5890758) --- .../beans/factory/support/DefaultSingletonBeanRegistry.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java index 9042bad5cbf..d3d21347023 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -449,10 +449,10 @@ protected boolean isDependent(String beanName, String dependentBeanName) { } private boolean isDependent(String beanName, String dependentBeanName, Set alreadySeen) { - String canonicalName = canonicalName(beanName); if (alreadySeen != null && alreadySeen.contains(beanName)) { return false; } + String canonicalName = canonicalName(beanName); Set dependentBeans = this.dependentBeanMap.get(canonicalName); if (dependentBeans == null) { return false; From 16d5ba9b3a4d93b997e9a54f6c754923b5163fd7 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 6 Jul 2016 16:11:15 +0200 Subject: [PATCH 0004/1274] Restored binary compatibility with Hibernate 5.0/5.1's Query type Issue: SPR-14425 --- .../orm/hibernate5/HibernateTemplate.java | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateTemplate.java b/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateTemplate.java index 06537de1df3..601d389ae0a 100644 --- a/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateTemplate.java +++ b/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateTemplate.java @@ -45,6 +45,7 @@ import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.util.Assert; +import org.springframework.util.ReflectionUtils; /** * Helper class that simplifies Hibernate data access code. Automatically @@ -83,6 +84,20 @@ */ public class HibernateTemplate implements HibernateOperations, InitializingBean { + private static final Method createQueryMethod; + + static { + // Hibernate 5.2's createQuery method declares a new subtype as return type, + // so we need to use reflection for binary compatibility with 5.0/5.1 here. + try { + createQueryMethod = Session.class.getMethod("createQuery", String.class); + } + catch (NoSuchMethodException ex) { + throw new IllegalStateException("Incompatible Hibernate Session API", ex); + } + } + + protected final Log logger = LogFactory.getLog(getClass()); private SessionFactory sessionFactory; @@ -863,7 +878,8 @@ public List find(final String queryString, final Object... values) throws Dat @Override @SuppressWarnings({"rawtypes", "deprecation"}) public List doInHibernate(Session session) throws HibernateException { - org.hibernate.Query queryObject = session.createQuery(queryString); + org.hibernate.Query queryObject = (org.hibernate.Query) + ReflectionUtils.invokeMethod(createQueryMethod, session, queryString); prepareQuery(queryObject); if (values != null) { for (int i = 0; i < values.length; i++) { @@ -893,7 +909,8 @@ public List findByNamedParam(final String queryString, final String[] paramNa @Override @SuppressWarnings({"rawtypes", "deprecation"}) public List doInHibernate(Session session) throws HibernateException { - org.hibernate.Query queryObject = session.createQuery(queryString); + org.hibernate.Query queryObject = (org.hibernate.Query) + ReflectionUtils.invokeMethod(createQueryMethod, session, queryString); prepareQuery(queryObject); for (int i = 0; i < values.length; i++) { applyNamedParameterToQuery(queryObject, paramNames[i], values[i]); @@ -911,7 +928,8 @@ public List findByValueBean(final String queryString, final Object valueBean) @Override @SuppressWarnings({"rawtypes", "deprecation"}) public List doInHibernate(Session session) throws HibernateException { - org.hibernate.Query queryObject = session.createQuery(queryString); + org.hibernate.Query queryObject = (org.hibernate.Query) + ReflectionUtils.invokeMethod(createQueryMethod, session, queryString); prepareQuery(queryObject); queryObject.setProperties(valueBean); return queryObject.list(); @@ -1072,7 +1090,8 @@ public Iterator iterate(final String queryString, final Object... values) thr @Override @SuppressWarnings({"rawtypes", "deprecation"}) public Iterator doInHibernate(Session session) throws HibernateException { - org.hibernate.Query queryObject = session.createQuery(queryString); + org.hibernate.Query queryObject = (org.hibernate.Query) + ReflectionUtils.invokeMethod(createQueryMethod, session, queryString); prepareQuery(queryObject); if (values != null) { for (int i = 0; i < values.length; i++) { @@ -1100,7 +1119,8 @@ public int bulkUpdate(final String queryString, final Object... values) throws D @Override @SuppressWarnings({"rawtypes", "deprecation"}) public Integer doInHibernate(Session session) throws HibernateException { - org.hibernate.Query queryObject = session.createQuery(queryString); + org.hibernate.Query queryObject = (org.hibernate.Query) + ReflectionUtils.invokeMethod(createQueryMethod, session, queryString); prepareQuery(queryObject); if (values != null) { for (int i = 0; i < values.length; i++) { From 92d78c10a2f467db17d17831cdf65e13d032335d Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 6 Jul 2016 15:29:15 +0200 Subject: [PATCH 0005/1274] Polishing (backported from master) --- build.gradle | 6 +- .../intercept/MethodInterceptor.java | 2 +- .../beans/TypeConverterDelegate.java | 2 +- .../support/DefaultListableBeanFactory.java | 2 +- .../factory/support/RootBeanDefinition.java | 4 +- .../cache/ehcache/package-info.java | 2 +- .../CachingConfigurationSelector.java | 8 +- ...tationDrivenCacheBeanDefinitionParser.java | 9 +- .../interceptor/CacheOperationInvoker.java | 5 +- .../AnnotatedBeanDefinitionReader.java | 12 +- .../groovy/GroovyScriptFactoryTests.java | 2 +- .../jruby-with-xsd-proxy-target-class.xml | 17 -- .../core/MethodIntrospector.java | 7 +- .../core/convert/TypeDescriptor.java | 4 +- .../org/springframework/util/Base64Utils.java | 8 +- .../org/springframework/util/DigestUtils.java | 5 +- .../util/UpdateMessageDigestInputStream.java | 4 +- .../CompletableToListenableFutureAdapter.java | 5 +- .../util/concurrent/FutureAdapter.java | 9 +- .../core/env/PropertySourceTests.java | 14 +- .../expression/spel/CodeFlow.java | 147 ++++++++++-------- .../expression/spel/ast/InlineList.java | 12 +- .../jdbc/core/simple/AbstractJdbcInsert.java | 3 +- .../jdbc/core/simple/SimpleJdbcInsert.java | 3 +- .../simple/SimpleJdbcInsertOperations.java | 3 +- ...ransactionAwareConnectionFactoryProxy.java | 4 +- .../endpoint/JmsMessageEndpointFactory.java | 4 +- .../MappingJackson2MessageConverter.java | 5 +- .../converter/MessagingMessageConverter.java | 3 - .../AbstractMessageBrokerConfiguration.java | 2 +- .../AbstractMessageConverterTests.java | 136 ---------------- .../converter/MessageConverterTests.java | 5 +- .../orm/jpa/SharedEntityManagerCreator.java | 6 +- spring-orm/src/main/java/overview.html | 2 +- .../oxm/config/OxmNamespaceHandlerTests.java | 10 +- .../support/TestPropertySourceUtils.java | 21 +-- .../test/jdbc/JdbcTestUtils.java | 7 +- .../client/ClientHttpRequestExecution.java | 13 +- .../json/Jackson2ObjectMapperBuilder.java | 2 +- .../jaxws/SimpleJaxWsServiceExporter.java | 6 +- .../bind/support/WebRequestDataBinder.java | 7 +- .../context/request/ServletWebRequest.java | 1 - .../support/MultipartResolutionDelegate.java | 2 +- .../web/servlet/HandlerInterceptor.java | 8 +- .../web/servlet/LocaleResolver.java | 10 +- .../handler/BeanNameUrlHandlerMapping.java | 8 +- .../handler/HandlerInterceptorAdapter.java | 7 +- .../CompletionStageReturnValueHandler.java | 3 +- ...eferredResultMethodReturnValueHandler.java | 2 + .../ListenableFutureReturnValueHandler.java | 1 - .../resource/CssLinkResourceTransformer.java | 21 ++- .../servlet/resource/ResourceTransformer.java | 6 +- ...plateServletAnnotationControllerTests.java | 21 +-- .../standard/StandardWebSocketClient.java | 4 +- .../WebMvcStompEndpointRegistry.java | 19 ++- .../AbstractTyrusRequestUpgradeStrategy.java | 8 +- .../WebLogicRequestUpgradeStrategy.java | 3 +- .../sockjs/client/AbstractXhrTransport.java | 9 +- .../session/PollingSockJsSession.java | 1 - .../web/socket/config/spring-websocket.gif | Bin 0 -> 1025 bytes .../AbstractWebSocketIntegrationTests.java | 13 +- ...ests.java => WebSocketHandshakeTests.java} | 13 +- 62 files changed, 273 insertions(+), 415 deletions(-) delete mode 100644 spring-context/src/test/resources/org/springframework/scripting/groovy/jruby-with-xsd-proxy-target-class.xml delete mode 100644 spring-messaging/src/test/java/org/springframework/messaging/converter/AbstractMessageConverterTests.java create mode 100644 spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket.gif rename spring-websocket/src/test/java/org/springframework/web/socket/{WebSocketIntegrationTests.java => WebSocketHandshakeTests.java} (95%) diff --git a/build.gradle b/build.gradle index 85b1d260626..314f67a6f42 100644 --- a/build.gradle +++ b/build.gradle @@ -879,10 +879,10 @@ project("spring-webmvc") { testCompile("commons-io:commons-io:1.3") testCompile("joda-time:joda-time:${jodaVersion}") testCompile("org.slf4j:slf4j-jcl:${slf4jVersion}") - testCompile("org.jruby:jruby:${jrubyVersion}") - testCompile("org.python:jython-standalone:2.5.3") testCompile("org.mozilla:rhino:1.7.7.1") - testCompile("org.webjars:underscorejs:1.8.3") + testRuntime("org.jruby:jruby:${jrubyVersion}") + testRuntime("org.python:jython-standalone:2.5.3") + testRuntime("org.webjars:underscorejs:1.8.3") } } diff --git a/spring-aop/src/main/java/org/aopalliance/intercept/MethodInterceptor.java b/spring-aop/src/main/java/org/aopalliance/intercept/MethodInterceptor.java index 7fa2b872691..c08fd7443f2 100644 --- a/spring-aop/src/main/java/org/aopalliance/intercept/MethodInterceptor.java +++ b/spring-aop/src/main/java/org/aopalliance/intercept/MethodInterceptor.java @@ -46,7 +46,7 @@ public interface MethodInterceptor extends Interceptor { * after the invocation. Polite implementations would certainly * like to invoke {@link Joinpoint#proceed()}. * @param invocation the method invocation joinpoint - * @return the result of the call to {@link Joinpoint#proceed(); + * @return the result of the call to {@link Joinpoint#proceed()}; * might be intercepted by the interceptor * @throws Throwable if the interceptors or the target object * throws an exception diff --git a/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java b/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java index 9928530dbd4..908bfeafe66 100644 --- a/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java +++ b/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java @@ -269,7 +269,7 @@ else if (convertedValue instanceof Number && Number.class.isAssignableFrom(requi } else { // convertedValue == null - if (javaUtilOptionalEmpty != null && requiredType.equals(javaUtilOptionalEmpty.getClass())) { + if (javaUtilOptionalEmpty != null && requiredType == javaUtilOptionalEmpty.getClass()) { convertedValue = javaUtilOptionalEmpty; } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index 6205fa69cfe..9cc3c907ea4 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -1002,7 +1002,7 @@ public Object resolveDependency(DependencyDescriptor descriptor, String beanName Set autowiredBeanNames, TypeConverter typeConverter) throws BeansException { descriptor.initParameterNameDiscovery(getParameterNameDiscoverer()); - if (descriptor.getDependencyType().equals(javaUtilOptionalClass)) { + if (javaUtilOptionalClass == descriptor.getDependencyType()) { return new OptionalDependencyFactory().createOptionalDependency(descriptor, beanName); } else if (ObjectFactory.class == descriptor.getDependencyType() || diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java index d2ac971cbc9..91ad5d8e96c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 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. @@ -125,7 +125,7 @@ public RootBeanDefinition(Class beanClass, int autowireMode, boolean dependen setBeanClass(beanClass); setAutowireMode(autowireMode); if (dependencyCheck && getResolvedAutowireMode() != AUTOWIRE_CONSTRUCTOR) { - setDependencyCheck(RootBeanDefinition.DEPENDENCY_CHECK_OBJECTS); + setDependencyCheck(DEPENDENCY_CHECK_OBJECTS); } } diff --git a/spring-context-support/src/main/java/org/springframework/cache/ehcache/package-info.java b/spring-context-support/src/main/java/org/springframework/cache/ehcache/package-info.java index 4291b2deae8..edb951a4ce5 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/ehcache/package-info.java +++ b/spring-context-support/src/main/java/org/springframework/cache/ehcache/package-info.java @@ -7,6 +7,6 @@ *

Note: EhCache 3.x lives in a different package namespace * and is not covered by the traditional support classes here. * Instead, consider using it through JCache (JSR-107), with - * Spring's support in {@link org.springframework.cache.jcache}. + * Spring's support in {@code org.springframework.cache.jcache}. */ package org.springframework.cache.ehcache; diff --git a/spring-context/src/main/java/org/springframework/cache/annotation/CachingConfigurationSelector.java b/spring-context/src/main/java/org/springframework/cache/annotation/CachingConfigurationSelector.java index f99b40d756e..f7f6fa4ec7c 100644 --- a/spring-context/src/main/java/org/springframework/cache/annotation/CachingConfigurationSelector.java +++ b/spring-context/src/main/java/org/springframework/cache/annotation/CachingConfigurationSelector.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -52,7 +52,7 @@ public class CachingConfigurationSelector extends AdviceModeImportSelector result = new ArrayList(); result.add(AutoProxyRegistrar.class.getName()); result.add(ProxyCachingConfiguration.class.getName()); - if (jsr107Present && jCacheImplPresent) { + if (jsr107Present && jcacheImplPresent) { result.add(PROXY_JCACHE_CONFIGURATION_CLASS); } return result.toArray(new String[result.size()]); @@ -94,7 +94,7 @@ private String[] getProxyImports() { private String[] getAspectJImports() { List result = new ArrayList(); result.add(CACHE_ASPECT_CONFIGURATION_CLASS_NAME); - if (jsr107Present && jCacheImplPresent) { + if (jsr107Present && jcacheImplPresent) { result.add(JCACHE_ASPECT_CONFIGURATION_CLASS_NAME); } return result.toArray(new String[result.size()]); diff --git a/spring-context/src/main/java/org/springframework/cache/config/AnnotationDrivenCacheBeanDefinitionParser.java b/spring-context/src/main/java/org/springframework/cache/config/AnnotationDrivenCacheBeanDefinitionParser.java index 4ea5ccad0aa..d9f90dc2f14 100644 --- a/spring-context/src/main/java/org/springframework/cache/config/AnnotationDrivenCacheBeanDefinitionParser.java +++ b/spring-context/src/main/java/org/springframework/cache/config/AnnotationDrivenCacheBeanDefinitionParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -60,11 +60,10 @@ class AnnotationDrivenCacheBeanDefinitionParser implements BeanDefinitionParser private static final String JCACHE_ASPECT_CLASS_NAME = "org.springframework.cache.aspectj.JCacheCacheAspect"; - private static final boolean jsr107Present = ClassUtils.isPresent( "javax.cache.Cache", AnnotationDrivenCacheBeanDefinitionParser.class.getClassLoader()); - private static final boolean jCacheImplPresent = ClassUtils.isPresent( + private static final boolean jcacheImplPresent = ClassUtils.isPresent( "org.springframework.cache.jcache.interceptor.DefaultJCacheOperationSource", AnnotationDrivenCacheBeanDefinitionParser.class.getClassLoader()); @@ -91,7 +90,7 @@ public BeanDefinition parse(Element element, ParserContext parserContext) { private void registerCacheAspect(Element element, ParserContext parserContext) { SpringCachingConfigurer.registerCacheAspect(element, parserContext); - if (jsr107Present && jCacheImplPresent) { // Register JCache aspect + if (jsr107Present && jcacheImplPresent) { JCacheCachingConfigurer.registerCacheAspect(element, parserContext); } } @@ -99,7 +98,7 @@ private void registerCacheAspect(Element element, ParserContext parserContext) { private void registerCacheAdvisor(Element element, ParserContext parserContext) { AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element); SpringCachingConfigurer.registerCacheAdvisor(element, parserContext); - if (jsr107Present && jCacheImplPresent) { // Register JCache advisor + if (jsr107Present && jcacheImplPresent) { JCacheCachingConfigurer.registerCacheAdvisor(element, parserContext); } } diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationInvoker.java b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationInvoker.java index 8901ba1945c..3529438d784 100644 --- a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationInvoker.java +++ b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationInvoker.java @@ -30,9 +30,8 @@ public interface CacheOperationInvoker { /** - * Invoke the cache operation defined by this instance. Wraps any - * exception that is thrown during the invocation in a - * {@link ThrowableWrapper}. + * Invoke the cache operation defined by this instance. Wraps any exception + * that is thrown during the invocation in a {@link ThrowableWrapper}. * @return the result of the operation * @throws ThrowableWrapper if an error occurred while invoking the operation */ diff --git a/spring-context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java b/spring-context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java index 780f8be00b6..4c16bb0e64c 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 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. @@ -127,15 +127,13 @@ public void registerBean(Class annotatedClass) { registerBean(annotatedClass, null, (Class[]) null); } - public void registerBean(Class annotatedClass, - @SuppressWarnings("unchecked") Class... qualifiers) { - + @SuppressWarnings("unchecked") + public void registerBean(Class annotatedClass, Class... qualifiers) { registerBean(annotatedClass, null, qualifiers); } - public void registerBean(Class annotatedClass, String name, - @SuppressWarnings("unchecked") Class... qualifiers) { - + @SuppressWarnings("unchecked") + public void registerBean(Class annotatedClass, String name, Class... qualifiers) { AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass); if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) { return; diff --git a/spring-context/src/test/java/org/springframework/scripting/groovy/GroovyScriptFactoryTests.java b/spring-context/src/test/java/org/springframework/scripting/groovy/GroovyScriptFactoryTests.java index 9ab27884430..10d7c7ac1ca 100644 --- a/spring-context/src/test/java/org/springframework/scripting/groovy/GroovyScriptFactoryTests.java +++ b/spring-context/src/test/java/org/springframework/scripting/groovy/GroovyScriptFactoryTests.java @@ -466,7 +466,7 @@ public void testRefreshableFromTagProxyTargetClass() throws Exception { @Test // SPR-6268 public void testProxyTargetClassNotAllowedIfNotGroovy() throws Exception { try { - new ClassPathXmlApplicationContext("jruby-with-xsd-proxy-target-class.xml", getClass()); + new ClassPathXmlApplicationContext("groovy-with-xsd-proxy-target-class.xml", getClass()); } catch (BeanCreationException ex) { assertTrue(ex.getMessage().contains("Cannot use proxyTargetClass=true")); diff --git a/spring-context/src/test/resources/org/springframework/scripting/groovy/jruby-with-xsd-proxy-target-class.xml b/spring-context/src/test/resources/org/springframework/scripting/groovy/jruby-with-xsd-proxy-target-class.xml deleted file mode 100644 index 58a9b4027b4..00000000000 --- a/spring-context/src/test/resources/org/springframework/scripting/groovy/jruby-with-xsd-proxy-target-class.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - diff --git a/spring-core/src/main/java/org/springframework/core/MethodIntrospector.java b/spring-core/src/main/java/org/springframework/core/MethodIntrospector.java index 5a2b2df765d..804c26e74ba 100644 --- a/spring-core/src/main/java/org/springframework/core/MethodIntrospector.java +++ b/spring-core/src/main/java/org/springframework/core/MethodIntrospector.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -85,10 +85,9 @@ public void doWith(Method method) { /** * Select methods on the given target type based on a filter. - *

Callers define methods of interest through the - * {@link ReflectionUtils.MethodFilter} parameter. + *

Callers define methods of interest through the {@code MethodFilter} parameter. * @param targetType the target type to search methods on - * @param methodFilter a {@link ReflectionUtils.MethodFilter} to help + * @param methodFilter a {@code MethodFilter} to help * recognize handler methods of interest * @return the selected methods, or an empty set in case of no match */ diff --git a/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java b/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java index dd5f180180d..ffbbd7ab0d0 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java +++ b/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java @@ -343,7 +343,7 @@ public TypeDescriptor getElementTypeDescriptor() { if (streamAvailable && StreamDelegate.isStream(this.type)) { return StreamDelegate.getStreamElementType(this); } - return getRelatedIfResolvable(this, this.resolvableType.asCollection().getGeneric()); + return getRelatedIfResolvable(this, this.resolvableType.asCollection().getGeneric(0)); } /** @@ -706,7 +706,7 @@ public static boolean isStream(Class type) { } public static TypeDescriptor getStreamElementType(TypeDescriptor source) { - return getRelatedIfResolvable(source, source.resolvableType.as(Stream.class).getGeneric()); + return getRelatedIfResolvable(source, source.resolvableType.as(Stream.class).getGeneric(0)); } } diff --git a/spring-core/src/main/java/org/springframework/util/Base64Utils.java b/spring-core/src/main/java/org/springframework/util/Base64Utils.java index 17c48614b83..f10b60d62f0 100644 --- a/spring-core/src/main/java/org/springframework/util/Base64Utils.java +++ b/spring-core/src/main/java/org/springframework/util/Base64Utils.java @@ -30,11 +30,11 @@ * Codec present, {@link #encode}/{@link #decode} calls will throw an IllegalStateException. * However, as of Spring 4.2, {@link #encodeToString} and {@link #decodeFromString} will * nevertheless work since they can delegate to the JAXB DatatypeConverter as a fallback. - * However, this does not apply when using the ...UrlSafe... methods for RFC 4648 "URL and + * However, this does not apply when using the "UrlSafe" methods for RFC 4648 "URL and * Filename Safe Alphabet"; a delegate is required. - *

- * Note: Apache Commons Codec does not add padding ({@code =}) when encoding with - * the URL and Filename Safe Alphabet. + * + *

Note: Apache Commons Codec does not add padding ({@code =}) when encoding + * with the URL and Filename Safe Alphabet. * * @author Juergen Hoeller * @author Gary Russell diff --git a/spring-core/src/main/java/org/springframework/util/DigestUtils.java b/spring-core/src/main/java/org/springframework/util/DigestUtils.java index 8de0b513cdc..6251c7aa968 100644 --- a/spring-core/src/main/java/org/springframework/util/DigestUtils.java +++ b/spring-core/src/main/java/org/springframework/util/DigestUtils.java @@ -24,13 +24,12 @@ /** * Miscellaneous methods for calculating digests. *

Mainly for internal use within the framework; consider - * Apache Commons Codec for a - * more comprehensive suite of digest utilities. + * Apache Commons Codec + * for a more comprehensive suite of digest utilities. * * @author Arjen Poutsma * @author Craig Andrews * @since 3.0 - * @see org.apache.commons.codec.digest.DigestUtils */ public abstract class DigestUtils { diff --git a/spring-core/src/main/java/org/springframework/util/UpdateMessageDigestInputStream.java b/spring-core/src/main/java/org/springframework/util/UpdateMessageDigestInputStream.java index 37890be94f2..90bfa4a7e8d 100644 --- a/spring-core/src/main/java/org/springframework/util/UpdateMessageDigestInputStream.java +++ b/spring-core/src/main/java/org/springframework/util/UpdateMessageDigestInputStream.java @@ -33,7 +33,7 @@ abstract class UpdateMessageDigestInputStream extends InputStream { * Update the message digest with the rest of the bytes in this stream. *

Using this method is more optimized since it avoids creating new * byte arrays for each call. - * @param messageDigest The message digest to update + * @param messageDigest the message digest to update * @throws IOException when propagated from {@link #read()} */ public void updateMessageDigest(MessageDigest messageDigest) throws IOException { @@ -47,7 +47,7 @@ public void updateMessageDigest(MessageDigest messageDigest) throws IOException * Update the message digest with the next len bytes in this stream. *

Using this method is more optimized since it avoids creating new * byte arrays for each call. - * @param messageDigest The message digest to update + * @param messageDigest the message digest to update * @param len how many bytes to read from this stream and use to update the message digest * @throws IOException when propagated from {@link #read()} */ diff --git a/spring-core/src/main/java/org/springframework/util/concurrent/CompletableToListenableFutureAdapter.java b/spring-core/src/main/java/org/springframework/util/concurrent/CompletableToListenableFutureAdapter.java index f42a88f4af4..1a6cd14fbdc 100644 --- a/spring-core/src/main/java/org/springframework/util/concurrent/CompletableToListenableFutureAdapter.java +++ b/spring-core/src/main/java/org/springframework/util/concurrent/CompletableToListenableFutureAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,6 @@ import org.springframework.lang.UsesJava8; - /** * Adapts a {@link CompletableFuture} into a {@link ListenableFuture}. * @@ -38,6 +37,7 @@ public class CompletableToListenableFutureAdapter implements ListenableFuture private final ListenableFutureCallbackRegistry callbacks = new ListenableFutureCallbackRegistry(); + public CompletableToListenableFutureAdapter(CompletableFuture completableFuture) { this.completableFuture = completableFuture; this.completableFuture.handle(new BiFunction() { @@ -54,6 +54,7 @@ public Object apply(T result, Throwable ex) { }); } + @Override public void addCallback(ListenableFutureCallback callback) { this.callbacks.addCallback(callback); diff --git a/spring-core/src/main/java/org/springframework/util/concurrent/FutureAdapter.java b/spring-core/src/main/java/org/springframework/util/concurrent/FutureAdapter.java index 37aac9310eb..2a36c9e6042 100644 --- a/spring-core/src/main/java/org/springframework/util/concurrent/FutureAdapter.java +++ b/spring-core/src/main/java/org/springframework/util/concurrent/FutureAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,10 +24,9 @@ import org.springframework.util.Assert; /** - * Abstract class that adapts a {@link Future} parameterized over S into a {@code - * Future} parameterized over T. All methods are delegated to the adaptee, where {@link - * #get()} and {@link #get(long, TimeUnit)} call {@link #adapt(Object)} on the adaptee's - * result. + * Abstract class that adapts a {@link Future} parameterized over S into a {@code Future} + * parameterized over T. All methods are delegated to the adaptee, where {@link #get()} + * and {@link #get(long, TimeUnit)} call {@link #adapt(Object)} on the adaptee's result. * * @author Arjen Poutsma * @since 4.0 diff --git a/spring-core/src/test/java/org/springframework/core/env/PropertySourceTests.java b/spring-core/src/test/java/org/springframework/core/env/PropertySourceTests.java index 79dd4f07ddb..fda1f06dba5 100644 --- a/spring-core/src/test/java/org/springframework/core/env/PropertySourceTests.java +++ b/spring-core/src/test/java/org/springframework/core/env/PropertySourceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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,13 +30,15 @@ import static org.junit.Assert.*; /** - * Unit tests for {@link AbstractPropertySource} implementations. + * Unit tests for {@link PropertySource} implementations. * * @author Chris Beams * @since 3.1 */ public class PropertySourceTests { - @Test @SuppressWarnings("serial") + + @Test + @SuppressWarnings("serial") public void equals() { Map map1 = new HashMap() {{ put("a", "b"); }}; Map map2 = new HashMap() {{ put("c", "d"); }}; @@ -59,14 +61,15 @@ public void equals() { assertThat(new MapPropertySource("x", map1).equals(new PropertiesPropertySource("y", props2)), is(false)); } - @Test @SuppressWarnings("serial") + @Test + @SuppressWarnings("serial") public void collectionsOperations() { Map map1 = new HashMap() {{ put("a", "b"); }}; Map map2 = new HashMap() {{ put("c", "d"); }}; PropertySource ps1 = new MapPropertySource("ps1", map1); ps1.getSource(); - List> propertySources = new ArrayList>(); + List> propertySources = new ArrayList<>(); assertThat(propertySources.add(ps1), equalTo(true)); assertThat(propertySources.contains(ps1), is(true)); assertThat(propertySources.contains(PropertySource.named("ps1")), is(true)); @@ -116,4 +119,5 @@ public void toString_verbosityVariesOnLogLevel() { logger.setLevel(original); } } + } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java b/spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java index 2d20e195d15..decb2f1cb43 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -147,6 +147,72 @@ public void unboxBooleanIfNecessary(MethodVisitor mv) { } } + /** + * Called after the main expression evaluation method has been generated, this + * method will callback any registered FieldAdders or ClinitAdders to add any + * extra information to the class representing the compiled expression. + */ + public void finish() { + if (this.fieldAdders != null) { + for (FieldAdder fieldAdder : this.fieldAdders) { + fieldAdder.generateField(cw,this); + } + } + if (this.clinitAdders != null) { + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "", "()V", null, null); + mv.visitCode(); + this.nextFreeVariableId = 0; // To 0 because there is no 'this' in a clinit + for (ClinitAdder clinitAdder : this.clinitAdders) { + clinitAdder.generateCode(mv, this); + } + mv.visitInsn(RETURN); + mv.visitMaxs(0,0); // not supplied due to COMPUTE_MAXS + mv.visitEnd(); + } + } + + /** + * Register a FieldAdder which will add a new field to the generated + * class to support the code produced by an ast nodes primary + * generateCode() method. + */ + public void registerNewField(FieldAdder fieldAdder) { + if (this.fieldAdders == null) { + this.fieldAdders = new ArrayList(); + } + this.fieldAdders.add(fieldAdder); + } + + /** + * Register a ClinitAdder which will add code to the static + * initializer in the generated class to support the code + * produced by an ast nodes primary generateCode() method. + */ + public void registerNewClinit(ClinitAdder clinitAdder) { + if (this.clinitAdders == null) { + this.clinitAdders = new ArrayList(); + } + this.clinitAdders.add(clinitAdder); + } + + public int nextFieldId() { + return this.nextFieldId++; + } + + public int nextFreeVariableId() { + return this.nextFreeVariableId++; + } + + public String getClassName() { + return this.clazzName; + } + + @Deprecated + public String getClassname() { + return this.clazzName; + } + + /** * Insert any necessary cast and value call to convert from a boxed type to a * primitive value @@ -778,74 +844,6 @@ public static String[] toDescriptors(Class[] types) { return descriptors; } - /** - * Called after the main expression evaluation method has been generated, this - * method will callback any registered FieldAdders or ClinitAdders to add any - * extra information to the class representing the compiled expression. - */ - public void finish() { - if (fieldAdders != null) { - for (FieldAdder fieldAdder: fieldAdders) { - fieldAdder.generateField(cw,this); - } - } - if (clinitAdders != null) { - MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "", "()V", null, null); - mv.visitCode(); - nextFreeVariableId = 0; // To 0 because there is no 'this' in a clinit - for (ClinitAdder clinitAdder: clinitAdders) { - clinitAdder.generateCode(mv, this); - } - mv.visitInsn(RETURN); - mv.visitMaxs(0,0); // not supplied due to COMPUTE_MAXS - mv.visitEnd(); - } - } - - /** - * Register a FieldAdder which will add a new field to the generated - * class to support the code produced by an ast nodes primary - * generateCode() method. - */ - public void registerNewField(FieldAdder fieldAdder) { - if (fieldAdders == null) { - fieldAdders = new ArrayList(); - } - fieldAdders.add(fieldAdder); - } - - /** - * Register a ClinitAdder which will add code to the static - * initializer in the generated class to support the code - * produced by an ast nodes primary generateCode() method. - */ - public void registerNewClinit(ClinitAdder clinitAdder) { - if (clinitAdders == null) { - clinitAdders = new ArrayList(); - } - clinitAdders.add(clinitAdder); - } - - public int nextFieldId() { - return nextFieldId++; - } - - public int nextFreeVariableId() { - return nextFreeVariableId++; - } - - public String getClassname() { - return clazzName; - } - - public interface FieldAdder { - public void generateField(ClassWriter cw, CodeFlow codeflow); - } - - public interface ClinitAdder { - public void generateCode(MethodVisitor mv, CodeFlow codeflow); - } - /** * Create the optimal instruction for loading a number on the stack. * @param mv where to insert the bytecode @@ -977,4 +975,15 @@ public static void insertNumericUnboxOrPrimitiveTypeCoercion( } + public interface FieldAdder { + + void generateField(ClassWriter cw, CodeFlow codeflow); + } + + + public interface ClinitAdder { + + void generateCode(MethodVisitor mv, CodeFlow codeflow); + } + } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/InlineList.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/InlineList.java index b4134b6351e..1a435b861d9 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/InlineList.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/InlineList.java @@ -132,8 +132,8 @@ public boolean isCompilable() { @Override public void generateCode(MethodVisitor mv, CodeFlow codeflow) { - final String constantFieldName = "inlineList$"+codeflow.nextFieldId(); - final String clazzname = codeflow.getClassname(); + final String constantFieldName = "inlineList$" + codeflow.nextFieldId(); + final String className = codeflow.getClassName(); codeflow.registerNewField(new CodeFlow.FieldAdder() { public void generateField(ClassWriter cw, CodeFlow codeflow) { @@ -143,11 +143,11 @@ public void generateField(ClassWriter cw, CodeFlow codeflow) { codeflow.registerNewClinit(new CodeFlow.ClinitAdder() { public void generateCode(MethodVisitor mv, CodeFlow codeflow) { - generateClinitCode(clazzname,constantFieldName, mv,codeflow,false); + generateClinitCode(className, constantFieldName, mv, codeflow, false); } }); - mv.visitFieldInsn(GETSTATIC, clazzname, constantFieldName, "Ljava/util/List;"); + mv.visitFieldInsn(GETSTATIC, className, constantFieldName, "Ljava/util/List;"); codeflow.pushDescriptor("Ljava/util/List"); } @@ -158,8 +158,8 @@ void generateClinitCode(String clazzname, String constantFieldName, MethodVisito if (!nested) { mv.visitFieldInsn(PUTSTATIC, clazzname, constantFieldName, "Ljava/util/List;"); } - int childcount = getChildCount(); - for (int c=0; c < childcount; c++) { + int childCount = getChildCount(); + for (int c = 0; c < childCount; c++) { if (!nested) { mv.visitFieldInsn(GETSTATIC, clazzname, constantFieldName, "Ljava/util/List;"); } diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/AbstractJdbcInsert.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/AbstractJdbcInsert.java index 3af9f4635a9..3d7c25f1d3d 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/AbstractJdbcInsert.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/AbstractJdbcInsert.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -542,6 +542,7 @@ private PreparedStatement prepareStatementForGeneratedKeys(Connection con) throw * @param batch array of Maps with parameter names and values to be used in batch insert * @return array of number of rows affected */ + @SuppressWarnings("unchecked") protected int[] doExecuteBatch(Map... batch) { checkCompiled(); List> batchValues = new ArrayList>(batch.length); diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcInsert.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcInsert.java index c5d15e78add..417423507fc 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcInsert.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcInsert.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -148,6 +148,7 @@ public KeyHolder executeAndReturnKeyHolder(SqlParameterSource parameterSource) { } @Override + @SuppressWarnings("unchecked") public int[] executeBatch(Map... batch) { return doExecuteBatch(batch); } diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcInsertOperations.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcInsertOperations.java index 0e1dd15ceb5..645a2a9785e 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcInsertOperations.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcInsertOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -152,6 +152,7 @@ public interface SimpleJdbcInsertOperations { * @param batch an array of Maps containing a batch of column names and corresponding value * @return the array of number of rows affected as returned by the JDBC driver */ + @SuppressWarnings("unchecked") int[] executeBatch(Map... batch); /** diff --git a/spring-jms/src/main/java/org/springframework/jms/connection/TransactionAwareConnectionFactoryProxy.java b/spring-jms/src/main/java/org/springframework/jms/connection/TransactionAwareConnectionFactoryProxy.java index 50c61409252..19a9ffc041a 100644 --- a/spring-jms/src/main/java/org/springframework/jms/connection/TransactionAwareConnectionFactoryProxy.java +++ b/spring-jms/src/main/java/org/springframework/jms/connection/TransactionAwareConnectionFactoryProxy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -101,7 +101,7 @@ public TransactionAwareConnectionFactoryProxy(ConnectionFactory targetConnection * Set the target ConnectionFactory that this ConnectionFactory should delegate to. */ public final void setTargetConnectionFactory(ConnectionFactory targetConnectionFactory) { - Assert.notNull(targetConnectionFactory, "targetConnectionFactory must not be nul"); + Assert.notNull(targetConnectionFactory, "'targetConnectionFactory' must not be null"); this.targetConnectionFactory = targetConnectionFactory; } diff --git a/spring-jms/src/main/java/org/springframework/jms/listener/endpoint/JmsMessageEndpointFactory.java b/spring-jms/src/main/java/org/springframework/jms/listener/endpoint/JmsMessageEndpointFactory.java index fe448aa2bc9..5beb87333ea 100644 --- a/spring-jms/src/main/java/org/springframework/jms/listener/endpoint/JmsMessageEndpointFactory.java +++ b/spring-jms/src/main/java/org/springframework/jms/listener/endpoint/JmsMessageEndpointFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -61,7 +61,7 @@ public void setMessageListener(MessageListener messageListener) { * Return the JMS MessageListener for this endpoint. */ protected MessageListener getMessageListener() { - return messageListener; + return this.messageListener; } /** diff --git a/spring-jms/src/main/java/org/springframework/jms/support/converter/MappingJackson2MessageConverter.java b/spring-jms/src/main/java/org/springframework/jms/support/converter/MappingJackson2MessageConverter.java index 0767c15fb1f..5d9bc44b53f 100644 --- a/spring-jms/src/main/java/org/springframework/jms/support/converter/MappingJackson2MessageConverter.java +++ b/spring-jms/src/main/java/org/springframework/jms/support/converter/MappingJackson2MessageConverter.java @@ -196,6 +196,7 @@ public Message toMessage(Object object, Session session) throws JMSException, Me @Override public Message toMessage(Object object, Session session, Object conversionHint) throws JMSException, MessageConversionException { + return toMessage(object, session, getSerializationView(conversionHint)); } @@ -234,6 +235,7 @@ public Object fromMessage(Message message) throws JMSException, MessageConversio protected Message toMessage(Object object, Session session, ObjectWriter objectWriter) throws JMSException, MessageConversionException { + Message message; try { switch (this.targetType) { @@ -319,8 +321,8 @@ protected BytesMessage mapToBytesMessage(Object object, Session session, ObjectM * @return the resulting message * @throws JMSException if thrown by JMS methods * @throws IOException in case of I/O errors - * @see Session#createBytesMessage * @since 4.3 + * @see Session#createBytesMessage */ protected BytesMessage mapToBytesMessage(Object object, Session session, ObjectWriter objectWriter) throws JMSException, IOException { @@ -400,7 +402,6 @@ protected void setTypeIdOnMessage(Object object, Message message) throws JMSExce } } - /** * Convenience method to dispatch to converters for individual message types. */ diff --git a/spring-jms/src/main/java/org/springframework/jms/support/converter/MessagingMessageConverter.java b/spring-jms/src/main/java/org/springframework/jms/support/converter/MessagingMessageConverter.java index c791d289eec..fbd1d5adc60 100644 --- a/spring-jms/src/main/java/org/springframework/jms/support/converter/MessagingMessageConverter.java +++ b/spring-jms/src/main/java/org/springframework/jms/support/converter/MessagingMessageConverter.java @@ -152,7 +152,4 @@ protected final MessageHeaders extractHeaders(javax.jms.Message message) { return this.headerMapper.toHeaders(message); } - - - } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/config/AbstractMessageBrokerConfiguration.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/config/AbstractMessageBrokerConfiguration.java index 2d27db7d2b7..84d29f92961 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/config/AbstractMessageBrokerConfiguration.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/config/AbstractMessageBrokerConfiguration.java @@ -86,7 +86,7 @@ public abstract class AbstractMessageBrokerConfiguration implements ApplicationC private static final String MVC_VALIDATOR_NAME = "mvcValidator"; - private static final boolean jackson2Present= ClassUtils.isPresent( + private static final boolean jackson2Present = ClassUtils.isPresent( "com.fasterxml.jackson.databind.ObjectMapper", AbstractMessageBrokerConfiguration.class.getClassLoader()); diff --git a/spring-messaging/src/test/java/org/springframework/messaging/converter/AbstractMessageConverterTests.java b/spring-messaging/src/test/java/org/springframework/messaging/converter/AbstractMessageConverterTests.java deleted file mode 100644 index 3f2524e81b7..00000000000 --- a/spring-messaging/src/test/java/org/springframework/messaging/converter/AbstractMessageConverterTests.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2002-2013 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.messaging.converter; - -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import org.junit.Before; -import org.junit.Test; - -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.util.MimeType; -import org.springframework.util.MimeTypeUtils; - -import static org.junit.Assert.*; - -/** - * Test fixture for {@link org.springframework.messaging.converter.AbstractMessageConverter}. - * - * @author Rossen Stoyanchev - */ -public class AbstractMessageConverterTests { - - private TestMessageConverter converter; - - - @Before - public void setup() { - this.converter = new TestMessageConverter(); - this.converter.setContentTypeResolver(new DefaultContentTypeResolver()); - } - - @Test - public void supportsTargetClass() { - Message message = MessageBuilder.withPayload("ABC").build(); - - assertEquals("success-from", this.converter.fromMessage(message, String.class)); - assertNull(this.converter.fromMessage(message, Integer.class)); - } - - @Test - public void supportsMimeType() { - Message message = MessageBuilder.withPayload( - "ABC").setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.TEXT_PLAIN).build(); - - assertEquals("success-from", this.converter.fromMessage(message, String.class)); - } - - @Test - public void supportsMimeTypeNotSupported() { - Message message = MessageBuilder.withPayload( - "ABC").setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON).build(); - - assertNull(this.converter.fromMessage(message, String.class)); - } - - @Test - public void supportsMimeTypeNotSpecified() { - Message message = MessageBuilder.withPayload("ABC").build(); - assertEquals("success-from", this.converter.fromMessage(message, String.class)); - } - - @Test - public void supportsMimeTypeNoneConfigured() { - - Message message = MessageBuilder.withPayload( - "ABC").setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON).build(); - - this.converter = new TestMessageConverter(Collections.emptyList()); - this.converter.setContentTypeResolver(new DefaultContentTypeResolver()); - - assertEquals("success-from", this.converter.fromMessage(message, String.class)); - } - - @Test - public void toMessageHeadersCopied() { - Map map = new HashMap(); - map.put("foo", "bar"); - MessageHeaders headers = new MessageHeaders(map ); - Message message = this.converter.toMessage("ABC", headers); - - assertEquals("bar", message.getHeaders().get("foo")); - } - - @Test - public void toMessageContentTypeHeader() { - Message message = this.converter.toMessage("ABC", null); - assertEquals(MimeTypeUtils.TEXT_PLAIN, message.getHeaders().get(MessageHeaders.CONTENT_TYPE)); - } - - - private static class TestMessageConverter extends AbstractMessageConverter { - - public TestMessageConverter() { - super(MimeTypeUtils.TEXT_PLAIN); - } - - public TestMessageConverter(Collection supportedMimeTypes) { - super(supportedMimeTypes); - } - - @Override - protected boolean supports(Class clazz) { - return String.class.equals(clazz); - } - - @Override - public Object convertFromInternal(Message message, Class targetClass) { - return "success-from"; - } - - @Override - public Object convertToInternal(Object payload, MessageHeaders headers) { - return "success-to"; - } - } - -} diff --git a/spring-messaging/src/test/java/org/springframework/messaging/converter/MessageConverterTests.java b/spring-messaging/src/test/java/org/springframework/messaging/converter/MessageConverterTests.java index f10f2a174ee..be6d9cf672f 100644 --- a/spring-messaging/src/test/java/org/springframework/messaging/converter/MessageConverterTests.java +++ b/spring-messaging/src/test/java/org/springframework/messaging/converter/MessageConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -44,6 +44,7 @@ public class MessageConverterTests { private TestMessageConverter converter = new TestMessageConverter(); + @Test public void supportsTargetClass() { Message message = MessageBuilder.withPayload("ABC").build(); @@ -105,7 +106,7 @@ public void setStrictContentTypeMatchWithNoSupportedMimeTypes() { @Test public void toMessageWithHeaders() { - Map map = new HashMap(); + Map map = new HashMap<>(); map.put("foo", "bar"); MessageHeaders headers = new MessageHeaders(map); Message message = this.converter.toMessage("ABC", headers); diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/SharedEntityManagerCreator.java b/spring-orm/src/main/java/org/springframework/orm/jpa/SharedEntityManagerCreator.java index a3bac0ce612..46f6ae689de 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/SharedEntityManagerCreator.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/SharedEntityManagerCreator.java @@ -115,11 +115,11 @@ public static EntityManager createSharedEntityManager(EntityManagerFactory emf, */ public static EntityManager createSharedEntityManager( EntityManagerFactory emf, Map properties, boolean synchronizedWithTransaction) { - Class entityManagerInterface = (emf instanceof EntityManagerFactoryInfo ? + + Class emIfc = (emf instanceof EntityManagerFactoryInfo ? ((EntityManagerFactoryInfo) emf).getEntityManagerInterface() : EntityManager.class); return createSharedEntityManager(emf, properties, synchronizedWithTransaction, - (entityManagerInterface == null ? NO_ENTITY_MANAGER_INTERFACES : - new Class[] { entityManagerInterface })); + (emIfc == null ? NO_ENTITY_MANAGER_INTERFACES : new Class[] {emIfc})); } /** diff --git a/spring-orm/src/main/java/overview.html b/spring-orm/src/main/java/overview.html index 37f532b39ba..67b448a9064 100644 --- a/spring-orm/src/main/java/overview.html +++ b/spring-orm/src/main/java/overview.html @@ -1,7 +1,7 @@

-Spring's O/R Mapping package: supporting Hibernate, JPA, JDO, and iBATIS SQL Maps. +Spring's O/R Mapping package: supporting Hibernate, JPA, and JDO.

\ No newline at end of file diff --git a/spring-oxm/src/test/java/org/springframework/oxm/config/OxmNamespaceHandlerTests.java b/spring-oxm/src/test/java/org/springframework/oxm/config/OxmNamespaceHandlerTests.java index 9595d8087e8..4a6a46ba79f 100644 --- a/spring-oxm/src/test/java/org/springframework/oxm/config/OxmNamespaceHandlerTests.java +++ b/spring-oxm/src/test/java/org/springframework/oxm/config/OxmNamespaceHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,8 +37,9 @@ @SuppressWarnings("deprecation") public class OxmNamespaceHandlerTests { - private final ApplicationContext applicationContext = new ClassPathXmlApplicationContext( - "oxmNamespaceHandlerTest.xml", getClass()); + private final ApplicationContext applicationContext = + new ClassPathXmlApplicationContext("oxmNamespaceHandlerTest.xml", getClass()); + @Test public void xmlBeansMarshaller() throws Exception { @@ -85,4 +86,5 @@ public void castorMappingLocationMarshaller() throws Exception { CastorMarshaller castorMarshaller = applicationContext.getBean("castorMappingLocationMarshaller", CastorMarshaller.class); assertNotNull(castorMarshaller); } -} \ No newline at end of file + +} diff --git a/spring-test/src/main/java/org/springframework/test/context/support/TestPropertySourceUtils.java b/spring-test/src/main/java/org/springframework/test/context/support/TestPropertySourceUtils.java index c4b2a11ea4d..47e488cf29d 100644 --- a/spring-test/src/main/java/org/springframework/test/context/support/TestPropertySourceUtils.java +++ b/spring-test/src/main/java/org/springframework/test/context/support/TestPropertySourceUtils.java @@ -61,7 +61,7 @@ public abstract class TestPropertySourceUtils { /** * The name of the {@link MapPropertySource} created from inlined properties. * @since 4.1.5 - * @see {@link #addInlinedPropertiesToEnvironment(ConfigurableEnvironment, String[])} + * @see #addInlinedPropertiesToEnvironment */ public static final String INLINED_PROPERTIES_PROPERTY_SOURCE_NAME = "Inlined Test Properties"; @@ -224,8 +224,7 @@ public static void addPropertiesFilesToEnvironment(ConfigurableEnvironment envir * @see TestPropertySource#properties * @see #addInlinedPropertiesToEnvironment(ConfigurableEnvironment, String[]) */ - public static void addInlinedPropertiesToEnvironment(ConfigurableApplicationContext context, - String... inlinedProperties) { + public static void addInlinedPropertiesToEnvironment(ConfigurableApplicationContext context, String... inlinedProperties) { Assert.notNull(context, "context must not be null"); Assert.notNull(inlinedProperties, "inlinedProperties must not be null"); addInlinedPropertiesToEnvironment(context.getEnvironment(), inlinedProperties); @@ -252,13 +251,11 @@ public static void addInlinedPropertiesToEnvironment(ConfigurableEnvironment env Assert.notNull(inlinedProperties, "inlinedProperties must not be null"); if (!ObjectUtils.isEmpty(inlinedProperties)) { if (logger.isDebugEnabled()) { - logger.debug("Adding inlined properties to environment: " - + ObjectUtils.nullSafeToString(inlinedProperties)); + logger.debug("Adding inlined properties to environment: " + ObjectUtils.nullSafeToString(inlinedProperties)); } MapPropertySource ps = (MapPropertySource) environment.getPropertySources().get(INLINED_PROPERTIES_PROPERTY_SOURCE_NAME); if (ps == null) { - ps = new MapPropertySource(INLINED_PROPERTIES_PROPERTY_SOURCE_NAME, - new LinkedHashMap()); + ps = new MapPropertySource(INLINED_PROPERTIES_PROPERTY_SOURCE_NAME, new LinkedHashMap()); environment.getPropertySources().addFirst(ps); } ps.getSource().putAll(convertInlinedPropertiesToMap(inlinedProperties)); @@ -285,21 +282,19 @@ public static void addInlinedPropertiesToEnvironment(ConfigurableEnvironment env public static Map convertInlinedPropertiesToMap(String... inlinedProperties) { Assert.notNull(inlinedProperties, "inlinedProperties must not be null"); Map map = new LinkedHashMap(); - Properties props = new Properties(); + for (String pair : inlinedProperties) { if (!StringUtils.hasText(pair)) { continue; } - try { props.load(new StringReader(pair)); } - catch (Exception e) { - throw new IllegalStateException("Failed to load test environment property from [" + pair + "].", e); + catch (Exception ex) { + throw new IllegalStateException("Failed to load test environment property from [" + pair + "]", ex); } - Assert.state(props.size() == 1, "Failed to load exactly one test environment property from [" + pair + "]."); - + Assert.state(props.size() == 1, "Failed to load exactly one test environment property from [" + pair + "]"); for (String name : props.stringPropertyNames()) { map.put(name, props.getProperty(name)); } diff --git a/spring-test/src/main/java/org/springframework/test/jdbc/JdbcTestUtils.java b/spring-test/src/main/java/org/springframework/test/jdbc/JdbcTestUtils.java index 8e3e9a05c8d..e0e1bc0e656 100644 --- a/spring-test/src/main/java/org/springframework/test/jdbc/JdbcTestUtils.java +++ b/spring-test/src/main/java/org/springframework/test/jdbc/JdbcTestUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -124,6 +124,7 @@ public static int deleteFromTables(JdbcTemplate jdbcTemplate, String... tableNam */ public static int deleteFromTableWhere(JdbcTemplate jdbcTemplate, String tableName, String whereClause, Object... args) { + String sql = "DELETE FROM " + tableName; if (StringUtils.hasText(whereClause)) { sql += " WHERE " + whereClause; @@ -170,6 +171,7 @@ public static void dropTables(JdbcTemplate jdbcTemplate, String... tableNames) { @Deprecated public static void executeSqlScript(JdbcTemplate jdbcTemplate, ResourceLoader resourceLoader, String sqlResourcePath, boolean continueOnError) throws DataAccessException { + Resource resource = resourceLoader.getResource(sqlResourcePath); executeSqlScript(jdbcTemplate, resource, continueOnError); } @@ -197,6 +199,7 @@ public static void executeSqlScript(JdbcTemplate jdbcTemplate, ResourceLoader re @Deprecated public static void executeSqlScript(JdbcTemplate jdbcTemplate, Resource resource, boolean continueOnError) throws DataAccessException { + executeSqlScript(jdbcTemplate, new EncodedResource(resource), continueOnError); } @@ -220,6 +223,7 @@ public static void executeSqlScript(JdbcTemplate jdbcTemplate, Resource resource @Deprecated public static void executeSqlScript(JdbcTemplate jdbcTemplate, EncodedResource resource, boolean continueOnError) throws DataAccessException { + new ResourceDatabasePopulator(continueOnError, false, resource.getEncoding(), resource.getResource()).execute(jdbcTemplate.getDataSource()); } @@ -288,4 +292,5 @@ public static boolean containsSqlScriptDelimiters(String script, char delim) { public static void splitSqlScript(String script, char delim, List statements) { ScriptUtils.splitSqlScript(script, delim, statements); } + } diff --git a/spring-web/src/main/java/org/springframework/http/client/ClientHttpRequestExecution.java b/spring-web/src/main/java/org/springframework/http/client/ClientHttpRequestExecution.java index df6e10c6f0e..2fe64f52874 100644 --- a/spring-web/src/main/java/org/springframework/http/client/ClientHttpRequestExecution.java +++ b/spring-web/src/main/java/org/springframework/http/client/ClientHttpRequestExecution.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2016 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,22 +23,23 @@ /** * Represents the context of a client-side HTTP request execution. * - *

Used to invoke the next interceptor in the interceptor chain, or - if the calling interceptor is last - execute - * the request itself. + *

Used to invoke the next interceptor in the interceptor chain, + * or - if the calling interceptor is last - execute the request itself. * * @author Arjen Poutsma - * @see ClientHttpRequestInterceptor * @since 3.1 + * @see ClientHttpRequestInterceptor */ public interface ClientHttpRequestExecution { /** - * Execute the request with the given request attributes and body, and return the response. - * + * Execute the request with the given request attributes and body, + * and return the response. * @param request the request, containing method, URI, and headers * @param body the body of the request to execute * @return the response * @throws IOException in case of I/O errors */ ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException; + } diff --git a/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java b/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java index beee0a4f0f6..12cee3308a2 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java +++ b/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java @@ -812,7 +812,7 @@ public ObjectMapper create(boolean defaultUseWrapper) { return new XmlMapper(new XmlFactory(xmlInputFactory()), module); } - private static final XMLInputFactory xmlInputFactory() { + private static XMLInputFactory xmlInputFactory() { XMLInputFactory inputFactory = XMLInputFactory.newInstance(); inputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false); inputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false); diff --git a/spring-web/src/main/java/org/springframework/remoting/jaxws/SimpleJaxWsServiceExporter.java b/spring-web/src/main/java/org/springframework/remoting/jaxws/SimpleJaxWsServiceExporter.java index 08c47e614c8..8746786f144 100644 --- a/spring-web/src/main/java/org/springframework/remoting/jaxws/SimpleJaxWsServiceExporter.java +++ b/spring-web/src/main/java/org/springframework/remoting/jaxws/SimpleJaxWsServiceExporter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2016 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,9 +31,9 @@ *

Note that this exporter will only work if the JAX-WS runtime actually * supports publishing with an address argument, i.e. if the JAX-WS runtime * ships an internal HTTP server. This is the case with the JAX-WS runtime - * that's inclued in Sun's JDK 1.6 but not with the standalone JAX-WS 2.1 RI. + * that's included in Sun's JDK 6 but not with the standalone JAX-WS 2.1 RI. * - *

For explicit configuration of JAX-WS endpoints with Sun's JDK 1.6 + *

For explicit configuration of JAX-WS endpoints with Sun's JDK 6 * HTTP server, consider using {@link SimpleHttpServerJaxWsServiceExporter}! * * @author Juergen Hoeller diff --git a/spring-web/src/main/java/org/springframework/web/bind/support/WebRequestDataBinder.java b/spring-web/src/main/java/org/springframework/web/bind/support/WebRequestDataBinder.java index 94b40ae979f..ea3c0ae844c 100644 --- a/spring-web/src/main/java/org/springframework/web/bind/support/WebRequestDataBinder.java +++ b/spring-web/src/main/java/org/springframework/web/bind/support/WebRequestDataBinder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -70,6 +70,9 @@ */ public class WebRequestDataBinder extends WebDataBinder { + private static final boolean servlet3Parts = ClassUtils.hasMethod(HttpServletRequest.class, "getParts"); + + /** * Create a new WebRequestDataBinder instance, with default object name. * @param target the target object to bind onto (or {@code null} @@ -116,7 +119,7 @@ public void bind(WebRequest request) { if (multipartRequest != null) { bindMultipart(multipartRequest.getMultiFileMap(), mpvs); } - else if (ClassUtils.hasMethod(HttpServletRequest.class, "getParts")) { + else if (servlet3Parts) { HttpServletRequest serlvetRequest = ((NativeWebRequest) request).getNativeRequest(HttpServletRequest.class); new Servlet3MultipartHelper(isBindEmptyMultipartFiles()).bindParts(serlvetRequest, mpvs); } diff --git a/spring-web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java b/spring-web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java index b29397f2140..97a227af112 100644 --- a/spring-web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java +++ b/spring-web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java @@ -23,7 +23,6 @@ import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; - import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; diff --git a/spring-web/src/main/java/org/springframework/web/multipart/support/MultipartResolutionDelegate.java b/spring-web/src/main/java/org/springframework/web/multipart/support/MultipartResolutionDelegate.java index 4356d14603d..305b639642f 100644 --- a/spring-web/src/main/java/org/springframework/web/multipart/support/MultipartResolutionDelegate.java +++ b/spring-web/src/main/java/org/springframework/web/multipart/support/MultipartResolutionDelegate.java @@ -122,7 +122,7 @@ else if (isMultipartFileArray(parameter)) { return null; } } - else if (parameter.getNestedParameterType() == servletPartClass) { + else if (servletPartClass == parameter.getNestedParameterType()) { return (isMultipart ? RequestPartResolver.resolvePart(request, name) : null); } else if (isPartCollection(parameter)) { diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/HandlerInterceptor.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/HandlerInterceptor.java index 6f8e803674f..e49eeafbfb6 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/HandlerInterceptor.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/HandlerInterceptor.java @@ -93,7 +93,7 @@ public interface HandlerInterceptor { * @throws Exception in case of errors */ boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) - throws Exception; + throws Exception; /** * Intercept the execution of a handler. Called after HandlerAdapter actually @@ -114,7 +114,8 @@ boolean preHandle(HttpServletRequest request, HttpServletResponse response, Obje * (can also be {@code null}) * @throws Exception in case of errors */ - void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) + void postHandle( + HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception; /** @@ -136,7 +137,8 @@ void postHandle(HttpServletRequest request, HttpServletResponse response, Object * @param ex exception thrown on handler execution, if any * @throws Exception in case of errors */ - void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) + void afterCompletion( + HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception; } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/LocaleResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/LocaleResolver.java index 80998d0f6e7..2d0f9540229 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/LocaleResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/LocaleResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 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. @@ -51,8 +51,8 @@ public interface LocaleResolver { /** - * Resolve the current locale via the given request. Can return a default locale as - * fallback in any case. + * Resolve the current locale via the given request. + * Can return a default locale as fallback in any case. * @param request the request to resolve the locale for * @return the current locale (never {@code null}) */ @@ -63,8 +63,8 @@ public interface LocaleResolver { * @param request the request to be used for locale modification * @param response the response to be used for locale modification * @param locale the new locale, or {@code null} to clear the locale - * @throws UnsupportedOperationException if the LocaleResolver implementation does not - * support dynamic changing of the locale + * @throws UnsupportedOperationException if the LocaleResolver + * implementation does not support dynamic changing of the locale */ void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/BeanNameUrlHandlerMapping.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/BeanNameUrlHandlerMapping.java index 397c179b039..44376cbd6aa 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/BeanNameUrlHandlerMapping.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/BeanNameUrlHandlerMapping.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2016 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,9 +28,9 @@ * *

This is the default implementation used by the * {@link org.springframework.web.servlet.DispatcherServlet}, along with - * {@link org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping} - * (on Java 5 and higher). Alternatively, {@link SimpleUrlHandlerMapping} allows for - * customizing a handler mapping declaratively. + * {@link org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping}. + * Alternatively, {@link SimpleUrlHandlerMapping} allows for customizing a + * handler mapping declaratively. * *

The mapping is from URL to bean name. Thus an incoming URL "/foo" would map * to a handler named "/foo", or to "/foo /foo2" in case of multiple mappings to diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/HandlerInterceptorAdapter.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/HandlerInterceptorAdapter.java index 602886ba297..b00d46898a1 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/HandlerInterceptorAdapter.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/HandlerInterceptorAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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,7 @@ import org.springframework.web.servlet.ModelAndView; /** - * Abstract adapter class for the HandlerInterceptor interface, + * Abstract adapter class for the {@link AsyncHandlerInterceptor} interface, * for simplified implementation of pre-only/post-only interceptors. * * @author Juergen Hoeller @@ -36,7 +36,8 @@ public abstract class HandlerInterceptorAdapter implements AsyncHandlerIntercept */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) - throws Exception { + throws Exception { + return true; } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/CompletionStageReturnValueHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/CompletionStageReturnValueHandler.java index 7ec8e7c0f92..c3c7b1d0142 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/CompletionStageReturnValueHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/CompletionStageReturnValueHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,7 +34,6 @@ * * @author Sebastien Deleuze * @since 4.2 - * * @deprecated as of 4.3 {@link DeferredResultMethodReturnValueHandler} supports * CompletionStage return values via an adapter mechanism. */ diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/DeferredResultMethodReturnValueHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/DeferredResultMethodReturnValueHandler.java index 6361c77ff13..789606dc637 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/DeferredResultMethodReturnValueHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/DeferredResultMethodReturnValueHandler.java @@ -114,6 +114,7 @@ public DeferredResult adaptToDeferredResult(Object returnValue) { } } + /** * Adapter for {@code ListenableFuture} return values. */ @@ -137,6 +138,7 @@ public void onFailure(Throwable ex) { } } + /** * Adapter for {@code CompletionStage} return values. */ diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ListenableFutureReturnValueHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ListenableFutureReturnValueHandler.java index 3a3706f06ba..d4a8fe62bc2 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ListenableFutureReturnValueHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ListenableFutureReturnValueHandler.java @@ -31,7 +31,6 @@ * * @author Rossen Stoyanchev * @since 4.1 - * * @deprecated as of 4.3 {@link DeferredResultMethodReturnValueHandler} supports * ListenableFuture return values via an adapter mechanism. */ diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/CssLinkResourceTransformer.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/CssLinkResourceTransformer.java index 4380246e843..fa884f7a496 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/CssLinkResourceTransformer.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/CssLinkResourceTransformer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -49,12 +49,11 @@ */ public class CssLinkResourceTransformer extends ResourceTransformerSupport { - private static final Log logger = LogFactory.getLog(CssLinkResourceTransformer.class); - private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); + private static final Log logger = LogFactory.getLog(CssLinkResourceTransformer.class); - private final List linkParsers = new ArrayList(); + private final List linkParsers = new ArrayList(2); public CssLinkResourceTransformer() { @@ -81,7 +80,7 @@ public Resource transform(HttpServletRequest request, Resource resource, Resourc byte[] bytes = FileCopyUtils.copyToByteArray(resource.getInputStream()); String content = new String(bytes, DEFAULT_CHARSET); - Set infos = new HashSet(5); + Set infos = new HashSet(8); for (CssLinkParser parser : this.linkParsers) { parser.parseLink(content, infos); } @@ -123,17 +122,16 @@ public Resource transform(HttpServletRequest request, Resource resource, Resourc private boolean hasScheme(String link) { int schemeIndex = link.indexOf(":"); - return (schemeIndex > 0 && !link.substring(0, schemeIndex).contains("/")) - || link.indexOf("//") == 0; + return (schemeIndex > 0 && !link.substring(0, schemeIndex).contains("/")) || link.indexOf("//") == 0; } - protected static interface CssLinkParser { + protected interface CssLinkParser { void parseLink(String content, Set linkInfos); - } + protected static abstract class AbstractCssLinkParser implements CssLinkParser { /** @@ -189,6 +187,7 @@ protected int addLink(int index, String endKey, String content, Set } + private static class ImportStatementCssLinkParser extends AbstractCssLinkParser { @Override @@ -208,6 +207,7 @@ else if (logger.isErrorEnabled()) { } } + private static class UrlFunctionCssLinkParser extends AbstractCssLinkParser { @Override @@ -229,8 +229,7 @@ private static class CssLinkInfo implements Comparable { private final int end; - - private CssLinkInfo(int start, int end) { + public CssLinkInfo(int start, int end) { this.start = start; this.end = end; } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceTransformer.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceTransformer.java index fe941d830bf..de17faf1b5d 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceTransformer.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceTransformer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,10 +35,10 @@ public interface ResourceTransformer { * @param request the current request * @param resource the resource to transform * @param transformerChain the chain of remaining transformers to delegate to - * @return the transformed resource, never {@code null} + * @return the transformed resource (never {@code null}) * @throws IOException if the transformation fails */ Resource transform(HttpServletRequest request, Resource resource, ResourceTransformerChain transformerChain) throws IOException; -} \ No newline at end of file +} diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/annotation/UriTemplateServletAnnotationControllerTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/annotation/UriTemplateServletAnnotationControllerTests.java index ca2df2ca23f..79d50c93a6d 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/annotation/UriTemplateServletAnnotationControllerTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/annotation/UriTemplateServletAnnotationControllerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -54,6 +54,7 @@ public class UriTemplateServletAnnotationControllerTests { private DispatcherServlet servlet; + @Test public void simple() throws Exception { initServlet(SimpleUriTemplateController.class); @@ -318,8 +319,7 @@ public void customRegex() throws Exception { assertEquals("test-42", response.getContentAsString()); } - // SPR-6640 - @Test + @Test // SPR-6640 public void menuTree() throws Exception { initServlet(MenuTreeController.class); @@ -329,8 +329,7 @@ public void menuTree() throws Exception { assertEquals("M5", response.getContentAsString()); } - // SPR-6876 - @Test + @Test // SPR-6876 public void variableNames() throws Exception { initServlet(VariableNamesController.class); @@ -345,8 +344,7 @@ public void variableNames() throws Exception { assertEquals("bar-bar", response.getContentAsString()); } - // SPR-8543 - @Test + @Test // SPR-8543 public void variableNamesWithUrlExtension() throws Exception { initServlet(VariableNamesController.class); @@ -356,8 +354,7 @@ public void variableNamesWithUrlExtension() throws Exception { assertEquals("foo-foo", response.getContentAsString()); } - // SPR-9333 - @Test + @Test // SPR-9333 @SuppressWarnings("serial") public void suppressDefaultSuffixPattern() throws Exception { servlet = new DispatcherServlet() { @@ -381,8 +378,7 @@ protected WebApplicationContext createWebApplicationContext(WebApplicationContex assertEquals("foo-jsmith@mail.com", response.getContentAsString()); } - // SPR-6906 - @Test + @Test // SPR-6906 @SuppressWarnings("serial") public void controllerClassName() throws Exception { servlet = new DispatcherServlet() { @@ -416,8 +412,7 @@ protected WebApplicationContext createWebApplicationContext(WebApplicationContex assertEquals("plain-bar", response.getContentAsString()); } - // SPR-6978 - @Test + @Test // SPR-6978 public void doIt() throws Exception { initServlet(Spr6978Controller.class); diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/client/standard/StandardWebSocketClient.java b/spring-websocket/src/main/java/org/springframework/web/socket/client/standard/StandardWebSocketClient.java index 2dbd8157064..faa32b6911d 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/client/standard/StandardWebSocketClient.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/client/standard/StandardWebSocketClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,7 +26,6 @@ import java.util.Locale; import java.util.Map; import java.util.concurrent.Callable; - import javax.websocket.ClientEndpointConfig; import javax.websocket.ClientEndpointConfig.Configurator; import javax.websocket.ContainerProvider; @@ -51,7 +50,6 @@ import org.springframework.web.socket.adapter.standard.WebSocketToStandardExtensionAdapter; import org.springframework.web.socket.client.AbstractWebSocketClient; - /** * A WebSocketClient based on standard Java WebSocket API. * diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/WebMvcStompEndpointRegistry.java b/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/WebMvcStompEndpointRegistry.java index bbe55538fcf..048cd3a03b8 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/WebMvcStompEndpointRegistry.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/WebMvcStompEndpointRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -67,8 +67,8 @@ public WebMvcStompEndpointRegistry(WebSocketHandler webSocketHandler, org.springframework.messaging.simp.user.UserSessionRegistry userSessionRegistry, TaskScheduler defaultSockJsTaskScheduler) { - Assert.notNull(webSocketHandler, "'webSocketHandler' is required "); - Assert.notNull(transportRegistration, "'transportRegistration' is required"); + Assert.notNull(webSocketHandler, "WebSocketHandler is required "); + Assert.notNull(transportRegistration, "WebSocketTransportRegistration is required"); this.webSocketHandler = webSocketHandler; this.subProtocolWebSocketHandler = unwrapSubProtocolWebSocketHandler(webSocketHandler); @@ -87,19 +87,17 @@ public WebMvcStompEndpointRegistry(WebSocketHandler webSocketHandler, this.stompHandler.setMessageSizeLimit(transportRegistration.getMessageSizeLimit()); } - this.sockJsScheduler = defaultSockJsTaskScheduler; } private static SubProtocolWebSocketHandler unwrapSubProtocolWebSocketHandler(WebSocketHandler handler) { WebSocketHandler actual = WebSocketHandlerDecorator.unwrap(handler); - Assert.isInstanceOf(SubProtocolWebSocketHandler.class, actual, "No SubProtocolWebSocketHandler in " + handler); + if (!(actual instanceof SubProtocolWebSocketHandler)) { + throw new IllegalArgumentException("No SubProtocolWebSocketHandler in " + handler); + }; return (SubProtocolWebSocketHandler) actual; } - protected void setApplicationContext(ApplicationContext applicationContext) { - this.stompHandler.setApplicationEventPublisher(applicationContext); - } @Override public StompWebSocketEndpointRegistration addEndpoint(String... paths) { @@ -144,6 +142,11 @@ public WebMvcStompEndpointRegistry setErrorHandler(StompSubProtocolErrorHandler return this; } + protected void setApplicationContext(ApplicationContext applicationContext) { + this.stompHandler.setApplicationEventPublisher(applicationContext); + } + + /** * Return a handler mapping with the mapped ViewControllers; or {@code null} * in case of no registrations. diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractTyrusRequestUpgradeStrategy.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractTyrusRequestUpgradeStrategy.java index 0e6a31df0d0..35886efc724 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractTyrusRequestUpgradeStrategy.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractTyrusRequestUpgradeStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -193,7 +193,7 @@ protected static class Tyrus17EndpointHelper implements TyrusEndpointHelper { private static final Method registerMethod; - private static final Method unRegisterMethod; + private static final Method unregisterMethod; static { try { @@ -204,7 +204,7 @@ protected static class Tyrus17EndpointHelper implements TyrusEndpointHelper { throw new IllegalStateException("Expected TyrusEndpointWrapper constructor with 9 or 10 arguments"); } registerMethod = TyrusWebSocketEngine.class.getDeclaredMethod("register", TyrusEndpointWrapper.class); - unRegisterMethod = TyrusWebSocketEngine.class.getDeclaredMethod("unregister", TyrusEndpointWrapper.class); + unregisterMethod = TyrusWebSocketEngine.class.getDeclaredMethod("unregister", TyrusEndpointWrapper.class); ReflectionUtils.makeAccessible(registerMethod); } catch (Exception ex) { @@ -259,7 +259,7 @@ public void register(TyrusWebSocketEngine engine, Object endpoint) { @Override public void unregister(TyrusWebSocketEngine engine, Object endpoint) { try { - unRegisterMethod.invoke(engine, endpoint); + unregisterMethod.invoke(engine, endpoint); } catch (Exception ex) { throw new HandshakeFailureException("Failed to unregister " + endpoint, ex); diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/WebLogicRequestUpgradeStrategy.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/WebLogicRequestUpgradeStrategy.java index c16d747a802..58b294077fe 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/WebLogicRequestUpgradeStrategy.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/WebLogicRequestUpgradeStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -60,7 +60,6 @@ public class WebLogicRequestUpgradeStrategy extends AbstractTyrusRequestUpgradeS private static final WebLogicServletWriterHelper servletWriterHelper = new WebLogicServletWriterHelper(); private static final Connection.CloseListener noOpCloseListener = new Connection.CloseListener() { - @Override public void close(CloseReason reason) { } diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/AbstractXhrTransport.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/AbstractXhrTransport.java index 7b7f51d22d7..2d0fac299e9 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/AbstractXhrTransport.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/AbstractXhrTransport.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -54,6 +54,7 @@ public abstract class AbstractXhrTransport implements XhrTransport { PRELUDE = new String(bytes, SockJsFrame.CHARSET); } + protected Log logger = LogFactory.getLog(getClass()); private boolean xhrStreamingDisabled; @@ -137,6 +138,7 @@ protected abstract void connectInternal(TransportRequest request, WebSocketHandl URI receiveUrl, HttpHeaders handshakeHeaders, XhrClientSockJsSession session, SettableListenableFuture connectFuture); + // InfoReceiver methods @Override @@ -165,6 +167,7 @@ public String executeInfoRequest(URI infoUrl, HttpHeaders headers) { protected abstract ResponseEntity executeInfoRequestInternal(URI infoUrl, HttpHeaders headers); + // XhrTransport methods @Override @@ -184,8 +187,8 @@ public void executeSendRequest(URI url, HttpHeaders headers, TextMessage message } } - protected abstract ResponseEntity executeSendRequestInternal(URI url, - HttpHeaders headers, TextMessage message); + protected abstract ResponseEntity executeSendRequestInternal( + URI url, HttpHeaders headers, TextMessage message); @Override diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/PollingSockJsSession.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/PollingSockJsSession.java index e3cb44882ee..d8066c2e06d 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/PollingSockJsSession.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/PollingSockJsSession.java @@ -35,7 +35,6 @@ */ public class PollingSockJsSession extends AbstractHttpSockJsSession { - public PollingSockJsSession(String sessionId, SockJsServiceConfig config, WebSocketHandler wsHandler, Map attributes) { diff --git a/spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket.gif b/spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket.gif new file mode 100644 index 0000000000000000000000000000000000000000..9e4c800e48d73839d489f8ad34dfe55fedd83742 GIT binary patch literal 1025 zcmZ?wbhEHb6krfw_|5x!i2%NadZP9VhWoLYspY~sI)^FL#fR$&0R-6u8 zb~14J$)dhS#i>P*$Ka8Cj!@=_g`@;VEKul6(_@1pNU*|CTi0KujOm})@*D$ zdVI>6vr|u<3O;vc%9+!XPn`@oeEwy+XHRcEePZ*Q)2E*wzxe8G z+o}A^uTEZib?ox1|NH;{zlQqM zmoLt|dU<}^rtnk8dZtazy>WHZj4Ao=UZ35vI(bS@;JRhmTi18**fin7sTJkh=&N4ntNCPc;=M(wm8FGA_HrqX z3I(2OeMuJAXC&O5lQOf==k4(oS*{ur6Ktj=+vRxbU7ZxWw<~#FWBjWl%a6B&lm?j3 zZ7o?jx%p^A==&2J@*LDV!Yq%}2G37(-BucKtRZ4&ZA_M(`qq-*O(h{2HtK7#{Wj%A ztjvj8o*Fhi!e?eo;IxpSX@LR2hz3XaD1it8#h)yU3=B&cbU;F&Ji)+mkHM2u#$!Xm zK|^t)DFzD}dmI~8bZP=09cJrc7vyPMF)`JvK}Dx+&kTk}g+4AX7TwH68V-Dtd@2c& z8jsu=)IwTpUM@Q1%)}JLR>rYNnL$O)b&mq$q5}={8Wobh+(>-dA*hsf?|`Rr!UJ|b zy{I)S9xVx&XOOFQSF$3RxmS{7%NkDM6T)-tr@rEN()5s}OGM<(1506b!9~tfq&V*w zB{?#)6fOv1CmHadK_akQZsx%+p2EsafeSVW8ng$f`K6k98hr^^AZx$a VVPfZ#726KD)qFE~B`d&S4FG&~ckciI literal 0 HcmV?d00001 diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/AbstractWebSocketIntegrationTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/AbstractWebSocketIntegrationTests.java index 7067ef25db0..2309f74f853 100644 --- a/spring-websocket/src/test/java/org/springframework/web/socket/AbstractWebSocketIntegrationTests.java +++ b/spring-websocket/src/test/java/org/springframework/web/socket/AbstractWebSocketIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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 org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -48,9 +47,7 @@ */ public abstract class AbstractWebSocketIntegrationTests { - protected Log logger = LogFactory.getLog(getClass()); - - private static Map, Class> upgradeStrategyConfigTypes = new HashMap, Class>(); + private static Map, Class> upgradeStrategyConfigTypes = new HashMap<>(); static { upgradeStrategyConfigTypes.put(JettyWebSocketTestServer.class, JettyUpgradeStrategyConfig.class); @@ -58,6 +55,7 @@ public abstract class AbstractWebSocketIntegrationTests { upgradeStrategyConfigTypes.put(UndertowTestServer.class, UndertowUpgradeStrategyConfig.class); } + @Rule public final TestName testName = new TestName(); @@ -67,12 +65,13 @@ public abstract class AbstractWebSocketIntegrationTests { @Parameter(1) public WebSocketClient webSocketClient; + protected final Log logger = LogFactory.getLog(getClass()); + protected AnnotationConfigWebApplicationContext wac; @Before public void setup() throws Exception { - logger.debug("Setting up '" + this.testName.getMethodName() + "', client=" + this.webSocketClient.getClass().getSimpleName() + ", server=" + this.server.getClass().getSimpleName()); @@ -155,6 +154,7 @@ public RequestUpgradeStrategy requestUpgradeStrategy() { } } + @Configuration static class TomcatUpgradeStrategyConfig extends AbstractRequestUpgradeStrategyConfig { @@ -164,6 +164,7 @@ public RequestUpgradeStrategy requestUpgradeStrategy() { } } + @Configuration static class UndertowUpgradeStrategyConfig extends AbstractRequestUpgradeStrategyConfig { diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/WebSocketIntegrationTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/WebSocketHandshakeTests.java similarity index 95% rename from spring-websocket/src/test/java/org/springframework/web/socket/WebSocketIntegrationTests.java rename to spring-websocket/src/test/java/org/springframework/web/socket/WebSocketHandshakeTests.java index 1d86998daed..813b7b5fbf8 100644 --- a/spring-websocket/src/test/java/org/springframework/web/socket/WebSocketIntegrationTests.java +++ b/spring-websocket/src/test/java/org/springframework/web/socket/WebSocketHandshakeTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -46,9 +46,10 @@ * Client and server-side WebSocket integration tests. * * @author Rossen Stoyanchev + * @author Juergen Hoeller */ @RunWith(Parameterized.class) -public class WebSocketIntegrationTests extends AbstractWebSocketIntegrationTests { +public class WebSocketHandshakeTests extends AbstractWebSocketIntegrationTests { @Parameters(name = "server [{0}], client [{1}]") public static Iterable arguments() { @@ -62,7 +63,7 @@ public static Iterable arguments() { @Override protected Class[] getAnnotatedConfigClasses() { - return new Class[] { TestConfig.class }; + return new Class[] {TestConfig.class}; } @Test @@ -75,11 +76,8 @@ public void subProtocolNegotiation() throws Exception { session.close(); } - // SPR-12727 - - @Test + @Test // SPR-12727 public void unsolicitedPongWithEmptyPayload() throws Exception { - String url = getWsBaseUrl() + "/ws"; WebSocketSession session = this.webSocketClient.doHandshake(new AbstractWebSocketHandler() {}, url).get(); @@ -126,7 +124,6 @@ private static class TestWebSocketHandler extends AbstractWebSocketHandler { private Throwable transportError; - public void setWaitMessageCount(int waitMessageCount) { this.waitMessageCount = waitMessageCount; } From 4337f146276898ef8879981ffa7129cce20bed16 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 6 Jul 2016 17:42:10 +0200 Subject: [PATCH 0006/1274] Upgrade to Jackson 2.8 GA --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 314f67a6f42..52e3b8594e8 100644 --- a/build.gradle +++ b/build.gradle @@ -52,7 +52,7 @@ configure(allprojects) { project -> ext.hsqldbVersion = "2.3.4" ext.httpasyncVersion = "4.1.2" ext.httpclientVersion = "4.5.2" - ext.jackson2Version = "2.8.0.rc2" + ext.jackson2Version = "2.8.0" ext.jasperreportsVersion = "6.2.1" // our tests fail with JR-internal NPEs against 6.2.2 and higher ext.javamailVersion = "1.5.5" ext.jettyVersion = "9.3.10.v20160621" From 3c149114014526512575bb22771451b310a7fb3c Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 6 Jul 2016 17:59:49 +0200 Subject: [PATCH 0007/1274] Polishing --- .../support/TestPropertySourceUtils.java | 57 ++++++++----------- .../support/TestPropertySourceUtilsTests.java | 41 ++++++------- .../json/Jackson2ObjectMapperBuilder.java | 5 +- 3 files changed, 48 insertions(+), 55 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/context/support/TestPropertySourceUtils.java b/spring-test/src/main/java/org/springframework/test/context/support/TestPropertySourceUtils.java index 47e488cf29d..98e9c936533 100644 --- a/spring-test/src/main/java/org/springframework/test/context/support/TestPropertySourceUtils.java +++ b/spring-test/src/main/java/org/springframework/test/context/support/TestPropertySourceUtils.java @@ -75,7 +75,6 @@ static MergedTestPropertySources buildMergedTestPropertySources(Class testCla return new MergedTestPropertySources(); } - // else... List attributesList = resolveTestPropertySourceAttributes(testClass); String[] locations = mergeLocations(attributesList); String[] properties = mergeProperties(attributesList); @@ -84,30 +83,27 @@ static MergedTestPropertySources buildMergedTestPropertySources(Class testCla private static List resolveTestPropertySourceAttributes(Class testClass) { Assert.notNull(testClass, "Class must not be null"); + List attributesList = new ArrayList(); + Class annotationType = TestPropertySource.class; - final List attributesList = new ArrayList(); - final Class annotationType = TestPropertySource.class; AnnotationDescriptor descriptor = findAnnotationDescriptor(testClass, annotationType); Assert.notNull(descriptor, String.format( - "Could not find an 'annotation declaring class' for annotation type [%s] and class [%s]", - annotationType.getName(), testClass.getName())); + "Could not find an 'annotation declaring class' for annotation type [%s] and class [%s]", + annotationType.getName(), testClass.getName())); while (descriptor != null) { TestPropertySource testPropertySource = descriptor.synthesizeAnnotation(); Class rootDeclaringClass = descriptor.getRootDeclaringClass(); - if (logger.isTraceEnabled()) { logger.trace(String.format("Retrieved @TestPropertySource [%s] for declaring class [%s].", testPropertySource, rootDeclaringClass.getName())); } - - TestPropertySourceAttributes attributes = new TestPropertySourceAttributes(rootDeclaringClass, - testPropertySource); + TestPropertySourceAttributes attributes = + new TestPropertySourceAttributes(rootDeclaringClass, testPropertySource); if (logger.isTraceEnabled()) { logger.trace("Resolved TestPropertySource attributes: " + attributes); } attributesList.add(attributes); - descriptor = findAnnotationDescriptor(rootDeclaringClass.getSuperclass(), annotationType); } @@ -116,39 +112,31 @@ private static List resolveTestPropertySourceAttri private static String[] mergeLocations(List attributesList) { final List locations = new ArrayList(); - for (TestPropertySourceAttributes attrs : attributesList) { if (logger.isTraceEnabled()) { logger.trace(String.format("Processing locations for TestPropertySource attributes %s", attrs)); } - String[] locationsArray = TestContextResourceUtils.convertToClasspathResourcePaths( - attrs.getDeclaringClass(), attrs.getLocations()); + attrs.getDeclaringClass(), attrs.getLocations()); locations.addAll(0, Arrays. asList(locationsArray)); - if (!attrs.isInheritLocations()) { break; } } - return StringUtils.toStringArray(locations); } private static String[] mergeProperties(List attributesList) { final List properties = new ArrayList(); - for (TestPropertySourceAttributes attrs : attributesList) { if (logger.isTraceEnabled()) { logger.trace(String.format("Processing inlined properties for TestPropertySource attributes %s", attrs)); } - - properties.addAll(0, Arrays. asList(attrs.getProperties())); - + properties.addAll(0, Arrays.asList(attrs.getProperties())); if (!attrs.isInheritProperties()) { break; } } - return StringUtils.toStringArray(properties); } @@ -168,8 +156,8 @@ private static String[] mergeProperties(List attri * @throws IllegalStateException if an error occurs while processing a properties file */ public static void addPropertiesFilesToEnvironment(ConfigurableApplicationContext context, String... locations) { - Assert.notNull(context, "context must not be null"); - Assert.notNull(locations, "locations must not be null"); + Assert.notNull(context, "'context' must not be null"); + Assert.notNull(locations, "'locations' must not be null"); addPropertiesFilesToEnvironment(context.getEnvironment(), context, locations); } @@ -196,9 +184,9 @@ public static void addPropertiesFilesToEnvironment(ConfigurableApplicationContex public static void addPropertiesFilesToEnvironment(ConfigurableEnvironment environment, ResourceLoader resourceLoader, String... locations) { - Assert.notNull(environment, "environment must not be null"); - Assert.notNull(resourceLoader, "resourceLoader must not be null"); - Assert.notNull(locations, "locations must not be null"); + Assert.notNull(environment, "'environment' must not be null"); + Assert.notNull(resourceLoader, "'resourceLoader' must not be null"); + Assert.notNull(locations, "'locations' must not be null"); try { for (String location : locations) { String resolvedLocation = environment.resolveRequiredPlaceholders(location); @@ -225,8 +213,8 @@ public static void addPropertiesFilesToEnvironment(ConfigurableEnvironment envir * @see #addInlinedPropertiesToEnvironment(ConfigurableEnvironment, String[]) */ public static void addInlinedPropertiesToEnvironment(ConfigurableApplicationContext context, String... inlinedProperties) { - Assert.notNull(context, "context must not be null"); - Assert.notNull(inlinedProperties, "inlinedProperties must not be null"); + Assert.notNull(context, "'context' must not be null"); + Assert.notNull(inlinedProperties, "'inlinedProperties' must not be null"); addInlinedPropertiesToEnvironment(context.getEnvironment(), inlinedProperties); } @@ -247,15 +235,18 @@ public static void addInlinedPropertiesToEnvironment(ConfigurableApplicationCont * @see #convertInlinedPropertiesToMap */ public static void addInlinedPropertiesToEnvironment(ConfigurableEnvironment environment, String... inlinedProperties) { - Assert.notNull(environment, "environment must not be null"); - Assert.notNull(inlinedProperties, "inlinedProperties must not be null"); + Assert.notNull(environment, "'environment' must not be null"); + Assert.notNull(inlinedProperties, "'inlinedProperties' must not be null"); if (!ObjectUtils.isEmpty(inlinedProperties)) { if (logger.isDebugEnabled()) { - logger.debug("Adding inlined properties to environment: " + ObjectUtils.nullSafeToString(inlinedProperties)); + logger.debug("Adding inlined properties to environment: " + + ObjectUtils.nullSafeToString(inlinedProperties)); } - MapPropertySource ps = (MapPropertySource) environment.getPropertySources().get(INLINED_PROPERTIES_PROPERTY_SOURCE_NAME); + MapPropertySource ps = (MapPropertySource) + environment.getPropertySources().get(INLINED_PROPERTIES_PROPERTY_SOURCE_NAME); if (ps == null) { - ps = new MapPropertySource(INLINED_PROPERTIES_PROPERTY_SOURCE_NAME, new LinkedHashMap()); + ps = new MapPropertySource(INLINED_PROPERTIES_PROPERTY_SOURCE_NAME, + new LinkedHashMap()); environment.getPropertySources().addFirst(ps); } ps.getSource().putAll(convertInlinedPropertiesToMap(inlinedProperties)); @@ -280,7 +271,7 @@ public static void addInlinedPropertiesToEnvironment(ConfigurableEnvironment env * @see #addInlinedPropertiesToEnvironment(ConfigurableEnvironment, String[]) */ public static Map convertInlinedPropertiesToMap(String... inlinedProperties) { - Assert.notNull(inlinedProperties, "inlinedProperties must not be null"); + Assert.notNull(inlinedProperties, "'inlinedProperties' must not be null"); Map map = new LinkedHashMap(); Properties props = new Properties(); diff --git a/spring-test/src/test/java/org/springframework/test/context/support/TestPropertySourceUtilsTests.java b/spring-test/src/test/java/org/springframework/test/context/support/TestPropertySourceUtilsTests.java index 1324c219026..b1d7ddfdbb1 100644 --- a/spring-test/src/test/java/org/springframework/test/context/support/TestPropertySourceUtilsTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/support/TestPropertySourceUtilsTests.java @@ -32,10 +32,10 @@ import org.springframework.mock.env.MockPropertySource; import org.springframework.test.context.TestPropertySource; -import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.CoreMatchers.startsWith; import static org.junit.Assert.*; -import static org.mockito.Matchers.*; +import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.*; import static org.springframework.test.context.support.TestPropertySourceUtils.*; @@ -48,8 +48,11 @@ public class TestPropertySourceUtilsTests { private static final String[] EMPTY_STRING_ARRAY = new String[0]; - private static final String[] KEY_VALUE_PAIR = new String[] { "key = value" }; - private static final String[] FOO_LOCATIONS = new String[] { "classpath:/foo.properties" }; + + private static final String[] KEY_VALUE_PAIR = new String[] {"key = value"}; + + private static final String[] FOO_LOCATIONS = new String[] {"classpath:/foo.properties"}; + @Rule public ExpectedException expectedException = ExpectedException.none(); @@ -74,7 +77,7 @@ public void extendedEmptyAnnotation() { @Test public void value() { assertMergedTestPropertySources(ValuePropertySources.class, asArray("classpath:/value.xml"), - EMPTY_STRING_ARRAY); + EMPTY_STRING_ARRAY); } @Test @@ -86,67 +89,66 @@ public void locationsAndValueAttributes() { @Test public void locationsAndProperties() { assertMergedTestPropertySources(LocationsAndPropertiesPropertySources.class, - asArray("classpath:/foo1.xml", "classpath:/foo2.xml"), asArray("k1a=v1a", "k1b: v1b")); + asArray("classpath:/foo1.xml", "classpath:/foo2.xml"), asArray("k1a=v1a", "k1b: v1b")); } @Test public void inheritedLocationsAndProperties() { assertMergedTestPropertySources(InheritedPropertySources.class, - asArray("classpath:/foo1.xml", "classpath:/foo2.xml"), asArray("k1a=v1a", "k1b: v1b")); + asArray("classpath:/foo1.xml", "classpath:/foo2.xml"), asArray("k1a=v1a", "k1b: v1b")); } @Test public void extendedLocationsAndProperties() { assertMergedTestPropertySources(ExtendedPropertySources.class, - asArray("classpath:/foo1.xml", "classpath:/foo2.xml", "classpath:/bar1.xml", "classpath:/bar2.xml"), - asArray("k1a=v1a", "k1b: v1b", "k2a v2a", "k2b: v2b")); + asArray("classpath:/foo1.xml", "classpath:/foo2.xml", "classpath:/bar1.xml", "classpath:/bar2.xml"), + asArray("k1a=v1a", "k1b: v1b", "k2a v2a", "k2b: v2b")); } @Test public void overriddenLocations() { assertMergedTestPropertySources(OverriddenLocationsPropertySources.class, - asArray("classpath:/baz.properties"), asArray("k1a=v1a", "k1b: v1b", "key = value")); + asArray("classpath:/baz.properties"), asArray("k1a=v1a", "k1b: v1b", "key = value")); } @Test public void overriddenProperties() { assertMergedTestPropertySources(OverriddenPropertiesPropertySources.class, - asArray("classpath:/foo1.xml", "classpath:/foo2.xml", "classpath:/baz.properties"), KEY_VALUE_PAIR); + asArray("classpath:/foo1.xml", "classpath:/foo2.xml", "classpath:/baz.properties"), KEY_VALUE_PAIR); } @Test public void overriddenLocationsAndProperties() { assertMergedTestPropertySources(OverriddenLocationsAndPropertiesPropertySources.class, - asArray("classpath:/baz.properties"), KEY_VALUE_PAIR); + asArray("classpath:/baz.properties"), KEY_VALUE_PAIR); } - // ------------------------------------------------------------------------- @Test public void addPropertiesFilesToEnvironmentWithNullContext() { expectedException.expect(IllegalArgumentException.class); - expectedException.expectMessage("context must not be null"); + expectedException.expectMessage("must not be null"); addPropertiesFilesToEnvironment((ConfigurableApplicationContext) null, FOO_LOCATIONS); } @Test public void addPropertiesFilesToEnvironmentWithContextAndNullLocations() { expectedException.expect(IllegalArgumentException.class); - expectedException.expectMessage("locations must not be null"); + expectedException.expectMessage("must not be null"); addPropertiesFilesToEnvironment(mock(ConfigurableApplicationContext.class), (String[]) null); } @Test public void addPropertiesFilesToEnvironmentWithNullEnvironment() { expectedException.expect(IllegalArgumentException.class); - expectedException.expectMessage("environment must not be null"); + expectedException.expectMessage("must not be null"); addPropertiesFilesToEnvironment((ConfigurableEnvironment) null, mock(ResourceLoader.class), FOO_LOCATIONS); } @Test public void addPropertiesFilesToEnvironmentWithEnvironmentAndNullLocations() { expectedException.expect(IllegalArgumentException.class); - expectedException.expectMessage("locations must not be null"); + expectedException.expectMessage("must not be null"); addPropertiesFilesToEnvironment(new MockEnvironment(), mock(ResourceLoader.class), (String[]) null); } @@ -168,8 +170,6 @@ public void addPropertiesFilesToEnvironmentWithSinglePropertyFromVirtualFile() { assertEquals("value", environment.getProperty("key")); } - // ------------------------------------------------------------------------- - @Test public void addInlinedPropertiesToEnvironmentWithNullContext() { expectedException.expect(IllegalArgumentException.class); @@ -231,16 +231,17 @@ public void convertInlinedPropertiesToMapWithNullInlinedProperties() { convertInlinedPropertiesToMap((String[]) null); } - // ------------------------------------------------------------------- private static void assertMergedTestPropertySources(Class testClass, String[] expectedLocations, String[] expectedProperties) { + MergedTestPropertySources mergedPropertySources = buildMergedTestPropertySources(testClass); assertNotNull(mergedPropertySources); assertArrayEquals(expectedLocations, mergedPropertySources.getLocations()); assertArrayEquals(expectedProperties, mergedPropertySources.getProperties()); } + @SafeVarargs private static T[] asArray(T... arr) { return arr; diff --git a/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java b/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java index 12cee3308a2..f0ee1c89ee0 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java +++ b/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java @@ -580,8 +580,9 @@ public Jackson2ObjectMapperBuilder applicationContext(ApplicationContext applica public T build() { ObjectMapper mapper; if (this.createXmlMapper) { - mapper = (this.defaultUseWrapper == null ? new XmlObjectMapperInitializer().create() - : new XmlObjectMapperInitializer().create(this.defaultUseWrapper)); + mapper = (this.defaultUseWrapper != null ? + new XmlObjectMapperInitializer().create(this.defaultUseWrapper) : + new XmlObjectMapperInitializer().create()); } else { mapper = new ObjectMapper(); From 813108a928fd50a1fb87e9333b974b6e7242b117 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Sun, 10 Jul 2016 16:00:30 +0200 Subject: [PATCH 0008/1274] Ensure TestContextManager always tracks after-class exception This commit fixes a minor bug introduced in 0adc4921ed. Issue: SPR-14447 --- .../springframework/test/context/TestContextManager.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/context/TestContextManager.java b/spring-test/src/main/java/org/springframework/test/context/TestContextManager.java index 33598520741..289796477e0 100644 --- a/spring-test/src/main/java/org/springframework/test/context/TestContextManager.java +++ b/spring-test/src/main/java/org/springframework/test/context/TestContextManager.java @@ -366,9 +366,9 @@ public void afterTestClass() throws Exception { if (logger.isWarnEnabled()) { logger.warn("Caught exception while allowing TestExecutionListener [" + testExecutionListener + "] to process 'after class' callback for test class [" + testClass + "]", ex); - if (afterTestClassException == null) { - afterTestClassException = ex; - } + } + if (afterTestClassException == null) { + afterTestClassException = ex; } } } From ab62edeeaa56e8acf246d3f121e4d95ca19a1ba4 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Mon, 11 Jul 2016 10:34:27 +0200 Subject: [PATCH 0009/1274] Fix NoOpCache handling of get(key,callable) This commit fixes the method that takes a Callable to actually always invoke it rather than returning null. Issue: SPR-14445 --- .../cache/support/NoOpCacheManager.java | 7 ++- .../cache/NoOpCacheManagerTests.java | 57 ++++++++++++++----- 2 files changed, 49 insertions(+), 15 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/cache/support/NoOpCacheManager.java b/spring-context/src/main/java/org/springframework/cache/support/NoOpCacheManager.java index faa56c30991..c27f59e1a90 100644 --- a/spring-context/src/main/java/org/springframework/cache/support/NoOpCacheManager.java +++ b/spring-context/src/main/java/org/springframework/cache/support/NoOpCacheManager.java @@ -102,7 +102,12 @@ public T get(Object key, Class type) { @Override public T get(Object key, Callable valueLoader) { - return null; + try { + return valueLoader.call(); + } + catch (Exception ex) { + throw new ValueRetrievalException(key, valueLoader, ex); + } } @Override diff --git a/spring-context/src/test/java/org/springframework/cache/NoOpCacheManagerTests.java b/spring-context/src/test/java/org/springframework/cache/NoOpCacheManagerTests.java index dc65b678271..9489b84775b 100644 --- a/spring-context/src/test/java/org/springframework/cache/NoOpCacheManagerTests.java +++ b/spring-context/src/test/java/org/springframework/cache/NoOpCacheManagerTests.java @@ -18,33 +18,33 @@ import java.util.UUID; -import org.junit.Before; import org.junit.Test; import org.springframework.cache.support.NoOpCacheManager; import static org.junit.Assert.*; +/** + * Tests for {@link NoOpCacheManager}. + * + * @author Costin Leau + * @author Stephane Nicoll + */ public class NoOpCacheManagerTests { - private CacheManager manager; - - @Before - public void setup() { - manager = new NoOpCacheManager(); - } + private final CacheManager manager = new NoOpCacheManager(); @Test public void testGetCache() throws Exception { - Cache cache = manager.getCache("bucket"); + Cache cache = this.manager.getCache("bucket"); assertNotNull(cache); - assertSame(cache, manager.getCache("bucket")); + assertSame(cache, this.manager.getCache("bucket")); } @Test public void testNoOpCache() throws Exception { - String name = UUID.randomUUID().toString(); - Cache cache = manager.getCache(name); + String name = createRandomKey(); + Cache cache = this.manager.getCache(name); assertEquals(name, cache.getName()); Object key = new Object(); cache.put(key, new Object()); @@ -56,8 +56,37 @@ public void testNoOpCache() throws Exception { @Test public void testCacheName() throws Exception { String name = "bucket"; - assertFalse(manager.getCacheNames().contains(name)); - manager.getCache(name); - assertTrue(manager.getCacheNames().contains(name)); + assertFalse(this.manager.getCacheNames().contains(name)); + this.manager.getCache(name); + assertTrue(this.manager.getCacheNames().contains(name)); + } + + @Test + public void testCacheCallable() throws Exception { + String name = createRandomKey(); + Cache cache = this.manager.getCache(name); + Object returnValue = new Object(); + Object value = cache.get(new Object(), () -> returnValue); + assertEquals(returnValue, value); } + + @Test + public void testCacheGetCallableFail() { + Cache cache = this.manager.getCache(createRandomKey()); + String key = createRandomKey(); + try { + cache.get(key, () -> { + throw new UnsupportedOperationException("Expected exception"); + }); + } + catch (Cache.ValueRetrievalException ex) { + assertNotNull(ex.getCause()); + assertEquals(UnsupportedOperationException.class, ex.getCause().getClass()); + } + } + + private String createRandomKey() { + return UUID.randomUUID().toString(); + } + } From 275e51b19d7569675507698865778c4e04c138da Mon Sep 17 00:00:00 2001 From: nkjackzhang Date: Tue, 12 Jul 2016 14:39:21 +0800 Subject: [PATCH 0010/1274] Polish doc Closes gh-1107 --- src/asciidoc/core-beans.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/asciidoc/core-beans.adoc b/src/asciidoc/core-beans.adoc index 7a76f3f1aab..b009d5e02bf 100644 --- a/src/asciidoc/core-beans.adoc +++ b/src/asciidoc/core-beans.adoc @@ -6520,7 +6520,7 @@ resulting bean. This functionality can be overridden, however, with the `name` a ==== Bean aliasing As discussed in <>, it is sometimes desirable to give a single bean -multiple names, otherwise known as__bean aliasing__. The `name` attribute of the `@Bean` +multiple names, otherwise known as __bean aliasing__. The `name` attribute of the `@Bean` annotation accepts a String array for this purpose. [source,java,indent=0] From d11c624fb024289d04d16a9f2ebf7c0960ee6e27 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Tue, 12 Jul 2016 14:47:18 +0200 Subject: [PATCH 0011/1274] Polish AntPathMatcher.setTrimTokens javadoc Issue: SPR-14247 Cherry-picked from 147a35f --- .../src/main/java/org/springframework/util/AntPathMatcher.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-core/src/main/java/org/springframework/util/AntPathMatcher.java b/spring-core/src/main/java/org/springframework/util/AntPathMatcher.java index 2d050218ec1..5e91704bbe7 100644 --- a/spring-core/src/main/java/org/springframework/util/AntPathMatcher.java +++ b/spring-core/src/main/java/org/springframework/util/AntPathMatcher.java @@ -132,7 +132,7 @@ public void setCaseSensitive(boolean caseSensitive) { /** * Specify whether to trim tokenized paths and patterns. - *

Default is {@code true}. + *

Default is {@code false}. */ public void setTrimTokens(boolean trimTokens) { this.trimTokens = trimTokens; From aaa223ae6668085b2c4cd5c9f942655ec540b0e9 Mon Sep 17 00:00:00 2001 From: spodgurskiy Date: Tue, 31 May 2016 11:29:01 -0700 Subject: [PATCH 0012/1274] Fix MethodBasedEvaluationContext.lazyLoadArguments This commit fix a potential `ArrayIndexOutOfBoundsException` if `lazyLoadArguments` is called with an empty variable argument. See gh-1070 --- .../MethodBasedEvaluationContext.java | 2 +- .../MethodBasedEvaluationContextTests.java | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/spring-context/src/main/java/org/springframework/context/expression/MethodBasedEvaluationContext.java b/spring-context/src/main/java/org/springframework/context/expression/MethodBasedEvaluationContext.java index b6d783b2d7d..515c5dc6b67 100644 --- a/spring-context/src/main/java/org/springframework/context/expression/MethodBasedEvaluationContext.java +++ b/spring-context/src/main/java/org/springframework/context/expression/MethodBasedEvaluationContext.java @@ -89,7 +89,7 @@ protected void lazyLoadArguments() { String[] parameterNames = this.paramDiscoverer.getParameterNames(this.method); // save parameter names (if discovered) if (parameterNames != null) { - for (int i = 0; i < parameterNames.length; i++) { + for (int i = 0; i < args.length; i++) { setVariable(parameterNames[i], this.args[i]); } } diff --git a/spring-context/src/test/java/org/springframework/context/expression/MethodBasedEvaluationContextTests.java b/spring-context/src/test/java/org/springframework/context/expression/MethodBasedEvaluationContextTests.java index 4c15698993e..12bf9640164 100644 --- a/spring-context/src/test/java/org/springframework/context/expression/MethodBasedEvaluationContextTests.java +++ b/spring-context/src/test/java/org/springframework/context/expression/MethodBasedEvaluationContextTests.java @@ -62,6 +62,42 @@ public void nullArgument() { assertNull(context.lookupVariable("p0")); } + @Test + public void varArgEmpty() { + Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", Boolean.class, String[].class); + MethodBasedEvaluationContext context = createEvaluationContext(method, new Object[] {null}); + + assertNull(context.lookupVariable("p0")); + assertNull(context.lookupVariable("p1")); + } + + @Test + public void varArgNull() { + Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", Boolean.class, String[].class); + MethodBasedEvaluationContext context = createEvaluationContext(method, new Object[] {null, null}); + + assertNull(context.lookupVariable("p0")); + assertNull(context.lookupVariable("p1")); + } + + @Test + public void varArgSingle() { + Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", Boolean.class, String[].class); + MethodBasedEvaluationContext context = createEvaluationContext(method, new Object[] {null, "hello"}); + + assertNull(context.lookupVariable("p0")); + assertEquals("hello", context.lookupVariable("p1")); + } + + @Test + public void varArgMultiple() { + Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", Boolean.class, String[].class); + MethodBasedEvaluationContext context = createEvaluationContext(method, new Object[] {null, new String[]{"hello", "hi"}}); + + assertNull(context.lookupVariable("p0")); + assertNotNull(context.lookupVariable("p1")); + } + private MethodBasedEvaluationContext createEvaluationContext(Method method, Object[] args) { return new MethodBasedEvaluationContext(this, method, args, this.paramDiscover); } @@ -73,6 +109,9 @@ private static class SampleMethods { private void hello(String foo, Boolean flag) { } + private void hello(Boolean flag, String ... vararg){ + + } } } \ No newline at end of file From 7d7a16110216cc8a521270e07aee74663f0b29a7 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Thu, 14 Jul 2016 11:12:06 +0200 Subject: [PATCH 0013/1274] Polish contribution Closes gh-1070 --- .../expression/MethodBasedEvaluationContext.java | 3 ++- .../expression/MethodBasedEvaluationContextTests.java | 11 +++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/expression/MethodBasedEvaluationContext.java b/spring-context/src/main/java/org/springframework/context/expression/MethodBasedEvaluationContext.java index 515c5dc6b67..6c564535046 100644 --- a/spring-context/src/main/java/org/springframework/context/expression/MethodBasedEvaluationContext.java +++ b/spring-context/src/main/java/org/springframework/context/expression/MethodBasedEvaluationContext.java @@ -34,6 +34,7 @@ * * * @author Stephane Nicoll + * @author Sergey Podgurskiy * @since 4.2 */ public class MethodBasedEvaluationContext extends StandardEvaluationContext { @@ -89,7 +90,7 @@ protected void lazyLoadArguments() { String[] parameterNames = this.paramDiscoverer.getParameterNames(this.method); // save parameter names (if discovered) if (parameterNames != null) { - for (int i = 0; i < args.length; i++) { + for (int i = 0; i < this.args.length; i++) { setVariable(parameterNames[i], this.args[i]); } } diff --git a/spring-context/src/test/java/org/springframework/context/expression/MethodBasedEvaluationContextTests.java b/spring-context/src/test/java/org/springframework/context/expression/MethodBasedEvaluationContextTests.java index 12bf9640164..2711b006ba2 100644 --- a/spring-context/src/test/java/org/springframework/context/expression/MethodBasedEvaluationContextTests.java +++ b/spring-context/src/test/java/org/springframework/context/expression/MethodBasedEvaluationContextTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,6 +30,7 @@ * Unit tests for {@link MethodBasedEvaluationContext}. * * @author Stephane Nicoll + * @author Sergey Podgurskiy */ public class MethodBasedEvaluationContextTests { @@ -92,10 +93,11 @@ public void varArgSingle() { @Test public void varArgMultiple() { Method method = ReflectionUtils.findMethod(SampleMethods.class, "hello", Boolean.class, String[].class); - MethodBasedEvaluationContext context = createEvaluationContext(method, new Object[] {null, new String[]{"hello", "hi"}}); + MethodBasedEvaluationContext context = createEvaluationContext(method, + new Object[] {null, new String[]{"hello", "hi"}}); assertNull(context.lookupVariable("p0")); - assertNotNull(context.lookupVariable("p1")); + assertArrayEquals(new String[]{"hello", "hi"}, (String[]) context.lookupVariable("p1")); } private MethodBasedEvaluationContext createEvaluationContext(Method method, Object[] args) { @@ -109,9 +111,10 @@ private static class SampleMethods { private void hello(String foo, Boolean flag) { } - private void hello(Boolean flag, String ... vararg){ + private void hello(Boolean flag, String... vararg){ } + } } \ No newline at end of file From 453688f6dfcc17eb6cb036b5f5d68281aefb60b3 Mon Sep 17 00:00:00 2001 From: nkjackzhang Date: Thu, 14 Jul 2016 17:51:58 +0800 Subject: [PATCH 0014/1274] Polish doc Closes gh-1110 --- src/asciidoc/core-beans.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/asciidoc/core-beans.adoc b/src/asciidoc/core-beans.adoc index b009d5e02bf..8af1139edaf 100644 --- a/src/asciidoc/core-beans.adoc +++ b/src/asciidoc/core-beans.adoc @@ -6704,7 +6704,7 @@ where the magic comes in: All `@Configuration` classes are subclassed at startup with `CGLIB`. In the subclass, the child method checks the container first for any cached (scoped) beans before it calls the parent method and creates a new instance. Note that as of Spring 3.2, it is no longer necessary to add CGLIB to your classpath because -CGLIB classes have been repackaged under org.springframework and included directly +CGLIB classes have been repackaged under `org.springframework.cglib` and included directly within the spring-core JAR. [NOTE] From 942ead75e2c1c59c6e4e33d600100dfb36fc3ca0 Mon Sep 17 00:00:00 2001 From: fisache Date: Wed, 13 Jul 2016 03:54:38 +0900 Subject: [PATCH 0015/1274] Polish doc Closes gh-1108 --- .../request/async/StandardServletAsyncWebRequest.java | 2 +- src/asciidoc/web-mvc.adoc | 8 ++++---- src/asciidoc/web-view.adoc | 1 + 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/context/request/async/StandardServletAsyncWebRequest.java b/spring-web/src/main/java/org/springframework/web/context/request/async/StandardServletAsyncWebRequest.java index fc0bb983235..9bd4ac5c509 100644 --- a/spring-web/src/main/java/org/springframework/web/context/request/async/StandardServletAsyncWebRequest.java +++ b/spring-web/src/main/java/org/springframework/web/context/request/async/StandardServletAsyncWebRequest.java @@ -34,7 +34,7 @@ * *

The servlet and all filters involved in an async request must have async * support enabled using the Servlet API or by adding an - * {@code true} element to servlet and filter + * {@code true} element to servlet and filter * declarations in {@code web.xml}. * * @author Rossen Stoyanchev diff --git a/src/asciidoc/web-mvc.adoc b/src/asciidoc/web-mvc.adoc index b6bf3f3479b..72b9c6d0fad 100644 --- a/src/asciidoc/web-mvc.adoc +++ b/src/asciidoc/web-mvc.adoc @@ -2541,7 +2541,7 @@ For applications configured with a `web.xml` be sure to update to version 3.0: ---- Asynchronous support must be enabled on the `DispatcherServlet` through the -`true` web.xml sub-element. Additionally +`true` sub-element in `web.xml`. Additionally any `Filter` that participates in asyncrequest processing must be configured to support the ASYNC dispatcher type. It should be safe to enable the ASYNC dispatcher type for all filters provided with the Spring Framework since they @@ -2703,7 +2703,7 @@ the example below: - + ---- [source,java,indent=0] @@ -3331,7 +3331,7 @@ Spring MVC also provides a mechanism for building links to controller methods. F public String getBooking(@PathVariable Long booking) { // ... - + } } ---- @@ -3502,7 +3502,7 @@ maximum age. Find below an example of defining a `CookieLocaleResolver`. - + ---- diff --git a/src/asciidoc/web-view.adoc b/src/asciidoc/web-view.adoc index f200e5a4d8e..c892d266333 100644 --- a/src/asciidoc/web-view.adoc +++ b/src/asciidoc/web-view.adoc @@ -1387,6 +1387,7 @@ The HTML would look like: + ---- From e30429051d372228eb362b532869cbff3c9d3907 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 7 Jul 2016 01:05:25 +0200 Subject: [PATCH 0016/1274] Polishing (cherry picked from commit 6aa5931) --- .../standard/DateTimeFormattingTests.java | 2 +- .../support/ObjectToObjectConverter.java | 5 +- .../env/ConfigurablePropertyResolver.java | 20 ++++---- .../org/springframework/util/StopWatch.java | 4 +- .../core/SerializableTypeWrapperTests.java | 50 +++++++++---------- ...equestAttributesArgumentResolverTests.java | 15 +++--- ...tAttributeMethodArgumentResolverTests.java | 8 +-- ...nAttributeMethodArgumentResolverTests.java | 8 +-- 8 files changed, 53 insertions(+), 59 deletions(-) diff --git a/spring-context/src/test/java/org/springframework/format/datetime/standard/DateTimeFormattingTests.java b/spring-context/src/test/java/org/springframework/format/datetime/standard/DateTimeFormattingTests.java index f112d4462c5..3bc0efd3d56 100644 --- a/spring-context/src/test/java/org/springframework/format/datetime/standard/DateTimeFormattingTests.java +++ b/spring-context/src/test/java/org/springframework/format/datetime/standard/DateTimeFormattingTests.java @@ -124,7 +124,7 @@ public void testBindLocalDateWithSpecificFormatter() throws Exception { @Test public void testBindLocalDateArray() { MutablePropertyValues propertyValues = new MutablePropertyValues(); - propertyValues.add("localDate", new String[]{"10/31/09"}); + propertyValues.add("localDate", new String[] {"10/31/09"}); binder.bind(propertyValues); assertEquals(0, binder.getBindingResult().getErrorCount()); } diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/ObjectToObjectConverter.java b/spring-core/src/main/java/org/springframework/core/convert/support/ObjectToObjectConverter.java index b3183c6e17f..5631f2ca6e1 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/support/ObjectToObjectConverter.java +++ b/spring-core/src/main/java/org/springframework/core/convert/support/ObjectToObjectConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -185,9 +185,6 @@ private static Method determineFactoryMethod(Class targetClass, Class sour method = ClassUtils.getStaticMethod(targetClass, "of", sourceClass); if (method == null) { method = ClassUtils.getStaticMethod(targetClass, "from", sourceClass); - if (method == null) { - return null; - } } } return method; diff --git a/spring-core/src/main/java/org/springframework/core/env/ConfigurablePropertyResolver.java b/spring-core/src/main/java/org/springframework/core/env/ConfigurablePropertyResolver.java index e1ec8a2732d..bc626d64310 100644 --- a/spring-core/src/main/java/org/springframework/core/env/ConfigurablePropertyResolver.java +++ b/spring-core/src/main/java/org/springframework/core/env/ConfigurablePropertyResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -19,10 +19,10 @@ import org.springframework.core.convert.support.ConfigurableConversionService; /** - * Configuration interface to be implemented by most if not all {@link PropertyResolver - * PropertyResolver} types. Provides facilities for accessing and customizing the - * {@link org.springframework.core.convert.ConversionService ConversionService} used when - * converting property values from one type to another. + * Configuration interface to be implemented by most if not all {@link PropertyResolver} + * types. Provides facilities for accessing and customizing the + * {@link org.springframework.core.convert.ConversionService ConversionService} + * used when converting property values from one type to another. * * @author Chris Beams * @since 3.1 @@ -30,7 +30,7 @@ public interface ConfigurablePropertyResolver extends PropertyResolver { /** - * @return the {@link ConfigurableConversionService} used when performing type + * Return the {@link ConfigurableConversionService} used when performing type * conversions on properties. *

The configurable nature of the returned conversion service allows for * the convenient addition and removal of individual {@code Converter} instances: @@ -46,10 +46,10 @@ public interface ConfigurablePropertyResolver extends PropertyResolver { /** * Set the {@link ConfigurableConversionService} to be used when performing type * conversions on properties. - *

Note: as an alternative to fully replacing the {@code - * ConversionService}, consider adding or removing individual {@code Converter} - * instances by drilling into {@link #getConversionService()} and calling methods - * such as {@code #addConverter}. + *

Note: as an alternative to fully replacing the + * {@code ConversionService}, consider adding or removing individual + * {@code Converter} instances by drilling into {@link #getConversionService()} + * and calling methods such as {@code #addConverter}. * @see PropertyResolver#getProperty(String, Class) * @see #getConversionService() * @see org.springframework.core.convert.converter.ConverterRegistry#addConverter diff --git a/spring-core/src/main/java/org/springframework/util/StopWatch.java b/spring-core/src/main/java/org/springframework/util/StopWatch.java index 1ba39880035..b4701164360 100644 --- a/spring-core/src/main/java/org/springframework/util/StopWatch.java +++ b/spring-core/src/main/java/org/springframework/util/StopWatch.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -134,7 +134,7 @@ public void start(String taskName) throws IllegalStateException { /** * Stop the current task. The results are undefined if timing * methods are called without invoking at least one pair - * {@code #start()} / {@code #stop()} methods. + * {@code start()} / {@code stop()} methods. * @see #start() */ public void stop() throws IllegalStateException { diff --git a/spring-core/src/test/java/org/springframework/core/SerializableTypeWrapperTests.java b/spring-core/src/test/java/org/springframework/core/SerializableTypeWrapperTests.java index ac68dacec6f..9cb3d539286 100644 --- a/spring-core/src/test/java/org/springframework/core/SerializableTypeWrapperTests.java +++ b/spring-core/src/test/java/org/springframework/core/SerializableTypeWrapperTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 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. @@ -46,7 +46,7 @@ public class SerializableTypeWrapperTests { public void forField() throws Exception { Type type = SerializableTypeWrapper.forField(Fields.class.getField("parameterizedType")); assertThat(type.toString(), equalTo("java.util.List")); - assertSerialzable(type); + assertSerializable(type); } @Test @@ -54,7 +54,7 @@ public void forMethodParameter() throws Exception { Method method = Methods.class.getDeclaredMethod("method", Class.class, Object.class); Type type = SerializableTypeWrapper.forMethodParameter(MethodParameter.forMethodOrConstructor(method, 0)); assertThat(type.toString(), equalTo("java.lang.Class")); - assertSerialzable(type); + assertSerializable(type); } @Test @@ -62,62 +62,62 @@ public void forConstructor() throws Exception { Constructor constructor = Constructors.class.getDeclaredConstructor(List.class); Type type = SerializableTypeWrapper.forMethodParameter(MethodParameter.forMethodOrConstructor(constructor, 0)); assertThat(type.toString(), equalTo("java.util.List")); - assertSerialzable(type); + assertSerializable(type); } @Test public void forGenericSuperClass() throws Exception { Type type = SerializableTypeWrapper.forGenericSuperclass(ArrayList.class); assertThat(type.toString(), equalTo("java.util.AbstractList")); - assertSerialzable(type); + assertSerializable(type); } @Test public void forGenericInterfaces() throws Exception { Type type = SerializableTypeWrapper.forGenericInterfaces(List.class)[0]; assertThat(type.toString(), equalTo("java.util.Collection")); - assertSerialzable(type); + assertSerializable(type); } @Test public void forTypeParamters() throws Exception { Type type = SerializableTypeWrapper.forTypeParameters(List.class)[0]; assertThat(type.toString(), equalTo("E")); - assertSerialzable(type); + assertSerializable(type); } @Test public void classType() throws Exception { Type type = SerializableTypeWrapper.forField(Fields.class.getField("classType")); assertThat(type.toString(), equalTo("class java.lang.String")); - assertSerialzable(type); + assertSerializable(type); } @Test public void genericArrayType() throws Exception { GenericArrayType type = (GenericArrayType) SerializableTypeWrapper.forField(Fields.class.getField("genericArrayType")); assertThat(type.toString(), equalTo("java.util.List[]")); - assertSerialzable(type); - assertSerialzable(type.getGenericComponentType()); + assertSerializable(type); + assertSerializable(type.getGenericComponentType()); } @Test public void parameterizedType() throws Exception { ParameterizedType type = (ParameterizedType) SerializableTypeWrapper.forField(Fields.class.getField("parameterizedType")); assertThat(type.toString(), equalTo("java.util.List")); - assertSerialzable(type); - assertSerialzable(type.getOwnerType()); - assertSerialzable(type.getRawType()); - assertSerialzable(type.getActualTypeArguments()); - assertSerialzable(type.getActualTypeArguments()[0]); + assertSerializable(type); + assertSerializable(type.getOwnerType()); + assertSerializable(type.getRawType()); + assertSerializable(type.getActualTypeArguments()); + assertSerializable(type.getActualTypeArguments()[0]); } @Test public void typeVariableType() throws Exception { TypeVariable type = (TypeVariable) SerializableTypeWrapper.forField(Fields.class.getField("typeVariableType")); assertThat(type.toString(), equalTo("T")); - assertSerialzable(type); - assertSerialzable(type.getBounds()); + assertSerializable(type); + assertSerializable(type.getBounds()); } @Test @@ -125,13 +125,13 @@ public void wildcardType() throws Exception { ParameterizedType typeSource = (ParameterizedType) SerializableTypeWrapper.forField(Fields.class.getField("wildcardType")); WildcardType type = (WildcardType) typeSource.getActualTypeArguments()[0]; assertThat(type.toString(), equalTo("? extends java.lang.CharSequence")); - assertSerialzable(type); - assertSerialzable(type.getLowerBounds()); - assertSerialzable(type.getUpperBounds()); + assertSerializable(type); + assertSerializable(type.getLowerBounds()); + assertSerializable(type.getUpperBounds()); } - private void assertSerialzable(Object source) throws Exception { + private void assertSerializable(Object source) throws Exception { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(source); @@ -152,19 +152,19 @@ static class Fields { public T typeVariableType; public List wildcardType; - } - static interface Methods { - List method(Class p1, T p2); + interface Methods { + List method(Class p1, T p2); } + static class Constructors { public Constructors(List p) { } - } + } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/AbstractRequestAttributesArgumentResolverTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/AbstractRequestAttributesArgumentResolverTests.java index 3369883e7fe..cc2dc56e8c9 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/AbstractRequestAttributesArgumentResolverTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/AbstractRequestAttributesArgumentResolverTests.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.web.servlet.mvc.method.annotation; import java.lang.reflect.Method; @@ -40,14 +41,8 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.BDDMockito.given; +import static org.junit.Assert.*; +import static org.mockito.BDDMockito.*; import static org.mockito.Mockito.mock; /** @@ -87,7 +82,7 @@ public void setUp() throws Exception { @Test public void supportsParameter() throws Exception { assertTrue(this.resolver.supportsParameter(new MethodParameter(this.handleMethod, 0))); - assertFalse(this.resolver.supportsParameter(new MethodParameter(this.handleMethod, 4))); + assertFalse(this.resolver.supportsParameter(new MethodParameter(this.handleMethod, -1))); } @Test @@ -180,6 +175,8 @@ private void handleWithSessionAttribute( @SessionAttribute(name="foo") Optional optionalFoo) { } + private static class Foo { } + } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestAttributeMethodArgumentResolverTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestAttributeMethodArgumentResolverTests.java index 90f875b26b1..5ef464d8c9e 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestAttributeMethodArgumentResolverTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestAttributeMethodArgumentResolverTests.java @@ -13,19 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.web.servlet.mvc.method.annotation; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.method.support.HandlerMethodArgumentResolver; - /** * Unit tests for {@link RequestAttributeMethodArgumentResolver}. + * * @author Rossen Stoyanchev + * @since 4.3 */ -public class RequestAttributeMethodArgumentResolverTests - extends AbstractRequestAttributesArgumentResolverTests { - +public class RequestAttributeMethodArgumentResolverTests extends AbstractRequestAttributesArgumentResolverTests { @Override protected HandlerMethodArgumentResolver createResolver() { diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/SessionAttributeMethodArgumentResolverTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/SessionAttributeMethodArgumentResolverTests.java index 5a4f76f9abd..20cd57e0d5a 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/SessionAttributeMethodArgumentResolverTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/SessionAttributeMethodArgumentResolverTests.java @@ -13,19 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.web.servlet.mvc.method.annotation; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.method.support.HandlerMethodArgumentResolver; - /** * Unit tests for {@link SessionAttributeMethodArgumentResolver}. + * * @author Rossen Stoyanchev + * @since 4.3 */ -public class SessionAttributeMethodArgumentResolverTests - extends AbstractRequestAttributesArgumentResolverTests { - +public class SessionAttributeMethodArgumentResolverTests extends AbstractRequestAttributesArgumentResolverTests { @Override protected HandlerMethodArgumentResolver createResolver() { From a1b58ee60140396ccae5277375d8844831a60ac1 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 8 Jul 2016 12:20:49 +0200 Subject: [PATCH 0017/1274] DigestUtils processes InputStream with buffered read instead of full copy Issue: SPR-14427 (cherry picked from commit 61db8e9) --- .../org/springframework/util/DigestUtils.java | 21 +++++++----- .../util/DigestUtilsTests.java | 32 +++++++++++++++---- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/util/DigestUtils.java b/spring-core/src/main/java/org/springframework/util/DigestUtils.java index 6251c7aa968..36b1a583c8a 100644 --- a/spring-core/src/main/java/org/springframework/util/DigestUtils.java +++ b/spring-core/src/main/java/org/springframework/util/DigestUtils.java @@ -23,11 +23,13 @@ /** * Miscellaneous methods for calculating digests. + * *

Mainly for internal use within the framework; consider * Apache Commons Codec * for a more comprehensive suite of digest utilities. * * @author Arjen Poutsma + * @author Juergen Hoeller * @author Craig Andrews * @since 3.0 */ @@ -49,8 +51,8 @@ public static byte[] md5Digest(byte[] bytes) { } /** - * Calculate the MD5 digest of the given InputStream. - * @param inputStream the inputStream to calculate the digest over + * Calculate the MD5 digest of the given stream. + * @param inputStream the InputStream to calculate the digest over * @return the digest * @since 4.2 */ @@ -59,8 +61,7 @@ public static byte[] md5Digest(InputStream inputStream) throws IOException { } /** - * Return a hexadecimal string representation of the MD5 digest of the given - * bytes. + * Return a hexadecimal string representation of the MD5 digest of the given bytes. * @param bytes the bytes to calculate the digest over * @return a hexadecimal digest string */ @@ -69,9 +70,8 @@ public static String md5DigestAsHex(byte[] bytes) { } /** - * Return a hexadecimal string representation of the MD5 digest of the given - * inputStream. - * @param inputStream the inputStream to calculate the digest over + * Return a hexadecimal string representation of the MD5 digest of the given stream. + * @param inputStream the InputStream to calculate the digest over * @return a hexadecimal digest string * @since 4.2 */ @@ -127,7 +127,12 @@ private static byte[] digest(String algorithm, InputStream inputStream) throws I return messageDigest.digest(); } else { - return messageDigest.digest(StreamUtils.copyToByteArray(inputStream)); + final byte[] buffer = new byte[StreamUtils.BUFFER_SIZE]; + int bytesRead = -1; + while ((bytesRead = inputStream.read(buffer)) != -1) { + messageDigest.update(buffer, 0, bytesRead); + } + return messageDigest.digest(); } } diff --git a/spring-core/src/test/java/org/springframework/util/DigestUtilsTests.java b/spring-core/src/test/java/org/springframework/util/DigestUtilsTests.java index 6842bfee1d1..5db3ea51b3a 100644 --- a/spring-core/src/test/java/org/springframework/util/DigestUtilsTests.java +++ b/spring-core/src/test/java/org/springframework/util/DigestUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,6 +16,8 @@ package org.springframework.util; +import java.io.ByteArrayInputStream; +import java.io.IOException; import java.io.UnsupportedEncodingException; import org.junit.Before; @@ -25,6 +27,7 @@ /** * @author Arjen Poutsma + * @author Juergen Hoeller */ public class DigestUtilsTests { @@ -38,24 +41,39 @@ public void createBytes() throws UnsupportedEncodingException { @Test - public void md5() { - byte[] result = DigestUtils.md5Digest(bytes); + public void md5() throws IOException { byte[] expected = new byte[] {-0x4f, 0xa, -0x73, -0x4f, 0x64, -0x20, 0x75, 0x41, 0x5, -0x49, -0x57, -0x65, -0x19, 0x2e, 0x3f, -0x1b}; + + byte[] result = DigestUtils.md5Digest(bytes); + assertArrayEquals("Invalid hash", expected, result); + + result = DigestUtils.md5Digest(new ByteArrayInputStream(bytes)); assertArrayEquals("Invalid hash", expected, result); } @Test - public void md5Hex() throws UnsupportedEncodingException { + public void md5Hex() throws IOException { + String expected = "b10a8db164e0754105b7a99be72e3fe5"; + String hash = DigestUtils.md5DigestAsHex(bytes); - assertEquals("Invalid hash", "b10a8db164e0754105b7a99be72e3fe5", hash); + assertEquals("Invalid hash", expected, hash); + + hash = DigestUtils.md5DigestAsHex(new ByteArrayInputStream(bytes)); + assertEquals("Invalid hash", expected, hash); } @Test - public void md5StringBuilder() throws UnsupportedEncodingException { + public void md5StringBuilder() throws IOException { + String expected = "b10a8db164e0754105b7a99be72e3fe5"; + StringBuilder builder = new StringBuilder(); DigestUtils.appendMd5DigestAsHex(bytes, builder); - assertEquals("Invalid hash", "b10a8db164e0754105b7a99be72e3fe5", builder.toString()); + assertEquals("Invalid hash", expected, builder.toString()); + + builder = new StringBuilder(); + DigestUtils.appendMd5DigestAsHex(new ByteArrayInputStream(bytes), builder); + assertEquals("Invalid hash", expected, builder.toString()); } } From f85d48dd31f4c8df7eb7db4acfc4985f219a051c Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 8 Jul 2016 14:58:38 +0200 Subject: [PATCH 0018/1274] Added PathEditor for NIO file system resolution Issue: SPR-14436 (cherry picked from commit 23c2b6a) --- .../beans/PropertyEditorRegistrySupport.java | 16 ++- .../beans/propertyeditors/FileEditor.java | 10 +- .../propertyeditors/InputStreamEditor.java | 8 +- .../beans/propertyeditors/PathEditor.java | 116 ++++++++++++++++++ .../beans/propertyeditors/ReaderEditor.java | 8 +- .../beans/propertyeditors/URIEditor.java | 9 +- .../beans/propertyeditors/URLEditor.java | 4 +- .../support/ResourceEditorRegistrar.java | 20 ++- .../propertyeditors/FileEditorTests.java | 19 +-- .../InputStreamEditorTests.java | 5 +- .../propertyeditors/PathEditorTests.java | 80 ++++++++++++ .../propertyeditors/ReaderEditorTests.java | 4 +- .../beans/propertyeditors/URIEditorTests.java | 21 ++-- .../beans/propertyeditors/URLEditorTests.java | 16 +-- 14 files changed, 278 insertions(+), 58 deletions(-) create mode 100644 spring-beans/src/main/java/org/springframework/beans/propertyeditors/PathEditor.java create mode 100644 spring-beans/src/test/java/org/springframework/beans/propertyeditors/PathEditorTests.java diff --git a/spring-beans/src/main/java/org/springframework/beans/PropertyEditorRegistrySupport.java b/spring-beans/src/main/java/org/springframework/beans/PropertyEditorRegistrySupport.java index f42d6c332ec..71825c38cff 100644 --- a/spring-beans/src/main/java/org/springframework/beans/PropertyEditorRegistrySupport.java +++ b/spring-beans/src/main/java/org/springframework/beans/PropertyEditorRegistrySupport.java @@ -59,6 +59,7 @@ import org.springframework.beans.propertyeditors.InputSourceEditor; import org.springframework.beans.propertyeditors.InputStreamEditor; import org.springframework.beans.propertyeditors.LocaleEditor; +import org.springframework.beans.propertyeditors.PathEditor; import org.springframework.beans.propertyeditors.PatternEditor; import org.springframework.beans.propertyeditors.PropertiesEditor; import org.springframework.beans.propertyeditors.ReaderEditor; @@ -87,11 +88,21 @@ */ public class PropertyEditorRegistrySupport implements PropertyEditorRegistry { + private static Class pathClass; + private static Class zoneIdClass; static { + ClassLoader cl = PropertyEditorRegistrySupport.class.getClassLoader(); + try { + pathClass = ClassUtils.forName("java.nio.file.Path", cl); + } + catch (ClassNotFoundException ex) { + // Java 7 Path class not available + pathClass = null; + } try { - zoneIdClass = ClassUtils.forName("java.time.ZoneId", PropertyEditorRegistrySupport.class.getClassLoader()); + zoneIdClass = ClassUtils.forName("java.time.ZoneId", cl); } catch (ClassNotFoundException ex) { // Java 8 ZoneId class not available @@ -211,6 +222,9 @@ private void createDefaultEditors() { this.defaultEditors.put(InputStream.class, new InputStreamEditor()); this.defaultEditors.put(InputSource.class, new InputSourceEditor()); this.defaultEditors.put(Locale.class, new LocaleEditor()); + if (pathClass != null) { + this.defaultEditors.put(pathClass, new PathEditor()); + } this.defaultEditors.put(Pattern.class, new PatternEditor()); this.defaultEditors.put(Properties.class, new PropertiesEditor()); this.defaultEditors.put(Reader.class, new ReaderEditor()); diff --git a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/FileEditor.java b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/FileEditor.java index a57b69eaede..676fd0c6434 100644 --- a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/FileEditor.java +++ b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/FileEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -59,16 +59,14 @@ public class FileEditor extends PropertyEditorSupport { /** - * Create a new FileEditor, - * using the default ResourceEditor underneath. + * Create a new FileEditor, using a default ResourceEditor underneath. */ public FileEditor() { this.resourceEditor = new ResourceEditor(); } /** - * Create a new FileEditor, - * using the given ResourceEditor underneath. + * Create a new FileEditor, using the given ResourceEditor underneath. * @param resourceEditor the ResourceEditor to use */ public FileEditor(ResourceEditor resourceEditor) { @@ -105,7 +103,7 @@ public void setAsText(String text) throws IllegalArgumentException { } catch (IOException ex) { throw new IllegalArgumentException( - "Could not retrieve File for " + resource + ": " + ex.getMessage()); + "Could not retrieve file for " + resource + ": " + ex.getMessage()); } } else { diff --git a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/InputStreamEditor.java b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/InputStreamEditor.java index b4b014b735b..dc9823935aa 100644 --- a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/InputStreamEditor.java +++ b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/InputStreamEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,16 +47,14 @@ public class InputStreamEditor extends PropertyEditorSupport { /** - * Create a new InputStreamEditor, - * using the default ResourceEditor underneath. + * Create a new InputStreamEditor, using the default ResourceEditor underneath. */ public InputStreamEditor() { this.resourceEditor = new ResourceEditor(); } /** - * Create a new InputStreamEditor, - * using the given ResourceEditor underneath. + * Create a new InputStreamEditor, using the given ResourceEditor underneath. * @param resourceEditor the ResourceEditor to use */ public InputStreamEditor(ResourceEditor resourceEditor) { diff --git a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/PathEditor.java b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/PathEditor.java new file mode 100644 index 00000000000..4767b198548 --- /dev/null +++ b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/PathEditor.java @@ -0,0 +1,116 @@ +/* + * Copyright 2002-2016 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.beans.propertyeditors; + +import java.beans.PropertyEditorSupport; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.FileSystemNotFoundException; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceEditor; +import org.springframework.core.io.ResourceLoader; +import org.springframework.lang.UsesJava7; +import org.springframework.util.Assert; + +/** + * Editor for {@code java.nio.file.Path}, to directly populate a Path + * property instead of using a String property as bridge. + * + *

Based on {@link Paths#get(URI)}'s resolution algorithm, checking + * registered NIO file system providers, including the default file system + * for "file:..." paths. Also supports Spring-style URL notation: any fully + * qualified standard URL and Spring's special "classpath:" pseudo-URL, + * as well as Spring's context-specific relative file paths. + * + *

Note that, in contrast to {@link FileEditor}, relative paths are only + * supported by Spring's resource abstraction here. Direct {@code Paths.get} + * resolution in a file system always has to go through the corresponding + * file system provider's scheme, i.e. "file" for the default file system. + * + * @author Juergen Hoeller + * @since 4.3.2 + * @see java.nio.file.Path + * @see Paths#get(URI) + * @see ResourceEditor + * @see org.springframework.core.io.ResourceLoader + * @see FileEditor + * @see URLEditor + */ +@UsesJava7 +public class PathEditor extends PropertyEditorSupport { + + private final ResourceEditor resourceEditor; + + + /** + * Create a new PathEditor, using the default ResourceEditor underneath. + */ + public PathEditor() { + this.resourceEditor = new ResourceEditor(); + } + + /** + * Create a new PathEditor, using the given ResourceEditor underneath. + * @param resourceEditor the ResourceEditor to use + */ + public PathEditor(ResourceEditor resourceEditor) { + Assert.notNull(resourceEditor, "ResourceEditor must not be null"); + this.resourceEditor = resourceEditor; + } + + + @Override + public void setAsText(String text) throws IllegalArgumentException { + if (!text.startsWith("/") && !text.startsWith(ResourceLoader.CLASSPATH_URL_PREFIX)) { + try { + URI uri = new URI(text); + if (uri.getScheme() != null) { + // Let's try NIO file system providers via Paths.get(URI) + setValue(Paths.get(uri).normalize()); + return; + } + } + catch (URISyntaxException ex) { + // Not a valid URI: Let's try as Spring resource location. + } + catch (FileSystemNotFoundException ex) { + // URI scheme not registered for NIO: + // Let's try URL protocol handlers via Spring's resource mechanism. + } + } + + this.resourceEditor.setAsText(text); + Resource resource = (Resource) this.resourceEditor.getValue(); + try { + setValue(resource != null ? resource.getFile().toPath() : null); + } + catch (IOException ex) { + throw new IllegalArgumentException("Failed to retrieve file for " + resource, ex); + } + } + + @Override + public String getAsText() { + Path value = (Path) getValue(); + return (value != null ? value.toString() : ""); + } + +} diff --git a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/ReaderEditor.java b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/ReaderEditor.java index 826760eef04..894d97709ee 100644 --- a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/ReaderEditor.java +++ b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/ReaderEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,16 +47,14 @@ public class ReaderEditor extends PropertyEditorSupport { /** - * Create a new ReaderEditor, - * using the default ResourceEditor underneath. + * Create a new ReaderEditor, using the default ResourceEditor underneath. */ public ReaderEditor() { this.resourceEditor = new ResourceEditor(); } /** - * Create a new ReaderEditor, - * using the given ResourceEditor underneath. + * Create a new ReaderEditor, using the given ResourceEditor underneath. * @param resourceEditor the ResourceEditor to use */ public ReaderEditor(ResourceEditor resourceEditor) { diff --git a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/URIEditor.java b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/URIEditor.java index f5ac6d6a487..7dc173a0d26 100644 --- a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/URIEditor.java +++ b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/URIEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -60,8 +60,7 @@ public class URIEditor extends PropertyEditorSupport { * standard URIs (not trying to resolve them into physical resources). */ public URIEditor() { - this.classLoader = null; - this.encode = true; + this(true); } /** @@ -74,7 +73,6 @@ public URIEditor(boolean encode) { this.encode = encode; } - /** * Create a new URIEditor, using the given ClassLoader to resolve * "classpath:" locations into physical resource URLs. @@ -82,8 +80,7 @@ public URIEditor(boolean encode) { * (may be {@code null} to indicate the default ClassLoader) */ public URIEditor(ClassLoader classLoader) { - this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader()); - this.encode = true; + this(classLoader, true); } /** diff --git a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/URLEditor.java b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/URLEditor.java index c5f9755d85a..85ef824d12f 100644 --- a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/URLEditor.java +++ b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/URLEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -50,7 +50,7 @@ public class URLEditor extends PropertyEditorSupport { /** - * Create a new URLEditor, using the default ResourceEditor underneath. + * Create a new URLEditor, using a default ResourceEditor underneath. */ public URLEditor() { this.resourceEditor = new ResourceEditor(); diff --git a/spring-beans/src/main/java/org/springframework/beans/support/ResourceEditorRegistrar.java b/spring-beans/src/main/java/org/springframework/beans/support/ResourceEditorRegistrar.java index 06d2ee5d688..8cffcc782c7 100644 --- a/spring-beans/src/main/java/org/springframework/beans/support/ResourceEditorRegistrar.java +++ b/spring-beans/src/main/java/org/springframework/beans/support/ResourceEditorRegistrar.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -33,6 +33,7 @@ import org.springframework.beans.propertyeditors.FileEditor; import org.springframework.beans.propertyeditors.InputSourceEditor; import org.springframework.beans.propertyeditors.InputStreamEditor; +import org.springframework.beans.propertyeditors.PathEditor; import org.springframework.beans.propertyeditors.ReaderEditor; import org.springframework.beans.propertyeditors.URIEditor; import org.springframework.beans.propertyeditors.URLEditor; @@ -43,6 +44,7 @@ import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.support.ResourceArrayPropertyEditor; import org.springframework.core.io.support.ResourcePatternResolver; +import org.springframework.util.ClassUtils; /** * PropertyEditorRegistrar implementation that populates a given @@ -58,6 +60,19 @@ */ public class ResourceEditorRegistrar implements PropertyEditorRegistrar { + private static Class pathClass; + + static { + try { + pathClass = ClassUtils.forName("java.nio.file.Path", ResourceEditorRegistrar.class.getClassLoader()); + } + catch (ClassNotFoundException ex) { + // Java 7 Path class not available + pathClass = null; + } + } + + private final PropertyResolver propertyResolver; private final ResourceLoader resourceLoader; @@ -103,6 +118,9 @@ public void registerCustomEditors(PropertyEditorRegistry registry) { doRegisterEditor(registry, InputStream.class, new InputStreamEditor(baseEditor)); doRegisterEditor(registry, InputSource.class, new InputSourceEditor(baseEditor)); doRegisterEditor(registry, File.class, new FileEditor(baseEditor)); + if (pathClass != null) { + doRegisterEditor(registry, pathClass, new PathEditor(baseEditor)); + } doRegisterEditor(registry, Reader.class, new ReaderEditor(baseEditor)); doRegisterEditor(registry, URL.class, new URLEditor(baseEditor)); diff --git a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/FileEditorTests.java b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/FileEditorTests.java index 17f6d984034..ae6a5c6838a 100644 --- a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/FileEditorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/FileEditorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2016 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,20 +29,20 @@ * @author Thomas Risberg * @author Chris Beams */ -public final class FileEditorTests { +public class FileEditorTests { @Test public void testClasspathFileName() throws Exception { PropertyEditor fileEditor = new FileEditor(); - fileEditor.setAsText("classpath:" + ClassUtils.classPackageAsResourcePath(getClass()) + "/" - + ClassUtils.getShortName(getClass()) + ".class"); + fileEditor.setAsText("classpath:" + ClassUtils.classPackageAsResourcePath(getClass()) + "/" + + ClassUtils.getShortName(getClass()) + ".class"); Object value = fileEditor.getValue(); assertTrue(value instanceof File); File file = (File) value; assertTrue(file.exists()); } - @Test(expected=IllegalArgumentException.class) + @Test(expected = IllegalArgumentException.class) public void testWithNonExistentResource() throws Exception { PropertyEditor propertyEditor = new FileEditor(); propertyEditor.setAsText("classpath:no_way_this_file_is_found.doc"); @@ -71,8 +71,8 @@ public void testAbsoluteFileName() throws Exception { @Test public void testUnqualifiedFileNameFound() throws Exception { PropertyEditor fileEditor = new FileEditor(); - String fileName = ClassUtils.classPackageAsResourcePath(getClass()) + "/" + ClassUtils.getShortName(getClass()) - + ".class"; + String fileName = ClassUtils.classPackageAsResourcePath(getClass()) + "/" + + ClassUtils.getShortName(getClass()) + ".class"; fileEditor.setAsText(fileName); Object value = fileEditor.getValue(); assertTrue(value instanceof File); @@ -88,8 +88,8 @@ public void testUnqualifiedFileNameFound() throws Exception { @Test public void testUnqualifiedFileNameNotFound() throws Exception { PropertyEditor fileEditor = new FileEditor(); - String fileName = ClassUtils.classPackageAsResourcePath(getClass()) + "/" + ClassUtils.getShortName(getClass()) - + ".clazz"; + String fileName = ClassUtils.classPackageAsResourcePath(getClass()) + "/" + + ClassUtils.getShortName(getClass()) + ".clazz"; fileEditor.setAsText(fileName); Object value = fileEditor.getValue(); assertTrue(value instanceof File); @@ -101,4 +101,5 @@ public void testUnqualifiedFileNameNotFound() throws Exception { } assertTrue(absolutePath.endsWith(fileName)); } + } diff --git a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/InputStreamEditorTests.java b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/InputStreamEditorTests.java index 5073e5d5570..6e90e92a53f 100644 --- a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/InputStreamEditorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/InputStreamEditorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -60,9 +60,8 @@ public void testSunnyDay() throws Exception { @Test(expected = IllegalArgumentException.class) public void testWhenResourceDoesNotExist() throws Exception { - String resource = "classpath:bingo!"; InputStreamEditor editor = new InputStreamEditor(); - editor.setAsText(resource); + editor.setAsText("classpath:bingo!"); } @Test diff --git a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/PathEditorTests.java b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/PathEditorTests.java new file mode 100644 index 00000000000..440a3e38c73 --- /dev/null +++ b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/PathEditorTests.java @@ -0,0 +1,80 @@ +/* + * Copyright 2002-2016 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.beans.propertyeditors; + +import java.beans.PropertyEditor; +import java.io.File; +import java.nio.file.Path; + +import org.junit.Test; + +import org.springframework.util.ClassUtils; + +import static org.junit.Assert.*; + +/** + * @author Juergen Hoeller + * @since 4.3.2 + */ +public class PathEditorTests { + + @Test + public void testClasspathPathName() throws Exception { + PropertyEditor pathEditor = new PathEditor(); + pathEditor.setAsText("classpath:" + ClassUtils.classPackageAsResourcePath(getClass()) + "/" + + ClassUtils.getShortName(getClass()) + ".class"); + Object value = pathEditor.getValue(); + assertTrue(value instanceof Path); + Path path = (Path) value; + assertTrue(path.toFile().exists()); + } + + @Test(expected = IllegalArgumentException.class) + public void testWithNonExistentResource() throws Exception { + PropertyEditor propertyEditor = new PathEditor(); + propertyEditor.setAsText("classpath:/no_way_this_file_is_found.doc"); + } + + @Test + public void testWithNonExistentPath() throws Exception { + PropertyEditor pathEditor = new PathEditor(); + pathEditor.setAsText("file:/no_way_this_file_is_found.doc"); + Object value = pathEditor.getValue(); + assertTrue(value instanceof Path); + Path path = (Path) value; + assertTrue(!path.toFile().exists()); + } + + @Test + public void testUnqualifiedPathNameFound() throws Exception { + PropertyEditor pathEditor = new PathEditor(); + String fileName = ClassUtils.classPackageAsResourcePath(getClass()) + "/" + + ClassUtils.getShortName(getClass()) + ".class"; + pathEditor.setAsText(fileName); + Object value = pathEditor.getValue(); + assertTrue(value instanceof Path); + Path path = (Path) value; + File file = path.toFile(); + assertTrue(file.exists()); + String absolutePath = file.getAbsolutePath(); + if (File.separatorChar == '\\') { + absolutePath = absolutePath.replace('\\', '/'); + } + assertTrue(absolutePath.endsWith(fileName)); + } + +} diff --git a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/ReaderEditorTests.java b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/ReaderEditorTests.java index 2729922ad9c..d4e19b8bae8 100644 --- a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/ReaderEditorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/ReaderEditorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,7 +34,7 @@ public class ReaderEditorTests { @Test(expected = IllegalArgumentException.class) public void testCtorWithNullResourceEditor() throws Exception { - new InputStreamEditor(null); + new ReaderEditor(null); } @Test diff --git a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/URIEditorTests.java b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/URIEditorTests.java index a347cfa86d3..4eb6245dd4c 100644 --- a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/URIEditorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/URIEditorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2016 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,15 +31,6 @@ */ public class URIEditorTests { - private void doTestURI(String uriSpec) { - PropertyEditor uriEditor = new URIEditor(); - uriEditor.setAsText(uriSpec); - Object value = uriEditor.getValue(); - assertTrue(value instanceof URI); - URI uri = (URI) value; - assertEquals(uriSpec, uri.toString()); - } - @Test public void standardURI() throws Exception { doTestURI("mailto:juergen.hoeller@interface21.com"); @@ -141,4 +132,14 @@ public void encodeAlreadyEncodedURI() throws Exception { assertEquals("http://example.com/spaces%20and%20%E2%82%AC", uri.toASCIIString()); } + + private void doTestURI(String uriSpec) { + PropertyEditor uriEditor = new URIEditor(); + uriEditor.setAsText(uriSpec); + Object value = uriEditor.getValue(); + assertTrue(value instanceof URI); + URI uri = (URI) value; + assertEquals(uriSpec, uri.toString()); + } + } diff --git a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/URLEditorTests.java b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/URLEditorTests.java index 36738e1b212..3259ec47d66 100644 --- a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/URLEditorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/URLEditorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2006 the original author or authors. + * Copyright 2002-2016 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,7 +29,12 @@ * @author Rick Evans * @author Chris Beams */ -public final class URLEditorTests { +public class URLEditorTests { + + @Test(expected = IllegalArgumentException.class) + public void testCtorWithNullResourceEditor() throws Exception { + new URLEditor(null); + } @Test public void testStandardURI() throws Exception { @@ -63,7 +68,7 @@ public void testClasspathURL() throws Exception { assertTrue(!url.getProtocol().startsWith("classpath")); } - @Test(expected=IllegalArgumentException.class) + @Test(expected = IllegalArgumentException.class) public void testWithNonExistentResource() throws Exception { PropertyEditor urlEditor = new URLEditor(); urlEditor.setAsText("gonna:/freak/in/the/morning/freak/in/the.evening"); @@ -83,9 +88,4 @@ public void testGetAsTextReturnsEmptyStringIfValueNotSet() throws Exception { assertEquals("", urlEditor.getAsText()); } - @Test(expected=IllegalArgumentException.class) - public void testCtorWithNullResourceEditor() throws Exception { - new URLEditor(null); - } - } From be0b71ce31d79d45c630bb21cbdb63021ffa3c16 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 8 Jul 2016 15:12:16 +0200 Subject: [PATCH 0019/1274] Fixed typo: "occured"->"occurred" (cherry picked from commit c43e749) --- .../beans/AbstractPropertyAccessor.java | 2 +- .../springframework/beans/PropertyAccessor.java | 14 +++++++------- .../beans/factory/BeanCreationException.java | 2 +- .../beans/factory/config/AbstractFactoryBean.java | 4 ++-- .../factory/config/PropertiesFactoryBean.java | 4 ++-- .../context/annotation/AutoProxyRegistrar.java | 4 ++-- .../jmx/support/ConnectorServerFactoryBean.java | 2 +- .../jms/UncategorizedJmsException.java | 4 ++-- .../springframework/oxm/jaxb/Jaxb2Marshaller.java | 4 ++-- .../springframework/oxm/jibx/JibxMarshaller.java | 4 ++-- 10 files changed, 22 insertions(+), 22 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/AbstractPropertyAccessor.java b/spring-beans/src/main/java/org/springframework/beans/AbstractPropertyAccessor.java index 078712179a1..216e5a4aabd 100644 --- a/spring-beans/src/main/java/org/springframework/beans/AbstractPropertyAccessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/AbstractPropertyAccessor.java @@ -148,7 +148,7 @@ public Class getPropertyType(String propertyPath) { * @throws InvalidPropertyException if there is no such property or * if the property isn't writable * @throws PropertyAccessException if the property was valid but the - * accessor method failed or a type mismatch occured + * accessor method failed or a type mismatch occurred */ @Override public abstract void setPropertyValue(String propertyName, Object value) throws BeansException; diff --git a/spring-beans/src/main/java/org/springframework/beans/PropertyAccessor.java b/spring-beans/src/main/java/org/springframework/beans/PropertyAccessor.java index 00068ea4b60..c7faf35524e 100644 --- a/spring-beans/src/main/java/org/springframework/beans/PropertyAccessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/PropertyAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -120,7 +120,7 @@ public interface PropertyAccessor { * @throws InvalidPropertyException if there is no such property or * if the property isn't writable * @throws PropertyAccessException if the property was valid but the - * accessor method failed or a type mismatch occured + * accessor method failed or a type mismatch occurred */ void setPropertyValue(String propertyName, Object value) throws BeansException; @@ -130,7 +130,7 @@ public interface PropertyAccessor { * @throws InvalidPropertyException if there is no such property or * if the property isn't writable * @throws PropertyAccessException if the property was valid but the - * accessor method failed or a type mismatch occured + * accessor method failed or a type mismatch occurred */ void setPropertyValue(PropertyValue pv) throws BeansException; @@ -144,7 +144,7 @@ public interface PropertyAccessor { * @throws InvalidPropertyException if there is no such property or * if the property isn't writable * @throws PropertyBatchUpdateException if one or more PropertyAccessExceptions - * occured for specific properties during the batch update. This exception bundles + * occurred for specific properties during the batch update. This exception bundles * all individual PropertyAccessExceptions. All other properties will have been * successfully updated. */ @@ -164,7 +164,7 @@ public interface PropertyAccessor { * @throws InvalidPropertyException if there is no such property or * if the property isn't writable * @throws PropertyBatchUpdateException if one or more PropertyAccessExceptions - * occured for specific properties during the batch update. This exception bundles + * occurred for specific properties during the batch update. This exception bundles * all individual PropertyAccessExceptions. All other properties will have been * successfully updated. * @see #setPropertyValues(PropertyValues, boolean, boolean) @@ -185,7 +185,7 @@ public interface PropertyAccessor { * @throws InvalidPropertyException if there is no such property or * if the property isn't writable * @throws PropertyBatchUpdateException if one or more PropertyAccessExceptions - * occured for specific properties during the batch update. This exception bundles + * occurred for specific properties during the batch update. This exception bundles * all individual PropertyAccessExceptions. All other properties will have been * successfully updated. * @see #setPropertyValues(PropertyValues, boolean, boolean) @@ -208,7 +208,7 @@ void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown) * @throws InvalidPropertyException if there is no such property or * if the property isn't writable * @throws PropertyBatchUpdateException if one or more PropertyAccessExceptions - * occured for specific properties during the batch update. This exception bundles + * occurred for specific properties during the batch update. This exception bundles * all individual PropertyAccessExceptions. All other properties will have been * successfully updated. */ diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/BeanCreationException.java b/spring-beans/src/main/java/org/springframework/beans/factory/BeanCreationException.java index dc60afb4622..75a38386925 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/BeanCreationException.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/BeanCreationException.java @@ -123,7 +123,7 @@ public String getResourceDescription() { /** * Add a related cause to this bean creation exception, - * not being a direct cause of the failure but having occured + * not being a direct cause of the failure but having occurred * earlier in the creation of the same bean instance. * @param ex the related cause to add */ diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/AbstractFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/AbstractFactoryBean.java index bc8a42f71b6..85faab006b0 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/AbstractFactoryBean.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/AbstractFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -208,7 +208,7 @@ public void destroy() throws Exception { *

Invoked on initialization of this FactoryBean in case of * a singleton; else, on each {@link #getObject()} call. * @return the object returned by this factory - * @throws Exception if an exception occured during object creation + * @throws Exception if an exception occurred during object creation * @see #getObject() */ protected abstract T createInstance() throws Exception; diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertiesFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertiesFactoryBean.java index 93a97a8dcf2..adc415d0d57 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertiesFactoryBean.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertiesFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 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. @@ -95,7 +95,7 @@ public Class getObjectType() { *

Invoked on initialization of this FactoryBean in case of a * shared singleton; else, on each {@link #getObject()} call. * @return the object returned by this factory - * @throws IOException if an exception occured during properties loading + * @throws IOException if an exception occurred during properties loading * @see #mergeProperties() */ protected Properties createProperties() throws IOException { diff --git a/spring-context/src/main/java/org/springframework/context/annotation/AutoProxyRegistrar.java b/spring-context/src/main/java/org/springframework/context/annotation/AutoProxyRegistrar.java index cc5c911c01e..58b5481461a 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/AutoProxyRegistrar.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/AutoProxyRegistrar.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -79,7 +79,7 @@ public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, B logger.warn(String.format("%s was imported but no annotations were found " + "having both 'mode' and 'proxyTargetClass' attributes of type " + "AdviceMode and boolean respectively. This means that auto proxy " + - "creator registration and configuration may not have occured as " + + "creator registration and configuration may not have occurred as " + "intended, and components may not be proxied as expected. Check to " + "ensure that %s has been @Import'ed on the same class where these " + "annotations are declared; otherwise remove the import of %s " + diff --git a/spring-context/src/main/java/org/springframework/jmx/support/ConnectorServerFactoryBean.java b/spring-context/src/main/java/org/springframework/jmx/support/ConnectorServerFactoryBean.java index 5a4d05a7fb0..48d8eb7ffb6 100644 --- a/spring-context/src/main/java/org/springframework/jmx/support/ConnectorServerFactoryBean.java +++ b/spring-context/src/main/java/org/springframework/jmx/support/ConnectorServerFactoryBean.java @@ -138,7 +138,7 @@ public void setDaemon(boolean daemon) { * the {@code JMXConnectorServer} will be started in a separate thread. * If the {@code daemon} flag is set to {@code true}, that thread will be * started as a daemon thread. - * @throws JMException if a problem occured when registering the connector server + * @throws JMException if a problem occurred when registering the connector server * with the {@code MBeanServer} * @throws IOException if there is a problem starting the connector server */ diff --git a/spring-jms/src/main/java/org/springframework/jms/UncategorizedJmsException.java b/spring-jms/src/main/java/org/springframework/jms/UncategorizedJmsException.java index b9531906a4f..e55ba0d61e9 100644 --- a/spring-jms/src/main/java/org/springframework/jms/UncategorizedJmsException.java +++ b/spring-jms/src/main/java/org/springframework/jms/UncategorizedJmsException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -51,7 +51,7 @@ public UncategorizedJmsException(String msg, Throwable cause) { * but can also be a JNDI NamingException or the like. */ public UncategorizedJmsException(Throwable cause) { - super("Uncategorized exception occured during JMS processing", cause); + super("Uncategorized exception occurred during JMS processing", cause); } } diff --git a/spring-oxm/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java b/spring-oxm/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java index 981db621238..e198d10bc9f 100644 --- a/spring-oxm/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java +++ b/spring-oxm/src/main/java/org/springframework/oxm/jaxb/Jaxb2Marshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -897,7 +897,7 @@ protected void initJaxbUnmarshaller(Unmarshaller unmarshaller) throws JAXBExcept /** * Convert the given {@code JAXBException} to an appropriate exception from the * {@code org.springframework.oxm} hierarchy. - * @param ex {@code JAXBException} that occured + * @param ex {@code JAXBException} that occurred * @return the corresponding {@code XmlMappingException} */ protected XmlMappingException convertJaxbException(JAXBException ex) { diff --git a/spring-oxm/src/main/java/org/springframework/oxm/jibx/JibxMarshaller.java b/spring-oxm/src/main/java/org/springframework/oxm/jibx/JibxMarshaller.java index 4815b8c3955..c481fd1b219 100644 --- a/spring-oxm/src/main/java/org/springframework/oxm/jibx/JibxMarshaller.java +++ b/spring-oxm/src/main/java/org/springframework/oxm/jibx/JibxMarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -461,7 +461,7 @@ protected IUnmarshallingContext createUnmarshallingContext() throws JiBXExceptio * {@code org.springframework.oxm} hierarchy. *

A boolean flag is used to indicate whether this exception occurs during marshalling or * unmarshalling, since JiBX itself does not make this distinction in its exception hierarchy. - * @param ex {@code JiBXException} that occured + * @param ex {@code JiBXException} that occurred * @param marshalling indicates whether the exception occurs during marshalling ({@code true}), * or unmarshalling ({@code false}) * @return the corresponding {@code XmlMappingException} From 5c3c0f73c1912602163741d6d74e610f0b33f68c Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 13 Jul 2016 15:14:42 +0200 Subject: [PATCH 0020/1274] Configurable UrlPathHelper in PathExtensionContentNegotiationStrategy This commit also aligns ResourceUrlProvider's and RequestMappingInfo's UrlPathHelper setter/getter signatures. Issue: SPR-14454 (cherry picked from commit 84afc60) --- ...thExtensionContentNegotiationStrategy.java | 34 +++++++------- .../mvc/method/RequestMappingInfo.java | 45 +++++++++++++++---- .../RequestMappingHandlerMapping.java | 2 +- .../resource/ResourceUrlEncodingFilter.java | 8 ++-- .../servlet/resource/ResourceUrlProvider.java | 26 +++++++---- 5 files changed, 80 insertions(+), 35 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/accept/PathExtensionContentNegotiationStrategy.java b/spring-web/src/main/java/org/springframework/web/accept/PathExtensionContentNegotiationStrategy.java index ac4f3059c4a..0a042f4384a 100644 --- a/spring-web/src/main/java/org/springframework/web/accept/PathExtensionContentNegotiationStrategy.java +++ b/spring-web/src/main/java/org/springframework/web/accept/PathExtensionContentNegotiationStrategy.java @@ -52,42 +52,46 @@ * @author Rossen Stoyanchev * @since 3.2 */ -public class PathExtensionContentNegotiationStrategy - extends AbstractMappingContentNegotiationStrategy { - - private static final Log logger = LogFactory.getLog(PathExtensionContentNegotiationStrategy.class); +public class PathExtensionContentNegotiationStrategy extends AbstractMappingContentNegotiationStrategy { private static final boolean JAF_PRESENT = ClassUtils.isPresent("javax.activation.FileTypeMap", PathExtensionContentNegotiationStrategy.class.getClassLoader()); - private static final UrlPathHelper PATH_HELPER = new UrlPathHelper(); - - static { - PATH_HELPER.setUrlDecode(false); - } + private static final Log logger = LogFactory.getLog(PathExtensionContentNegotiationStrategy.class); + private UrlPathHelper urlPathHelper = new UrlPathHelper(); private boolean useJaf = true; private boolean ignoreUnknownExtensions = true; + /** + * Create an instance without any mappings to start with. Mappings may be added + * later on if any extensions are resolved through the Java Activation framework. + */ + public PathExtensionContentNegotiationStrategy() { + this(null); + } + /** * Create an instance with the given map of file extensions and media types. */ public PathExtensionContentNegotiationStrategy(Map mediaTypes) { super(mediaTypes); + this.urlPathHelper.setUrlDecode(false); } + /** - * Create an instance without any mappings to start with. Mappings may be added - * later on if any extensions are resolved through the Java Activation framework. + * Configure a {@code UrlPathHelper} to use in {@link #getMediaTypeKey} + * in order to derive the lookup path for a target request URL path. + * @since 4.2.8 */ - public PathExtensionContentNegotiationStrategy() { - super(null); + public void setUrlPathHelper(UrlPathHelper urlPathHelper) { + this.urlPathHelper = urlPathHelper; } - /** * Whether to use the Java Activation Framework to look up file extensions. *

By default this is set to "true" but depends on JAF being present. @@ -113,7 +117,7 @@ protected String getMediaTypeKey(NativeWebRequest webRequest) { logger.warn("An HttpServletRequest is required to determine the media type key"); return null; } - String path = PATH_HELPER.getLookupPathForRequest(request); + String path = this.urlPathHelper.getLookupPathForRequest(request); String filename = WebUtils.extractFullFilenameFromUrlPath(path); String extension = StringUtils.getFilenameExtension(filename); return (StringUtils.hasText(extension)) ? extension.toLowerCase(Locale.ENGLISH) : null; diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfo.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfo.java index a9cf31bb3ee..6ecc7687b9b 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfo.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfo.java @@ -521,13 +521,25 @@ public static class BuilderConfiguration { private ContentNegotiationManager contentNegotiationManager; /** - * Set a custom UrlPathHelper to use for the PatternsRequestCondition. - *

By default this is not set. + * @deprecated as of Spring 4.2.8, in favor of {@link #setUrlPathHelper} */ + @Deprecated public void setPathHelper(UrlPathHelper pathHelper) { this.urlPathHelper = pathHelper; } + /** + * Set a custom UrlPathHelper to use for the PatternsRequestCondition. + *

By default this is not set. + * @since 4.2.8 + */ + public void setUrlPathHelper(UrlPathHelper urlPathHelper) { + this.urlPathHelper = urlPathHelper; + } + + /** + * Return a custom UrlPathHelper to use for the PatternsRequestCondition, if any. + */ public UrlPathHelper getUrlPathHelper() { return this.urlPathHelper; } @@ -540,24 +552,30 @@ public void setPathMatcher(PathMatcher pathMatcher) { this.pathMatcher = pathMatcher; } + /** + * Return a custom PathMatcher to use for the PatternsRequestCondition, if any. + */ public PathMatcher getPathMatcher() { return this.pathMatcher; } /** - * Whether to apply trailing slash matching in PatternsRequestCondition. + * Set whether to apply trailing slash matching in PatternsRequestCondition. *

By default this is set to 'true'. */ public void setTrailingSlashMatch(boolean trailingSlashMatch) { this.trailingSlashMatch = trailingSlashMatch; } + /** + * Return whether to apply trailing slash matching in PatternsRequestCondition. + */ public boolean useTrailingSlashMatch() { return this.trailingSlashMatch; } /** - * Whether to apply suffix pattern matching in PatternsRequestCondition. + * Set whether to apply suffix pattern matching in PatternsRequestCondition. *

By default this is set to 'true'. * @see #setRegisteredSuffixPatternMatch(boolean) */ @@ -565,14 +583,17 @@ public void setSuffixPatternMatch(boolean suffixPatternMatch) { this.suffixPatternMatch = suffixPatternMatch; } + /** + * Return whether to apply suffix pattern matching in PatternsRequestCondition. + */ public boolean useSuffixPatternMatch() { return this.suffixPatternMatch; } /** - * Whether suffix pattern matching should be restricted to registered + * Set whether suffix pattern matching should be restricted to registered * file extensions only. Setting this property also sets - * suffixPatternMatch=true and requires that a + * {@code suffixPatternMatch=true} and requires that a * {@link #setContentNegotiationManager} is also configured in order to * obtain the registered file extensions. */ @@ -581,6 +602,10 @@ public void setRegisteredSuffixPatternMatch(boolean registeredSuffixPatternMatch this.suffixPatternMatch = (registeredSuffixPatternMatch || this.suffixPatternMatch); } + /** + * Return whether suffix pattern matching should be restricted to registered + * file extensions only. + */ public boolean useRegisteredSuffixPatternMatch() { return this.registeredSuffixPatternMatch; } @@ -601,10 +626,14 @@ public List getFileExtensions() { * Set the ContentNegotiationManager to use for the ProducesRequestCondition. *

By default this is not set. */ - public void setContentNegotiationManager(ContentNegotiationManager manager) { - this.contentNegotiationManager = manager; + public void setContentNegotiationManager(ContentNegotiationManager contentNegotiationManager) { + this.contentNegotiationManager = contentNegotiationManager; } + /** + * Return the ContentNegotiationManager to use for the ProducesRequestCondition, + * if any. + */ public ContentNegotiationManager getContentNegotiationManager() { return this.contentNegotiationManager; } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java index c547b3e8f62..b4abdc16985 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java @@ -118,7 +118,7 @@ public void setEmbeddedValueResolver(StringValueResolver resolver) { @Override public void afterPropertiesSet() { this.config = new RequestMappingInfo.BuilderConfiguration(); - this.config.setPathHelper(getUrlPathHelper()); + this.config.setUrlPathHelper(getUrlPathHelper()); this.config.setPathMatcher(getPathMatcher()); this.config.setSuffixPatternMatch(this.useSuffixPatternMatch); this.config.setTrailingSlashMatch(this.useTrailingSlashMatch); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlEncodingFilter.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlEncodingFilter.java index 471ae154613..901d0c5eac6 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlEncodingFilter.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlEncodingFilter.java @@ -27,6 +27,7 @@ import org.apache.commons.logging.LogFactory; import org.springframework.web.filter.OncePerRequestFilter; +import org.springframework.web.util.UrlPathHelper; /** * A filter that wraps the {@link HttpServletResponse} and overrides its @@ -96,13 +97,14 @@ private ResourceUrlProvider getResourceUrlProvider() { private void initLookupPath(ResourceUrlProvider urlProvider) { if (this.indexLookupPath == null) { - String requestUri = urlProvider.getPathHelper().getRequestUri(this.request); - String lookupPath = urlProvider.getPathHelper().getLookupPathForRequest(this.request); + UrlPathHelper pathHelper = urlProvider.getUrlPathHelper(); + String requestUri = pathHelper.getRequestUri(this.request); + String lookupPath = pathHelper.getLookupPathForRequest(this.request); this.indexLookupPath = requestUri.lastIndexOf(lookupPath); this.prefixLookupPath = requestUri.substring(0, this.indexLookupPath); if ("/".equals(lookupPath) && !"/".equals(requestUri)) { - String contextPath = urlProvider.getPathHelper().getContextPath(this.request); + String contextPath = pathHelper.getContextPath(this.request); if (requestUri.equals(contextPath)) { this.indexLookupPath = requestUri.length(); this.prefixLookupPath = requestUri; diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlProvider.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlProvider.java index 9ee0c5a8b70..f39e02ff22b 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlProvider.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -51,7 +51,7 @@ public class ResourceUrlProvider implements ApplicationListener Date: Wed, 13 Jul 2016 15:42:34 +0200 Subject: [PATCH 0021/1274] AbstractHandlerMethodMapping adds type+method info to getMappingForMethod exceptions Issue: SPR-14452 (cherry picked from commit 8ccd727) --- .../web/servlet/handler/AbstractHandlerMethodMapping.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java index 266cdfb78ea..65a8db26116 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java @@ -231,7 +231,13 @@ protected void detectHandlerMethods(final Object handler) { new MethodIntrospector.MetadataLookup() { @Override public T inspect(Method method) { - return getMappingForMethod(method, userType); + try { + return getMappingForMethod(method, userType); + } + catch (Throwable ex) { + throw new IllegalStateException("Invalid mapping on handler class [" + + userType.getName() + "]: " + method, ex); + } } }); From 52f46c7fea03a1924f4cf399fe91b5c823cc182f Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 13 Jul 2016 22:31:41 +0200 Subject: [PATCH 0022/1274] Aspect actually applies in PersistenceExceptionTranslationPostProcessorTests Issue: SPR-14457 (cherry picked from commit 04e9c2b) --- .../PersistenceExceptionTranslationPostProcessorTests.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spring-tx/src/test/java/org/springframework/dao/annotation/PersistenceExceptionTranslationPostProcessorTests.java b/spring-tx/src/test/java/org/springframework/dao/annotation/PersistenceExceptionTranslationPostProcessorTests.java index 39c2bf78ea3..968eab46ca2 100644 --- a/spring-tx/src/test/java/org/springframework/dao/annotation/PersistenceExceptionTranslationPostProcessorTests.java +++ b/spring-tx/src/test/java/org/springframework/dao/annotation/PersistenceExceptionTranslationPostProcessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -22,6 +22,7 @@ import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.junit.Test; + import org.springframework.aop.Advisor; import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator; import org.springframework.aop.framework.Advised; @@ -138,7 +139,7 @@ public DataAccessException translateExceptionIfPossible(RuntimeException ex) { @Aspect public static class LogAllAspect { - @Before("execution(void *.additionalMethod())") + @Before("execution(void *.additionalMethod(*))") public void log(JoinPoint jp) { System.out.println("Before " + jp.getSignature().getName()); } From 0065a160cc8316fec43adb8da80078d1ff3be242 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 14 Jul 2016 21:10:24 +0200 Subject: [PATCH 0023/1274] StandardTypeConverter initializes default ConversionService against volatile field Issue: SPR-14465 (cherry picked from commit 6d91d54) --- .../expression/spel/support/StandardTypeConverter.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java index 690a7e57267..253e4bb3899 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 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. @@ -36,7 +36,7 @@ */ public class StandardTypeConverter implements TypeConverter { - private static ConversionService defaultConversionService; + private static volatile ConversionService defaultConversionService; private final ConversionService conversionService; @@ -45,10 +45,8 @@ public class StandardTypeConverter implements TypeConverter { * Create a StandardTypeConverter for the default ConversionService. */ public StandardTypeConverter() { - synchronized (this) { - if (defaultConversionService == null) { - defaultConversionService = new DefaultConversionService(); - } + if (defaultConversionService == null) { + defaultConversionService = new DefaultConversionService(); } this.conversionService = defaultConversionService; } From da59b4da9be5bc1a7c84a80eed2c4f4fd95f3ec5 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 14 Jul 2016 21:11:28 +0200 Subject: [PATCH 0024/1274] CronSequenceGenerator prevents stack overflow in case of inverted range Issue: SPR-14462 (cherry picked from commit e431624) --- .../support/CronSequenceGenerator.java | 6 ++++++ .../support/CronSequenceGeneratorTests.java | 20 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/spring-context/src/main/java/org/springframework/scheduling/support/CronSequenceGenerator.java b/spring-context/src/main/java/org/springframework/scheduling/support/CronSequenceGenerator.java index a088205d438..c7b356fd137 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/support/CronSequenceGenerator.java +++ b/spring-context/src/main/java/org/springframework/scheduling/support/CronSequenceGenerator.java @@ -95,6 +95,7 @@ public CronSequenceGenerator(String expression, TimeZone timeZone) { parse(expression); } + /** * Return the cron pattern that this sequence generator has been built for. */ @@ -378,6 +379,10 @@ private int[] getRange(String field, int min, int max) { throw new IllegalArgumentException("Range less than minimum (" + min + "): '" + field + "' in expression \"" + this.expression + "\""); } + if (result[0] > result[1]) { + throw new IllegalArgumentException("Invalid inverted range: '" + field + + "' in expression \"" + this.expression + "\""); + } return result; } @@ -388,6 +393,7 @@ private int[] getRange(String field, int min, int max) { * fields separated by single spaces. * @param expression the expression to evaluate * @return {@code true} if the given expression is a valid cron expression + * @since 4.3 */ public static boolean isValidExpression(String expression) { String[] fields = StringUtils.tokenizeToStringArray(expression, " "); diff --git a/spring-context/src/test/java/org/springframework/scheduling/support/CronSequenceGeneratorTests.java b/spring-context/src/test/java/org/springframework/scheduling/support/CronSequenceGeneratorTests.java index 8c558a22456..07b7537c0e4 100644 --- a/spring-context/src/test/java/org/springframework/scheduling/support/CronSequenceGeneratorTests.java +++ b/spring-context/src/test/java/org/springframework/scheduling/support/CronSequenceGeneratorTests.java @@ -56,6 +56,26 @@ public void withNegativeIncrement() { new CronSequenceGenerator("*/-1 * * * * *").next(new Date(2012, 6, 1, 9, 0)); } + @Test(expected = IllegalArgumentException.class) + public void withInvertedMinuteRange() { + new CronSequenceGenerator("* 6-5 * * * *").next(new Date(2012, 6, 1, 9, 0)); + } + + @Test(expected = IllegalArgumentException.class) + public void withInvertedHourRange() { + new CronSequenceGenerator("* * 6-5 * * *").next(new Date(2012, 6, 1, 9, 0)); + } + + @Test + public void withSameMinuteRange() { + new CronSequenceGenerator("* 6-6 * * * *").next(new Date(2012, 6, 1, 9, 0)); + } + + @Test + public void withSameHourRange() { + new CronSequenceGenerator("* * 6-6 * * *").next(new Date(2012, 6, 1, 9, 0)); + } + @Test public void validExpression() { assertTrue(CronSequenceGenerator.isValidExpression("0 */2 1-4 * * *")); From 4ea5f070a4c0f7c3291fb21218d4201493fbdb0d Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 15 Jul 2016 00:15:46 +0200 Subject: [PATCH 0025/1274] MessageHeaderAccessor properly removes header even in case of null value Issue: SPR-14468 (cherry picked from commit b166358) --- .../support/MessageHeaderAccessor.java | 15 ++++++++---- .../support/MessageHeaderAccessorTests.java | 24 ++++++++++++++++--- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/spring-messaging/src/main/java/org/springframework/messaging/support/MessageHeaderAccessor.java b/spring-messaging/src/main/java/org/springframework/messaging/support/MessageHeaderAccessor.java index 4c3141c42b2..38cbe0451ab 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/support/MessageHeaderAccessor.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/support/MessageHeaderAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -306,12 +306,17 @@ public void setHeader(String name, Object value) { throw new IllegalArgumentException("'" + name + "' header is read-only"); } verifyType(name, value); - if (!ObjectUtils.nullSafeEquals(value, getHeader(name))) { - this.modified = true; - if (value != null) { + if (value != null) { + // Modify header if necessary + if (!ObjectUtils.nullSafeEquals(value, getHeader(name))) { + this.modified = true; this.headers.getRawHeaders().put(name, value); } - else { + } + else { + // Remove header if available + if (this.headers.containsKey(name)) { + this.modified = true; this.headers.getRawHeaders().remove(name); } } diff --git a/spring-messaging/src/test/java/org/springframework/messaging/support/MessageHeaderAccessorTests.java b/spring-messaging/src/test/java/org/springframework/messaging/support/MessageHeaderAccessorTests.java index a09429fb62b..7d350e8e7a3 100644 --- a/spring-messaging/src/test/java/org/springframework/messaging/support/MessageHeaderAccessorTests.java +++ b/spring-messaging/src/test/java/org/springframework/messaging/support/MessageHeaderAccessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -18,6 +18,7 @@ import java.nio.charset.Charset; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -40,6 +41,7 @@ * * @author Rossen Stoyanchev * @author Sebastien Deleuze + * @author Juergen Hoeller */ public class MessageHeaderAccessorTests { @@ -89,6 +91,24 @@ public void existingHeadersModification() throws InterruptedException { assertEquals("baz", actual.get("bar")); } + @Test + public void testRemoveHeader() { + Message message = new GenericMessage<>("payload", Collections.singletonMap("foo", "bar")); + MessageHeaderAccessor accessor = new MessageHeaderAccessor(message); + accessor.removeHeader("foo"); + Map headers = accessor.toMap(); + assertFalse(headers.containsKey("foo")); + } + + @Test + public void testRemoveHeaderEvenIfNull() { + Message message = new GenericMessage<>("payload", Collections.singletonMap("foo", null)); + MessageHeaderAccessor accessor = new MessageHeaderAccessor(message); + accessor.removeHeader("foo"); + Map headers = accessor.toMap(); + assertFalse(headers.containsKey("foo")); + } + @Test public void removeHeaders() { Map map = new HashMap<>(); @@ -153,7 +173,6 @@ public void copyHeadersFromNullMap() { @Test public void toMap() { - MessageHeaderAccessor accessor = new MessageHeaderAccessor(); accessor.setHeader("foo", "bar1"); @@ -380,7 +399,6 @@ public String toString() { } - public static class TestMessageHeaderAccessor extends MessageHeaderAccessor { private TestMessageHeaderAccessor() { From 4d6d5e0ddd7ef32ac93fd6da59794f4f737c790b Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 15 Jul 2016 13:59:25 +0200 Subject: [PATCH 0026/1274] Consistent alias processing behind AnnotatedTypeMetadata abstraction (also for ASM) Issue: SPR-14427 (cherry picked from commit 3d3407c) --- .../AnnotationScopeMetadataResolver.java | 7 +- .../ComponentScanAnnotationParser.java | 8 +- ...onfigurationClassBeanDefinitionReader.java | 2 +- .../annotation/ConfigurationClassParser.java | 5 +- .../ConfigurationClassPostProcessorTests.java | 10 +- ...liasAwareAnnotationAttributeExtractor.java | 16 +- .../AnnotationAttributeExtractor.java | 5 +- .../core/annotation/AnnotationAttributes.java | 91 +++-- .../core/annotation/AnnotationUtils.java | 180 ++++++--- .../DefaultAnnotationAttributeExtractor.java | 5 +- .../MapAnnotationAttributeExtractor.java | 13 +- ...ynthesizedAnnotationInvocationHandler.java | 31 +- .../AbstractRecursiveAnnotationVisitor.java | 4 +- .../AnnotationAttributesReadingVisitor.java | 51 +-- .../AnnotationMetadataReadingVisitor.java | 5 +- .../AnnotationReadingVisitorUtils.java | 43 ++- .../MethodMetadataReadingVisitor.java | 8 +- .../RecursiveAnnotationArrayVisitor.java | 2 +- .../RecursiveAnnotationAttributesVisitor.java | 51 +-- .../annotation/AnnotationAttributesTests.java | 361 +----------------- .../core/type/AnnotationMetadataTests.java | 24 +- 21 files changed, 327 insertions(+), 595 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationScopeMetadataResolver.java b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationScopeMetadataResolver.java index d7d86e96701..ddbf312a08a 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationScopeMetadataResolver.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationScopeMetadataResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -79,9 +79,10 @@ public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) { ScopeMetadata metadata = new ScopeMetadata(); if (definition instanceof AnnotatedBeanDefinition) { AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition; - AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(annDef.getMetadata(), this.scopeAnnotationType); + AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor( + annDef.getMetadata(), this.scopeAnnotationType); if (attributes != null) { - metadata.setScopeName(attributes.getAliasedString("value", this.scopeAnnotationType, definition.getSource())); + metadata.setScopeName(attributes.getString("value")); ScopedProxyMode proxyMode = attributes.getEnum("proxyMode"); if (proxyMode == null || proxyMode == ScopedProxyMode.DEFAULT) { proxyMode = this.defaultProxyMode; diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java b/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java index 18277312714..247c5838db7 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java @@ -122,7 +122,7 @@ public Set parse(AnnotationAttributes componentScan, final } Set basePackages = new LinkedHashSet(); - String[] basePackagesArray = componentScan.getAliasedStringArray("basePackages", ComponentScan.class, declaringClass); + String[] basePackagesArray = componentScan.getStringArray("basePackages"); for (String pkg : basePackagesArray) { String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg), ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); @@ -148,11 +148,11 @@ private List typeFiltersFor(AnnotationAttributes filterAttributes) { List typeFilters = new ArrayList(); FilterType filterType = filterAttributes.getEnum("type"); - for (Class filterClass : filterAttributes.getAliasedClassArray("classes", ComponentScan.Filter.class, null)) { + for (Class filterClass : filterAttributes.getClassArray("classes")) { switch (filterType) { case ANNOTATION: Assert.isAssignable(Annotation.class, filterClass, - "An error occured while processing a @ComponentScan ANNOTATION type filter: "); + "An error occurred while processing a @ComponentScan ANNOTATION type filter: "); @SuppressWarnings("unchecked") Class annotationType = (Class) filterClass; typeFilters.add(new AnnotationTypeFilter(annotationType)); @@ -162,7 +162,7 @@ private List typeFiltersFor(AnnotationAttributes filterAttributes) { break; case CUSTOM: Assert.isAssignable(TypeFilter.class, filterClass, - "An error occured while processing a @ComponentScan CUSTOM type filter: "); + "An error occurred while processing a @ComponentScan CUSTOM type filter: "); TypeFilter filter = BeanUtils.instantiateClass(filterClass, TypeFilter.class); invokeAwareMethods(filter); typeFilters.add(filter); diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java index 9031872e5b0..522acf33cb9 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java @@ -236,7 +236,7 @@ private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) { ScopedProxyMode proxyMode = ScopedProxyMode.NO; AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(metadata, Scope.class); if (attributes != null) { - beanDef.setScope(attributes.getAliasedString("value", Scope.class, configClass.getResource())); + beanDef.setScope(attributes.getString("value")); proxyMode = attributes.getEnum("proxyMode"); if (proxyMode == ScopedProxyMode.DEFAULT) { proxyMode = ScopedProxyMode.NO; diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java index ffed74571f9..632c40f07b2 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java @@ -287,8 +287,9 @@ protected final SourceClass doProcessConfigurationClass(ConfigurationClass confi // Process any @ImportResource annotations if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) { - AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class); - String[] resources = importResource.getAliasedStringArray("locations", ImportResource.class, sourceClass); + AnnotationAttributes importResource = + AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class); + String[] resources = importResource.getStringArray("locations"); Class readerClass = importResource.getClass("reader"); for (String resource : resources) { String resolvedResource = this.environment.resolveRequiredPlaceholders(resource); diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java index 5bebb953b60..8c96aacd42b 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java @@ -955,7 +955,7 @@ public Object repoConsumer(Repository repo) { @ComponentScan(basePackages = "org.springframework.context.annotation.componentscan.simple") @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) - public static @interface ComposedConfiguration { + public @interface ComposedConfiguration { } @ComposedConfiguration @@ -966,7 +966,7 @@ public static class ComposedConfigurationClass { @ComponentScan @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) - public static @interface ComposedConfigurationWithAttributeOverrides { + public @interface ComposedConfigurationWithAttributeOverrides { String[] basePackages() default {}; @@ -985,7 +985,7 @@ public static class ComposedConfigurationWithAttributeOverrideForExcludeFilter { @ComposedConfigurationWithAttributeOverrides @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) - public static @interface ComposedComposedConfigurationWithAttributeOverrides { + public @interface ComposedComposedConfigurationWithAttributeOverrides { String[] basePackages() default {}; } @@ -997,14 +997,14 @@ public static class ComposedComposedConfigurationWithAttributeOverridesClass { @ComponentScan @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) - public static @interface MetaComponentScan { + public @interface MetaComponentScan { } @MetaComponentScan @Configuration @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) - public static @interface MetaComponentScanConfigurationWithAttributeOverrides { + public @interface MetaComponentScanConfigurationWithAttributeOverrides { String[] basePackages() default {}; } diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AbstractAliasAwareAnnotationAttributeExtractor.java b/spring-core/src/main/java/org/springframework/core/annotation/AbstractAliasAwareAnnotationAttributeExtractor.java index 0febd916c24..bc44e15ff5e 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AbstractAliasAwareAnnotationAttributeExtractor.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AbstractAliasAwareAnnotationAttributeExtractor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,13 +35,13 @@ * @param the type of source supported by this extractor * @see Annotation * @see AliasFor - * @see AnnotationUtils#synthesizeAnnotation(Annotation, AnnotatedElement) + * @see AnnotationUtils#synthesizeAnnotation(Annotation, Object) */ abstract class AbstractAliasAwareAnnotationAttributeExtractor implements AnnotationAttributeExtractor { private final Class annotationType; - private final AnnotatedElement annotatedElement; + private final Object annotatedElement; private final S source; @@ -56,7 +56,7 @@ abstract class AbstractAliasAwareAnnotationAttributeExtractor implements Anno * @param source the underlying source of annotation attributes; never {@code null} */ AbstractAliasAwareAnnotationAttributeExtractor( - Class annotationType, AnnotatedElement annotatedElement, S source) { + Class annotationType, Object annotatedElement, S source) { Assert.notNull(annotationType, "annotationType must not be null"); Assert.notNull(source, "source must not be null"); @@ -73,7 +73,7 @@ public final Class getAnnotationType() { } @Override - public final AnnotatedElement getAnnotatedElement() { + public final Object getAnnotatedElement() { return this.annotatedElement; } @@ -89,18 +89,18 @@ public final Object getAttributeValue(Method attributeMethod) { List aliasNames = this.attributeAliasMap.get(attributeName); if (aliasNames != null) { - Object defaultValue = AnnotationUtils.getDefaultValue(getAnnotationType(), attributeName); + Object defaultValue = AnnotationUtils.getDefaultValue(this.annotationType, attributeName); for (String aliasName : aliasNames) { Object aliasValue = getRawAttributeValue(aliasName); if (!ObjectUtils.nullSafeEquals(attributeValue, aliasValue) && !ObjectUtils.nullSafeEquals(attributeValue, defaultValue) && !ObjectUtils.nullSafeEquals(aliasValue, defaultValue)) { - String elementName = (getAnnotatedElement() != null ? getAnnotatedElement().toString() : "unknown element"); + String elementName = (this.annotatedElement != null ? this.annotatedElement.toString() : "unknown element"); throw new AnnotationConfigurationException(String.format( "In annotation [%s] declared on %s and synthesized from [%s], attribute '%s' and its " + "alias '%s' are present with values of [%s] and [%s], but only one is permitted.", - getAnnotationType().getName(), elementName, getSource(), attributeName, aliasName, + this.annotationType.getName(), elementName, this.source, attributeName, aliasName, ObjectUtils.nullSafeToString(attributeValue), ObjectUtils.nullSafeToString(aliasValue))); } diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAttributeExtractor.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAttributeExtractor.java index 86ca194bce8..7fd8dd4263d 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAttributeExtractor.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAttributeExtractor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -17,7 +17,6 @@ package org.springframework.core.annotation; import java.lang.annotation.Annotation; -import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; /** @@ -44,7 +43,7 @@ interface AnnotationAttributeExtractor { * type supported by this extractor. * @return the annotated element, or {@code null} if unknown */ - AnnotatedElement getAnnotatedElement(); + Object getAnnotatedElement(); /** * Get the underlying source of annotation attributes. diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAttributes.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAttributes.java index 395d2dba309..689e4b31cf8 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAttributes.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAttributes.java @@ -50,17 +50,32 @@ @SuppressWarnings("serial") public class AnnotationAttributes extends LinkedHashMap { - private final Class annotationType; + private static final String UNKNOWN = "unknown"; + + private Class annotationType; private final String displayName; + boolean validated = false; + /** * Create a new, empty {@link AnnotationAttributes} instance. */ public AnnotationAttributes() { this.annotationType = null; - this.displayName = "unknown"; + this.displayName = UNKNOWN; + } + + /** + * Create a new, empty {@link AnnotationAttributes} instance with the + * given initial capacity to optimize performance. + * @param initialCapacity initial size of the underlying map + */ + public AnnotationAttributes(int initialCapacity) { + super(initialCapacity); + this.annotationType = null; + this.displayName = UNKNOWN; } /** @@ -71,33 +86,57 @@ public AnnotationAttributes() { * @since 4.2 */ public AnnotationAttributes(Class annotationType) { - Assert.notNull(annotationType, "annotationType must not be null"); + Assert.notNull(annotationType, "'annotationType' must not be null"); this.annotationType = annotationType; this.displayName = annotationType.getName(); } /** - * Create a new, empty {@link AnnotationAttributes} instance with the - * given initial capacity to optimize performance. - * @param initialCapacity initial size of the underlying map + * Create a new, empty {@link AnnotationAttributes} instance for the + * specified {@code annotationType}. + * @param annotationType the type of annotation represented by this + * {@code AnnotationAttributes} instance; never {@code null} + * @param classLoader the ClassLoader to try to load the annotation type on, + * or {@code null} to just store the annotation type name + * @since 4.3.2 */ - public AnnotationAttributes(int initialCapacity) { - super(initialCapacity); - this.annotationType = null; - this.displayName = "unknown"; + @SuppressWarnings("unchecked") + public AnnotationAttributes(String annotationType, ClassLoader classLoader) { + Assert.notNull(annotationType, "'annotationType' must not be null"); + if (classLoader != null) { + try { + this.annotationType = (Class) classLoader.loadClass(annotationType); + } + catch (ClassNotFoundException ex) { + // Annotation Class not resolvable + } + } + this.displayName = annotationType; } /** - * Create a new {@link AnnotationAttributes} instance, wrapping the - * provided map and all its key-value pairs. - * @param map original source of annotation attribute key-value - * pairs + * Create a new {@link AnnotationAttributes} instance, wrapping the provided + * map and all its key-value pairs. + * @param map original source of annotation attribute key-value pairs * @see #fromMap(Map) */ public AnnotationAttributes(Map map) { super(map); this.annotationType = null; - this.displayName = "unknown"; + this.displayName = UNKNOWN; + } + + /** + * Create a new {@link AnnotationAttributes} instance, wrapping the provided + * map and all its key-value pairs. + * @param other original source of annotation attribute key-value pairs + * @see #fromMap(Map) + */ + public AnnotationAttributes(AnnotationAttributes other) { + super(other); + this.annotationType = other.annotationType; + this.displayName = other.displayName; + this.validated = other.validated; } @@ -144,8 +183,10 @@ public String getString(String attributeName) { * @throws AnnotationConfigurationException if the attribute and its * alias are both present with different non-empty values * @since 4.2 - * @see ObjectUtils#isEmpty(Object) + * @deprecated as of Spring 4.3.2, in favor of built-in alias resolution + * in {@link #getString} itself */ + @Deprecated public String getAliasedString(String attributeName, Class annotationType, Object annotationSource) { @@ -188,7 +229,10 @@ public String[] getStringArray(String attributeName) { * @throws AnnotationConfigurationException if the attribute and its * alias are both present with different non-empty values * @since 4.2 + * @deprecated as of Spring 4.3.2, in favor of built-in alias resolution + * in {@link #getStringArray} itself */ + @Deprecated public String[] getAliasedStringArray(String attributeName, Class annotationType, Object annotationSource) { @@ -286,7 +330,10 @@ public Class[] getClassArray(String attributeName) { * @throws AnnotationConfigurationException if the attribute and its * alias are both present with different non-empty values * @since 4.2 + * @deprecated as of Spring 4.3.2, in favor of built-in alias resolution + * in {@link #getClassArray} itself */ + @Deprecated public Class[] getAliasedClassArray(String attributeName, Class annotationType, Object annotationSource) { @@ -378,7 +425,7 @@ public A[] getAnnotationArray(String attributeName, Class */ @SuppressWarnings("unchecked") private T getRequiredAttribute(String attributeName, Class expectedType) { - Assert.hasText(attributeName, "attributeName must not be null or empty"); + Assert.hasText(attributeName, "'attributeName' must not be null or empty"); Object value = get(attributeName); assertAttributePresence(attributeName, value); assertNotException(attributeName, value); @@ -418,9 +465,9 @@ private T getRequiredAttribute(String attributeName, Class expectedType) private T getRequiredAttributeWithAlias(String attributeName, Class annotationType, Object annotationSource, Class expectedType) { - Assert.hasText(attributeName, "attributeName must not be null or empty"); - Assert.notNull(annotationType, "annotationType must not be null"); - Assert.notNull(expectedType, "expectedType must not be null"); + Assert.hasText(attributeName, "'attributeName' must not be null or empty"); + Assert.notNull(annotationType, "'annotationType' must not be null"); + Assert.notNull(expectedType, "'expectedType' must not be null"); T attributeValue = getAttribute(attributeName, expectedType); @@ -433,8 +480,8 @@ private T getRequiredAttributeWithAlias(String attributeName, Class A searchOnInterfaces(Method method, Class< static boolean isInterfaceWithAnnotatedMethods(Class iface) { Boolean found = annotatedInterfaceCache.get(iface); if (found != null) { - return found.booleanValue(); + return found; } found = Boolean.FALSE; for (Method ifcMethod : iface.getMethods()) { @@ -636,7 +637,7 @@ static boolean isInterfaceWithAnnotatedMethods(Class iface) { } } annotatedInterfaceCache.put(iface, found); - return found.booleanValue(); + return found; } /** @@ -888,14 +889,14 @@ public static boolean isAnnotationMetaPresent(Class annota AnnotationCacheKey cacheKey = new AnnotationCacheKey(annotationType, metaAnnotationType); Boolean metaPresent = metaPresentCache.get(cacheKey); if (metaPresent != null) { - return metaPresent.booleanValue(); + return metaPresent; } metaPresent = Boolean.FALSE; if (findAnnotation(annotationType, metaAnnotationType, false) != null) { metaPresent = Boolean.TRUE; } metaPresentCache.put(cacheKey, metaPresent); - return metaPresent.booleanValue(); + return metaPresent; } /** @@ -1018,6 +1019,13 @@ public static AnnotationAttributes getAnnotationAttributes(AnnotatedElement anno public static AnnotationAttributes getAnnotationAttributes(AnnotatedElement annotatedElement, Annotation annotation, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { + return getAnnotationAttributes( + (Object) annotatedElement, annotation, classValuesAsString, nestedAnnotationsAsMap); + } + + private static AnnotationAttributes getAnnotationAttributes(Object annotatedElement, + Annotation annotation, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { + AnnotationAttributes attributes = retrieveAnnotationAttributes(annotatedElement, annotation, classValuesAsString, nestedAnnotationsAsMap); postProcessAnnotationAttributes(annotatedElement, attributes, classValuesAsString, nestedAnnotationsAsMap); @@ -1052,7 +1060,7 @@ public static AnnotationAttributes getAnnotationAttributes(AnnotatedElement anno * @since 4.2 * @see #postProcessAnnotationAttributes */ - static AnnotationAttributes retrieveAnnotationAttributes(AnnotatedElement annotatedElement, Annotation annotation, + static AnnotationAttributes retrieveAnnotationAttributes(Object annotatedElement, Annotation annotation, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { Class annotationType = annotation.annotationType(); @@ -1096,14 +1104,14 @@ static AnnotationAttributes retrieveAnnotationAttributes(AnnotatedElement annota * {@code Annotation} instances * @return the adapted value, or the original value if no adaptation is needed */ - static Object adaptValue(AnnotatedElement annotatedElement, Object value, boolean classValuesAsString, + static Object adaptValue(Object annotatedElement, Object value, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { if (classValuesAsString) { - if (value instanceof Class) { + if (value instanceof Class) { return ((Class) value).getName(); } - else if (value instanceof Class[]) { + else if (value instanceof Class[]) { Class[] clazzArray = (Class[]) value; String[] classNames = new String[clazzArray.length]; for (int i = 0; i < clazzArray.length; i++) { @@ -1142,6 +1150,64 @@ else if (value instanceof Class[]) { return value; } + /** + * Register the annotation-declared default values for the given attributes, + * if available. + * @param attributes the annotation attributes to process + * @since 4.3.2 + */ + public static void registerDefaultValues(AnnotationAttributes attributes) { + // Only do defaults scanning for public annotations; we'd run into + // IllegalAccessExceptions otherwise, and we don't want to mess with + // accessibility in a SecurityManager environment. + Class annotationType = attributes.annotationType(); + if (annotationType != null && Modifier.isPublic(annotationType.getModifiers())) { + // Check declared default values of attributes in the annotation type. + Method[] annotationAttributes = annotationType.getMethods(); + for (Method annotationAttribute : annotationAttributes) { + String attributeName = annotationAttribute.getName(); + Object defaultValue = annotationAttribute.getDefaultValue(); + if (defaultValue != null && !attributes.containsKey(attributeName)) { + if (defaultValue instanceof Annotation) { + defaultValue = getAnnotationAttributes((Annotation) defaultValue, false, true); + } + else if (defaultValue instanceof Annotation[]) { + Annotation[] realAnnotations = (Annotation[]) defaultValue; + AnnotationAttributes[] mappedAnnotations = new AnnotationAttributes[realAnnotations.length]; + for (int i = 0; i < realAnnotations.length; i++) { + mappedAnnotations[i] = getAnnotationAttributes(realAnnotations[i], false, true); + } + defaultValue = mappedAnnotations; + } + attributes.put(attributeName, new DefaultValueHolder(defaultValue)); + } + } + } + } + + /** + * Post-process the supplied {@link AnnotationAttributes}, preserving nested + * annotations as {@code Annotation} instances. + *

Specifically, this method enforces attribute alias semantics + * for annotation attributes that are annotated with {@link AliasFor @AliasFor} + * and replaces default value placeholders with their original default values. + * @param annotatedElement the element that is annotated with an annotation or + * annotation hierarchy from which the supplied attributes were created; + * may be {@code null} if unknown + * @param attributes the annotation attributes to post-process + * @param classValuesAsString whether to convert Class references into Strings (for + * compatibility with {@link org.springframework.core.type.AnnotationMetadata}) + * or to preserve them as Class references + * @since 4.3.2 + * @see #postProcessAnnotationAttributes(Object, AnnotationAttributes, boolean, boolean) + * @see #getDefaultValue(Class, String) + */ + public static void postProcessAnnotationAttributes(Object annotatedElement, + AnnotationAttributes attributes, boolean classValuesAsString) { + + postProcessAnnotationAttributes(annotatedElement, attributes, classValuesAsString, false); + } + /** * Post-process the supplied {@link AnnotationAttributes}. *

Specifically, this method enforces attribute alias semantics @@ -1159,10 +1225,10 @@ else if (value instanceof Class[]) { * {@link org.springframework.core.type.AnnotationMetadata}) or to preserve them as * {@code Annotation} instances * @since 4.2 - * @see #retrieveAnnotationAttributes(AnnotatedElement, Annotation, boolean, boolean) + * @see #retrieveAnnotationAttributes(Object, Annotation, boolean, boolean) * @see #getDefaultValue(Class, String) */ - static void postProcessAnnotationAttributes(AnnotatedElement annotatedElement, + static void postProcessAnnotationAttributes(Object annotatedElement, AnnotationAttributes attributes, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { // Abort? @@ -1176,51 +1242,55 @@ static void postProcessAnnotationAttributes(AnnotatedElement annotatedElement, // circuit the search algorithms. Set valuesAlreadyReplaced = new HashSet(); - // Validate @AliasFor configuration - Map> aliasMap = getAttributeAliasMap(annotationType); - for (String attributeName : aliasMap.keySet()) { - if (valuesAlreadyReplaced.contains(attributeName)) { - continue; - } - Object value = attributes.get(attributeName); - boolean valuePresent = (value != null && !(value instanceof DefaultValueHolder)); - - for (String aliasedAttributeName : aliasMap.get(attributeName)) { - if (valuesAlreadyReplaced.contains(aliasedAttributeName)) { + if (!attributes.validated) { + // Validate @AliasFor configuration + Map> aliasMap = getAttributeAliasMap(annotationType); + for (String attributeName : aliasMap.keySet()) { + if (valuesAlreadyReplaced.contains(attributeName)) { continue; } + Object value = attributes.get(attributeName); + boolean valuePresent = (value != null && !(value instanceof DefaultValueHolder)); - Object aliasedValue = attributes.get(aliasedAttributeName); - boolean aliasPresent = (aliasedValue != null && !(aliasedValue instanceof DefaultValueHolder)); - - // Something to validate or replace with an alias? - if (valuePresent || aliasPresent) { - if (valuePresent && aliasPresent) { - // Since annotation attributes can be arrays, we must use ObjectUtils.nullSafeEquals(). - if (!ObjectUtils.nullSafeEquals(value, aliasedValue)) { - String elementAsString = (annotatedElement != null ? annotatedElement.toString() : "unknown element"); - throw new AnnotationConfigurationException(String.format( - "In AnnotationAttributes for annotation [%s] declared on %s, " + - "attribute '%s' and its alias '%s' are declared with values of [%s] and [%s], " + - "but only one is permitted.", annotationType.getName(), elementAsString, - attributeName, aliasedAttributeName, ObjectUtils.nullSafeToString(value), - ObjectUtils.nullSafeToString(aliasedValue))); - } - } - else if (aliasPresent) { - // Replace value with aliasedValue - attributes.put(attributeName, - adaptValue(annotatedElement, aliasedValue, classValuesAsString, nestedAnnotationsAsMap)); - valuesAlreadyReplaced.add(attributeName); + for (String aliasedAttributeName : aliasMap.get(attributeName)) { + if (valuesAlreadyReplaced.contains(aliasedAttributeName)) { + continue; } - else { - // Replace aliasedValue with value - attributes.put(aliasedAttributeName, - adaptValue(annotatedElement, value, classValuesAsString, nestedAnnotationsAsMap)); - valuesAlreadyReplaced.add(aliasedAttributeName); + + Object aliasedValue = attributes.get(aliasedAttributeName); + boolean aliasPresent = (aliasedValue != null && !(aliasedValue instanceof DefaultValueHolder)); + + // Something to validate or replace with an alias? + if (valuePresent || aliasPresent) { + if (valuePresent && aliasPresent) { + // Since annotation attributes can be arrays, we must use ObjectUtils.nullSafeEquals(). + if (!ObjectUtils.nullSafeEquals(value, aliasedValue)) { + String elementAsString = + (annotatedElement != null ? annotatedElement.toString() : "unknown element"); + throw new AnnotationConfigurationException(String.format( + "In AnnotationAttributes for annotation [%s] declared on %s, " + + "attribute '%s' and its alias '%s' are declared with values of [%s] and [%s], " + + "but only one is permitted.", annotationType.getName(), elementAsString, + attributeName, aliasedAttributeName, ObjectUtils.nullSafeToString(value), + ObjectUtils.nullSafeToString(aliasedValue))); + } + } + else if (aliasPresent) { + // Replace value with aliasedValue + attributes.put(attributeName, + adaptValue(annotatedElement, aliasedValue, classValuesAsString, nestedAnnotationsAsMap)); + valuesAlreadyReplaced.add(attributeName); + } + else { + // Replace aliasedValue with value + attributes.put(aliasedAttributeName, + adaptValue(annotatedElement, value, classValuesAsString, nestedAnnotationsAsMap)); + valuesAlreadyReplaced.add(aliasedAttributeName); + } } } } + attributes.validated = true; } // Replace any remaining placeholders with actual default values @@ -1360,8 +1430,12 @@ static A synthesizeAnnotation(A annotation) { * @see #synthesizeAnnotation(Map, Class, AnnotatedElement) * @see #synthesizeAnnotation(Class) */ - @SuppressWarnings("unchecked") public static A synthesizeAnnotation(A annotation, AnnotatedElement annotatedElement) { + return synthesizeAnnotation(annotation, (Object) annotatedElement); + } + + @SuppressWarnings("unchecked") + static A synthesizeAnnotation(A annotation, Object annotatedElement) { if (annotation == null) { return null; } @@ -1466,7 +1540,7 @@ public static A synthesizeAnnotation(Class annotationT * @see #synthesizeAnnotation(Annotation, AnnotatedElement) * @see #synthesizeAnnotation(Map, Class, AnnotatedElement) */ - public static Annotation[] synthesizeAnnotationArray(Annotation[] annotations, AnnotatedElement annotatedElement) { + static Annotation[] synthesizeAnnotationArray(Annotation[] annotations, Object annotatedElement) { if (annotations == null) { return null; } @@ -1494,7 +1568,7 @@ public static Annotation[] synthesizeAnnotationArray(Annotation[] annotations, A * {@code @AliasFor} is detected * @since 4.2.1 * @see #synthesizeAnnotation(Map, Class, AnnotatedElement) - * @see #synthesizeAnnotationArray(Annotation[], AnnotatedElement) + * @see #synthesizeAnnotationArray(Annotation[], Object) */ @SuppressWarnings("unchecked") static A[] synthesizeAnnotationArray(Map[] maps, Class annotationType) { @@ -1582,7 +1656,7 @@ private static boolean canExposeSynthesizedMarker(Class an private static boolean isSynthesizable(Class annotationType) { Boolean synthesizable = synthesizableCache.get(annotationType); if (synthesizable != null) { - return synthesizable.booleanValue(); + return synthesizable; } synthesizable = Boolean.FALSE; @@ -1610,7 +1684,7 @@ else if (Annotation.class.isAssignableFrom(returnType)) { } synthesizableCache.put(annotationType, synthesizable); - return synthesizable.booleanValue(); + return synthesizable; } /** diff --git a/spring-core/src/main/java/org/springframework/core/annotation/DefaultAnnotationAttributeExtractor.java b/spring-core/src/main/java/org/springframework/core/annotation/DefaultAnnotationAttributeExtractor.java index 805c769b783..dfcf28ad172 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/DefaultAnnotationAttributeExtractor.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/DefaultAnnotationAttributeExtractor.java @@ -17,7 +17,6 @@ package org.springframework.core.annotation; import java.lang.annotation.Annotation; -import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; import org.springframework.util.ReflectionUtils; @@ -32,7 +31,7 @@ * @see AliasFor * @see AbstractAliasAwareAnnotationAttributeExtractor * @see MapAnnotationAttributeExtractor - * @see AnnotationUtils#synthesizeAnnotation(Annotation, AnnotatedElement) + * @see AnnotationUtils#synthesizeAnnotation */ class DefaultAnnotationAttributeExtractor extends AbstractAliasAwareAnnotationAttributeExtractor { @@ -42,7 +41,7 @@ class DefaultAnnotationAttributeExtractor extends AbstractAliasAwareAnnotationAt * @param annotatedElement the element that is annotated with the supplied * annotation; may be {@code null} if unknown */ - DefaultAnnotationAttributeExtractor(Annotation annotation, AnnotatedElement annotatedElement) { + DefaultAnnotationAttributeExtractor(Annotation annotation, Object annotatedElement) { super(annotation.annotationType(), annotatedElement, annotation); } diff --git a/spring-core/src/main/java/org/springframework/core/annotation/MapAnnotationAttributeExtractor.java b/spring-core/src/main/java/org/springframework/core/annotation/MapAnnotationAttributeExtractor.java index 15ca984e81b..a2491fe5f41 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/MapAnnotationAttributeExtractor.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/MapAnnotationAttributeExtractor.java @@ -24,10 +24,9 @@ import java.util.List; import java.util.Map; +import org.springframework.util.Assert; import org.springframework.util.ClassUtils; -import static org.springframework.core.annotation.AnnotationUtils.*; - /** * Implementation of the {@link AnnotationAttributeExtractor} strategy that * is backed by a {@link Map}. @@ -89,9 +88,9 @@ private static Map enrichAndValidateAttributes( Map originalAttributes, Class annotationType) { Map attributes = new LinkedHashMap(originalAttributes); - Map> attributeAliasMap = getAttributeAliasMap(annotationType); + Map> attributeAliasMap = AnnotationUtils.getAttributeAliasMap(annotationType); - for (Method attributeMethod : getAttributeMethods(annotationType)) { + for (Method attributeMethod : AnnotationUtils.getAttributeMethods(annotationType)) { String attributeName = attributeMethod.getName(); Object attributeValue = attributes.get(attributeName); @@ -112,7 +111,7 @@ private static Map enrichAndValidateAttributes( // if aliases not present, check default if (attributeValue == null) { - Object defaultValue = getDefaultValue(annotationType, attributeName); + Object defaultValue = AnnotationUtils.getDefaultValue(annotationType, attributeName); if (defaultValue != null) { attributeValue = defaultValue; attributes.put(attributeName, attributeValue); @@ -147,7 +146,7 @@ else if (Annotation.class.isAssignableFrom(requiredReturnType) && Class nestedAnnotationType = (Class) requiredReturnType; Map map = (Map) attributeValue; - attributes.put(attributeName, synthesizeAnnotation(map, nestedAnnotationType, null)); + attributes.put(attributeName, AnnotationUtils.synthesizeAnnotation(map, nestedAnnotationType, null)); converted = true; } @@ -158,7 +157,7 @@ else if (requiredReturnType.isArray() && actualReturnType.isArray() && Class nestedAnnotationType = (Class) requiredReturnType.getComponentType(); Map[] maps = (Map[]) attributeValue; - attributes.put(attributeName, synthesizeAnnotationArray(maps, nestedAnnotationType)); + attributes.put(attributeName, AnnotationUtils.synthesizeAnnotationArray(maps, nestedAnnotationType)); converted = true; } diff --git a/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedAnnotationInvocationHandler.java b/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedAnnotationInvocationHandler.java index c8614eb575e..27e14c43f03 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedAnnotationInvocationHandler.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedAnnotationInvocationHandler.java @@ -27,11 +27,9 @@ import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; +import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; -import static org.springframework.core.annotation.AnnotationUtils.*; -import static org.springframework.util.ReflectionUtils.*; - /** * {@link InvocationHandler} for an {@link Annotation} that Spring has * synthesized (i.e., wrapped in a dynamic proxy) with additional @@ -63,22 +61,21 @@ class SynthesizedAnnotationInvocationHandler implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - if (isEqualsMethod(method)) { + if (ReflectionUtils.isEqualsMethod(method)) { return annotationEquals(args[0]); } - if (isHashCodeMethod(method)) { + if (ReflectionUtils.isHashCodeMethod(method)) { return annotationHashCode(); } - if (isToStringMethod(method)) { + if (ReflectionUtils.isToStringMethod(method)) { return annotationToString(); } - if (isAnnotationTypeMethod(method)) { + if (AnnotationUtils.isAnnotationTypeMethod(method)) { return annotationType(); } - if (!isAttributeMethod(method)) { - String msg = String.format("Method [%s] is unsupported for synthesized annotation type [%s]", - method, annotationType()); - throw new AnnotationConfigurationException(msg); + if (!AnnotationUtils.isAttributeMethod(method)) { + throw new AnnotationConfigurationException(String.format( + "Method [%s] is unsupported for synthesized annotation type [%s]", method, annotationType())); } return getAttributeValue(method); } @@ -100,10 +97,10 @@ private Object getAttributeValue(Method attributeMethod) { // Synthesize nested annotations before returning them. if (value instanceof Annotation) { - value = synthesizeAnnotation((Annotation) value, this.attributeExtractor.getAnnotatedElement()); + value = AnnotationUtils.synthesizeAnnotation((Annotation) value, this.attributeExtractor.getAnnotatedElement()); } else if (value instanceof Annotation[]) { - value = synthesizeAnnotationArray((Annotation[]) value, this.attributeExtractor.getAnnotatedElement()); + value = AnnotationUtils.synthesizeAnnotationArray((Annotation[]) value, this.attributeExtractor.getAnnotatedElement()); } this.valueCache.put(attributeName, value); @@ -164,9 +161,9 @@ private boolean annotationEquals(Object other) { return false; } - for (Method attributeMethod : getAttributeMethods(annotationType())) { + for (Method attributeMethod : AnnotationUtils.getAttributeMethods(annotationType())) { Object thisValue = getAttributeValue(attributeMethod); - Object otherValue = invokeMethod(attributeMethod, other); + Object otherValue = ReflectionUtils.invokeMethod(attributeMethod, other); if (!ObjectUtils.nullSafeEquals(thisValue, otherValue)) { return false; } @@ -181,7 +178,7 @@ private boolean annotationEquals(Object other) { private int annotationHashCode() { int result = 0; - for (Method attributeMethod : getAttributeMethods(annotationType())) { + for (Method attributeMethod : AnnotationUtils.getAttributeMethods(annotationType())) { Object value = getAttributeValue(attributeMethod); int hashCode; if (value.getClass().isArray()) { @@ -239,7 +236,7 @@ private int hashCodeForArray(Object array) { private String annotationToString() { StringBuilder sb = new StringBuilder("@").append(annotationType().getName()).append("("); - Iterator iterator = getAttributeMethods(annotationType()).iterator(); + Iterator iterator = AnnotationUtils.getAttributeMethods(annotationType()).iterator(); while (iterator.hasNext()) { Method attributeMethod = iterator.next(); sb.append(attributeMethod.getName()); diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/AbstractRecursiveAnnotationVisitor.java b/spring-core/src/main/java/org/springframework/core/type/classreading/AbstractRecursiveAnnotationVisitor.java index 2cc12717b2f..bd42e059d34 100644 --- a/spring-core/src/main/java/org/springframework/core/type/classreading/AbstractRecursiveAnnotationVisitor.java +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/AbstractRecursiveAnnotationVisitor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -58,7 +58,7 @@ public void visit(String attributeName, Object attributeValue) { @Override public AnnotationVisitor visitAnnotation(String attributeName, String asmTypeDescriptor) { String annotationType = Type.getType(asmTypeDescriptor).getClassName(); - AnnotationAttributes nestedAttributes = new AnnotationAttributes(); + AnnotationAttributes nestedAttributes = new AnnotationAttributes(annotationType, this.classLoader); this.attributes.put(attributeName, nestedAttributes); return new RecursiveAnnotationAttributesVisitor(annotationType, nestedAttributes, this.classLoader); } diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationAttributesReadingVisitor.java b/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationAttributesReadingVisitor.java index ab001b50ecb..f35ecd6881a 100644 --- a/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationAttributesReadingVisitor.java +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationAttributesReadingVisitor.java @@ -44,8 +44,6 @@ */ final class AnnotationAttributesReadingVisitor extends RecursiveAnnotationAttributesVisitor { - private final String annotationType; - private final MultiValueMap attributesMap; private final Map> metaAnnotationMap; @@ -55,38 +53,41 @@ public AnnotationAttributesReadingVisitor(String annotationType, MultiValueMap attributesMap, Map> metaAnnotationMap, ClassLoader classLoader) { - super(annotationType, new AnnotationAttributes(), classLoader); - this.annotationType = annotationType; + super(annotationType, new AnnotationAttributes(annotationType, classLoader), classLoader); this.attributesMap = attributesMap; this.metaAnnotationMap = metaAnnotationMap; } @Override - public void doVisitEnd(Class annotationClass) { - super.doVisitEnd(annotationClass); - List attributes = this.attributesMap.get(this.annotationType); - if (attributes == null) { - this.attributesMap.add(this.annotationType, this.attributes); - } - else { - attributes.add(0, this.attributes); - } - Set visited = new LinkedHashSet(); - Annotation[] metaAnnotations = AnnotationUtils.getAnnotations(annotationClass); - if (!ObjectUtils.isEmpty(metaAnnotations)) { - for (Annotation metaAnnotation : metaAnnotations) { - if (!AnnotationUtils.isInJavaLangAnnotationPackage(metaAnnotation)) { - recursivelyCollectMetaAnnotations(visited, metaAnnotation); + public void visitEnd() { + super.visitEnd(); + + Class annotationClass = this.attributes.annotationType(); + if (annotationClass != null) { + List attributeList = this.attributesMap.get(this.annotationType); + if (attributeList == null) { + this.attributesMap.add(this.annotationType, this.attributes); + } + else { + attributeList.add(0, this.attributes); + } + Set visited = new LinkedHashSet(); + Annotation[] metaAnnotations = AnnotationUtils.getAnnotations(annotationClass); + if (!ObjectUtils.isEmpty(metaAnnotations)) { + for (Annotation metaAnnotation : metaAnnotations) { + if (!AnnotationUtils.isInJavaLangAnnotationPackage(metaAnnotation)) { + recursivelyCollectMetaAnnotations(visited, metaAnnotation); + } } } - } - if (this.metaAnnotationMap != null) { - Set metaAnnotationTypeNames = new LinkedHashSet(visited.size()); - for (Annotation ann : visited) { - metaAnnotationTypeNames.add(ann.annotationType().getName()); + if (this.metaAnnotationMap != null) { + Set metaAnnotationTypeNames = new LinkedHashSet(visited.size()); + for (Annotation ann : visited) { + metaAnnotationTypeNames.add(ann.annotationType().getName()); + } + this.metaAnnotationMap.put(annotationClass.getName(), metaAnnotationTypeNames); } - this.metaAnnotationMap.put(annotationClass.getName(), metaAnnotationTypeNames); } } diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationMetadataReadingVisitor.java b/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationMetadataReadingVisitor.java index eea3d825806..3b3a17dd60e 100644 --- a/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationMetadataReadingVisitor.java +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationMetadataReadingVisitor.java @@ -131,7 +131,8 @@ public AnnotationAttributes getAnnotationAttributes(String annotationName) { public AnnotationAttributes getAnnotationAttributes(String annotationName, boolean classValuesAsString) { AnnotationAttributes raw = AnnotationReadingVisitorUtils.getMergedAnnotationAttributes( this.attributesMap, this.metaAnnotationMap, annotationName); - return AnnotationReadingVisitorUtils.convertClassValues(this.classLoader, raw, classValuesAsString); + return AnnotationReadingVisitorUtils.convertClassValues( + "class '" + getClassName() + "'", this.classLoader, raw, classValuesAsString); } @Override @@ -148,7 +149,7 @@ public MultiValueMap getAllAnnotationAttributes(String annotatio } for (AnnotationAttributes raw : attributes) { for (Map.Entry entry : AnnotationReadingVisitorUtils.convertClassValues( - this.classLoader, raw, classValuesAsString).entrySet()) { + "class '" + getClassName() + "'", this.classLoader, raw, classValuesAsString).entrySet()) { allAttributes.add(entry.getKey(), entry.getValue()); } } diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationReadingVisitorUtils.java b/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationReadingVisitorUtils.java index 0d7ba35f259..93c3e76d5d9 100644 --- a/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationReadingVisitorUtils.java +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationReadingVisitorUtils.java @@ -41,25 +41,29 @@ */ abstract class AnnotationReadingVisitorUtils { - public static AnnotationAttributes convertClassValues(ClassLoader classLoader, AnnotationAttributes original, - boolean classValuesAsString) { + public static AnnotationAttributes convertClassValues(Object annotatedElement, + ClassLoader classLoader, AnnotationAttributes original, boolean classValuesAsString) { if (original == null) { return null; } - AnnotationAttributes result = new AnnotationAttributes(original.size()); - for (Map.Entry entry : original.entrySet()) { + AnnotationAttributes result = new AnnotationAttributes(original); + AnnotationUtils.postProcessAnnotationAttributes(annotatedElement, result, classValuesAsString); + + for (Map.Entry entry : result.entrySet()) { try { Object value = entry.getValue(); if (value instanceof AnnotationAttributes) { - value = convertClassValues(classLoader, (AnnotationAttributes) value, classValuesAsString); + value = convertClassValues( + annotatedElement, classLoader, (AnnotationAttributes) value, classValuesAsString); } else if (value instanceof AnnotationAttributes[]) { AnnotationAttributes[] values = (AnnotationAttributes[]) value; for (int i = 0; i < values.length; i++) { - values[i] = convertClassValues(classLoader, values[i], classValuesAsString); + values[i] = convertClassValues(annotatedElement, classLoader, values[i], classValuesAsString); } + value = values; } else if (value instanceof Type) { value = (classValuesAsString ? ((Type) value).getClassName() : @@ -67,7 +71,8 @@ else if (value instanceof Type) { } else if (value instanceof Type[]) { Type[] array = (Type[]) value; - Object[] convArray = (classValuesAsString ? new String[array.length] : new Class[array.length]); + Object[] convArray = + (classValuesAsString ? new String[array.length] : new Class[array.length]); for (int i = 0; i < array.length; i++) { convArray[i] = (classValuesAsString ? array[i].getClassName() : classLoader.loadClass(array[i].getClassName())); @@ -75,11 +80,11 @@ else if (value instanceof Type[]) { value = convArray; } else if (classValuesAsString) { - if (value instanceof Class) { + if (value instanceof Class) { value = ((Class) value).getName(); } - else if (value instanceof Class[]) { - Class[] clazzArray = (Class[]) value; + else if (value instanceof Class[]) { + Class[] clazzArray = (Class[]) value; String[] newValue = new String[clazzArray.length]; for (int i = 0; i < clazzArray.length; i++) { newValue[i] = clazzArray[i].getName(); @@ -87,13 +92,14 @@ else if (value instanceof Class[]) { value = newValue; } } - result.put(entry.getKey(), value); + entry.setValue(value); } catch (Exception ex) { // Class not found - can't resolve class reference in annotation attribute. result.put(entry.getKey(), ex); } } + return result; } @@ -123,13 +129,12 @@ public static AnnotationAttributes getMergedAnnotationAttributes( return null; } - // To start with, we populate the results with a copy of all attribute - // values from the target annotation. A copy is necessary so that we do - // not inadvertently mutate the state of the metadata passed to this - // method. - AnnotationAttributes results = new AnnotationAttributes(attributesList.get(0)); + // To start with, we populate the result with a copy of all attribute values + // from the target annotation. A copy is necessary so that we do not + // inadvertently mutate the state of the metadata passed to this method. + AnnotationAttributes result = new AnnotationAttributes(attributesList.get(0)); - Set overridableAttributeNames = new HashSet(results.keySet()); + Set overridableAttributeNames = new HashSet(result.keySet()); overridableAttributeNames.remove(AnnotationUtils.VALUE); // Since the map is a LinkedMultiValueMap, we depend on the ordering of @@ -152,14 +157,14 @@ public static AnnotationAttributes getMergedAnnotationAttributes( if (value != null) { // Store the value, potentially overriding a value from an attribute // of the same name found higher in the annotation hierarchy. - results.put(overridableAttributeName, value); + result.put(overridableAttributeName, value); } } } } } - return results; + return result; } } diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/MethodMetadataReadingVisitor.java b/spring-core/src/main/java/org/springframework/core/type/classreading/MethodMetadataReadingVisitor.java index 00bd9cb1303..9dfcfd1a82d 100644 --- a/spring-core/src/main/java/org/springframework/core/type/classreading/MethodMetadataReadingVisitor.java +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/MethodMetadataReadingVisitor.java @@ -122,7 +122,8 @@ public AnnotationAttributes getAnnotationAttributes(String annotationName) { public AnnotationAttributes getAnnotationAttributes(String annotationName, boolean classValuesAsString) { AnnotationAttributes raw = AnnotationReadingVisitorUtils.getMergedAnnotationAttributes( this.attributesMap, this.metaAnnotationMap, annotationName); - return AnnotationReadingVisitorUtils.convertClassValues(this.classLoader, raw, classValuesAsString); + return AnnotationReadingVisitorUtils.convertClassValues( + "method '" + getMethodName() + "'", this.classLoader, raw, classValuesAsString); } @Override @@ -137,8 +138,9 @@ public MultiValueMap getAllAnnotationAttributes(String annotatio } MultiValueMap allAttributes = new LinkedMultiValueMap(); for (AnnotationAttributes annotationAttributes : this.attributesMap.get(annotationName)) { - for (Map.Entry entry : AnnotationReadingVisitorUtils.convertClassValues( - this.classLoader, annotationAttributes, classValuesAsString).entrySet()) { + AnnotationAttributes convertedAttributes = AnnotationReadingVisitorUtils.convertClassValues( + "method '" + getMethodName() + "'", this.classLoader, annotationAttributes, classValuesAsString); + for (Map.Entry entry : convertedAttributes.entrySet()) { allAttributes.add(entry.getKey(), entry.getValue()); } } diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/RecursiveAnnotationArrayVisitor.java b/spring-core/src/main/java/org/springframework/core/type/classreading/RecursiveAnnotationArrayVisitor.java index 3c5bd9a43f6..9c466b777d6 100644 --- a/spring-core/src/main/java/org/springframework/core/type/classreading/RecursiveAnnotationArrayVisitor.java +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/RecursiveAnnotationArrayVisitor.java @@ -69,7 +69,7 @@ public void visit(String attributeName, Object attributeValue) { @Override public AnnotationVisitor visitAnnotation(String attributeName, String asmTypeDescriptor) { String annotationType = Type.getType(asmTypeDescriptor).getClassName(); - AnnotationAttributes nestedAttributes = new AnnotationAttributes(); + AnnotationAttributes nestedAttributes = new AnnotationAttributes(annotationType, this.classLoader); this.allNestedAttributes.add(nestedAttributes); return new RecursiveAnnotationAttributesVisitor(annotationType, nestedAttributes, this.classLoader); } diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/RecursiveAnnotationAttributesVisitor.java b/spring-core/src/main/java/org/springframework/core/type/classreading/RecursiveAnnotationAttributesVisitor.java index 0d2176f015e..ff7b92fba5f 100644 --- a/spring-core/src/main/java/org/springframework/core/type/classreading/RecursiveAnnotationAttributesVisitor.java +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/RecursiveAnnotationAttributesVisitor.java @@ -16,10 +16,6 @@ package org.springframework.core.type.classreading; -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; - import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.annotation.AnnotationUtils; @@ -30,7 +26,7 @@ */ class RecursiveAnnotationAttributesVisitor extends AbstractRecursiveAnnotationVisitor { - private final String annotationType; + protected final String annotationType; public RecursiveAnnotationAttributesVisitor( @@ -42,49 +38,8 @@ public RecursiveAnnotationAttributesVisitor( @Override - public final void visitEnd() { - try { - Class annotationClass = this.classLoader.loadClass(this.annotationType); - doVisitEnd(annotationClass); - } - catch (ClassNotFoundException ex) { - logger.debug("Failed to class-load type while reading annotation metadata. " + - "This is a non-fatal error, but certain annotation metadata may be unavailable.", ex); - } - } - - protected void doVisitEnd(Class annotationClass) { - registerDefaultValues(annotationClass); - } - - private void registerDefaultValues(Class annotationClass) { - // Only do defaults scanning for public annotations; we'd run into - // IllegalAccessExceptions otherwise, and we don't want to mess with - // accessibility in a SecurityManager environment. - if (Modifier.isPublic(annotationClass.getModifiers())) { - // Check declared default values of attributes in the annotation type. - Method[] annotationAttributes = annotationClass.getMethods(); - for (Method annotationAttribute : annotationAttributes) { - String attributeName = annotationAttribute.getName(); - Object defaultValue = annotationAttribute.getDefaultValue(); - if (defaultValue != null && !this.attributes.containsKey(attributeName)) { - if (defaultValue instanceof Annotation) { - defaultValue = AnnotationAttributes.fromMap(AnnotationUtils.getAnnotationAttributes( - (Annotation) defaultValue, false, true)); - } - else if (defaultValue instanceof Annotation[]) { - Annotation[] realAnnotations = (Annotation[]) defaultValue; - AnnotationAttributes[] mappedAnnotations = new AnnotationAttributes[realAnnotations.length]; - for (int i = 0; i < realAnnotations.length; i++) { - mappedAnnotations[i] = AnnotationAttributes.fromMap( - AnnotationUtils.getAnnotationAttributes(realAnnotations[i], false, true)); - } - defaultValue = mappedAnnotations; - } - this.attributes.put(attributeName, defaultValue); - } - } - } + public void visitEnd() { + AnnotationUtils.registerDefaultValues(this.attributes); } } diff --git a/spring-core/src/test/java/org/springframework/core/annotation/AnnotationAttributesTests.java b/spring-core/src/test/java/org/springframework/core/annotation/AnnotationAttributesTests.java index 57cdd7ab2eb..99818400e67 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/AnnotationAttributesTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/AnnotationAttributesTests.java @@ -18,16 +18,11 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Arrays; -import java.util.List; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import org.springframework.core.annotation.AnnotationUtilsTests.ContextConfig; -import org.springframework.core.annotation.AnnotationUtilsTests.ImplicitAliasesContextConfig; - import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; @@ -133,21 +128,21 @@ public void nestedAnnotations() throws Exception { @Test public void getEnumWithNullAttributeName() { exception.expect(IllegalArgumentException.class); - exception.expectMessage(containsString("attributeName must not be null or empty")); + exception.expectMessage("must not be null or empty"); attributes.getEnum(null); } @Test public void getEnumWithEmptyAttributeName() { exception.expect(IllegalArgumentException.class); - exception.expectMessage(containsString("attributeName must not be null or empty")); + exception.expectMessage("must not be null or empty"); attributes.getEnum(""); } @Test public void getEnumWithUnknownAttributeName() { exception.expect(IllegalArgumentException.class); - exception.expectMessage(containsString("Attribute 'bogus' not found")); + exception.expectMessage("Attribute 'bogus' not found"); attributes.getEnum("bogus"); } @@ -159,337 +154,6 @@ public void getEnumWithTypeMismatch() { attributes.getEnum("color"); } - @Test - public void getAliasedString() { - final String value = "metaverse"; - - attributes.clear(); - attributes.put("name", value); - assertEquals(value, getAliasedString("name")); - assertEquals(value, getAliasedString("value")); - - attributes.clear(); - attributes.put("value", value); - assertEquals(value, getAliasedString("name")); - assertEquals(value, getAliasedString("value")); - - attributes.clear(); - attributes.put("name", value); - attributes.put("value", value); - assertEquals(value, getAliasedString("name")); - assertEquals(value, getAliasedString("value")); - } - - @Test - public void getAliasedStringWithImplicitAliases() { - final String value = "metaverse"; - final List aliases = Arrays.asList("value", "location1", "location2", "location3", "xmlFile", "groovyScript"); - - attributes = new AnnotationAttributes(ImplicitAliasesContextConfig.class); - attributes.put("value", value); - aliases.stream().forEach(alias -> assertEquals(value, getAliasedStringWithImplicitAliases(alias))); - - attributes.clear(); - attributes.put("location1", value); - aliases.stream().forEach(alias -> assertEquals(value, getAliasedStringWithImplicitAliases(alias))); - - attributes.clear(); - attributes.put("value", value); - attributes.put("location1", value); - attributes.put("xmlFile", value); - attributes.put("groovyScript", value); - aliases.stream().forEach(alias -> assertEquals(value, getAliasedStringWithImplicitAliases(alias))); - } - - @Test - public void getAliasedStringWithImplicitAliasesWithMissingAliasedAttributes() { - final List aliases = Arrays.asList("value", "location1", "location2", "location3", "xmlFile", "groovyScript"); - attributes = new AnnotationAttributes(ImplicitAliasesContextConfig.class); - - exception.expect(IllegalArgumentException.class); - exception.expectMessage(startsWith("Neither attribute 'value' nor one of its aliases [")); - aliases.stream().forEach(alias -> exception.expectMessage(containsString(alias))); - exception.expectMessage(endsWith("] was found in attributes for annotation [" + ImplicitAliasesContextConfig.class.getName() + "]")); - getAliasedStringWithImplicitAliases("value"); - } - - @Test - public void getAliasedStringFromSynthesizedAnnotationAttributes() { - Scope scope = ScopedComponent.class.getAnnotation(Scope.class); - AnnotationAttributes scopeAttributes = AnnotationUtils.getAnnotationAttributes(ScopedComponent.class, scope); - - assertEquals("custom", getAliasedString(scopeAttributes, "name")); - assertEquals("custom", getAliasedString(scopeAttributes, "value")); - } - - @Test - public void getAliasedStringWithMissingAliasedAttributes() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage(equalTo("Neither attribute 'name' nor one of its aliases [value] was found in attributes for annotation [unknown]")); - getAliasedString("name"); - } - - @Test - public void getAliasedStringWithDifferentAliasedValues() { - attributes.put("name", "request"); - attributes.put("value", "session"); - - exception.expect(AnnotationConfigurationException.class); - exception.expectMessage(containsString("In annotation [" + Scope.class.getName() + "]")); - exception.expectMessage(containsString("attribute [name] and its alias [value]")); - exception.expectMessage(containsString("[request] and [session]")); - exception.expectMessage(containsString("but only one is permitted")); - - getAliasedString("name"); - } - - private String getAliasedString(String attributeName) { - return getAliasedString(this.attributes, attributeName); - } - - private String getAliasedString(AnnotationAttributes attrs, String attributeName) { - return attrs.getAliasedString(attributeName, Scope.class, null); - } - - private String getAliasedStringWithImplicitAliases(String attributeName) { - return this.attributes.getAliasedString(attributeName, ImplicitAliasesContextConfig.class, null); - } - - @Test - public void getAliasedStringArray() { - final String[] INPUT = new String[] {"test.xml"}; - final String[] EMPTY = new String[0]; - - attributes.clear(); - attributes.put("location", INPUT); - assertArrayEquals(INPUT, getAliasedStringArray("location")); - assertArrayEquals(INPUT, getAliasedStringArray("value")); - - attributes.clear(); - attributes.put("value", INPUT); - assertArrayEquals(INPUT, getAliasedStringArray("location")); - assertArrayEquals(INPUT, getAliasedStringArray("value")); - - attributes.clear(); - attributes.put("location", INPUT); - attributes.put("value", INPUT); - assertArrayEquals(INPUT, getAliasedStringArray("location")); - assertArrayEquals(INPUT, getAliasedStringArray("value")); - - attributes.clear(); - attributes.put("location", INPUT); - attributes.put("value", EMPTY); - assertArrayEquals(INPUT, getAliasedStringArray("location")); - assertArrayEquals(INPUT, getAliasedStringArray("value")); - - attributes.clear(); - attributes.put("location", EMPTY); - attributes.put("value", INPUT); - assertArrayEquals(INPUT, getAliasedStringArray("location")); - assertArrayEquals(INPUT, getAliasedStringArray("value")); - - attributes.clear(); - attributes.put("location", EMPTY); - attributes.put("value", EMPTY); - assertArrayEquals(EMPTY, getAliasedStringArray("location")); - assertArrayEquals(EMPTY, getAliasedStringArray("value")); - } - - @Test - public void getAliasedStringArrayWithImplicitAliases() { - final String[] INPUT = new String[] {"test.xml"}; - final String[] EMPTY = new String[0]; - final List aliases = Arrays.asList("value", "location1", "location2", "location3", "xmlFile", "groovyScript"); - - attributes = new AnnotationAttributes(ImplicitAliasesContextConfig.class); - - attributes.put("location1", INPUT); - aliases.stream().forEach(alias -> assertArrayEquals(INPUT, getAliasedStringArrayWithImplicitAliases(alias))); - - attributes.clear(); - attributes.put("value", INPUT); - aliases.stream().forEach(alias -> assertArrayEquals(INPUT, getAliasedStringArrayWithImplicitAliases(alias))); - - attributes.clear(); - attributes.put("location1", INPUT); - attributes.put("value", INPUT); - aliases.stream().forEach(alias -> assertArrayEquals(INPUT, getAliasedStringArrayWithImplicitAliases(alias))); - - attributes.clear(); - attributes.put("location1", INPUT); - attributes.put("value", EMPTY); - aliases.stream().forEach(alias -> assertArrayEquals(INPUT, getAliasedStringArrayWithImplicitAliases(alias))); - - attributes.clear(); - attributes.put("location1", EMPTY); - attributes.put("value", INPUT); - aliases.stream().forEach(alias -> assertArrayEquals(INPUT, getAliasedStringArrayWithImplicitAliases(alias))); - - attributes.clear(); - attributes.put("location1", EMPTY); - attributes.put("value", EMPTY); - aliases.stream().forEach(alias -> assertArrayEquals(EMPTY, getAliasedStringArrayWithImplicitAliases(alias))); - } - - @Test - public void getAliasedStringArrayWithImplicitAliasesWithMissingAliasedAttributes() { - final List aliases = Arrays.asList("value", "location1", "location2", "location3", "xmlFile", "groovyScript"); - attributes = new AnnotationAttributes(ImplicitAliasesContextConfig.class); - - exception.expect(IllegalArgumentException.class); - exception.expectMessage(startsWith("Neither attribute 'value' nor one of its aliases [")); - aliases.stream().forEach(alias -> exception.expectMessage(containsString(alias))); - exception.expectMessage(endsWith("] was found in attributes for annotation [" + ImplicitAliasesContextConfig.class.getName() + "]")); - getAliasedStringArrayWithImplicitAliases("value"); - } - - @Test - public void getAliasedStringArrayWithMissingAliasedAttributes() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage(equalTo("Neither attribute 'location' nor one of its aliases [value] was found in attributes for annotation [unknown]")); - getAliasedStringArray("location"); - } - - @Test - public void getAliasedStringArrayWithDifferentAliasedValues() { - attributes.put("location", new String[] {"1.xml"}); - attributes.put("value", new String[] {"2.xml"}); - - exception.expect(AnnotationConfigurationException.class); - exception.expectMessage(containsString("In annotation [" + ContextConfig.class.getName() + "]")); - exception.expectMessage(containsString("attribute [location] and its alias [value]")); - exception.expectMessage(containsString("[{1.xml}] and [{2.xml}]")); - exception.expectMessage(containsString("but only one is permitted")); - - getAliasedStringArray("location"); - } - - private String[] getAliasedStringArray(String attributeName) { - // Note: even though the attributes we test against here are of type - // String instead of String[], it doesn't matter... since - // AnnotationAttributes does not validate the actual return type of - // attributes in the annotation. - return attributes.getAliasedStringArray(attributeName, ContextConfig.class, null); - } - - private String[] getAliasedStringArrayWithImplicitAliases(String attributeName) { - // Note: even though the attributes we test against here are of type - // String instead of String[], it doesn't matter... since - // AnnotationAttributes does not validate the actual return type of - // attributes in the annotation. - return this.attributes.getAliasedStringArray(attributeName, ImplicitAliasesContextConfig.class, null); - } - - @Test - public void getAliasedClassArray() { - final Class[] INPUT = new Class[] {String.class}; - final Class[] EMPTY = new Class[0]; - - attributes.clear(); - attributes.put("classes", INPUT); - assertArrayEquals(INPUT, getAliasedClassArray("classes")); - assertArrayEquals(INPUT, getAliasedClassArray("value")); - - attributes.clear(); - attributes.put("value", INPUT); - assertArrayEquals(INPUT, getAliasedClassArray("classes")); - assertArrayEquals(INPUT, getAliasedClassArray("value")); - - attributes.clear(); - attributes.put("classes", INPUT); - attributes.put("value", INPUT); - assertArrayEquals(INPUT, getAliasedClassArray("classes")); - assertArrayEquals(INPUT, getAliasedClassArray("value")); - - attributes.clear(); - attributes.put("classes", INPUT); - attributes.put("value", EMPTY); - assertArrayEquals(INPUT, getAliasedClassArray("classes")); - assertArrayEquals(INPUT, getAliasedClassArray("value")); - - attributes.clear(); - attributes.put("classes", EMPTY); - attributes.put("value", INPUT); - assertArrayEquals(INPUT, getAliasedClassArray("classes")); - assertArrayEquals(INPUT, getAliasedClassArray("value")); - - attributes.clear(); - attributes.put("classes", EMPTY); - attributes.put("value", EMPTY); - assertArrayEquals(EMPTY, getAliasedClassArray("classes")); - assertArrayEquals(EMPTY, getAliasedClassArray("value")); - } - - @Test - public void getAliasedClassArrayWithImplicitAliases() { - final Class[] INPUT = new Class[] {String.class}; - final Class[] EMPTY = new Class[0]; - final List aliases = Arrays.asList("value", "location1", "location2", "location3", "xmlFile", "groovyScript"); - - attributes = new AnnotationAttributes(ImplicitAliasesContextConfig.class); - - attributes.put("location1", INPUT); - aliases.stream().forEach(alias -> assertArrayEquals(INPUT, getAliasedClassArrayWithImplicitAliases(alias))); - - attributes.clear(); - attributes.put("value", INPUT); - aliases.stream().forEach(alias -> assertArrayEquals(INPUT, getAliasedClassArrayWithImplicitAliases(alias))); - - attributes.clear(); - attributes.put("location1", INPUT); - attributes.put("value", INPUT); - aliases.stream().forEach(alias -> assertArrayEquals(INPUT, getAliasedClassArrayWithImplicitAliases(alias))); - - attributes.clear(); - attributes.put("location1", INPUT); - attributes.put("value", EMPTY); - aliases.stream().forEach(alias -> assertArrayEquals(INPUT, getAliasedClassArrayWithImplicitAliases(alias))); - - attributes.clear(); - attributes.put("location1", EMPTY); - attributes.put("value", INPUT); - aliases.stream().forEach(alias -> assertArrayEquals(INPUT, getAliasedClassArrayWithImplicitAliases(alias))); - - attributes.clear(); - attributes.put("location1", EMPTY); - attributes.put("value", EMPTY); - aliases.stream().forEach(alias -> assertArrayEquals(EMPTY, getAliasedClassArrayWithImplicitAliases(alias))); - } - - @Test - public void getAliasedClassArrayWithMissingAliasedAttributes() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage(equalTo("Neither attribute 'classes' nor one of its aliases [value] was found in attributes for annotation [unknown]")); - getAliasedClassArray("classes"); - } - - @Test - public void getAliasedClassArrayWithDifferentAliasedValues() { - attributes.put("classes", new Class[] {String.class}); - attributes.put("value", new Class[] {Number.class}); - - exception.expect(AnnotationConfigurationException.class); - exception.expectMessage(containsString("In annotation [" + Filter.class.getName() + "]")); - exception.expectMessage(containsString("attribute [classes] and its alias [value]")); - exception.expectMessage(containsString("[{class java.lang.String}] and [{class java.lang.Number}]")); - exception.expectMessage(containsString("but only one is permitted")); - - getAliasedClassArray("classes"); - } - - - private Class[] getAliasedClassArray(String attributeName) { - return attributes.getAliasedClassArray(attributeName, Filter.class, null); - } - - private Class[] getAliasedClassArrayWithImplicitAliases(String attributeName) { - // Note: even though the attributes we test against here are of type - // String instead of Class[], it doesn't matter... since - // AnnotationAttributes does not validate the actual return type of - // attributes in the annotation. - return this.attributes.getAliasedClassArray(attributeName, ImplicitAliasesContextConfig.class, null); - } - enum Color { @@ -514,23 +178,4 @@ enum Color { static class FilteredClass { } - - /** - * Mock of {@code org.springframework.context.annotation.Scope}. - */ - @Retention(RetentionPolicy.RUNTIME) - @interface Scope { - - @AliasFor(attribute = "name") - String value() default "singleton"; - - @AliasFor(attribute = "value") - String name() default "singleton"; - } - - - @Scope(name = "custom") - static class ScopedComponent { - } - } diff --git a/spring-core/src/test/java/org/springframework/core/type/AnnotationMetadataTests.java b/spring-core/src/test/java/org/springframework/core/type/AnnotationMetadataTests.java index 5e4083e0fff..2f377fc0f58 100644 --- a/spring-core/src/test/java/org/springframework/core/type/AnnotationMetadataTests.java +++ b/spring-core/src/test/java/org/springframework/core/type/AnnotationMetadataTests.java @@ -30,6 +30,7 @@ import org.junit.Test; +import org.springframework.core.annotation.AliasFor; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; @@ -291,6 +292,7 @@ private void doTestAnnotationInfo(AnnotationMetadata metadata) { Set methods = metadata.getAnnotatedMethods(DirectAnnotation.class.getName()); MethodMetadata method = methods.iterator().next(); assertEquals("direct", method.getAnnotationAttributes(DirectAnnotation.class.getName()).get("value")); + assertEquals("direct", method.getAnnotationAttributes(DirectAnnotation.class.getName()).get("myValue")); List allMeta = method.getAllAnnotationAttributes(DirectAnnotation.class.getName()).get("value"); assertThat(new HashSet(allMeta), is(equalTo(new HashSet(Arrays.asList("direct", "meta"))))); allMeta = method.getAllAnnotationAttributes(DirectAnnotation.class.getName()).get("additional"); @@ -416,7 +418,11 @@ public static enum SomeEnum { @Retention(RetentionPolicy.RUNTIME) public @interface DirectAnnotation { - String value(); + @AliasFor("myValue") + String value() default ""; + + @AliasFor("value") + String myValue() default ""; String additional() default "direct"; } @@ -449,7 +455,7 @@ public static enum SomeEnum { } // SPR-10914 - public static enum SubclassEnum { + public enum SubclassEnum { FOO { /* Do not delete! This subclassing is intentional. */ }, @@ -489,14 +495,14 @@ private static class AnnotatedComponentSubClass extends AnnotatedComponent { @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Component - public static @interface TestConfiguration { + public @interface TestConfiguration { String value() default ""; } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) - public static @interface TestComponentScan { + public @interface TestComponentScan { String[] value() default {}; @@ -509,7 +515,7 @@ private static class AnnotatedComponentSubClass extends AnnotatedComponent { @TestComponentScan(basePackages = "bogus") @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) - public static @interface ComposedConfigurationWithAttributeOverrides { + public @interface ComposedConfigurationWithAttributeOverrides { String[] basePackages() default {}; } @@ -520,19 +526,19 @@ public static class ComposedConfigurationWithAttributeOverridesClass { @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) - public static @interface NamedAnnotation1 { + public @interface NamedAnnotation1 { String name() default ""; } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) - public static @interface NamedAnnotation2 { + public @interface NamedAnnotation2 { String name() default ""; } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) - public static @interface NamedAnnotation3 { + public @interface NamedAnnotation3 { String name() default ""; } @@ -547,7 +553,7 @@ public static class NamedAnnotationsClass { @NamedAnnotation3(name = "name 3") @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) - public static @interface NamedComposedAnnotation { + public @interface NamedComposedAnnotation { } @NamedComposedAnnotation From 1d39d762f016e1a98e17867fc5b3e65200fa2fe3 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 15 Jul 2016 15:40:26 +0200 Subject: [PATCH 0027/1274] HibernateExceptionTranslator converts JPA exceptions as well (for Hibernate 5.2) Issue: SPR-14455 --- .../hibernate5/HibernateExceptionTranslator.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateExceptionTranslator.java b/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateExceptionTranslator.java index 5e4886f2f8f..38070d625a0 100644 --- a/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateExceptionTranslator.java +++ b/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateExceptionTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,14 +16,18 @@ package org.springframework.orm.hibernate5; +import javax.persistence.PersistenceException; + import org.hibernate.HibernateException; import org.springframework.dao.DataAccessException; import org.springframework.dao.support.PersistenceExceptionTranslator; +import org.springframework.orm.jpa.EntityManagerFactoryUtils; /** * {@link PersistenceExceptionTranslator} capable of translating {@link HibernateException} - * instances to Spring's {@link DataAccessException} hierarchy. + * instances to Spring's {@link DataAccessException} hierarchy. As of Spring 4.3.2 and + * Hibernate 5.2, it also converts standard JPA {@link PersistenceException} instances. * *

Extended by {@link LocalSessionFactoryBean}, so there is no need to declare this * translator in addition to a {@code LocalSessionFactoryBean}. @@ -35,6 +39,7 @@ * @since 4.2 * @see org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor * @see SessionFactoryUtils#convertHibernateAccessException(HibernateException) + * @see EntityManagerFactoryUtils#convertJpaAccessExceptionIfPossible(RuntimeException) */ public class HibernateExceptionTranslator implements PersistenceExceptionTranslator { @@ -43,13 +48,16 @@ public DataAccessException translateExceptionIfPossible(RuntimeException ex) { if (ex instanceof HibernateException) { return convertHibernateAccessException((HibernateException) ex); } - return null; + if (ex instanceof PersistenceException && ex.getCause() instanceof HibernateException) { + return convertHibernateAccessException((HibernateException) ex.getCause()); + } + return EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(ex); } /** * Convert the given HibernateException to an appropriate exception from the * {@code org.springframework.dao} hierarchy. - * @param ex HibernateException that occured + * @param ex HibernateException that occurred * @return a corresponding DataAccessException * @see SessionFactoryUtils#convertHibernateAccessException */ From 29f980ec72dffa8b19a76630815bf5479e04d752 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 15 Jul 2016 15:40:38 +0200 Subject: [PATCH 0028/1274] Unwrap JPA PersistenceException on flush failure (for Hibernate 5.2) Issue: SPR-14457 --- .../orm/hibernate5/HibernateTemplate.java | 7 ++++ .../HibernateTransactionManager.java | 19 +++++++++++ .../orm/hibernate5/SessionFactoryUtils.java | 32 +++++++++++++++++++ .../SpringFlushSynchronization.java | 11 ++----- .../SpringSessionSynchronization.java | 17 ++-------- 5 files changed, 62 insertions(+), 24 deletions(-) diff --git a/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateTemplate.java b/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateTemplate.java index 601d389ae0a..83b43d4af36 100644 --- a/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateTemplate.java +++ b/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateTemplate.java @@ -24,6 +24,7 @@ import java.util.Collection; import java.util.Iterator; import java.util.List; +import javax.persistence.PersistenceException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -357,6 +358,12 @@ protected T doExecute(HibernateCallback action, boolean enforceNativeSess catch (HibernateException ex) { throw SessionFactoryUtils.convertHibernateAccessException(ex); } + catch (PersistenceException ex) { + if (ex.getCause() instanceof HibernateException) { + throw SessionFactoryUtils.convertHibernateAccessException((HibernateException) ex.getCause()); + } + throw ex; + } catch (RuntimeException ex) { // Callback code threw application exception... throw ex; diff --git a/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateTransactionManager.java b/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateTransactionManager.java index cafee104658..746d70c24d2 100644 --- a/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateTransactionManager.java +++ b/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateTransactionManager.java @@ -18,6 +18,7 @@ import java.sql.Connection; import java.sql.ResultSet; +import javax.persistence.PersistenceException; import javax.sql.DataSource; import org.hibernate.ConnectionReleaseMode; @@ -588,6 +589,12 @@ protected void doCommit(DefaultTransactionStatus status) { // assumably failed to flush changes to database throw convertHibernateAccessException(ex); } + catch (PersistenceException ex) { + if (ex.getCause() instanceof HibernateException) { + throw convertHibernateAccessException((HibernateException) ex.getCause()); + } + throw ex; + } } @Override @@ -607,6 +614,12 @@ protected void doRollback(DefaultTransactionStatus status) { // Shouldn't really happen, as a rollback doesn't cause a flush. throw convertHibernateAccessException(ex); } + catch (PersistenceException ex) { + if (ex.getCause() instanceof HibernateException) { + throw convertHibernateAccessException((HibernateException) ex.getCause()); + } + throw ex; + } finally { if (!txObject.isNewSession() && !this.hibernateManagedSession) { // Clear all pending inserts/updates/deletes in the Session. @@ -825,6 +838,12 @@ public void flush() { catch (HibernateException ex) { throw convertHibernateAccessException(ex); } + catch (PersistenceException ex) { + if (ex.getCause() instanceof HibernateException) { + throw convertHibernateAccessException((HibernateException) ex.getCause()); + } + throw ex; + } } } diff --git a/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/SessionFactoryUtils.java b/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/SessionFactoryUtils.java index f297f36066f..7f3d2e28a78 100644 --- a/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/SessionFactoryUtils.java +++ b/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/SessionFactoryUtils.java @@ -18,6 +18,7 @@ import java.lang.reflect.Method; import java.util.Map; +import javax.persistence.PersistenceException; import javax.sql.DataSource; import org.apache.commons.logging.Log; @@ -123,6 +124,37 @@ static FlushMode getFlushMode(Session session) { return (FlushMode) ReflectionUtils.invokeMethod(getFlushMode, session); } + /** + * Trigger a flush on the given Hibernate Session, converting regular + * {@link HibernateException} instances as well as Hibernate 5.2's + * {@link PersistenceException} wrappers accordingly. + * @param session the Hibernate Session to flush + * @param synch whether this flush is triggered by transaction synchronization + * @throws DataAccessException + * @since 4.3.2 + */ + static void flush(Session session, boolean synch) throws DataAccessException { + if (synch) { + logger.debug("Flushing Hibernate Session on transaction synchronization"); + } + else { + logger.debug("Flushing Hibernate Session on explicit request"); + } + try { + session.flush(); + } + catch (HibernateException ex) { + throw convertHibernateAccessException(ex); + } + catch (PersistenceException ex) { + if (ex.getCause() instanceof HibernateException) { + throw convertHibernateAccessException((HibernateException) ex.getCause()); + } + throw ex; + } + + } + /** * Perform actual closing of the Hibernate Session, * catching and logging any cleanup exceptions thrown. diff --git a/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/SpringFlushSynchronization.java b/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/SpringFlushSynchronization.java index b421a4f3740..1c370a00bd1 100644 --- a/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/SpringFlushSynchronization.java +++ b/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/SpringFlushSynchronization.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,6 @@ package org.springframework.orm.hibernate5; -import org.hibernate.HibernateException; import org.hibernate.Session; import org.springframework.transaction.support.TransactionSynchronizationAdapter; @@ -40,13 +39,7 @@ public SpringFlushSynchronization(Session session) { @Override public void flush() { - try { - SessionFactoryUtils.logger.debug("Flushing Hibernate Session on explicit request"); - this.session.flush(); - } - catch (HibernateException ex) { - throw SessionFactoryUtils.convertHibernateAccessException(ex); - } + SessionFactoryUtils.flush(this.session, false); } diff --git a/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/SpringSessionSynchronization.java b/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/SpringSessionSynchronization.java index 09239e5663e..b806672064b 100644 --- a/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/SpringSessionSynchronization.java +++ b/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/SpringSessionSynchronization.java @@ -17,7 +17,6 @@ package org.springframework.orm.hibernate5; import org.hibernate.FlushMode; -import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.SessionFactory; @@ -83,13 +82,7 @@ public void resume() { @Override public void flush() { - try { - SessionFactoryUtils.logger.debug("Flushing Hibernate Session on explicit request"); - getCurrentSession().flush(); - } - catch (HibernateException ex) { - throw SessionFactoryUtils.convertHibernateAccessException(ex); - } + SessionFactoryUtils.flush(getCurrentSession(), false); } @Override @@ -99,13 +92,7 @@ public void beforeCommit(boolean readOnly) throws DataAccessException { // Read-write transaction -> flush the Hibernate Session. // Further check: only flush when not FlushMode.MANUAL. if (!FlushMode.MANUAL.equals(SessionFactoryUtils.getFlushMode(session))) { - try { - SessionFactoryUtils.logger.debug("Flushing Hibernate Session on transaction synchronization"); - session.flush(); - } - catch (HibernateException ex) { - throw SessionFactoryUtils.convertHibernateAccessException(ex); - } + SessionFactoryUtils.flush(getCurrentSession(), true); } } } From 70e666b4a31d83c050daf23846b0fb3b68f1a011 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 15 Jul 2016 15:42:32 +0200 Subject: [PATCH 0029/1274] MultipartResolutionDelegate's resolveMultipartArgument properly operates on Servlet 2.5 Issue: SPR-14461 --- .../support/MultipartResolutionDelegate.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/multipart/support/MultipartResolutionDelegate.java b/spring-web/src/main/java/org/springframework/web/multipart/support/MultipartResolutionDelegate.java index 305b639642f..42248276181 100644 --- a/spring-web/src/main/java/org/springframework/web/multipart/support/MultipartResolutionDelegate.java +++ b/spring-web/src/main/java/org/springframework/web/multipart/support/MultipartResolutionDelegate.java @@ -122,18 +122,18 @@ else if (isMultipartFileArray(parameter)) { return null; } } - else if (servletPartClass == parameter.getNestedParameterType()) { - return (isMultipart ? RequestPartResolver.resolvePart(request, name) : null); - } - else if (isPartCollection(parameter)) { - return (isMultipart ? RequestPartResolver.resolvePartList(request, name) : null); - } - else if (isPartArray(parameter)) { - return (isMultipart ? RequestPartResolver.resolvePartArray(request, name) : null); - } - else { - return UNRESOLVABLE; + else if (servletPartClass != null) { + if (servletPartClass == parameter.getNestedParameterType()) { + return (isMultipart ? RequestPartResolver.resolvePart(request, name) : null); + } + else if (isPartCollection(parameter)) { + return (isMultipart ? RequestPartResolver.resolvePartList(request, name) : null); + } + else if (isPartArray(parameter)) { + return (isMultipart ? RequestPartResolver.resolvePartArray(request, name) : null); + } } + return UNRESOLVABLE; } private static boolean isMultipartFileCollection(MethodParameter methodParam) { From 12bff6b3a02cb720594d726f3a771792842d3726 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 15 Jul 2016 15:44:13 +0200 Subject: [PATCH 0030/1274] Velocity deprecation note in reference documentation Issue: SPR-14460 --- src/asciidoc/web-view.adoc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/asciidoc/web-view.adoc b/src/asciidoc/web-view.adoc index c892d266333..3735a8d3623 100644 --- a/src/asciidoc/web-view.adoc +++ b/src/asciidoc/web-view.adoc @@ -106,6 +106,13 @@ applications. The languages are quite similar and serve similar needs and so are considered together in this section. For semantic and syntactic differences between the two languages, see the http://www.freemarker.org[FreeMarker] web site. +[NOTE] +==== +As of Spring Framework 4.3, Velocity support has been deprecated due to six years +without active maintenance of the Apache Velocity project. We recommend Spring's +FreeMarker support instead, or Thymeleaf which comes with Spring support itself. +==== + [[view-velocity-dependencies]] From 9e9340385716ae0185ed9a016848a98a16d5281a Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 15 Jul 2016 16:02:06 +0200 Subject: [PATCH 0031/1274] ConfigurationClassParser load annotations through source class loader Issue: SPR-10343 --- .../context/annotation/ConfigurationClassParser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java index 632c40f07b2..673ee5c8a1b 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java @@ -857,7 +857,7 @@ public Collection getAnnotationAttributes(String annotationType, St private SourceClass getRelated(String className) throws IOException { if (this.source instanceof Class) { try { - Class clazz = resourceLoader.getClassLoader().loadClass(className); + Class clazz = ((Class) this.source).getClassLoader().loadClass(className); return asSourceClass(clazz); } catch (ClassNotFoundException ex) { From dee50d5e286ed126d274ee29d8bdd36399d2bbc7 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Fri, 15 Jul 2016 19:54:53 +0200 Subject: [PATCH 0032/1274] Polish annotation utils (cherry picked from commit 177f4ec) --- ...liasAwareAnnotationAttributeExtractor.java | 1 - .../core/annotation/AnnotationAttributes.java | 19 +++++++++++-------- .../core/annotation/AnnotationUtils.java | 5 ++--- .../DefaultAnnotationAttributeExtractor.java | 2 +- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AbstractAliasAwareAnnotationAttributeExtractor.java b/spring-core/src/main/java/org/springframework/core/annotation/AbstractAliasAwareAnnotationAttributeExtractor.java index bc44e15ff5e..32b8725c2d6 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AbstractAliasAwareAnnotationAttributeExtractor.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AbstractAliasAwareAnnotationAttributeExtractor.java @@ -17,7 +17,6 @@ package org.springframework.core.annotation; import java.lang.annotation.Annotation; -import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; import java.util.List; import java.util.Map; diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAttributes.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAttributes.java index 689e4b31cf8..97af36d5dee 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAttributes.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAttributes.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -36,8 +36,7 @@ * *

Provides 'pseudo-reification' to avoid noisy Map generics in the calling * code as well as convenience methods for looking up annotation attributes - * in a type-safe fashion, including support for attribute aliases configured - * via {@link AliasFor @AliasFor}. + * in a type-safe fashion. * * @author Chris Beams * @author Sam Brannen @@ -45,14 +44,13 @@ * @since 3.1.1 * @see AnnotationUtils#getAnnotationAttributes * @see AnnotatedElementUtils - * @see AliasFor */ @SuppressWarnings("serial") public class AnnotationAttributes extends LinkedHashMap { private static final String UNKNOWN = "unknown"; - private Class annotationType; + private final Class annotationType; private final String displayName; @@ -100,18 +98,23 @@ public AnnotationAttributes(Class annotationType) { * or {@code null} to just store the annotation type name * @since 4.3.2 */ - @SuppressWarnings("unchecked") public AnnotationAttributes(String annotationType, ClassLoader classLoader) { Assert.notNull(annotationType, "'annotationType' must not be null"); + this.annotationType = getAnnotationType(annotationType, classLoader); + this.displayName = annotationType; + } + + @SuppressWarnings("unchecked") + private static Class getAnnotationType(String annotationType, ClassLoader classLoader) { if (classLoader != null) { try { - this.annotationType = (Class) classLoader.loadClass(annotationType); + return (Class) classLoader.loadClass(annotationType); } catch (ClassNotFoundException ex) { // Annotation Class not resolvable } } - this.displayName = annotationType; + return null; } /** diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java index 7ea2c1cc14c..750b1ca46dd 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java @@ -1160,11 +1160,10 @@ public static void registerDefaultValues(AnnotationAttributes attributes) { // Only do defaults scanning for public annotations; we'd run into // IllegalAccessExceptions otherwise, and we don't want to mess with // accessibility in a SecurityManager environment. - Class annotationType = attributes.annotationType(); + Class annotationType = attributes.annotationType(); if (annotationType != null && Modifier.isPublic(annotationType.getModifiers())) { // Check declared default values of attributes in the annotation type. - Method[] annotationAttributes = annotationType.getMethods(); - for (Method annotationAttribute : annotationAttributes) { + for (Method annotationAttribute : getAttributeMethods(annotationType)) { String attributeName = annotationAttribute.getName(); Object defaultValue = annotationAttribute.getDefaultValue(); if (defaultValue != null && !attributes.containsKey(attributeName)) { diff --git a/spring-core/src/main/java/org/springframework/core/annotation/DefaultAnnotationAttributeExtractor.java b/spring-core/src/main/java/org/springframework/core/annotation/DefaultAnnotationAttributeExtractor.java index dfcf28ad172..0ea2cf0013b 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/DefaultAnnotationAttributeExtractor.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/DefaultAnnotationAttributeExtractor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. From 69dd40ec89cd96ef0f40834fa717981dde2d763b Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 15 Jul 2016 22:47:05 +0200 Subject: [PATCH 0033/1274] Javadoc fixes and pruning of deprecated references --- .../tomcat/TomcatLoadTimeWeaver.java | 4 ++-- .../jdbc/datasource/ConnectionHandle.java | 4 ++-- .../converter/MarshallingMessageConverter.java | 2 +- .../handler/HandlerMethodSelector.java | 2 +- .../web/method/HandlerMethodSelector.java | 2 +- .../ModelAttributeMethodProcessor.java | 2 +- .../org/springframework/web/util/HtmlUtils.java | 3 +-- .../web/servlet/mvc/WebContentInterceptor.java | 3 +-- .../annotation/MvcUriComponentsBuilder.java | 17 +++++++---------- .../web/servlet/support/RequestContext.java | 17 +++++++++-------- 10 files changed, 26 insertions(+), 30 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/instrument/classloading/tomcat/TomcatLoadTimeWeaver.java b/spring-context/src/main/java/org/springframework/instrument/classloading/tomcat/TomcatLoadTimeWeaver.java index 019952712b8..fe0f7980920 100644 --- a/spring-context/src/main/java/org/springframework/instrument/classloading/tomcat/TomcatLoadTimeWeaver.java +++ b/spring-context/src/main/java/org/springframework/instrument/classloading/tomcat/TomcatLoadTimeWeaver.java @@ -26,8 +26,8 @@ import org.springframework.util.ClassUtils; /** - * {@link org.springframework.instrument.classloading.LoadTimeWeaver} implementation for Tomcat's - * new {@link org.apache.tomcat.InstrumentableClassLoader InstrumentableClassLoader}. + * {@link org.springframework.instrument.classloading.LoadTimeWeaver} implementation + * for Tomcat's new {@code org.apache.tomcat.InstrumentableClassLoader}. * Also capable of handling Spring's TomcatInstrumentableClassLoader when encountered. * * @author Juergen Hoeller diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/ConnectionHandle.java b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/ConnectionHandle.java index 2343aef442a..7b361385ac0 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/ConnectionHandle.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/ConnectionHandle.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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,7 +26,7 @@ * @since 1.1 * @see SimpleConnectionHandle * @see ConnectionHolder - * @see org.springframework.orm.jdo.JpaDialect#getJdbcConnection + * @see org.springframework.orm.jpa.JpaDialect#getJdbcConnection * @see org.springframework.orm.jdo.JdoDialect#getJdbcConnection */ public interface ConnectionHandle { diff --git a/spring-messaging/src/main/java/org/springframework/messaging/converter/MarshallingMessageConverter.java b/spring-messaging/src/main/java/org/springframework/messaging/converter/MarshallingMessageConverter.java index 12d9d8d8bb9..965af158804 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/converter/MarshallingMessageConverter.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/converter/MarshallingMessageConverter.java @@ -40,7 +40,7 @@ * {@link Marshaller} and {@link Unmarshaller} abstractions. * *

This converter requires a {@code Marshaller} and {@code Unmarshaller} before it can - * be used. These can be injected by the {@linkplain MarshallingMessageConverter(Marshaller) + * be used. These can be injected by the {@linkplain #MarshallingMessageConverter(Marshaller) * constructor} or {@linkplain #setMarshaller(Marshaller) bean properties}. * * @author Arjen Poutsma diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/HandlerMethodSelector.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/HandlerMethodSelector.java index 69bd0c1aca1..3d438200c74 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/HandlerMethodSelector.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/HandlerMethodSelector.java @@ -39,7 +39,7 @@ public abstract class HandlerMethodSelector { * @param handlerType the handler type to search handler methods on * @param handlerMethodFilter a {@link MethodFilter} to help recognize handler methods of interest * @return the selected methods, or an empty set - * @see MethodIntrospector#selectMethods(Class, MethodFilter) + * @see MethodIntrospector#selectMethods */ public static Set selectMethods(Class handlerType, MethodFilter handlerMethodFilter) { return MethodIntrospector.selectMethods(handlerType, handlerMethodFilter); diff --git a/spring-web/src/main/java/org/springframework/web/method/HandlerMethodSelector.java b/spring-web/src/main/java/org/springframework/web/method/HandlerMethodSelector.java index 82acd2c3bda..4fc0545b151 100644 --- a/spring-web/src/main/java/org/springframework/web/method/HandlerMethodSelector.java +++ b/spring-web/src/main/java/org/springframework/web/method/HandlerMethodSelector.java @@ -39,7 +39,7 @@ public abstract class HandlerMethodSelector { * @param handlerType the handler type to search handler methods on * @param handlerMethodFilter a {@link MethodFilter} to help recognize handler methods of interest * @return the selected methods, or an empty set - * @see MethodIntrospector#selectMethods(Class, MethodFilter) + * @see MethodIntrospector#selectMethods */ public static Set selectMethods(Class handlerType, MethodFilter handlerMethodFilter) { return MethodIntrospector.selectMethods(handlerType, handlerMethodFilter); diff --git a/spring-web/src/main/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessor.java b/spring-web/src/main/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessor.java index 81030d3bf2b..8c82eefdd58 100644 --- a/spring-web/src/main/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessor.java +++ b/spring-web/src/main/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessor.java @@ -45,7 +45,7 @@ * constructor (and then added to the model). Once created the attribute is * populated via data binding to Servlet request parameters. Validation may be * applied if the argument is annotated with {@code @javax.validation.Valid}. - * or {@link @Validated}. + * or Spring's own {@code @org.springframework.validation.annotation.Validated}. * *

When this handler is created with {@code annotationNotRequired=true} * any non-simple type argument and return value is regarded as a model diff --git a/spring-web/src/main/java/org/springframework/web/util/HtmlUtils.java b/spring-web/src/main/java/org/springframework/web/util/HtmlUtils.java index cb43aa9e042..62109456269 100644 --- a/spring-web/src/main/java/org/springframework/web/util/HtmlUtils.java +++ b/spring-web/src/main/java/org/springframework/web/util/HtmlUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -36,7 +36,6 @@ * @author Martin Kersten * @author Craig Andrews * @since 01.03.2003 - * @see org.apache.commons.lang.StringEscapeUtils */ public abstract class HtmlUtils { diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/WebContentInterceptor.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/WebContentInterceptor.java index 038667f4b67..5e5810e5eac 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/WebContentInterceptor.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/WebContentInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -102,7 +102,6 @@ public void setUrlDecode(boolean urlDecode) { *

Only relevant for the "cacheMappings" setting. * @see #setCacheMappings * @see org.springframework.web.servlet.handler.AbstractUrlHandlerMapping#setUrlPathHelper - * @see org.springframework.web.servlet.mvc.multiaction.AbstractUrlMethodNameResolver#setUrlPathHelper */ public void setUrlPathHelper(UrlPathHelper urlPathHelper) { Assert.notNull(urlPathHelper, "UrlPathHelper must not be null"); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java index d449b45d71b..4091a9b93a1 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java @@ -38,8 +38,8 @@ import org.springframework.cglib.proxy.Factory; import org.springframework.cglib.proxy.MethodProxy; import org.springframework.core.DefaultParameterNameDiscoverer; -import org.springframework.core.MethodParameter; import org.springframework.core.MethodIntrospector; +import org.springframework.core.MethodParameter; import org.springframework.core.ParameterNameDiscoverer; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.SynthesizingMethodParameter; @@ -118,7 +118,7 @@ public class MvcUriComponentsBuilder { * @see #fromMethodName(Class, String, Object...) * @see #fromMethodCall(Object) * @see #fromMappingName(String) - * @see #fromMethod(java.lang.reflect.Method, Object...) + * @see #fromMethod(Class, Method, Object...) */ protected MvcUriComponentsBuilder(UriComponentsBuilder baseUrl) { Assert.notNull(baseUrl, "'baseUrl' is required"); @@ -168,7 +168,7 @@ public static UriComponentsBuilder fromController(UriComponentsBuilder builder, /** * Create a {@link UriComponentsBuilder} from the mapping of a controller * method and an array of method argument values. This method delegates - * to {@link #fromMethod(java.lang.reflect.Method, Object...)}. + * to {@link #fromMethod(Class, Method, Object...)}. * @param controllerType the controller * @param methodName the method name * @param args the argument values @@ -207,7 +207,7 @@ public static UriComponentsBuilder fromMethodName(UriComponentsBuilder builder, /** * Create a {@link UriComponentsBuilder} by invoking a "mock" controller method. * The controller method and the supplied argument values are then used to - * delegate to {@link #fromMethod(java.lang.reflect.Method, Object...)}. + * delegate to {@link #fromMethod(Class, Method, Object...)}. *

For example, given this controller: *

 	 * @RequestMapping("/people/{id}/addresses")
@@ -361,7 +361,7 @@ public static UriComponentsBuilder fromMethod(Class controllerType, Method me
 	}
 
 	/**
-	 * An alternative to {@link #fromMethod(java.lang.reflect.Method, Object...)}
+	 * An alternative to {@link #fromMethod(Class, Method, Object...)}
 	 * that accepts a {@code UriComponentsBuilder} representing the base URL.
 	 * This is useful when using MvcUriComponentsBuilder outside the context of
 	 * processing a request or to apply a custom baseUrl not matching the
@@ -557,8 +557,7 @@ private static WebApplicationContext getWebApplicationContext() {
 	 * on the controller is invoked, the supplied argument values are remembered
 	 * and the result can then be used to create a {@code UriComponentsBuilder}
 	 * via {@link #fromMethodCall(Object)}.
-	 * 

- * Note that this is a shorthand version of {@link #controller(Class)} intended + *

Note that this is a shorthand version of {@link #controller(Class)} intended * for inline use (with a static import), for example: *

 	 * MvcUriComponentsBuilder.fromMethodCall(on(FooController.class).getFoo(1)).build();
@@ -574,8 +573,7 @@ public static  T on(Class controllerType) {
 	 * on the controller is invoked, the supplied argument values are remembered
 	 * and the result can then be used to create {@code UriComponentsBuilder} via
 	 * {@link #fromMethodCall(Object)}.
-	 * 

- * This is a longer version of {@link #on(Class)}. It is needed with controller + *

This is a longer version of {@link #on(Class)}. It is needed with controller * methods returning void as well for repeated invocations. *

 	 * FooController fooController = controller(FooController.class);
@@ -778,7 +776,6 @@ public MethodArgumentBuilder(UriComponentsBuilder baseUrl, Class controllerTy
 		}
 
 		/**
-		 * @see #MethodArgumentBuilder(Class, Method)
 		 * @deprecated as of 4.2, this is deprecated in favor of alternative constructors
 		 * that accept a controllerType argument
 		 */
diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/RequestContext.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/RequestContext.java
index 9fae8679613..df008b774b4 100644
--- a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/RequestContext.java
+++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/RequestContext.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2016 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.
@@ -53,17 +53,18 @@
 import org.springframework.web.util.WebUtils;
 
 /**
- * Context holder for request-specific state, like current web application context, current locale, current theme,
- * and potential binding errors. Provides easy access to localized messages and Errors instances.
+ * Context holder for request-specific state, like current web application context, current locale,
+ * current theme, and potential binding errors. Provides easy access to localized messages and
+ * Errors instances.
  *
- * 

Suitable for exposition to views, and usage within JSP's "useBean" tag, JSP scriptlets, JSTL EL, Velocity - * templates, etc. Necessary for views that do not have access to the servlet request, like Velocity templates. + *

Suitable for exposition to views, and usage within JSP's "useBean" tag, JSP scriptlets, JSTL EL, + * etc. Necessary for views that do not have access to the servlet request, like FreeMarker templates. * *

Can be instantiated manually, or automatically exposed to views as model attribute via AbstractView's * "requestContextAttribute" property. * - *

Will also work outside of DispatcherServlet requests, accessing the root WebApplicationContext and using - * an appropriate fallback for the locale (the HttpServletRequest's primary locale). + *

Will also work outside of DispatcherServlet requests, accessing the root WebApplicationContext + * and using an appropriate fallback for the locale (the HttpServletRequest's primary locale). * * @author Juergen Hoeller * @author Rossen Stoyanchev @@ -467,7 +468,7 @@ public void changeTheme(String themeName) { /** * (De)activate default HTML escaping for messages and errors, for the scope of this RequestContext. *

The default is the application-wide setting (the "defaultHtmlEscape" context-param in web.xml). - * @see org.springframework.web.util.WebUtils#isDefaultHtmlEscape + * @see org.springframework.web.util.WebUtils#getDefaultHtmlEscape */ public void setDefaultHtmlEscape(boolean defaultHtmlEscape) { this.defaultHtmlEscape = defaultHtmlEscape; From afe106e2541dd2f7b8932d04c723d50ca15686e6 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 15 Jul 2016 22:47:12 +0200 Subject: [PATCH 0034/1274] Polishing --- .../web/socket/sockjs/client/UndertowXhrTransport.java | 8 +++----- .../config/annotation/WebSocketConfigurationTests.java | 4 ++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/UndertowXhrTransport.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/UndertowXhrTransport.java index 863059a4e53..4f70016a12d 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/UndertowXhrTransport.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/UndertowXhrTransport.java @@ -1,11 +1,11 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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 + * 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, @@ -326,14 +326,12 @@ protected void stringDone(String string) { result.getResponse().putAttachment(RESPONSE_BODY, string); latch.countDown(); } - @Override protected void error(IOException ex) { onFailure(latch, ex); } }.setup(result.getResponseChannel()); } - @Override public void failed(IOException ex) { onFailure(latch, ex); @@ -473,7 +471,7 @@ public void onSuccess() { public void onFailure(Throwable failure) { IoUtils.safeClose(this.connection); - if (connectFuture.setException(failure)) { + if (this.connectFuture.setException(failure)) { return; } if (this.session.isDisconnected()) { diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/config/annotation/WebSocketConfigurationTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/config/annotation/WebSocketConfigurationTests.java index 1a66397b7b4..f4aa0460480 100644 --- a/spring-websocket/src/test/java/org/springframework/web/socket/config/annotation/WebSocketConfigurationTests.java +++ b/spring-websocket/src/test/java/org/springframework/web/socket/config/annotation/WebSocketConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -61,7 +61,7 @@ public static Iterable arguments() { @Override protected Class[] getAnnotatedConfigClasses() { - return new Class[] { TestConfig.class }; + return new Class[] {TestConfig.class}; } @Test From 5d3c0f33f6acc2f3e6cb77d534593f7fcbe4378f Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 15 Jul 2016 22:47:22 +0200 Subject: [PATCH 0035/1274] Upgrade to Netty 4.1.3 and Tomcat 8.5.4 --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 52e3b8594e8..2990386617c 100644 --- a/build.gradle +++ b/build.gradle @@ -61,7 +61,7 @@ configure(allprojects) { project -> ext.jtaVersion = "1.2" ext.junitVersion = "4.12" ext.log4jVersion = "1.2.17" - ext.nettyVersion = "4.1.1.Final" + ext.nettyVersion = "4.1.3.Final" ext.okhttpVersion = "2.7.5" ext.okhttp3Version = "3.3.1" ext.openjpaVersion = "2.4.1" @@ -74,7 +74,7 @@ configure(allprojects) { project -> ext.testngVersion = "6.9.10" ext.tiles2Version = "2.2.2" ext.tiles3Version = "3.0.5" - ext.tomcatVersion = "8.5.3" + ext.tomcatVersion = "8.5.4" ext.tyrusVersion = "1.3.5" // constrained by WebLogic 12.1.3 support ext.undertowVersion = "1.4.0.CR2" ext.xmlunitVersion = "1.6" From 1c18100143578426a2267bffd43694338d1681c6 Mon Sep 17 00:00:00 2001 From: Fredrik Sundberg Date: Sun, 17 Jul 2016 20:56:53 +0200 Subject: [PATCH 0036/1274] Fix javadoc reference Method is called nextBackOff() and not nextBackOffMillis(). Closes gh-1115 --- .../src/main/java/org/springframework/util/backoff/BackOff.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-core/src/main/java/org/springframework/util/backoff/BackOff.java b/spring-core/src/main/java/org/springframework/util/backoff/BackOff.java index 0543ac917ac..2eb4b3ce48b 100644 --- a/spring-core/src/main/java/org/springframework/util/backoff/BackOff.java +++ b/spring-core/src/main/java/org/springframework/util/backoff/BackOff.java @@ -26,7 +26,7 @@ * BackOffExecution exec = backOff.start(); * * // In the operation recovery/retry loop: - * long waitInterval = exec.nextBackOffMillis(); + * long waitInterval = exec.nextBackOff(); * if (waitInterval == BackOffExecution.STOP) { * // do not retry operation * } From 479a83b6287b0e46177792776b98ffcf9cdedbfc Mon Sep 17 00:00:00 2001 From: Do Nhu Vy Date: Sun, 17 Jul 2016 22:31:22 +0700 Subject: [PATCH 0037/1274] Fix broken hyperlink Closes gh-1114 --- src/asciidoc/web-mvc.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/asciidoc/web-mvc.adoc b/src/asciidoc/web-mvc.adoc index 72b9c6d0fad..65f5bdf77ef 100644 --- a/src/asciidoc/web-mvc.adoc +++ b/src/asciidoc/web-mvc.adoc @@ -25,7 +25,7 @@ For an explanation of this principle, refer to __Expert Spring Web MVC and Web F Seth Ladd and others; specifically see the section "A Look At Design," on page 117 of the first edition. Alternatively, see -* http://www.objectmentor.com/resources/articles/ocp.pdf[Bob Martin, The Open-Closed +* https://www.cs.duke.edu/courses/fall07/cps108/papers/ocp.pdf[Bob Martin, The Open-Closed Principle (PDF)] You cannot add advice to final methods when you use Spring MVC. For example, you cannot From b187bbeca365a8091720c90531e14e23557c4361 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Mon, 18 Jul 2016 13:22:54 +0200 Subject: [PATCH 0038/1274] Upgrade to EJB 3.1 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 2990386617c..5c7a166def5 100644 --- a/build.gradle +++ b/build.gradle @@ -37,7 +37,7 @@ configure(allprojects) { project -> ext.ehcacheVersion = "2.10.2" ext.ehcachejcacheVersion = "1.0.1" ext.ehcache3Version = "3.1.0" - ext.ejbVersion = "3.0" + ext.ejbVersion = "3.1" ext.fileuploadVersion = "1.3.2" ext.freemarkerVersion = "2.3.23" ext.groovyVersion = "2.4.7" From 116f05eda69088870b99ea6691adcc877773696a Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Mon, 18 Jul 2016 13:37:43 +0200 Subject: [PATCH 0039/1274] Revert "Upgrade to EJB 3.1" --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 5c7a166def5..2990386617c 100644 --- a/build.gradle +++ b/build.gradle @@ -37,7 +37,7 @@ configure(allprojects) { project -> ext.ehcacheVersion = "2.10.2" ext.ehcachejcacheVersion = "1.0.1" ext.ehcache3Version = "3.1.0" - ext.ejbVersion = "3.1" + ext.ejbVersion = "3.0" ext.fileuploadVersion = "1.3.2" ext.freemarkerVersion = "2.3.23" ext.groovyVersion = "2.4.7" From df556333a8a4c070a4c989588ca4bb9724d48d65 Mon Sep 17 00:00:00 2001 From: Vladimir L Date: Mon, 11 Jul 2016 21:46:24 +0200 Subject: [PATCH 0040/1274] Add StreamingResponseBody MockMvc sample tests Issue: SPR-14456 (cherry picked from 7da63c) --- .../samples/standalone/AsyncTests.java | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/AsyncTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/AsyncTests.java index ee8f505e516..c922297fd35 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/AsyncTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/AsyncTests.java @@ -17,6 +17,7 @@ package org.springframework.test.web.servlet.samples.standalone; import java.io.StringWriter; +import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; @@ -26,6 +27,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.test.web.Person; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; @@ -36,6 +38,7 @@ import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.context.request.async.DeferredResult; +import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; import static org.junit.Assert.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; @@ -71,6 +74,34 @@ public void callable() throws Exception { .andExpect(content().string("{\"name\":\"Joe\",\"someDouble\":0.0,\"someBoolean\":false}")); } + @Test + public void streaming() throws Exception { + this.mockMvc.perform(get("/1").param("streaming", "true")) + .andExpect(request().asyncStarted()) + .andDo(r -> r.getAsyncResult()) // fetch async result similar to "asyncDispatch" builder + .andExpect(status().isOk()) + .andExpect(content().string("name=Joe")); + } + + @Test + public void streamingSlow() throws Exception { + this.mockMvc.perform(get("/1").param("streamingSlow", "true")) + .andExpect(request().asyncStarted()) + .andDo(r -> r.getAsyncResult()) + .andExpect(status().isOk()) + .andExpect(content().string("name=Joe&someBoolean=true")); + } + + @Test + public void streamingJson() throws Exception { + this.mockMvc.perform(get("/1").param("streamingJson", "true")) + .andExpect(request().asyncStarted()) + .andDo(r -> r.getAsyncResult()) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) + .andExpect(content().string("{\"name\":\"Joe\",\"someDouble\":0.5}")); + } + @Test public void deferredResult() throws Exception { MvcResult mvcResult = this.mockMvc.perform(get("/1").param("deferredResult", "true")) @@ -184,6 +215,31 @@ public Callable getCallable() { return () -> new Person("Joe"); } + @RequestMapping(params = "streaming") + public StreamingResponseBody getStreaming() { + return os -> os.write("name=Joe".getBytes()); + } + + @RequestMapping(params = "streamingSlow") + public StreamingResponseBody getStreamingSlow() { + return os -> { + os.write("name=Joe".getBytes()); + try { + Thread.sleep(200); + os.write("&someBoolean=true".getBytes()); + } + catch (InterruptedException e) { + /* no-op */ + } + }; + } + + @RequestMapping(params = "streamingJson") + public ResponseEntity getStreamingJson() { + return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON_UTF8) + .body(os -> os.write("{\"name\":\"Joe\",\"someDouble\":0.5}".getBytes(StandardCharsets.UTF_8))); + } + @RequestMapping(params = "deferredResult") public DeferredResult getDeferredResult() { DeferredResult deferredResult = new DeferredResult(); From 940bdd878efccb47f602c48376a24bb3fbd4994b Mon Sep 17 00:00:00 2001 From: Sebastien Deleuze Date: Tue, 19 Jul 2016 17:27:03 +0200 Subject: [PATCH 0041/1274] Fix ParameterizedType + contextClass support in Jackson converter Issue: SPR-14470 --- .../AbstractJackson2HttpMessageConverter.java | 37 ++++++++++++++++--- ...questResponseBodyMethodProcessorTests.java | 34 +++++++++++++++++ 2 files changed, 66 insertions(+), 5 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java index 546aca46989..2d3dd80e2f2 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java @@ -17,6 +17,7 @@ package org.springframework.http.converter.json; import java.io.IOException; +import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.nio.charset.Charset; @@ -311,11 +312,37 @@ protected void writeSuffix(JsonGenerator generator, Object object) throws IOExce */ protected JavaType getJavaType(Type type, Class contextClass) { TypeFactory typeFactory = this.objectMapper.getTypeFactory(); - if (type instanceof TypeVariable && contextClass != null) { - ResolvableType resolvedType = resolveVariable( - (TypeVariable) type, ResolvableType.forClass(contextClass)); - if (resolvedType != ResolvableType.NONE) { - return typeFactory.constructType(resolvedType.resolve()); + if (contextClass != null) { + ResolvableType resolvedType = ResolvableType.forType(type); + if (type instanceof TypeVariable) { + ResolvableType resolvedTypeVariable = resolveVariable( + (TypeVariable) type, ResolvableType.forClass(contextClass)); + if (resolvedTypeVariable != ResolvableType.NONE) { + return typeFactory.constructType(resolvedTypeVariable.resolve()); + } + } + else if (type instanceof ParameterizedType && resolvedType.hasUnresolvableGenerics()) { + ParameterizedType parameterizedType = (ParameterizedType) type; + Class[] generics = new Class[parameterizedType.getActualTypeArguments().length]; + Type[] typeArguments = parameterizedType.getActualTypeArguments(); + for (int i = 0; i < typeArguments.length; i++) { + Type typeArgument = typeArguments[i]; + if (typeArgument instanceof TypeVariable) { + ResolvableType resolvedTypeArgument = resolveVariable( + (TypeVariable) typeArgument, ResolvableType.forClass(contextClass)); + if (resolvedTypeArgument != ResolvableType.NONE) { + generics[i] = resolvedTypeArgument.resolve(); + } + else { + generics[i] = ResolvableType.forType(typeArgument).resolve(); + } + } + else { + generics[i] = ResolvableType.forType(typeArgument).resolve(); + } + } + return typeFactory.constructType(ResolvableType. + forClassWithGenerics(resolvedType.getRawClass(), generics).getType()); } } return typeFactory.constructType(type); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessorTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessorTests.java index c156cbd6bb9..866b37d1ad6 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessorTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessorTests.java @@ -240,6 +240,29 @@ public void resolveArgumentTypeVariable() throws Exception { assertEquals("Jad", result.getName()); } + @Test // SPR-14470 + public void resolveParameterizedWithTypeVariableArgument() throws Exception { + Method method = MyParameterizedControllerWithList.class.getMethod("handleDto", List.class); + HandlerMethod handlerMethod = new HandlerMethod(new MySimpleParameterizedController(), method); + MethodParameter methodParam = handlerMethod.getMethodParameters()[0]; + + String content = "[{\"name\" : \"Jad\"}, {\"name\" : \"Robert\"}]"; + this.servletRequest.setContent(content.getBytes("UTF-8")); + this.servletRequest.setContentType(MediaType.APPLICATION_JSON_VALUE); + + List> converters = new ArrayList<>(); + converters.add(new MappingJackson2HttpMessageConverter()); + RequestResponseBodyMethodProcessor processor = new RequestResponseBodyMethodProcessor(converters); + + @SuppressWarnings("unchecked") + List result = (List) + processor.resolveArgument(methodParam, container, request, factory); + + assertNotNull(result); + assertEquals("Jad", result.get(0).getName()); + assertEquals("Robert", result.get(1).getName()); + } + @Test // SPR-11225 public void resolveArgumentTypeVariableWithNonGenericConverter() throws Exception { Method method = MyParameterizedController.class.getMethod("handleDto", Identifiable.class); @@ -725,6 +748,17 @@ private interface Identifiable extends Serializable { void setId(Long id); } + @SuppressWarnings("unused") + private static abstract class MyParameterizedControllerWithList { + + public void handleDto(@RequestBody List dto) { + } + } + + @SuppressWarnings("unused") + private static class MySimpleParameterizedControllerWithList extends MyParameterizedControllerWithList { + } + @SuppressWarnings({ "serial" }) private static class SimpleBean implements Identifiable { From d98bd34200f21f435b9d50330174b7bfbf10dfc9 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 19 Jul 2016 16:43:03 -0400 Subject: [PATCH 0042/1274] Remove isAsyncStarted assertion Issue: SPR-14444 --- .../web/context/request/async/WebAsyncManager.java | 1 - 1 file changed, 1 deletion(-) diff --git a/spring-web/src/main/java/org/springframework/web/context/request/async/WebAsyncManager.java b/spring-web/src/main/java/org/springframework/web/context/request/async/WebAsyncManager.java index ea188badfa4..83913f02b7a 100644 --- a/spring-web/src/main/java/org/springframework/web/context/request/async/WebAsyncManager.java +++ b/spring-web/src/main/java/org/springframework/web/context/request/async/WebAsyncManager.java @@ -105,7 +105,6 @@ public final class WebAsyncManager { */ public void setAsyncWebRequest(final AsyncWebRequest asyncWebRequest) { Assert.notNull(asyncWebRequest, "AsyncWebRequest must not be null"); - Assert.state(!isConcurrentHandlingStarted(), "Can't set AsyncWebRequest with concurrent handling in progress"); this.asyncWebRequest = asyncWebRequest; this.asyncWebRequest.addCompletionHandler(new Runnable() { @Override From b2f0bdb0f495c55bb842b16d06bf9ab2f1744517 Mon Sep 17 00:00:00 2001 From: Sebastien Deleuze Date: Wed, 20 Jul 2016 09:16:50 +0200 Subject: [PATCH 0043/1274] Polishing Issue: SPR-14470 --- .../annotation/RequestResponseBodyMethodProcessorTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessorTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessorTests.java index 866b37d1ad6..2d7d789a83e 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessorTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessorTests.java @@ -243,7 +243,7 @@ public void resolveArgumentTypeVariable() throws Exception { @Test // SPR-14470 public void resolveParameterizedWithTypeVariableArgument() throws Exception { Method method = MyParameterizedControllerWithList.class.getMethod("handleDto", List.class); - HandlerMethod handlerMethod = new HandlerMethod(new MySimpleParameterizedController(), method); + HandlerMethod handlerMethod = new HandlerMethod(new MySimpleParameterizedControllerWithList(), method); MethodParameter methodParam = handlerMethod.getMethodParameters()[0]; String content = "[{\"name\" : \"Jad\"}, {\"name\" : \"Robert\"}]"; From 4e7e06f54911c9ecadff9d3c0c3a03c42602cb8a Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 20 Jul 2016 21:40:26 +0200 Subject: [PATCH 0044/1274] Reliably pass CurrentTenantIdentifierResolver to SessionFactory Issue: SPR-14476 --- .../orm/hibernate5/LocalSessionFactoryBuilder.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/LocalSessionFactoryBuilder.java b/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/LocalSessionFactoryBuilder.java index d345263046f..f74fb8ad7e0 100644 --- a/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/LocalSessionFactoryBuilder.java +++ b/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/LocalSessionFactoryBuilder.java @@ -43,6 +43,7 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Environment; +import org.hibernate.context.spi.CurrentTenantIdentifierResolver; import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -206,6 +207,17 @@ public LocalSessionFactoryBuilder setMultiTenantConnectionProvider(MultiTenantCo return this; } + /** + * Overridden to reliably pass a {@link CurrentTenantIdentifierResolver} to the SessionFactory. + * @since 4.3.2 + * @see AvailableSettings#MULTI_TENANT_IDENTIFIER_RESOLVER + */ + @Override + public void setCurrentTenantIdentifierResolver(CurrentTenantIdentifierResolver currentTenantIdentifierResolver) { + getProperties().put(AvailableSettings.MULTI_TENANT_IDENTIFIER_RESOLVER, currentTenantIdentifierResolver); + super.setCurrentTenantIdentifierResolver(currentTenantIdentifierResolver); + } + /** * Specify custom type filters for Spring-based scanning for entity classes. *

Default is to search all specified packages for classes annotated with From 1ca4b81856e4c836f81de7d0d58fb201244c9b48 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 19 Jul 2016 17:26:47 +0200 Subject: [PATCH 0045/1274] Reinstated tests for implicit aliases Issue: SPR-14437 (cherry picked from commit 5ea8c26) --- .../annotation/AnnotationAttributesTests.java | 67 +++++++++++++++++++ .../core/annotation/AnnotationUtilsTests.java | 4 +- 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/spring-core/src/test/java/org/springframework/core/annotation/AnnotationAttributesTests.java b/spring-core/src/test/java/org/springframework/core/annotation/AnnotationAttributesTests.java index 99818400e67..5908bf3693d 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/AnnotationAttributesTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/AnnotationAttributesTests.java @@ -18,11 +18,15 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; +import java.util.List; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.springframework.core.annotation.AnnotationUtilsTests.ImplicitAliasesContextConfig; + import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; @@ -154,6 +158,69 @@ public void getEnumWithTypeMismatch() { attributes.getEnum("color"); } + @Test + public void getAliasedStringWithImplicitAliases() { + String value = "metaverse"; + List aliases = Arrays.asList("value", "location1", "location2", "location3", "xmlFile", "groovyScript"); + + attributes = new AnnotationAttributes(ImplicitAliasesContextConfig.class); + attributes.put("value", value); + AnnotationUtils.postProcessAnnotationAttributes(null, attributes, false); + aliases.stream().forEach(alias -> assertEquals(value, attributes.getString(alias))); + + attributes = new AnnotationAttributes(ImplicitAliasesContextConfig.class); + attributes.put("location1", value); + AnnotationUtils.postProcessAnnotationAttributes(null, attributes, false); + aliases.stream().forEach(alias -> assertEquals(value, attributes.getString(alias))); + + attributes = new AnnotationAttributes(ImplicitAliasesContextConfig.class); + attributes.put("value", value); + attributes.put("location1", value); + attributes.put("xmlFile", value); + attributes.put("groovyScript", value); + AnnotationUtils.postProcessAnnotationAttributes(null, attributes, false); + aliases.stream().forEach(alias -> assertEquals(value, attributes.getString(alias))); + } + + @Test + public void getAliasedStringArrayWithImplicitAliases() { + String[] value = new String[] {"test.xml"}; + List aliases = Arrays.asList("value", "location1", "location2", "location3", "xmlFile", "groovyScript"); + + attributes = new AnnotationAttributes(ImplicitAliasesContextConfig.class); + attributes.put("location1", value); + AnnotationUtils.postProcessAnnotationAttributes(null, attributes, false); + aliases.stream().forEach(alias -> assertArrayEquals(value, attributes.getStringArray(alias))); + + attributes = new AnnotationAttributes(ImplicitAliasesContextConfig.class); + attributes.put("value", value); + AnnotationUtils.postProcessAnnotationAttributes(null, attributes, false); + aliases.stream().forEach(alias -> assertArrayEquals(value, attributes.getStringArray(alias))); + + attributes = new AnnotationAttributes(ImplicitAliasesContextConfig.class); + attributes.put("location1", value); + attributes.put("value", value); + AnnotationUtils.postProcessAnnotationAttributes(null, attributes, false); + aliases.stream().forEach(alias -> assertArrayEquals(value, attributes.getStringArray(alias))); + + attributes = new AnnotationAttributes(ImplicitAliasesContextConfig.class); + attributes.put("location1", value); + AnnotationUtils.registerDefaultValues(attributes); + AnnotationUtils.postProcessAnnotationAttributes(null, attributes, false); + aliases.stream().forEach(alias -> assertArrayEquals(value, attributes.getStringArray(alias))); + + attributes = new AnnotationAttributes(ImplicitAliasesContextConfig.class); + attributes.put("value", value); + AnnotationUtils.registerDefaultValues(attributes); + AnnotationUtils.postProcessAnnotationAttributes(null, attributes, false); + aliases.stream().forEach(alias -> assertArrayEquals(value, attributes.getStringArray(alias))); + + attributes = new AnnotationAttributes(ImplicitAliasesContextConfig.class); + AnnotationUtils.registerDefaultValues(attributes); + AnnotationUtils.postProcessAnnotationAttributes(null, attributes, false); + aliases.stream().forEach(alias -> assertArrayEquals(new String[] {""}, attributes.getStringArray(alias))); + } + enum Color { diff --git a/spring-core/src/test/java/org/springframework/core/annotation/AnnotationUtilsTests.java b/spring-core/src/test/java/org/springframework/core/annotation/AnnotationUtilsTests.java index 53327096962..c8d5170cdd2 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/AnnotationUtilsTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/AnnotationUtilsTests.java @@ -2121,12 +2121,12 @@ static class AliasedComposedContextConfigNotMetaPresentClass { @ContextConfig @Retention(RetentionPolicy.RUNTIME) - @interface ImplicitAliasesContextConfig { + public @interface ImplicitAliasesContextConfig { @AliasFor(annotation = ContextConfig.class, attribute = "location") String xmlFile() default ""; - @AliasFor(annotation = ContextConfig.class, value = "location") + @AliasFor(annotation = ContextConfig.class, attribute = "location") String groovyScript() default ""; @AliasFor(annotation = ContextConfig.class, attribute = "location") From 3663aa675ad79e0a09e65d3f264f0529a6016078 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 19 Jul 2016 18:44:06 +0200 Subject: [PATCH 0046/1274] GenericSqlQuery configured with RowMapper instance Issue: SPR-14489 (cherry picked from commit 7287bae) --- .../jdbc/object/GenericSqlQuery.java | 54 ++++++++++++------- .../jdbc/object/GenericSqlQueryTests.java | 19 ++++--- .../object/GenericSqlQueryTests-context.xml | 26 ++++++++- 3 files changed, 72 insertions(+), 27 deletions(-) diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/object/GenericSqlQuery.java b/spring-jdbc/src/main/java/org/springframework/jdbc/object/GenericSqlQuery.java index d2af62814c4..dbbfd5c1e05 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/object/GenericSqlQuery.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/object/GenericSqlQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -18,43 +18,57 @@ import java.util.Map; -import org.springframework.dao.InvalidDataAccessResourceUsageException; +import org.springframework.beans.BeanUtils; import org.springframework.jdbc.core.RowMapper; import org.springframework.util.Assert; +/** + * A concrete variant of {@link SqlQuery} which can be configured + * with a {@link RowMapper}. + * + * @author Thomas Risberg + * @author Juergen Hoeller + * @since 3.0 + * @see #setRowMapper + * @see #setRowMapperClass + */ public class GenericSqlQuery extends SqlQuery { - Class rowMapperClass; + private RowMapper rowMapper; + + @SuppressWarnings("rawtypes") + private Class rowMapperClass; + - RowMapper rowMapper; + /** + * Set a specific {@link RowMapper} instance to use for this query. + * @since 4.3.2 + */ + public void setRowMapper(RowMapper rowMapper) { + this.rowMapper = rowMapper; + } + /** + * Set a {@link RowMapper} class for this query, creating a fresh + * {@link RowMapper} instance per execution. + */ @SuppressWarnings("rawtypes") - public void setRowMapperClass(Class rowMapperClass) - throws IllegalAccessException, InstantiationException { + public void setRowMapperClass(Class rowMapperClass) { this.rowMapperClass = rowMapperClass; - if (!RowMapper.class.isAssignableFrom(rowMapperClass)) - throw new IllegalStateException("The specified class '" + - rowMapperClass.getName() + " is not a sub class of " + - "'org.springframework.jdbc.core.RowMapper'"); } @Override public void afterPropertiesSet() { super.afterPropertiesSet(); - Assert.notNull(rowMapperClass, "The 'rowMapperClass' property is required"); + Assert.isTrue(this.rowMapper != null || this.rowMapperClass != null, + "'rowMapper' or 'rowMapperClass' is required"); } + @Override @SuppressWarnings("unchecked") protected RowMapper newRowMapper(Object[] parameters, Map context) { - try { - return (RowMapper) rowMapperClass.newInstance(); - } - catch (InstantiationException e) { - throw new InvalidDataAccessResourceUsageException("Unable to instantiate RowMapper", e); - } - catch (IllegalAccessException e) { - throw new InvalidDataAccessResourceUsageException("Unable to instantiate RowMapper", e); - } + return (this.rowMapper != null ? this.rowMapper : BeanUtils.instantiateClass(this.rowMapperClass)); } + } diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/object/GenericSqlQueryTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/object/GenericSqlQueryTests.java index ed32b628ad8..5a90e703910 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/object/GenericSqlQueryTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/object/GenericSqlQueryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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,6 @@ package org.springframework.jdbc.object; - import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -43,11 +42,12 @@ /** * @author Thomas Risberg + * @author Juergen Hoeller */ public class GenericSqlQueryTests { private static final String SELECT_ID_FORENAME_NAMED_PARAMETERS_PARSED = - "select id, forename from custmr where id = ? and country = ?"; + "select id, forename from custmr where id = ? and country = ?"; private BeanFactory beanFactory; @@ -57,6 +57,7 @@ public class GenericSqlQueryTests { private ResultSet resultSet; + @Before public void setUp() throws Exception { this.beanFactory = new DefaultListableBeanFactory(); @@ -72,17 +73,23 @@ public void setUp() throws Exception { } @Test - public void testPlaceHoldersCustomerQuery() throws SQLException { - SqlQuery query = (SqlQuery) beanFactory.getBean("queryWithPlaceHolders"); + public void testCustomerQueryWithPlaceholders() throws SQLException { + SqlQuery query = (SqlQuery) beanFactory.getBean("queryWithPlaceholders"); doTestCustomerQuery(query, false); } @Test - public void testNamedParameterCustomerQuery() throws SQLException { + public void testCustomerQueryWithNamedParameters() throws SQLException { SqlQuery query = (SqlQuery) beanFactory.getBean("queryWithNamedParameters"); doTestCustomerQuery(query, true); } + @Test + public void testCustomerQueryWithRowMapperInstance() throws SQLException { + SqlQuery query = (SqlQuery) beanFactory.getBean("queryWithRowMapperBean"); + doTestCustomerQuery(query, true); + } + private void doTestCustomerQuery(SqlQuery query, boolean namedParameters) throws SQLException { given(resultSet.next()).willReturn(true); given(resultSet.getInt("id")).willReturn(1); diff --git a/spring-jdbc/src/test/resources/org/springframework/jdbc/object/GenericSqlQueryTests-context.xml b/spring-jdbc/src/test/resources/org/springframework/jdbc/object/GenericSqlQueryTests-context.xml index 908653e3fd0..719502060c2 100644 --- a/spring-jdbc/src/test/resources/org/springframework/jdbc/object/GenericSqlQueryTests-context.xml +++ b/spring-jdbc/src/test/resources/org/springframework/jdbc/object/GenericSqlQueryTests-context.xml @@ -6,7 +6,7 @@ - + @@ -50,4 +50,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 90752f9d87d4231df6768dd70ed6ae3d560fb0e9 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 20 Jul 2016 21:41:30 +0200 Subject: [PATCH 0047/1274] Deprecate mock.staticmock package Issue: SPR-14485 --- .../mock/staticmock/AbstractMethodMockingControl.aj | 2 ++ .../staticmock/AnnotationDrivenStaticEntityMockingControl.aj | 2 ++ .../mock/staticmock/MockStaticEntityMethods.java | 2 ++ 3 files changed, 6 insertions(+) diff --git a/spring-aspects/src/main/java/org/springframework/mock/staticmock/AbstractMethodMockingControl.aj b/spring-aspects/src/main/java/org/springframework/mock/staticmock/AbstractMethodMockingControl.aj index fe378642a78..f77838e37ba 100644 --- a/spring-aspects/src/main/java/org/springframework/mock/staticmock/AbstractMethodMockingControl.aj +++ b/spring-aspects/src/main/java/org/springframework/mock/staticmock/AbstractMethodMockingControl.aj @@ -36,7 +36,9 @@ import org.springframework.util.ObjectUtils; * @author Rod Johnson * @author Ramnivas Laddad * @author Sam Brannen + * @deprecated as of Spring 4.3, in favor of a custom aspect for such purposes */ +@Deprecated public abstract aspect AbstractMethodMockingControl percflow(mockStaticsTestMethod()) { private final Expectations expectations = new Expectations(); diff --git a/spring-aspects/src/main/java/org/springframework/mock/staticmock/AnnotationDrivenStaticEntityMockingControl.aj b/spring-aspects/src/main/java/org/springframework/mock/staticmock/AnnotationDrivenStaticEntityMockingControl.aj index b2979303bb9..d67744df3a6 100644 --- a/spring-aspects/src/main/java/org/springframework/mock/staticmock/AnnotationDrivenStaticEntityMockingControl.aj +++ b/spring-aspects/src/main/java/org/springframework/mock/staticmock/AnnotationDrivenStaticEntityMockingControl.aj @@ -59,7 +59,9 @@ import org.aspectj.lang.annotation.SuppressAjWarnings; * @author Ramnivas Laddad * @author Sam Brannen * @see MockStaticEntityMethods + * @deprecated as of Spring 4.3, in favor of a custom aspect for such purposes */ +@Deprecated @RequiredTypes("javax.persistence.Entity") public aspect AnnotationDrivenStaticEntityMockingControl extends AbstractMethodMockingControl { diff --git a/spring-aspects/src/main/java/org/springframework/mock/staticmock/MockStaticEntityMethods.java b/spring-aspects/src/main/java/org/springframework/mock/staticmock/MockStaticEntityMethods.java index f68b80640b8..3b2c128c577 100644 --- a/spring-aspects/src/main/java/org/springframework/mock/staticmock/MockStaticEntityMethods.java +++ b/spring-aspects/src/main/java/org/springframework/mock/staticmock/MockStaticEntityMethods.java @@ -29,7 +29,9 @@ * * @author Rod Johnson * @author Sam Brannen + * @deprecated as of Spring 4.3, in favor of a custom aspect for such purposes */ +@Deprecated @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface MockStaticEntityMethods { From e0d81b97bbfd472b0dc409b3599f671d2f5110a4 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 19 Jul 2016 19:22:32 +0200 Subject: [PATCH 0048/1274] SerializableTypeWrapper reobtains type accessors from declaring interface Issue: SPR-14487 (cherry picked from commit 8580483) --- .../org/springframework/core/SerializableTypeWrapper.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/SerializableTypeWrapper.java b/spring-core/src/main/java/org/springframework/core/SerializableTypeWrapper.java index 8f1fd9d9eb3..eaae658a4da 100644 --- a/spring-core/src/main/java/org/springframework/core/SerializableTypeWrapper.java +++ b/spring-core/src/main/java/org/springframework/core/SerializableTypeWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -372,6 +372,8 @@ static class MethodInvokeTypeProvider implements TypeProvider { private final String methodName; + private final Class declaringClass; + private final int index; private transient Method method; @@ -381,6 +383,7 @@ static class MethodInvokeTypeProvider implements TypeProvider { public MethodInvokeTypeProvider(TypeProvider provider, Method method, int index) { this.provider = provider; this.methodName = method.getName(); + this.declaringClass = method.getDeclaringClass(); this.index = index; this.method = method; } @@ -404,7 +407,7 @@ public Object getSource() { private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException { inputStream.defaultReadObject(); - this.method = ReflectionUtils.findMethod(this.provider.getType().getClass(), this.methodName); + this.method = ReflectionUtils.findMethod(this.declaringClass, this.methodName); Assert.state(Type.class == this.method.getReturnType() || Type[].class == this.method.getReturnType()); } } From b583aa1579f032a8921a27f4e99fb1d9dd6a225b Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 19 Jul 2016 23:30:33 +0200 Subject: [PATCH 0049/1274] Avoid dependency on WebUtils for extracting file extension Issue: SPR-14479 (cherry picked from commit adc595b) --- ...thExtensionContentNegotiationStrategy.java | 13 +++++---- .../springframework/web/util/UriUtils.java | 27 ++++++++++++++++++- .../springframework/web/util/WebUtils.java | 14 ++++++++-- .../web/util/UriUtilsTests.java | 21 ++++++++++++++- .../support/ServletUriComponentsBuilder.java | 6 ++--- 5 files changed, 66 insertions(+), 15 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/accept/PathExtensionContentNegotiationStrategy.java b/spring-web/src/main/java/org/springframework/web/accept/PathExtensionContentNegotiationStrategy.java index 0a042f4384a..000b6379649 100644 --- a/spring-web/src/main/java/org/springframework/web/accept/PathExtensionContentNegotiationStrategy.java +++ b/spring-web/src/main/java/org/springframework/web/accept/PathExtensionContentNegotiationStrategy.java @@ -35,8 +35,8 @@ import org.springframework.util.StringUtils; import org.springframework.web.HttpMediaTypeNotAcceptableException; import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.util.UriUtils; import org.springframework.web.util.UrlPathHelper; -import org.springframework.web.util.WebUtils; /** * A {@code ContentNegotiationStrategy} that resolves the file extension in the @@ -118,9 +118,8 @@ protected String getMediaTypeKey(NativeWebRequest webRequest) { return null; } String path = this.urlPathHelper.getLookupPathForRequest(request); - String filename = WebUtils.extractFullFilenameFromUrlPath(path); - String extension = StringUtils.getFilenameExtension(filename); - return (StringUtils.hasText(extension)) ? extension.toLowerCase(Locale.ENGLISH) : null; + String extension = UriUtils.extractFileExtension(path); + return (StringUtils.hasText(extension) ? extension.toLowerCase(Locale.ENGLISH) : null); } @Override @@ -128,7 +127,7 @@ protected MediaType handleNoMatch(NativeWebRequest webRequest, String extension) throws HttpMediaTypeNotAcceptableException { if (this.useJaf && JAF_PRESENT) { - MediaType mediaType = JafMediaTypeFactory.getMediaType("file." + extension); + MediaType mediaType = ActivationMediaTypeFactory.getMediaType("file." + extension); if (mediaType != null && !MediaType.APPLICATION_OCTET_STREAM.equals(mediaType)) { return mediaType; } @@ -157,7 +156,7 @@ public MediaType getMediaTypeForResource(Resource resource) { mediaType = lookupMediaType(extension); } if (mediaType == null && JAF_PRESENT) { - mediaType = JafMediaTypeFactory.getMediaType(filename); + mediaType = ActivationMediaTypeFactory.getMediaType(filename); } if (MediaType.APPLICATION_OCTET_STREAM.equals(mediaType)) { mediaType = null; @@ -169,7 +168,7 @@ public MediaType getMediaTypeForResource(Resource resource) { /** * Inner class to avoid hard-coded dependency on JAF. */ - private static class JafMediaTypeFactory { + private static class ActivationMediaTypeFactory { private static final FileTypeMap fileTypeMap; diff --git a/spring-web/src/main/java/org/springframework/web/util/UriUtils.java b/spring-web/src/main/java/org/springframework/web/util/UriUtils.java index c68092971fa..6b2f3e50495 100644 --- a/spring-web/src/main/java/org/springframework/web/util/UriUtils.java +++ b/spring-web/src/main/java/org/springframework/web/util/UriUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,7 @@ * * * @author Arjen Poutsma + * @author Juergen Hoeller * @since 3.0 * @see RFC 3986 */ @@ -213,4 +214,28 @@ public static String decode(String source, String encoding) throws UnsupportedEn return (changed ? new String(bos.toByteArray(), encoding) : source); } + /** + * Extract the file extension from the given URI path. + * @param path the URI path (e.g. "/products/index.html") + * @return the extracted file extension (e.g. "html") + * @since 4.3.2 + */ + public static String extractFileExtension(String path) { + int end = path.indexOf('?'); + if (end == -1) { + end = path.indexOf('#'); + if (end == -1) { + end = path.length(); + } + } + int begin = path.lastIndexOf('/', end) + 1; + int paramIndex = path.indexOf(';', begin); + end = (paramIndex != -1 && paramIndex < end ? paramIndex : end); + int extIndex = path.lastIndexOf('.', end); + if (extIndex != -1 && extIndex > begin) { + return path.substring(extIndex + 1, end); + } + return null; + } + } diff --git a/spring-web/src/main/java/org/springframework/web/util/WebUtils.java b/spring-web/src/main/java/org/springframework/web/util/WebUtils.java index 6d8062fbdc7..13195a76a79 100644 --- a/spring-web/src/main/java/org/springframework/web/util/WebUtils.java +++ b/spring-web/src/main/java/org/springframework/web/util/WebUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -278,7 +278,6 @@ public static String getRealPath(ServletContext servletContext, String path) thr return realPath; } - /** * Determine the session id of the given request, if any. * @param request current HTTP request @@ -353,7 +352,9 @@ public static void setSessionAttribute(HttpServletRequest request, String name, * @param clazz the class to instantiate for a new attribute * @return the value of the session attribute, newly created if not found * @throws IllegalArgumentException if the session attribute could not be instantiated + * @deprecated as of Spring 4.3.2, in favor of custom code for such purposes */ + @Deprecated public static Object getOrCreateSessionAttribute(HttpSession session, String name, Class clazz) throws IllegalArgumentException { @@ -527,7 +528,9 @@ public static void clearErrorRequestAttributes(HttpServletRequest request) { * and the values as corresponding attribute values. Keys need to be Strings. * @param request current HTTP request * @param attributes the attributes Map + * @deprecated as of Spring 4.3.2, in favor of custom code for such purposes */ + @Deprecated public static void exposeRequestAttributes(ServletRequest request, Map attributes) { Assert.notNull(request, "Request must not be null"); Assert.notNull(attributes, "Attributes Map must not be null"); @@ -689,7 +692,9 @@ else if (values.length > 1) { * @param currentPage the current page, to be returned as fallback * if no target page specified * @return the page specified in the request, or current page if not found + * @deprecated as of Spring 4.3.2, in favor of custom code for such purposes */ + @Deprecated public static int getTargetPage(ServletRequest request, String paramPrefix, int currentPage) { Enumeration paramNames = request.getParameterNames(); while (paramNames.hasMoreElements()) { @@ -713,7 +718,9 @@ public static int getTargetPage(ServletRequest request, String paramPrefix, int * Correctly resolves nested paths such as "/products/view.html" as well. * @param urlPath the request URL path (e.g. "/index.html") * @return the extracted URI filename (e.g. "index") + * @deprecated as of Spring 4.3.2, in favor of custom code for such purposes */ + @Deprecated public static String extractFilenameFromUrlPath(String urlPath) { String filename = extractFullFilenameFromUrlPath(urlPath); int dotIndex = filename.lastIndexOf('.'); @@ -729,7 +736,10 @@ public static String extractFilenameFromUrlPath(String urlPath) { * "/products/view.html" and remove any path and or query parameters. * @param urlPath the request URL path (e.g. "/products/index.html") * @return the extracted URI filename (e.g. "index.html") + * @deprecated as of Spring 4.3.2, in favor of custom code for such purposes + * (or {@link UriUtils#extractFileExtension} for the file extension use case) */ + @Deprecated public static String extractFullFilenameFromUrlPath(String urlPath) { int end = urlPath.indexOf('?'); if (end == -1) { diff --git a/spring-web/src/test/java/org/springframework/web/util/UriUtilsTests.java b/spring-web/src/test/java/org/springframework/web/util/UriUtilsTests.java index 6480c5fa2e3..2227674abc9 100644 --- a/spring-web/src/test/java/org/springframework/web/util/UriUtilsTests.java +++ b/spring-web/src/test/java/org/springframework/web/util/UriUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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 @@ /** * @author Arjen Poutsma + * @author Juergen Hoeller */ public class UriUtilsTests { @@ -104,4 +105,22 @@ public void decodeInvalidSequence() throws UnsupportedEncodingException { UriUtils.decode("foo%2", ENC); } + @Test + public void extractFileExtension() { + assertEquals("html", UriUtils.extractFileExtension("index.html")); + assertEquals("html", UriUtils.extractFileExtension("/index.html")); + assertEquals("html", UriUtils.extractFileExtension("/products/view.html")); + assertEquals("html", UriUtils.extractFileExtension("/products/view.html#/a")); + assertEquals("html", UriUtils.extractFileExtension("/products/view.html#/path/a")); + assertEquals("html", UriUtils.extractFileExtension("/products/view.html#/path/a.do")); + assertEquals("html", UriUtils.extractFileExtension("/products/view.html?param=a")); + assertEquals("html", UriUtils.extractFileExtension("/products/view.html?param=/path/a")); + assertEquals("html", UriUtils.extractFileExtension("/products/view.html?param=/path/a.do")); + assertEquals("html", UriUtils.extractFileExtension("/products/view.html?param=/path/a#/path/a")); + assertEquals("html", UriUtils.extractFileExtension("/products/view.html?param=/path/a.do#/path/a.do")); + assertEquals("html", UriUtils.extractFileExtension("/products;q=11/view.html?param=/path/a.do")); + assertEquals("html", UriUtils.extractFileExtension("/products;q=11/view.html;r=22?param=/path/a.do")); + assertEquals("html", UriUtils.extractFileExtension("/products;q=11/view.html;r=22;s=33?param=/path/a.do")); + } + } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/ServletUriComponentsBuilder.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/ServletUriComponentsBuilder.java index 852f5cd2dc7..367aed85033 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/ServletUriComponentsBuilder.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/ServletUriComponentsBuilder.java @@ -28,8 +28,8 @@ import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; +import org.springframework.web.util.UriUtils; import org.springframework.web.util.UrlPathHelper; -import org.springframework.web.util.WebUtils; /** * A UriComponentsBuilder that extracts information from the HttpServletRequest. @@ -44,7 +44,6 @@ public class ServletUriComponentsBuilder extends UriComponentsBuilder { /** * Default constructor. Protected to prevent direct instantiation. - * * @see #fromContextPath(HttpServletRequest) * @see #fromServletMapping(HttpServletRequest) * @see #fromRequest(HttpServletRequest) @@ -219,8 +218,7 @@ private void initPath(String path) { public String removePathExtension() { String extension = null; if (this.originalPath != null) { - String filename = WebUtils.extractFullFilenameFromUrlPath(this.originalPath); - extension = StringUtils.getFilenameExtension(filename); + extension = UriUtils.extractFileExtension(this.originalPath); if (!StringUtils.isEmpty(extension)) { int end = this.originalPath.length() - (extension.length() + 1); replacePath(this.originalPath.substring(0, end)); From fe17f8da41ac3f329da751caa69239140f0bf1ff Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 20 Jul 2016 18:02:23 +0200 Subject: [PATCH 0050/1274] BeanWrapperImpl.setBeanInstance correctly exposes root object Issue: SPR-14474 (cherry picked from commit 938b56c) --- .../beans/AbstractNestablePropertyAccessor.java | 8 +++----- .../java/org/springframework/beans/BeanWrapperImpl.java | 1 + 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java b/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java index 08e4ae39fef..f3e3bd14795 100644 --- a/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java @@ -94,7 +94,7 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA private String nestedPath = ""; - private Object rootObject; + Object rootObject; /** * Map with cached nested Accessors: nested path -> Accessor instance. @@ -914,11 +914,9 @@ else if (Map.class.isAssignableFrom(type)) { return BeanUtils.instantiate(type); } } - catch (Exception ex) { - // TODO: Root cause exception context is lost here; just exception message preserved. - // Should we throw another exception type that preserves context instead? + catch (Throwable ex) { throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + name, - "Could not instantiate property type [" + type.getName() + "] to auto-grow nested property path: " + ex); + "Could not instantiate property type [" + type.getName() + "] to auto-grow nested property path", ex); } } diff --git a/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java b/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java index e297d85fb8e..4d10dc0c818 100644 --- a/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java +++ b/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java @@ -141,6 +141,7 @@ private BeanWrapperImpl(Object object, String nestedPath, BeanWrapperImpl parent */ public void setBeanInstance(Object object) { this.wrappedObject = object; + this.rootObject = object; this.typeConverterDelegate = new TypeConverterDelegate(this, this.wrappedObject); setIntrospectionClass(object.getClass()); } From 503d65d57034674f3ae0b225d785791e66e52929 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 20 Jul 2016 18:05:51 +0200 Subject: [PATCH 0051/1274] Avoid JDK proxy against CGLIB Factory interface and assert required type when resolving dependency Issue: SPR-14478 (cherry picked from commit 0e3f0bd) --- .../aop/framework/ProxyProcessorSupport.java | 5 ++- .../AutowiredAnnotationBeanPostProcessor.java | 18 ++++---- .../factory/config/DependencyDescriptor.java | 37 +++++++++-------- .../support/DefaultListableBeanFactory.java | 16 ++++---- .../annotation/EnableAsyncTests.java | 41 ++++++++++++++++++- 5 files changed, 83 insertions(+), 34 deletions(-) diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/ProxyProcessorSupport.java b/spring-aop/src/main/java/org/springframework/aop/framework/ProxyProcessorSupport.java index af1cf603988..a94c53b1064 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/ProxyProcessorSupport.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/ProxyProcessorSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -139,7 +139,8 @@ protected boolean isConfigurationCallbackInterface(Class ifc) { * @return whether the given interface is an internal language interface */ protected boolean isInternalLanguageInterface(Class ifc) { - return ifc.getName().equals("groovy.lang.GroovyObject"); + return (ifc.getName().equals("groovy.lang.GroovyObject") || + ifc.getName().endsWith(".cglib.proxy.Factory")); } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java index 3848510f98f..258f66bf4d2 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java @@ -577,7 +577,8 @@ protected void inject(Object bean, String beanName, PropertyValues pvs) throws T String autowiredBeanName = autowiredBeanNames.iterator().next(); if (beanFactory.containsBean(autowiredBeanName)) { if (beanFactory.isTypeMatch(autowiredBeanName, field.getType())) { - this.cachedFieldValue = new ShortcutDependencyDescriptor(desc, autowiredBeanName); + this.cachedFieldValue = new ShortcutDependencyDescriptor( + desc, autowiredBeanName, field.getType()); } } } @@ -661,8 +662,8 @@ protected void inject(Object bean, String beanName, PropertyValues pvs) throws T String autowiredBeanName = it.next(); if (beanFactory.containsBean(autowiredBeanName)) { if (beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) { - this.cachedMethodArguments[i] = - new ShortcutDependencyDescriptor(descriptors[i], autowiredBeanName); + this.cachedMethodArguments[i] = new ShortcutDependencyDescriptor( + descriptors[i], autowiredBeanName, paramTypes[i]); } } } @@ -705,16 +706,19 @@ private Object[] resolveCachedArguments(String beanName) { @SuppressWarnings("serial") private static class ShortcutDependencyDescriptor extends DependencyDescriptor { - private final String shortcutBeanName; + private final String shortcutName; - public ShortcutDependencyDescriptor(DependencyDescriptor original, String shortcutBeanName) { + private final Class requiredType; + + public ShortcutDependencyDescriptor(DependencyDescriptor original, String shortcutName, Class requiredType) { super(original); - this.shortcutBeanName = shortcutBeanName; + this.shortcutName = shortcutName; + this.requiredType = requiredType; } @Override public Object resolveShortcut(BeanFactory beanFactory) { - return resolveCandidate(this.shortcutBeanName, beanFactory); + return resolveCandidate(this.shortcutName, this.requiredType, beanFactory); } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java index 3533840635a..82c6883cc18 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java @@ -172,21 +172,6 @@ public Object resolveNotUnique(Class type, Map matchingBeans) throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet()); } - /** - * Resolve the specified bean name, as a candidate result of the matching - * algorithm for this dependency, to a bean instance from the given factory. - *

The default implementation calls {@link BeanFactory#getBean(String)}. - * Subclasses may provide additional arguments or other customizations. - * @param beanName the bean name, as a candidate result for this dependency - * @param beanFactory the associated factory - * @return the bean instance (never {@code null}) - * @since 4.3 - * @see BeanFactory#getBean(String) - */ - public Object resolveCandidate(String beanName, BeanFactory beanFactory) { - return beanFactory.getBean(beanName); - } - /** * Resolve a shortcut for this dependency against the given factory, for example * taking some pre-resolved information into account. @@ -196,12 +181,32 @@ public Object resolveCandidate(String beanName, BeanFactory beanFactory) { * pre-cached information while still receiving {@link InjectionPoint} exposure etc. * @param beanFactory the associated factory * @return the shortcut result if any, or {@code null} if none + * @throws BeansException if the shortcut could not be obtained * @since 4.3.1 */ - public Object resolveShortcut(BeanFactory beanFactory) { + public Object resolveShortcut(BeanFactory beanFactory) throws BeansException { return null; } + /** + * Resolve the specified bean name, as a candidate result of the matching + * algorithm for this dependency, to a bean instance from the given factory. + *

The default implementation calls {@link BeanFactory#getBean(String)}. + * Subclasses may provide additional arguments or other customizations. + * @param beanName the bean name, as a candidate result for this dependency + * @param requiredType the expected type of the bean (as an assertion) + * @param beanFactory the associated factory + * @return the bean instance (never {@code null}) + * @throws BeansException if the bean could not be obtained + * @since 4.3.2 + * @see BeanFactory#getBean(String) + */ + public Object resolveCandidate(String beanName, Class requiredType, BeanFactory beanFactory) + throws BeansException { + + return beanFactory.getBean(beanName, requiredType); + } + /** * Increase this descriptor's nesting level. diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index 9cc3c907ea4..1ff07eaa6db 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -1210,7 +1210,7 @@ protected Map findAutowireCandidates( } for (String candidateName : candidateNames) { if (!isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, descriptor)) { - result.put(candidateName, descriptor.resolveCandidate(candidateName, this)); + result.put(candidateName, descriptor.resolveCandidate(candidateName, requiredType, this)); } } if (result.isEmpty() && !indicatesMultipleBeans(requiredType)) { @@ -1218,14 +1218,14 @@ protected Map findAutowireCandidates( DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch(); for (String candidateName : candidateNames) { if (!isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, fallbackDescriptor)) { - result.put(candidateName, descriptor.resolveCandidate(candidateName, this)); + result.put(candidateName, descriptor.resolveCandidate(candidateName, requiredType, this)); } } if (result.isEmpty()) { // Consider self references before as a final pass for (String candidateName : candidateNames) { if (isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, fallbackDescriptor)) { - result.put(candidateName, descriptor.resolveCandidate(candidateName, this)); + result.put(candidateName, descriptor.resolveCandidate(candidateName, requiredType, this)); } } } @@ -1479,9 +1479,9 @@ public boolean isRequired() { return false; } @Override - public Object resolveCandidate(String beanName, BeanFactory beanFactory) { - return (!ObjectUtils.isEmpty(args) ? beanFactory.getBean(beanName, args) : - super.resolveCandidate(beanName, beanFactory)); + public Object resolveCandidate(String beanName, Class requiredType, BeanFactory beanFactory) { + return (!ObjectUtils.isEmpty(args) ? beanFactory.getBean(beanName, requiredType, args) : + super.resolveCandidate(beanName, requiredType, beanFactory)); } }; descriptorToUse.increaseNestingLevel(); @@ -1526,8 +1526,8 @@ public Object getObject(final Object... args) throws BeansException { else { DependencyDescriptor descriptorToUse = new DependencyDescriptor(descriptor) { @Override - public Object resolveCandidate(String beanName, BeanFactory beanFactory) { - return beanFactory.getBean(beanName, args); + public Object resolveCandidate(String beanName, Class requiredType, BeanFactory beanFactory) { + return ((AbstractBeanFactory) beanFactory).getBean(beanName, requiredType, args); } }; return doResolveDependency(descriptorToUse, this.beanName, null, null); diff --git a/spring-context/src/test/java/org/springframework/scheduling/annotation/EnableAsyncTests.java b/spring-context/src/test/java/org/springframework/scheduling/annotation/EnableAsyncTests.java index 3f04c76db30..0a92dedb887 100644 --- a/spring-context/src/test/java/org/springframework/scheduling/annotation/EnableAsyncTests.java +++ b/spring-context/src/test/java/org/springframework/scheduling/annotation/EnableAsyncTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,6 +26,7 @@ import java.util.concurrent.Future; import org.junit.Test; +import org.mockito.Mockito; import org.springframework.aop.Advisor; import org.springframework.aop.framework.Advised; @@ -37,6 +38,7 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; import org.springframework.core.Ordered; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.util.ReflectionUtils; @@ -67,6 +69,18 @@ public void proxyingOccurs() { asyncBean.work(); } + @Test + public void proxyingOccursWithMockitoStub() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(AsyncConfigWithMockito.class, AsyncBeanUser.class); + ctx.refresh(); + + AsyncBeanUser asyncBeanUser = ctx.getBean(AsyncBeanUser.class); + AsyncBean asyncBean = asyncBeanUser.getAsyncBean(); + assertThat(AopUtils.isAopProxy(asyncBean), is(true)); + asyncBean.work(); + } + @Test public void withAsyncBeanWithExecutorQualifiedByName() throws ExecutionException, InterruptedException { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); @@ -200,6 +214,20 @@ public Thread getThreadOfExecution() { } + static class AsyncBeanUser { + + private final AsyncBean asyncBean; + + public AsyncBeanUser(AsyncBean asyncBean) { + this.asyncBean = asyncBean; + } + + public AsyncBean getAsyncBean() { + return asyncBean; + } + } + + @EnableAsync(annotation = CustomAsync.class) static class CustomAsyncAnnotationConfig { } @@ -252,6 +280,17 @@ public AsyncBean asyncBean() { } + @Configuration + @EnableAsync + static class AsyncConfigWithMockito { + + @Bean @Lazy + public AsyncBean asyncBean() { + return Mockito.mock(AsyncBean.class); + } + } + + @Configuration @EnableAsync static class CustomExecutorAsyncConfig implements AsyncConfigurer { From 36e1c82ef5432a2a72c8da5f35e76822e6418a7c Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 20 Jul 2016 21:46:25 +0200 Subject: [PATCH 0052/1274] Backported refinements and polishing --- .../aspectj/SimpleAspectInstanceFactory.java | 8 ++-- .../org/springframework/beans/BeanUtils.java | 25 ++++------ .../beans/NullValueInNestedPathException.java | 15 +++++- .../BeanNotOfRequiredTypeException.java | 6 +-- ...CglibSubclassingInstantiationStrategy.java | 4 +- .../factory/support/ConstructorResolver.java | 17 +++---- .../support/SimpleInstantiationStrategy.java | 2 +- .../CustomCollectionEditor.java | 6 +-- .../propertyeditors/CustomMapEditor.java | 6 +-- .../beans/factory/xml/import.xml | 2 +- .../ConfigurationClassEnhancer.java | 2 +- .../annotation/ConfigurationClassParser.java | 2 +- .../AnnotationJmxAttributeSource.java | 2 +- .../MethodNameBasedMBeanInfoAssembler.java | 4 +- .../scripting/bsh/BshScriptUtils.java | 3 +- .../config/ScriptingDefaultsParser.java | 6 +-- .../scripting/groovy/GroovyScriptFactory.java | 2 +- .../support/StandardScriptFactory.java | 4 +- .../autoproxy/AnnotationBindingTests.java | 9 ++-- ...StatelessSessionProxyFactoryBeanTests.java | 8 ++-- ...StatelessSessionProxyFactoryBeanTests.java | 11 ++--- .../FormattingConversionServiceTests.java | 4 +- ...ameBasedMBeanInfoAssemblerMappedTests.java | 2 +- ...ethodNameBasedMBeanInfoAssemblerTests.java | 2 +- .../groovy/GroovyScriptFactoryTests.java | 9 ++-- .../core/CollectionFactory.java | 6 +-- .../core/ConfigurableObjectInputStream.java | 12 ++--- .../core/annotation/AnnotationAttributes.java | 2 +- .../core/annotation/AnnotationUtils.java | 4 +- .../io/AbstractFileResolvingResource.java | 4 +- .../springframework/core/io/PathResource.java | 6 +-- .../org/springframework/core/io/Resource.java | 13 ++--- .../springframework/core/io/UrlResource.java | 3 +- .../io/support/SpringFactoriesLoader.java | 2 +- .../core/style/ToStringCreator.java | 14 +++--- .../util/AutoPopulatingList.java | 15 +++--- .../org/springframework/util/ClassUtils.java | 1 - .../springframework/util/ResourceUtils.java | 4 +- .../core/SerializableTypeWrapperTests.java | 2 +- .../CollectionToCollectionConverterTests.java | 8 ++-- .../expression/spel/ast/Indexer.java | 4 +- .../jdbc/core/BeanPropertyRowMapper.java | 4 +- .../jdbc/datasource/ConnectionHolder.java | 6 +-- .../jdbc/support/SQLErrorCodes.java | 4 +- .../config/JcaListenerContainerParser.java | 4 +- .../support/MessageHeaderAccessor.java | 2 +- .../simp/stomp/DefaultStompSessionTests.java | 39 ++++----------- ...erRelayMessageHandlerIntegrationTests.java | 47 ++++++++----------- spring-oxm/oxm.gradle | 4 +- .../MockMvcClientHttpRequestFactory.java | 8 +++- .../response/DefaultResponseCreator.java | 17 ++++--- .../MockHttpServletRequestBuilder.java | 11 ++--- .../org/springframework/http/HttpStatus.java | 1 + .../json/Jackson2ObjectMapperBuilder.java | 10 ++-- .../ProtobufHttpMessageConverter.java | 3 +- .../xml/SourceHttpMessageConverter.java | 17 ++++--- .../support/ServletContextResource.java | 4 +- .../web/util/HierarchicalUriComponents.java | 1 + .../springframework/web/util/HtmlUtils.java | 6 +-- .../web/util/UrlPathHelper.java | 10 ++-- .../AtomFeedHttpMessageConverterTests.java | 16 +++---- .../RssChannelHttpMessageConverterTests.java | 17 +++---- .../remoting/jaxws/JaxWsSupportTests.java | 6 +-- .../util/HtmlCharacterEntityReferences.dtd | 10 ++-- .../context/PortletContextResource.java | 2 +- .../SimpleMappingExceptionResolver.java | 5 +- .../mvc/ServletWrappingController.java | 14 +++--- .../annotation/MvcUriComponentsBuilder.java | 2 +- .../web/servlet/view/xslt/XsltView.java | 10 ++-- .../support/AbstractHandshakeHandler.java | 18 ++++--- 70 files changed, 271 insertions(+), 288 deletions(-) diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/SimpleAspectInstanceFactory.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/SimpleAspectInstanceFactory.java index f1275daea40..5d413bd697c 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/SimpleAspectInstanceFactory.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/SimpleAspectInstanceFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -55,10 +55,12 @@ public final Object getAspectInstance() { return this.aspectClass.newInstance(); } catch (InstantiationException ex) { - throw new AopConfigException("Unable to instantiate aspect class [" + this.aspectClass.getName() + "]", ex); + throw new AopConfigException( + "Unable to instantiate aspect class: " + this.aspectClass.getName(), ex); } catch (IllegalAccessException ex) { - throw new AopConfigException("Cannot access element class [" + this.aspectClass.getName() + "]", ex); + throw new AopConfigException( + "Could not access aspect constructor: " + this.aspectClass.getName(), ex); } } diff --git a/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java b/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java index 3956ca93570..50c04c3b2f6 100644 --- a/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java +++ b/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java @@ -63,11 +63,10 @@ public abstract class BeanUtils { /** * Convenience method to instantiate a class using its no-arg constructor. - * As this method doesn't try to load classes by name, it should avoid - * class-loading issues. * @param clazz class to instantiate * @return the new instance * @throws BeanInstantiationException if the bean cannot be instantiated + * @see Class#newInstance() */ public static T instantiate(Class clazz) throws BeanInstantiationException { Assert.notNull(clazz, "Class must not be null"); @@ -87,13 +86,12 @@ public static T instantiate(Class clazz) throws BeanInstantiationExceptio /** * Instantiate a class using its no-arg constructor. - * As this method doesn't try to load classes by name, it should avoid - * class-loading issues. *

Note that this method tries to set the constructor accessible * if given a non-accessible (that is, non-public) constructor. * @param clazz class to instantiate * @return the new instance * @throws BeanInstantiationException if the bean cannot be instantiated + * @see Constructor#newInstance */ public static T instantiateClass(Class clazz) throws BeanInstantiationException { Assert.notNull(clazz, "Class must not be null"); @@ -111,17 +109,15 @@ public static T instantiateClass(Class clazz) throws BeanInstantiationExc /** * Instantiate a class using its no-arg constructor and return the new instance * as the specified assignable type. - *

Useful in cases where - * the type of the class to instantiate (clazz) is not available, but the type - * desired (assignableTo) is known. - *

As this method doesn't try to load classes by name, it should avoid - * class-loading issues. - *

Note that this method tries to set the constructor accessible - * if given a non-accessible (that is, non-public) constructor. + *

Useful in cases where the type of the class to instantiate (clazz) is not + * available, but the type desired (assignableTo) is known. + *

Note that this method tries to set the constructor accessible if given a + * non-accessible (that is, non-public) constructor. * @param clazz class to instantiate * @param assignableTo type that clazz must be assignableTo * @return the new instance * @throws BeanInstantiationException if the bean cannot be instantiated + * @see Constructor#newInstance */ @SuppressWarnings("unchecked") public static T instantiateClass(Class clazz, Class assignableTo) throws BeanInstantiationException { @@ -131,14 +127,13 @@ public static T instantiateClass(Class clazz, Class assignableTo) thro /** * Convenience method to instantiate a class using the given constructor. - * As this method doesn't try to load classes by name, it should avoid - * class-loading issues. - *

Note that this method tries to set the constructor accessible - * if given a non-accessible (that is, non-public) constructor. + *

Note that this method tries to set the constructor accessible if given a + * non-accessible (that is, non-public) constructor. * @param ctor the constructor to instantiate * @param args the constructor arguments to apply * @return the new instance * @throws BeanInstantiationException if the bean cannot be instantiated + * @see Constructor#newInstance */ public static T instantiateClass(Constructor ctor, Object... args) throws BeanInstantiationException { Assert.notNull(ctor, "Constructor must not be null"); diff --git a/spring-beans/src/main/java/org/springframework/beans/NullValueInNestedPathException.java b/spring-beans/src/main/java/org/springframework/beans/NullValueInNestedPathException.java index e01fafbfec6..e15885bc8ad 100644 --- a/spring-beans/src/main/java/org/springframework/beans/NullValueInNestedPathException.java +++ b/spring-beans/src/main/java/org/springframework/beans/NullValueInNestedPathException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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 @@ * spouse property of the target object has a null value. * * @author Rod Johnson + * @author Juergen Hoeller */ @SuppressWarnings("serial") public class NullValueInNestedPathException extends InvalidPropertyException { @@ -47,4 +48,16 @@ public NullValueInNestedPathException(Class beanClass, String propertyName, S super(beanClass, propertyName, msg); } + /** + * Create a new NullValueInNestedPathException. + * @param beanClass the offending bean class + * @param propertyName the offending property + * @param msg the detail message + * @param cause the root cause + * @since 4.3.2 + */ + public NullValueInNestedPathException(Class beanClass, String propertyName, String msg, Throwable cause) { + super(beanClass, propertyName, msg, cause); + } + } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/BeanNotOfRequiredTypeException.java b/spring-beans/src/main/java/org/springframework/beans/factory/BeanNotOfRequiredTypeException.java index bd6d33fbc41..6f98f435391 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/BeanNotOfRequiredTypeException.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/BeanNotOfRequiredTypeException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -45,8 +45,8 @@ public class BeanNotOfRequiredTypeException extends BeansException { * the expected type */ public BeanNotOfRequiredTypeException(String beanName, Class requiredType, Class actualType) { - super("Bean named '" + beanName + "' must be of type [" + requiredType.getName() + - "], but was actually of type [" + actualType.getName() + "]"); + super("Bean named '" + beanName + "' is expected to be of type [" + requiredType.getName() + + "] but was actually of type [" + actualType.getName() + "]"); this.beanName = beanName; this.requiredType = requiredType; this.actualType = actualType; diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java index 41653c8453c..cbb9d2f1536 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -115,7 +115,7 @@ public Object instantiate(Constructor ctor, Object... args) { Class subclass = createEnhancedSubclass(this.beanDefinition); Object instance; if (ctor == null) { - instance = BeanUtils.instantiate(subclass); + instance = BeanUtils.instantiateClass(subclass); } else { try { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java index 157bb1cd9e8..7c871d467e8 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java @@ -609,8 +609,8 @@ public Object run() { private int resolveConstructorArguments(String beanName, RootBeanDefinition mbd, BeanWrapper bw, ConstructorArgumentValues cargs, ConstructorArgumentValues resolvedValues) { - TypeConverter converter = (this.beanFactory.getCustomTypeConverter() != null ? - this.beanFactory.getCustomTypeConverter() : bw); + TypeConverter customConverter = this.beanFactory.getCustomTypeConverter(); + TypeConverter converter = (customConverter != null ? customConverter : bw); BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this.beanFactory, beanName, mbd, converter); @@ -665,8 +665,8 @@ private ArgumentsHolder createArgumentArray( BeanWrapper bw, Class[] paramTypes, String[] paramNames, Object methodOrCtor, boolean autowiring) throws UnsatisfiedDependencyException { - TypeConverter converter = (this.beanFactory.getCustomTypeConverter() != null ? - this.beanFactory.getCustomTypeConverter() : bw); + TypeConverter customConverter = this.beanFactory.getCustomTypeConverter(); + TypeConverter converter = (customConverter != null ? customConverter : bw); ArgumentsHolder args = new ArgumentsHolder(paramTypes.length); Set usedValueHolders = @@ -769,12 +769,13 @@ private ArgumentsHolder createArgumentArray( private Object[] resolvePreparedArguments( String beanName, RootBeanDefinition mbd, BeanWrapper bw, Member methodOrCtor, Object[] argsToResolve) { - Class[] paramTypes = (methodOrCtor instanceof Method ? - ((Method) methodOrCtor).getParameterTypes() : ((Constructor) methodOrCtor).getParameterTypes()); - TypeConverter converter = (this.beanFactory.getCustomTypeConverter() != null ? - this.beanFactory.getCustomTypeConverter() : bw); + TypeConverter customConverter = this.beanFactory.getCustomTypeConverter(); + TypeConverter converter = (customConverter != null ? customConverter : bw); BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this.beanFactory, beanName, mbd, converter); + Class[] paramTypes = (methodOrCtor instanceof Method ? + ((Method) methodOrCtor).getParameterTypes() : ((Constructor) methodOrCtor).getParameterTypes()); + Object[] resolvedArgs = new Object[argsToResolve.length]; for (int argIndex = 0; argIndex < argsToResolve.length; argIndex++) { Object argValue = argsToResolve[argIndex]; diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/SimpleInstantiationStrategy.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/SimpleInstantiationStrategy.java index 74acbd2fee1..421805cb909 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/SimpleInstantiationStrategy.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/SimpleInstantiationStrategy.java @@ -81,7 +81,7 @@ public Constructor run() throws Exception { } bd.resolvedConstructorOrFactoryMethod = constructorToUse; } - catch (Exception ex) { + catch (Throwable ex) { throw new BeanInstantiationException(clazz, "No default constructor found", ex); } } diff --git a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/CustomCollectionEditor.java b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/CustomCollectionEditor.java index 607a99afcf9..4c63ac7995b 100644 --- a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/CustomCollectionEditor.java +++ b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/CustomCollectionEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -154,9 +154,9 @@ protected Collection createCollection(Class collec try { return collectionType.newInstance(); } - catch (Exception ex) { + catch (Throwable ex) { throw new IllegalArgumentException( - "Could not instantiate collection class [" + collectionType.getName() + "]: " + ex.getMessage()); + "Could not instantiate collection class: " + collectionType.getName(), ex); } } else if (List.class == collectionType) { diff --git a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/CustomMapEditor.java b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/CustomMapEditor.java index 85865e546ff..05b5d872dd9 100644 --- a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/CustomMapEditor.java +++ b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/CustomMapEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -132,9 +132,9 @@ protected Map createMap(Class mapType, int initia try { return mapType.newInstance(); } - catch (Exception ex) { + catch (Throwable ex) { throw new IllegalArgumentException( - "Could not instantiate map class [" + mapType.getName() + "]: " + ex.getMessage()); + "Could not instantiate map class: " + mapType.getName(), ex); } } else if (SortedMap.class == mapType) { diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/import.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/import.xml index 96446f02bae..278e5dff811 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/import.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/import.xml @@ -1,5 +1,5 @@ - + diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java index 52c27a3ce0a..63497ff91a6 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java @@ -466,7 +466,7 @@ private Object enhanceFactoryBean(final Object factoryBean, final ConfigurableBe try { fbProxy = fbClass.newInstance(); } - catch (Exception ex) { + catch (Throwable ex) { throw new IllegalStateException("Unable to instantiate enhanced FactoryBean using Objenesis, " + "and regular FactoryBean instantiation via default constructor fails as well", ex); } diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java index 673ee5c8a1b..5c3b3f61568 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java @@ -379,7 +379,7 @@ private void processPropertySource(AnnotationAttributes propertySource) throws I Class factoryClass = propertySource.getClass("factory"); PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ? - DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiate(factoryClass)); + DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass)); for (String location : locations) { try { diff --git a/spring-context/src/main/java/org/springframework/jmx/export/annotation/AnnotationJmxAttributeSource.java b/spring-context/src/main/java/org/springframework/jmx/export/annotation/AnnotationJmxAttributeSource.java index bb0a0abd007..23d0d3a5e87 100644 --- a/spring-context/src/main/java/org/springframework/jmx/export/annotation/AnnotationJmxAttributeSource.java +++ b/spring-context/src/main/java/org/springframework/jmx/export/annotation/AnnotationJmxAttributeSource.java @@ -135,7 +135,7 @@ private static T copyPropertiesToBean(Annotation ann, Class beanClass) { if (ann == null) { return null; } - T bean = BeanUtils.instantiate(beanClass); + T bean = BeanUtils.instantiateClass(beanClass); AnnotationBeanUtils.copyPropertiesToBean(ann, bean); return bean; } diff --git a/spring-context/src/main/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssembler.java b/spring-context/src/main/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssembler.java index 3df5350dfd0..30f5da7638b 100644 --- a/spring-context/src/main/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssembler.java +++ b/spring-context/src/main/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssembler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -72,7 +72,7 @@ public class MethodNameBasedMBeanInfoAssembler extends AbstractConfigurableMBean * @param methodNames an array of method names indicating the methods to use * @see #setMethodMappings */ - public void setManagedMethods(String[] methodNames) { + public void setManagedMethods(String... methodNames) { this.managedMethods = new HashSet(Arrays.asList(methodNames)); } diff --git a/spring-context/src/main/java/org/springframework/scripting/bsh/BshScriptUtils.java b/spring-context/src/main/java/org/springframework/scripting/bsh/BshScriptUtils.java index 5fb732dc410..57d18c2f165 100644 --- a/spring-context/src/main/java/org/springframework/scripting/bsh/BshScriptUtils.java +++ b/spring-context/src/main/java/org/springframework/scripting/bsh/BshScriptUtils.java @@ -94,8 +94,7 @@ public static Object createBshObject(String scriptSource, Class[] scriptInter return clazz.newInstance(); } catch (Throwable ex) { - throw new IllegalStateException("Could not instantiate script class [" + - clazz.getName() + "]. Root cause is " + ex); + throw new IllegalStateException("Could not instantiate script class: " + clazz.getName(), ex); } } else { diff --git a/spring-context/src/main/java/org/springframework/scripting/config/ScriptingDefaultsParser.java b/spring-context/src/main/java/org/springframework/scripting/config/ScriptingDefaultsParser.java index b5452624860..f967a3a6c92 100644 --- a/spring-context/src/main/java/org/springframework/scripting/config/ScriptingDefaultsParser.java +++ b/spring-context/src/main/java/org/springframework/scripting/config/ScriptingDefaultsParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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 @@ * @author Mark Fisher * @since 2.5 */ -public class ScriptingDefaultsParser implements BeanDefinitionParser { +class ScriptingDefaultsParser implements BeanDefinitionParser { private static final String REFRESH_CHECK_DELAY_ATTRIBUTE = "refresh-check-delay"; @@ -41,7 +41,7 @@ public BeanDefinition parse(Element element, ParserContext parserContext) { LangNamespaceUtils.registerScriptFactoryPostProcessorIfNecessary(parserContext.getRegistry()); String refreshCheckDelay = element.getAttribute(REFRESH_CHECK_DELAY_ATTRIBUTE); if (StringUtils.hasText(refreshCheckDelay)) { - bd.getPropertyValues().add("defaultRefreshCheckDelay", new Long(refreshCheckDelay)); + bd.getPropertyValues().add("defaultRefreshCheckDelay", Long.valueOf(refreshCheckDelay)); } String proxyTargetClass = element.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE); if (StringUtils.hasText(proxyTargetClass)) { diff --git a/spring-context/src/main/java/org/springframework/scripting/groovy/GroovyScriptFactory.java b/spring-context/src/main/java/org/springframework/scripting/groovy/GroovyScriptFactory.java index 439f3c1e0e0..fdc2bb83978 100644 --- a/spring-context/src/main/java/org/springframework/scripting/groovy/GroovyScriptFactory.java +++ b/spring-context/src/main/java/org/springframework/scripting/groovy/GroovyScriptFactory.java @@ -266,7 +266,7 @@ protected Object executeScript(ScriptSource scriptSource, Class scriptClass) } catch (InstantiationException ex) { throw new ScriptCompilationException( - scriptSource, "Could not instantiate Groovy script class: " + scriptClass.getName(), ex); + scriptSource, "Unable to instantiate Groovy script class: " + scriptClass.getName(), ex); } catch (IllegalAccessException ex) { throw new ScriptCompilationException( diff --git a/spring-context/src/main/java/org/springframework/scripting/support/StandardScriptFactory.java b/spring-context/src/main/java/org/springframework/scripting/support/StandardScriptFactory.java index bff51a79eaf..f8bc2365c39 100644 --- a/spring-context/src/main/java/org/springframework/scripting/support/StandardScriptFactory.java +++ b/spring-context/src/main/java/org/springframework/scripting/support/StandardScriptFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -154,7 +154,7 @@ public Object getScriptedObject(ScriptSource scriptSource, Class... actualInt } catch (InstantiationException ex) { throw new ScriptCompilationException( - scriptSource, "Could not instantiate script class: " + scriptClass.getName(), ex); + scriptSource, "Unable to instantiate script class: " + scriptClass.getName(), ex); } catch (IllegalAccessException ex) { throw new ScriptCompilationException( diff --git a/spring-context/src/test/java/org/springframework/aop/aspectj/autoproxy/AnnotationBindingTests.java b/spring-context/src/test/java/org/springframework/aop/aspectj/autoproxy/AnnotationBindingTests.java index 82b20d96630..a7f24b51bae 100644 --- a/spring-context/src/test/java/org/springframework/aop/aspectj/autoproxy/AnnotationBindingTests.java +++ b/spring-context/src/test/java/org/springframework/aop/aspectj/autoproxy/AnnotationBindingTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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,18 +27,19 @@ * @author Adrian Colyer * @author Chris Beams */ -public final class AnnotationBindingTests { +public class AnnotationBindingTests { private AnnotatedTestBean testBean; + @Before public void setUp() { ClassPathXmlApplicationContext ctx = - new ClassPathXmlApplicationContext(getClass().getSimpleName() + "-context.xml", getClass()); - + new ClassPathXmlApplicationContext(getClass().getSimpleName() + "-context.xml", getClass()); testBean = (AnnotatedTestBean) ctx.getBean("testBean"); } + @Test public void testAnnotationBindingInAroundAdvice() { assertEquals("this value", testBean.doThis()); diff --git a/spring-context/src/test/java/org/springframework/ejb/access/LocalStatelessSessionProxyFactoryBeanTests.java b/spring-context/src/test/java/org/springframework/ejb/access/LocalStatelessSessionProxyFactoryBeanTests.java index 9fbf0bbd7de..29b40fc1965 100644 --- a/spring-context/src/test/java/org/springframework/ejb/access/LocalStatelessSessionProxyFactoryBeanTests.java +++ b/spring-context/src/test/java/org/springframework/ejb/access/LocalStatelessSessionProxyFactoryBeanTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 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. @@ -182,19 +182,19 @@ public Object lookup(String name) throws NamingException { } - public static interface MyHome extends EJBLocalHome { + public interface MyHome extends EJBLocalHome { MyBusinessMethods create() throws CreateException; } - public static interface MyBusinessMethods { + public interface MyBusinessMethods { int getValue(); } - public static interface MyEjb extends EJBLocalObject, MyBusinessMethods { + public interface MyEjb extends EJBLocalObject, MyBusinessMethods { } } diff --git a/spring-context/src/test/java/org/springframework/ejb/access/SimpleRemoteStatelessSessionProxyFactoryBeanTests.java b/spring-context/src/test/java/org/springframework/ejb/access/SimpleRemoteStatelessSessionProxyFactoryBeanTests.java index 8efc5b5740b..2d62ee73945 100644 --- a/spring-context/src/test/java/org/springframework/ejb/access/SimpleRemoteStatelessSessionProxyFactoryBeanTests.java +++ b/spring-context/src/test/java/org/springframework/ejb/access/SimpleRemoteStatelessSessionProxyFactoryBeanTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 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,26 +279,25 @@ public Object lookup(String name) throws NamingException { } - protected static interface MyHome extends EJBHome { + protected interface MyHome extends EJBHome { MyBusinessMethods create() throws CreateException, RemoteException; } - protected static interface MyBusinessMethods { + protected interface MyBusinessMethods { int getValue() throws RemoteException; } - protected static interface MyLocalBusinessMethods { + protected interface MyLocalBusinessMethods { int getValue(); } - protected static interface MyEjb extends EJBObject, MyBusinessMethods { - + protected interface MyEjb extends EJBObject, MyBusinessMethods { } } diff --git a/spring-context/src/test/java/org/springframework/format/support/FormattingConversionServiceTests.java b/spring-context/src/test/java/org/springframework/format/support/FormattingConversionServiceTests.java index a6f5779b848..e7c034c3a1a 100644 --- a/spring-context/src/test/java/org/springframework/format/support/FormattingConversionServiceTests.java +++ b/spring-context/src/test/java/org/springframework/format/support/FormattingConversionServiceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -218,7 +218,7 @@ public Date convert(DateTime source) { assertEquals(new LocalDate(2009, 11, 1), new LocalDate(dates.get(1))); assertEquals(new LocalDate(2009, 11, 2), new LocalDate(dates.get(2))); - Object model = BeanUtils.instantiate(modelClass); + Object model = modelClass.newInstance(); ConfigurablePropertyAccessor accessor = directFieldAccess ? PropertyAccessorFactory.forDirectFieldAccess(model) : PropertyAccessorFactory.forBeanPropertyAccess(model); accessor.setConversionService(formattingService); diff --git a/spring-context/src/test/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssemblerMappedTests.java b/spring-context/src/test/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssemblerMappedTests.java index 21efe0ba529..9b8ec774e06 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssemblerMappedTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssemblerMappedTests.java @@ -47,7 +47,7 @@ public void testGetAgeIsReadOnly() throws Exception { public void testWithFallThrough() throws Exception { MethodNameBasedMBeanInfoAssembler assembler = getWithMapping("foobar", "add,myOperation,getName,setName,getAge"); - assembler.setManagedMethods(new String[]{"getNickName", "setNickName"}); + assembler.setManagedMethods("getNickName", "setNickName"); ModelMBeanInfo inf = assembler.getMBeanInfo(getBean(), getObjectName()); MBeanAttributeInfo attr = inf.getAttribute("NickName"); diff --git a/spring-context/src/test/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssemblerTests.java b/spring-context/src/test/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssemblerTests.java index 3b51e92bc8d..dae9a8a3672 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssemblerTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssemblerTests.java @@ -52,7 +52,7 @@ protected int getExpectedAttributeCount() { @Override protected MBeanInfoAssembler getAssembler() { MethodNameBasedMBeanInfoAssembler assembler = new MethodNameBasedMBeanInfoAssembler(); - assembler.setManagedMethods(new String[] {"add", "myOperation", "getName", "setName", "getAge"}); + assembler.setManagedMethods("add", "myOperation", "getName", "setName", "getAge"); return assembler; } diff --git a/spring-context/src/test/java/org/springframework/scripting/groovy/GroovyScriptFactoryTests.java b/spring-context/src/test/java/org/springframework/scripting/groovy/GroovyScriptFactoryTests.java index 10d7c7ac1ca..c8abcc425a6 100644 --- a/spring-context/src/test/java/org/springframework/scripting/groovy/GroovyScriptFactoryTests.java +++ b/spring-context/src/test/java/org/springframework/scripting/groovy/GroovyScriptFactoryTests.java @@ -268,7 +268,7 @@ public void testScriptCompilationException() throws Exception { @Test public void testScriptedClassThatDoesNotHaveANoArgCtor() throws Exception { ScriptSource script = mock(ScriptSource.class); - final String badScript = "class Foo { public Foo(String foo) {}}"; + String badScript = "class Foo { public Foo(String foo) {}}"; given(script.getScriptAsString()).willReturn(badScript); given(script.suggestedClassName()).willReturn("someName"); GroovyScriptFactory factory = new GroovyScriptFactory(ScriptFactoryPostProcessor.INLINE_SCRIPT_PREFIX @@ -285,11 +285,10 @@ public void testScriptedClassThatDoesNotHaveANoArgCtor() throws Exception { @Test public void testScriptedClassThatHasNoPublicNoArgCtor() throws Exception { ScriptSource script = mock(ScriptSource.class); - final String badScript = "class Foo { protected Foo() {}}"; + String badScript = "class Foo { protected Foo() {} \n String toString() { 'X' }}"; given(script.getScriptAsString()).willReturn(badScript); given(script.suggestedClassName()).willReturn("someName"); - GroovyScriptFactory factory = new GroovyScriptFactory(ScriptFactoryPostProcessor.INLINE_SCRIPT_PREFIX - + badScript); + GroovyScriptFactory factory = new GroovyScriptFactory(ScriptFactoryPostProcessor.INLINE_SCRIPT_PREFIX + badScript); try { factory.getScriptedObject(script); fail("Must have thrown a ScriptCompilationException (no oublic no-arg ctor in scripted class)."); @@ -562,7 +561,7 @@ public void testMetaClassWithXsd() { testMetaClass("org/springframework/scripting/groovy/calculators-with-xsd.xml"); } - private void testMetaClass(final String xmlFile) { + private void testMetaClass(String xmlFile) { // expect the exception we threw in the custom metaclass to show it got invoked try { ApplicationContext ctx = new ClassPathXmlApplicationContext(xmlFile); diff --git a/spring-core/src/main/java/org/springframework/core/CollectionFactory.java b/spring-core/src/main/java/org/springframework/core/CollectionFactory.java index e9af0617fcd..0a83068ce7b 100644 --- a/spring-core/src/main/java/org/springframework/core/CollectionFactory.java +++ b/spring-core/src/main/java/org/springframework/core/CollectionFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -203,7 +203,7 @@ else if (EnumSet.class == collectionType) { try { return (Collection) collectionType.newInstance(); } - catch (Exception ex) { + catch (Throwable ex) { throw new IllegalArgumentException( "Could not instantiate Collection type: " + collectionType.getName(), ex); } @@ -318,7 +318,7 @@ else if (EnumMap.class == mapType) { try { return (Map) mapType.newInstance(); } - catch (Exception ex) { + catch (Throwable ex) { throw new IllegalArgumentException("Could not instantiate Map type: " + mapType.getName(), ex); } } diff --git a/spring-core/src/main/java/org/springframework/core/ConfigurableObjectInputStream.java b/spring-core/src/main/java/org/springframework/core/ConfigurableObjectInputStream.java index 5af282f61d8..70a0a87c88d 100644 --- a/spring-core/src/main/java/org/springframework/core/ConfigurableObjectInputStream.java +++ b/spring-core/src/main/java/org/springframework/core/ConfigurableObjectInputStream.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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.NotSerializableException; import java.io.ObjectInputStream; import java.io.ObjectStreamClass; -import java.lang.reflect.Proxy; import org.springframework.util.ClassUtils; @@ -101,7 +100,7 @@ protected Class resolveProxyClass(String[] interfaces) throws IOException, Cl } } try { - return Proxy.getProxyClass(this.classLoader, resolvedInterfaces); + return ClassUtils.createCompositeInterface(resolvedInterfaces, this.classLoader); } catch (IllegalArgumentException ex) { throw new ClassNotFoundException(null, ex); @@ -117,7 +116,7 @@ protected Class resolveProxyClass(String[] interfaces) throws IOException, Cl for (int i = 0; i < interfaces.length; i++) { resolvedInterfaces[i] = resolveFallbackIfPossible(interfaces[i], ex); } - return Proxy.getProxyClass(getFallbackClassLoader(), resolvedInterfaces); + return ClassUtils.createCompositeInterface(resolvedInterfaces, getFallbackClassLoader()); } } } @@ -139,8 +138,9 @@ protected Class resolveFallbackIfPossible(String className, ClassNotFoundExce /** * Return the fallback ClassLoader to use when no ClassLoader was specified - * and ObjectInputStream's own default ClassLoader failed. - *

The default implementation simply returns {@code null}. + * and ObjectInputStream's own default class loader failed. + *

The default implementation simply returns {@code null}, indicating + * that no specific fallback is available. */ protected ClassLoader getFallbackClassLoader() throws IOException { return null; diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAttributes.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAttributes.java index 97af36d5dee..0cf257d6c90 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAttributes.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAttributes.java @@ -92,7 +92,7 @@ public AnnotationAttributes(Class annotationType) { /** * Create a new, empty {@link AnnotationAttributes} instance for the * specified {@code annotationType}. - * @param annotationType the type of annotation represented by this + * @param annotationType the annotation type name represented by this * {@code AnnotationAttributes} instance; never {@code null} * @param classLoader the ClassLoader to try to load the annotation type on, * or {@code null} to just store the annotation type name diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java index 750b1ca46dd..2fe059a5505 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java @@ -1268,8 +1268,8 @@ static void postProcessAnnotationAttributes(Object annotatedElement, (annotatedElement != null ? annotatedElement.toString() : "unknown element"); throw new AnnotationConfigurationException(String.format( "In AnnotationAttributes for annotation [%s] declared on %s, " + - "attribute '%s' and its alias '%s' are declared with values of [%s] and [%s], " + - "but only one is permitted.", annotationType.getName(), elementAsString, + "attribute '%s' and its alias '%s' are declared with values of [%s] and [%s], " + + "but only one is permitted.", annotationType.getName(), elementAsString, attributeName, aliasedAttributeName, ObjectUtils.nullSafeToString(value), ObjectUtils.nullSafeToString(aliasedValue))); } diff --git a/spring-core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java b/spring-core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java index a81f6c3818e..8f61f36b947 100644 --- a/spring-core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -72,7 +72,7 @@ protected File getFileForLastModifiedCheck() throws IOException { } /** - * This implementation returns a File reference for the underlying class path + * This implementation returns a File reference for the given URI-identified * resource, provided that it refers to a file in the file system. * @see org.springframework.util.ResourceUtils#getFile(java.net.URI, String) */ diff --git a/spring-core/src/main/java/org/springframework/core/io/PathResource.java b/spring-core/src/main/java/org/springframework/core/io/PathResource.java index f84ccfa9083..af046049c3c 100644 --- a/spring-core/src/main/java/org/springframework/core/io/PathResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/PathResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -182,8 +182,8 @@ public File getFile() throws IOException { return this.path.toFile(); } catch (UnsupportedOperationException ex) { - // only Paths on the default file system can be converted to a File - // do exception translation for cases where conversion is not possible + // Only paths on the default file system can be converted to a File: + // Do exception translation for cases where conversion is not possible. throw new FileNotFoundException(this.path + " cannot be resolved to " + "absolute file path"); } } diff --git a/spring-core/src/main/java/org/springframework/core/io/Resource.java b/spring-core/src/main/java/org/springframework/core/io/Resource.java index e3fc2a4e59d..82a073fb2aa 100644 --- a/spring-core/src/main/java/org/springframework/core/io/Resource.java +++ b/spring-core/src/main/java/org/springframework/core/io/Resource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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,7 +47,7 @@ public interface Resource extends InputStreamSource { /** - * Return whether this resource actually exists in physical form. + * Determine whether this resource actually exists in physical form. *

This method performs a definitive existence check, whereas the * existence of a {@code Resource} handle only guarantees a * valid descriptor handle. @@ -55,8 +55,8 @@ public interface Resource extends InputStreamSource { boolean exists(); /** - * Return whether the contents of this resource can be read, - * e.g. via {@link #getInputStream()} or {@link #getFile()}. + * Indicate whether the contents of this resource can be read via + * {@link #getInputStream()}. *

Will be {@code true} for typical resource descriptors; * note that actual content reading may still fail when attempted. * However, a value of {@code false} is a definitive indication @@ -66,8 +66,8 @@ public interface Resource extends InputStreamSource { boolean isReadable(); /** - * Return whether this resource represents a handle with an open - * stream. If true, the InputStream cannot be read multiple times, + * Indicate whether this resource represents a handle with an open stream. + * If {@code true}, the InputStream cannot be read multiple times, * and must be read and closed to avoid resource leaks. *

Will be {@code false} for typical resource descriptors. */ @@ -84,6 +84,7 @@ public interface Resource extends InputStreamSource { * Return a URI handle for this resource. * @throws IOException if the resource cannot be resolved as URI, * i.e. if the resource is not available as descriptor + * @since 2.5 */ URI getURI() throws IOException; diff --git a/spring-core/src/main/java/org/springframework/core/io/UrlResource.java b/spring-core/src/main/java/org/springframework/core/io/UrlResource.java index 41aa4a03b48..44e6516ed8e 100644 --- a/spring-core/src/main/java/org/springframework/core/io/UrlResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/UrlResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -61,6 +61,7 @@ public class UrlResource extends AbstractFileResolvingResource { * Create a new {@code UrlResource} based on the given URI object. * @param uri a URI * @throws MalformedURLException if the given URL path is not valid + * @since 2.5 */ public UrlResource(URI uri) throws MalformedURLException { Assert.notNull(uri, "URI must not be null"); diff --git a/spring-core/src/main/java/org/springframework/core/io/support/SpringFactoriesLoader.java b/spring-core/src/main/java/org/springframework/core/io/support/SpringFactoriesLoader.java index 8021208bafb..908b54d3417 100644 --- a/spring-core/src/main/java/org/springframework/core/io/support/SpringFactoriesLoader.java +++ b/spring-core/src/main/java/org/springframework/core/io/support/SpringFactoriesLoader.java @@ -139,7 +139,7 @@ private static T instantiateFactory(String instanceClassName, Class facto return (T) constructor.newInstance(); } catch (Throwable ex) { - throw new IllegalArgumentException("Cannot instantiate factory class: " + factoryClass.getName(), ex); + throw new IllegalArgumentException("Unable to instantiate factory class: " + factoryClass.getName(), ex); } } diff --git a/spring-core/src/main/java/org/springframework/core/style/ToStringCreator.java b/spring-core/src/main/java/org/springframework/core/style/ToStringCreator.java index 6f682939cc7..a7291f6f796 100644 --- a/spring-core/src/main/java/org/springframework/core/style/ToStringCreator.java +++ b/spring-core/src/main/java/org/springframework/core/style/ToStringCreator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -82,7 +82,7 @@ public ToStringCreator(Object obj, ToStringStyler styler) { * @return this, to support call-chaining */ public ToStringCreator append(String fieldName, byte value) { - return append(fieldName, new Byte(value)); + return append(fieldName, Byte.valueOf(value)); } /** @@ -92,7 +92,7 @@ public ToStringCreator append(String fieldName, byte value) { * @return this, to support call-chaining */ public ToStringCreator append(String fieldName, short value) { - return append(fieldName, new Short(value)); + return append(fieldName, Short.valueOf(value)); } /** @@ -102,7 +102,7 @@ public ToStringCreator append(String fieldName, short value) { * @return this, to support call-chaining */ public ToStringCreator append(String fieldName, int value) { - return append(fieldName, new Integer(value)); + return append(fieldName, Integer.valueOf(value)); } /** @@ -112,7 +112,7 @@ public ToStringCreator append(String fieldName, int value) { * @return this, to support call-chaining */ public ToStringCreator append(String fieldName, long value) { - return append(fieldName, new Long(value)); + return append(fieldName, Long.valueOf(value)); } /** @@ -122,7 +122,7 @@ public ToStringCreator append(String fieldName, long value) { * @return this, to support call-chaining */ public ToStringCreator append(String fieldName, float value) { - return append(fieldName, new Float(value)); + return append(fieldName, Float.valueOf(value)); } /** @@ -132,7 +132,7 @@ public ToStringCreator append(String fieldName, float value) { * @return this, to support call-chaining */ public ToStringCreator append(String fieldName, double value) { - return append(fieldName, new Double(value)); + return append(fieldName, Double.valueOf(value)); } /** diff --git a/spring-core/src/main/java/org/springframework/util/AutoPopulatingList.java b/spring-core/src/main/java/org/springframework/util/AutoPopulatingList.java index 9cea1c99dfd..4bea60c83d2 100644 --- a/spring-core/src/main/java/org/springframework/util/AutoPopulatingList.java +++ b/spring-core/src/main/java/org/springframework/util/AutoPopulatingList.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -263,13 +263,16 @@ public static class ElementInstantiationException extends RuntimeException { public ElementInstantiationException(String msg) { super(msg); } + + public ElementInstantiationException(String message, Throwable cause) { + super(message, cause); + } } /** * Reflective implementation of the ElementFactory interface, * using {@code Class.newInstance()} on a given element class. - * @see Class#newInstance() */ private static class ReflectiveElementFactory implements ElementFactory, Serializable { @@ -288,12 +291,12 @@ public E createElement(int index) { return this.elementClass.newInstance(); } catch (InstantiationException ex) { - throw new ElementInstantiationException("Unable to instantiate element class [" + - this.elementClass.getName() + "]. Root cause is " + ex); + throw new ElementInstantiationException( + "Unable to instantiate element class: " + this.elementClass.getName(), ex); } catch (IllegalAccessException ex) { - throw new ElementInstantiationException("Cannot access element class [" + - this.elementClass.getName() + "]. Root cause is " + ex); + throw new ElementInstantiationException( + "Could not access element constructor: " + this.elementClass.getName(), ex); } } } diff --git a/spring-core/src/main/java/org/springframework/util/ClassUtils.java b/spring-core/src/main/java/org/springframework/util/ClassUtils.java index 93dd3d616e2..ce09285746e 100644 --- a/spring-core/src/main/java/org/springframework/util/ClassUtils.java +++ b/spring-core/src/main/java/org/springframework/util/ClassUtils.java @@ -1158,7 +1158,6 @@ public static Set> getAllInterfacesForClassAsSet(Class clazz, ClassL */ public static Class createCompositeInterface(Class[] interfaces, ClassLoader classLoader) { Assert.notEmpty(interfaces, "Interfaces must not be empty"); - Assert.notNull(classLoader, "ClassLoader must not be null"); return Proxy.getProxyClass(classLoader, interfaces); } diff --git a/spring-core/src/main/java/org/springframework/util/ResourceUtils.java b/spring-core/src/main/java/org/springframework/util/ResourceUtils.java index 3ea92707e13..9108053f80b 100644 --- a/spring-core/src/main/java/org/springframework/util/ResourceUtils.java +++ b/spring-core/src/main/java/org/springframework/util/ResourceUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -235,6 +235,7 @@ public static File getFile(URL resourceUrl, String description) throws FileNotFo * @return a corresponding File object * @throws FileNotFoundException if the URL cannot be resolved to * a file in the file system + * @since 2.5 */ public static File getFile(URI resourceUri) throws FileNotFoundException { return getFile(resourceUri, "URI"); @@ -249,6 +250,7 @@ public static File getFile(URI resourceUri) throws FileNotFoundException { * @return a corresponding File object * @throws FileNotFoundException if the URL cannot be resolved to * a file in the file system + * @since 2.5 */ public static File getFile(URI resourceUri, String description) throws FileNotFoundException { Assert.notNull(resourceUri, "Resource URI must not be null"); diff --git a/spring-core/src/test/java/org/springframework/core/SerializableTypeWrapperTests.java b/spring-core/src/test/java/org/springframework/core/SerializableTypeWrapperTests.java index 9cb3d539286..ba669e0b538 100644 --- a/spring-core/src/test/java/org/springframework/core/SerializableTypeWrapperTests.java +++ b/spring-core/src/test/java/org/springframework/core/SerializableTypeWrapperTests.java @@ -80,7 +80,7 @@ public void forGenericInterfaces() throws Exception { } @Test - public void forTypeParamters() throws Exception { + public void forTypeParameters() throws Exception { Type type = SerializableTypeWrapper.forTypeParameters(List.class)[0]; assertThat(type.toString(), equalTo("E")); assertSerializable(type); diff --git a/spring-core/src/test/java/org/springframework/core/convert/support/CollectionToCollectionConverterTests.java b/spring-core/src/test/java/org/springframework/core/convert/support/CollectionToCollectionConverterTests.java index 86b5415875d..131e739ebb2 100644 --- a/spring-core/src/test/java/org/springframework/core/convert/support/CollectionToCollectionConverterTests.java +++ b/spring-core/src/test/java/org/springframework/core/convert/support/CollectionToCollectionConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,10 +75,10 @@ public void scalarList() throws Exception { conversionService.addConverterFactory(new StringToNumberConverterFactory()); assertTrue(conversionService.canConvert(sourceType, targetType)); @SuppressWarnings("unchecked") - List result = (List) conversionService.convert(list, sourceType, targetType); + List result = (List) conversionService.convert(list, sourceType, targetType); assertFalse(list.equals(result)); - assertEquals(9, result.get(0)); - assertEquals(37, result.get(1)); + assertEquals(9, result.get(0).intValue()); + assertEquals(37, result.get(1).intValue()); } @Test diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java index ae15a6f123d..32a837eaa9c 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -694,7 +694,7 @@ private void growCollectionIfNecessary() { newElements--; } } - catch (Exception ex) { + catch (Throwable ex) { throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.UNABLE_TO_GROW_COLLECTION); } } diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/BeanPropertyRowMapper.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/BeanPropertyRowMapper.java index af71c8763f9..fb280e86428 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/BeanPropertyRowMapper.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/BeanPropertyRowMapper.java @@ -60,7 +60,7 @@ *

To facilitate mapping between columns and fields that don't have matching names, * try using column aliases in the SQL statement like "select fname as first_name from customer". * - *

For 'null' values read from the databasem, we will attempt to call the setter, but in the case of + *

For 'null' values read from the database, we will attempt to call the setter, but in the case of * Java primitives, this causes a TypeMismatchException. This class can be configured (using the * primitivesDefaultedForNullValue property) to trap this exception and use the primitives default value. * Be aware that if you use the values from the generated bean to update the database the primitive value @@ -274,7 +274,7 @@ protected String lowerCaseName(String name) { @Override public T mapRow(ResultSet rs, int rowNumber) throws SQLException { Assert.state(this.mappedClass != null, "Mapped class was not specified"); - T mappedObject = BeanUtils.instantiate(this.mappedClass); + T mappedObject = BeanUtils.instantiateClass(this.mappedClass); BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(mappedObject); initBeanWrapper(bw); diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/ConnectionHolder.java b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/ConnectionHolder.java index 6ed77ef0c13..9544749071c 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/ConnectionHolder.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/ConnectionHolder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -161,9 +161,9 @@ public Connection getConnection() { */ public boolean supportsSavepoints() throws SQLException { if (this.savepointsSupported == null) { - this.savepointsSupported = new Boolean(getConnection().getMetaData().supportsSavepoints()); + this.savepointsSupported = getConnection().getMetaData().supportsSavepoints(); } - return this.savepointsSupported.booleanValue(); + return this.savepointsSupported; } /** diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLErrorCodes.java b/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLErrorCodes.java index 7a9c139973e..826b9db6821 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLErrorCodes.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/support/SQLErrorCodes.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -192,7 +192,7 @@ public void setCustomSqlExceptionTranslatorClass(Class * *

Note that the above examples aim to demonstrate the general idea of using - * header accessors. The most likely usage however is through sub-classes. + * header accessors. The most likely usage however is through subclasses. * * @author Rossen Stoyanchev * @author Juergen Hoeller diff --git a/spring-messaging/src/test/java/org/springframework/messaging/simp/stomp/DefaultStompSessionTests.java b/spring-messaging/src/test/java/org/springframework/messaging/simp/stomp/DefaultStompSessionTests.java index d4000748c20..eb838dcf8b7 100644 --- a/spring-messaging/src/test/java/org/springframework/messaging/simp/stomp/DefaultStompSessionTests.java +++ b/spring-messaging/src/test/java/org/springframework/messaging/simp/stomp/DefaultStompSessionTests.java @@ -16,11 +16,6 @@ package org.springframework.messaging.simp.stomp; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.*; - import java.nio.charset.Charset; import java.util.Date; import java.util.Map; @@ -51,6 +46,14 @@ import org.springframework.util.MimeTypeUtils; import org.springframework.util.concurrent.SettableListenableFuture; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.*; +import static org.mockito.Mockito.notNull; +import static org.mockito.Mockito.same; + /** * Unit tests for {@link DefaultStompSession}. * @@ -80,7 +83,6 @@ public class DefaultStompSessionTests { @Before public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); this.sessionHandler = mock(StompSessionHandler.class); @@ -96,7 +98,6 @@ public void setUp() throws Exception { @Test public void afterConnected() throws Exception { - assertFalse(this.session.isConnected()); this.connectHeaders.setHost("my-host"); this.connectHeaders.setHeartbeat(new long[] {11, 12}); @@ -122,7 +123,6 @@ public void afterConnectFailure() throws Exception { @Test public void handleConnectedFrame() throws Exception { - this.session.afterConnected(this.connection); assertTrue(this.session.isConnected()); @@ -141,7 +141,6 @@ public void handleConnectedFrame() throws Exception { @Test public void heartbeatValues() throws Exception { - this.session.afterConnected(this.connection); assertTrue(this.session.isConnected()); @@ -164,7 +163,6 @@ public void heartbeatValues() throws Exception { @Test public void heartbeatNotSupportedByServer() throws Exception { - this.session.afterConnected(this.connection); verify(this.connection).send(any()); @@ -181,7 +179,6 @@ public void heartbeatNotSupportedByServer() throws Exception { @Test public void heartbeatTasks() throws Exception { - this.session.afterConnected(this.connection); verify(this.connection).send(any()); @@ -217,7 +214,6 @@ public void heartbeatTasks() throws Exception { @Test public void handleErrorFrame() throws Exception { - StompHeaderAccessor accessor = StompHeaderAccessor.create(StompCommand.ERROR); accessor.setContentType(new MimeType("text", "plain", UTF_8)); accessor.addNativeHeader("foo", "bar"); @@ -236,7 +232,6 @@ public void handleErrorFrame() throws Exception { @Test public void handleErrorFrameWithEmptyPayload() throws Exception { - StompHeaderAccessor accessor = StompHeaderAccessor.create(StompCommand.ERROR); accessor.addNativeHeader("foo", "bar"); accessor.setLeaveMutable(true); @@ -249,7 +244,6 @@ public void handleErrorFrameWithEmptyPayload() throws Exception { @Test public void handleErrorFrameWithConversionException() throws Exception { - StompHeaderAccessor accessor = StompHeaderAccessor.create(StompCommand.ERROR); accessor.setContentType(MimeTypeUtils.APPLICATION_JSON); accessor.addNativeHeader("foo", "bar"); @@ -269,7 +263,6 @@ public void handleErrorFrameWithConversionException() throws Exception { @Test public void handleMessageFrame() throws Exception { - this.session.afterConnected(this.connection); StompFrameHandler frameHandler = mock(StompFrameHandler.class); @@ -296,7 +289,6 @@ public void handleMessageFrame() throws Exception { @Test public void handleMessageFrameWithConversionException() throws Exception { - this.session.afterConnected(this.connection); assertTrue(this.session.isConnected()); @@ -344,7 +336,6 @@ public void afterConnectionClosed() throws Exception { @Test public void send() throws Exception { - this.session.afterConnected(this.connection); assertTrue(this.session.isConnected()); @@ -361,13 +352,12 @@ public void send() throws Exception { assertEquals(destination, stompHeaders.getDestination()); assertEquals(new MimeType("text", "plain", UTF_8), stompHeaders.getContentType()); - assertEquals(-1, stompHeaders.getContentLength()); // StompEncoder isn't involved + assertEquals(-1, stompHeaders.getContentLength()); // StompEncoder isn't involved assertEquals(payload, new String(message.getPayload(), UTF_8)); } @Test public void sendWithReceipt() throws Exception { - this.session.afterConnected(this.connection); assertTrue(this.session.isConnected()); @@ -391,7 +381,6 @@ public void sendWithReceipt() throws Exception { @Test public void sendWithConversionException() throws Exception { - this.session.afterConnected(this.connection); assertTrue(this.session.isConnected()); @@ -407,7 +396,6 @@ public void sendWithConversionException() throws Exception { @Test public void sendWithExecutionException() throws Exception { - this.session.afterConnected(this.connection); assertTrue(this.session.isConnected()); @@ -426,7 +414,6 @@ public void sendWithExecutionException() throws Exception { @Test public void subscribe() throws Exception { - this.session.afterConnected(this.connection); assertTrue(this.session.isConnected()); @@ -446,7 +433,6 @@ public void subscribe() throws Exception { @Test public void subscribeWithHeaders() throws Exception { - this.session.afterConnected(this.connection); assertTrue(this.session.isConnected()); @@ -473,7 +459,6 @@ public void subscribeWithHeaders() throws Exception { @Test public void unsubscribe() throws Exception { - this.session.afterConnected(this.connection); assertTrue(this.session.isConnected()); @@ -493,7 +478,6 @@ public void unsubscribe() throws Exception { @Test public void ack() throws Exception { - this.session.afterConnected(this.connection); assertTrue(this.session.isConnected()); @@ -511,7 +495,6 @@ public void ack() throws Exception { @Test public void nack() throws Exception { - this.session.afterConnected(this.connection); assertTrue(this.session.isConnected()); @@ -529,7 +512,6 @@ public void nack() throws Exception { @Test public void receiptReceived() throws Exception { - this.session.afterConnected(this.connection); this.session.setTaskScheduler(mock(TaskScheduler.class)); @@ -554,7 +536,6 @@ public void receiptReceived() throws Exception { @Test public void receiptReceivedBeforeTaskAdded() throws Exception { - this.session.afterConnected(this.connection); this.session.setTaskScheduler(mock(TaskScheduler.class)); @@ -579,7 +560,6 @@ public void receiptReceivedBeforeTaskAdded() throws Exception { @Test @SuppressWarnings({ "unchecked", "rawtypes" }) public void receiptNotReceived() throws Exception { - TaskScheduler taskScheduler = mock(TaskScheduler.class); this.session.afterConnected(this.connection); @@ -611,7 +591,6 @@ public void receiptNotReceived() throws Exception { @Test public void disconnect() throws Exception { - this.session.afterConnected(this.connection); assertTrue(this.session.isConnected()); diff --git a/spring-messaging/src/test/java/org/springframework/messaging/simp/stomp/StompBrokerRelayMessageHandlerIntegrationTests.java b/spring-messaging/src/test/java/org/springframework/messaging/simp/stomp/StompBrokerRelayMessageHandlerIntegrationTests.java index 9f5cdfd7b3d..62e0407cb6e 100644 --- a/spring-messaging/src/test/java/org/springframework/messaging/simp/stomp/StompBrokerRelayMessageHandlerIntegrationTests.java +++ b/spring-messaging/src/test/java/org/springframework/messaging/simp/stomp/StompBrokerRelayMessageHandlerIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -33,6 +33,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; + import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEventPublisher; import org.springframework.messaging.Message; @@ -48,9 +49,7 @@ import org.springframework.util.Assert; import org.springframework.util.SocketUtils; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; /** * Integration tests for {@link StompBrokerRelayMessageHandler} running against ActiveMQ. @@ -59,13 +58,14 @@ */ public class StompBrokerRelayMessageHandlerIntegrationTests { + private static final Charset UTF_8 = Charset.forName("UTF-8"); + + @Rule public final TestName testName = new TestName(); private static final Log logger = LogFactory.getLog(StompBrokerRelayMessageHandlerIntegrationTests.class); - private static final Charset UTF_8 = Charset.forName("UTF-8"); - private StompBrokerRelayMessageHandler relay; private BrokerService activeMQBroker; @@ -142,9 +142,9 @@ public void run() { logger.debug("Broker stopped"); } + @Test public void publishSubscribe() throws Exception { - logger.debug("Starting test publishSubscribe()"); String sess1 = "sess1"; @@ -167,7 +167,7 @@ public void publishSubscribe() throws Exception { this.responseHandler.expectMessages(send); } - @Test(expected=MessageDeliveryException.class) + @Test(expected = MessageDeliveryException.class) public void messageDeliveryExceptionIfSystemSessionForwardFails() throws Exception { logger.debug("Starting test messageDeliveryExceptionIfSystemSessionForwardFails()"); @@ -181,7 +181,6 @@ public void messageDeliveryExceptionIfSystemSessionForwardFails() throws Excepti @Test public void brokerBecomingUnvailableTriggersErrorFrame() throws Exception { - logger.debug("Starting test brokerBecomingUnvailableTriggersErrorFrame()"); String sess1 = "sess1"; @@ -197,7 +196,6 @@ public void brokerBecomingUnvailableTriggersErrorFrame() throws Exception { @Test public void brokerAvailabilityEventWhenStopped() throws Exception { - logger.debug("Starting test brokerAvailabilityEventWhenStopped()"); stopActiveMqBrokerAndAwait(); @@ -206,7 +204,6 @@ public void brokerAvailabilityEventWhenStopped() throws Exception { @Test public void relayReconnectsIfBrokerComesBackUp() throws Exception { - logger.debug("Starting test relayReconnectsIfBrokerComesBackUp()"); String sess1 = "sess1"; @@ -232,7 +229,6 @@ public void relayReconnectsIfBrokerComesBackUp() throws Exception { @Test public void disconnectWithReceipt() throws Exception { - logger.debug("Starting test disconnectWithReceipt()"); MessageExchange connect = MessageExchangeBuilder.connect("sess1").build(); @@ -270,6 +266,7 @@ public void expectBrokerAvailabilityEvent(boolean isBrokerAvailable) throws Inte } } + private static class TestMessageHandler implements MessageHandler { private final BlockingQueue> queue = new LinkedBlockingQueue<>(); @@ -283,17 +280,13 @@ public void handleMessage(Message message) throws MessagingException { } public void expectMessages(MessageExchange... messageExchanges) throws InterruptedException { - List expectedMessages = new ArrayList(Arrays.asList(messageExchanges)); - while (expectedMessages.size() > 0) { Message message = this.queue.poll(10000, TimeUnit.MILLISECONDS); assertNotNull("Timed out waiting for messages, expected [" + expectedMessages + "]", message); - MessageExchange match = findMatch(expectedMessages, message); assertNotNull("Unexpected message=" + message + ", expected [" + expectedMessages + "]", match); - expectedMessages.remove(match); } } @@ -308,6 +301,7 @@ private MessageExchange findMatch(List expectedMessages, Messag } } + /** * Holds a message as well as expected and actual messages matched against expectations. */ @@ -343,6 +337,7 @@ public String toString() { } } + private static class MessageExchangeBuilder { private final Message message; @@ -351,8 +346,7 @@ private static class MessageExchangeBuilder { private final List expected = new ArrayList<>(); - - private MessageExchangeBuilder(Message message) { + public MessageExchangeBuilder(Message message) { this.message = message; this.headers = StompHeaderAccessor.wrap(message); } @@ -442,25 +436,24 @@ public MessageExchange build() { } } - private static interface MessageMatcher { - boolean match(Message message); + private interface MessageMatcher { + boolean match(Message message); } + private static class StompFrameMessageMatcher implements MessageMatcher { private final StompCommand command; private final String sessionId; - public StompFrameMessageMatcher(StompCommand command, String sessionId) { this.command = command; this.sessionId = sessionId; } - @Override public final boolean match(Message message) { StompHeaderAccessor headers = StompHeaderAccessor.wrap(message); @@ -480,6 +473,7 @@ public String toString() { } } + private static class StompReceiptFrameMessageMatcher extends StompFrameMessageMatcher { private final String receiptId; @@ -500,6 +494,7 @@ public String toString() { } } + private static class StompMessageFrameMessageMatcher extends StompFrameMessageMatcher { private final String subscriptionId; @@ -508,7 +503,6 @@ private static class StompMessageFrameMessageMatcher extends StompFrameMessageMa private final Object payload; - public StompMessageFrameMessageMatcher(String sessionId, String subscriptionId, String destination, Object payload) { super(StompCommand.MESSAGE, sessionId); this.subscriptionId = subscriptionId; @@ -536,18 +530,17 @@ public String toString() { } protected String getPayloadAsText() { - return (this.payload instanceof byte[]) - ? new String((byte[]) this.payload, UTF_8) : payload.toString(); + return (this.payload instanceof byte[]) ? + new String((byte[]) this.payload, UTF_8) : this.payload.toString(); } } - private static class StompConnectedFrameMessageMatcher extends StompFrameMessageMatcher { + private static class StompConnectedFrameMessageMatcher extends StompFrameMessageMatcher { public StompConnectedFrameMessageMatcher(String sessionId) { super(StompCommand.CONNECTED, sessionId); } - } } diff --git a/spring-oxm/oxm.gradle b/spring-oxm/oxm.gradle index 140b35130ee..23ce60ffd99 100644 --- a/spring-oxm/oxm.gradle +++ b/spring-oxm/oxm.gradle @@ -6,11 +6,9 @@ configurations { } dependencies { castor "org.codehaus.castor:castor-anttasks:1.4.1" - castor "org.apache.velocity:velocity:1.7" + jibx "org.jibx:jibx-bind:1.2.6" xjc "com.sun.xml.bind:jaxb-xjc:2.1.17" xmlbeans "org.apache.xmlbeans:xmlbeans:2.6.0" - jibx "org.jibx:jibx-bind:1.2.6" - jibx "bcel:bcel:5.1" } ext.genSourcesDir = "${buildDir}/generated-sources" diff --git a/spring-test/src/main/java/org/springframework/test/web/client/MockMvcClientHttpRequestFactory.java b/spring-test/src/main/java/org/springframework/test/web/client/MockMvcClientHttpRequestFactory.java index 7c7b6115edd..d1086a53fba 100644 --- a/spring-test/src/main/java/org/springframework/test/web/client/MockMvcClientHttpRequestFactory.java +++ b/spring-test/src/main/java/org/springframework/test/web/client/MockMvcClientHttpRequestFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -18,6 +18,7 @@ import java.io.IOException; import java.net.URI; +import java.nio.charset.Charset; import java.util.List; import org.springframework.http.HttpHeaders; @@ -43,6 +44,8 @@ */ public class MockMvcClientHttpRequestFactory implements ClientHttpRequestFactory { + private static final Charset UTF8_CHARSET = Charset.forName("UTF-8"); + private final MockMvc mockMvc; @@ -50,6 +53,7 @@ public MockMvcClientHttpRequestFactory(MockMvc mockMvc) { this.mockMvc = mockMvc; } + @Override public ClientHttpRequest createRequest(final URI uri, final HttpMethod httpMethod) throws IOException { return new MockClientHttpRequest(httpMethod, uri) { @@ -73,7 +77,7 @@ public ClientHttpResponse executeInternal() throws IOException { return clientResponse; } catch (Exception ex) { - byte[] body = ex.toString().getBytes("UTF-8"); + byte[] body = ex.toString().getBytes(UTF8_CHARSET); return new MockClientHttpResponse(body, HttpStatus.INTERNAL_SERVER_ERROR); } } diff --git a/spring-test/src/main/java/org/springframework/test/web/client/response/DefaultResponseCreator.java b/spring-test/src/main/java/org/springframework/test/web/client/response/DefaultResponseCreator.java index 3f2be1ae4cf..213bc5d76af 100644 --- a/spring-test/src/main/java/org/springframework/test/web/client/response/DefaultResponseCreator.java +++ b/spring-test/src/main/java/org/springframework/test/web/client/response/DefaultResponseCreator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -13,12 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.test.web.client.response; import java.io.IOException; import java.io.InputStream; -import java.io.UnsupportedEncodingException; import java.net.URI; +import java.nio.charset.Charset; import org.springframework.core.io.Resource; import org.springframework.http.HttpHeaders; @@ -38,6 +39,9 @@ */ public class DefaultResponseCreator implements ResponseCreator { + private static final Charset UTF8_CHARSET = Charset.forName("UTF-8"); + + private byte[] content; private Resource contentResource; @@ -56,6 +60,7 @@ protected DefaultResponseCreator(HttpStatus statusCode) { this.statusCode = statusCode; } + @Override public ClientHttpResponse createResponse(ClientHttpRequest request) throws IOException { MockClientHttpResponse response; @@ -74,13 +79,7 @@ public ClientHttpResponse createResponse(ClientHttpRequest request) throws IOExc * Set the body as a UTF-8 String. */ public DefaultResponseCreator body(String content) { - try { - this.content = content.getBytes("UTF-8"); - } - catch (UnsupportedEncodingException e) { - // should not happen, UTF-8 is always supported - throw new IllegalStateException(e); - } + this.content = content.getBytes(UTF8_CHARSET); return this; } diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java index 65be88c5ce5..d181a46fe78 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java @@ -21,6 +21,7 @@ import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.URI; +import java.nio.charset.Charset; import java.security.Principal; import java.util.ArrayList; import java.util.Arrays; @@ -78,6 +79,9 @@ public class MockHttpServletRequestBuilder implements ConfigurableSmartRequestBuilder, Mergeable { + private static final Charset UTF8_CHARSET = Charset.forName("UTF-8"); + + private final String method; private final URI url; @@ -272,12 +276,7 @@ public MockHttpServletRequestBuilder content(byte[] content) { * @param content the body content */ public MockHttpServletRequestBuilder content(String content) { - try { - this.content = content.getBytes("UTF-8"); - } - catch (UnsupportedEncodingException e) { - // should never happen - } + this.content = content.getBytes(UTF8_CHARSET); return this; } diff --git a/spring-web/src/main/java/org/springframework/http/HttpStatus.java b/spring-web/src/main/java/org/springframework/http/HttpStatus.java index 015a2969cbe..34a051d92aa 100644 --- a/spring-web/src/main/java/org/springframework/http/HttpStatus.java +++ b/spring-web/src/main/java/org/springframework/http/HttpStatus.java @@ -24,6 +24,7 @@ * @author Arjen Poutsma * @author Sebastien Deleuze * @author Brian Clozel + * @since 3.0 * @see HttpStatus.Series * @see HTTP Status Code Registry * @see List of HTTP status codes - Wikipedia diff --git a/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java b/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java index f0ee1c89ee0..046a3b1e9a8 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java +++ b/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java @@ -725,7 +725,7 @@ private void registerWellKnownModulesIfAvailable(ObjectMapper objectMapper) { try { Class jdk7Module = (Class) ClassUtils.forName("com.fasterxml.jackson.datatype.jdk7.Jdk7Module", this.moduleClassLoader); - objectMapper.registerModule(BeanUtils.instantiate(jdk7Module)); + objectMapper.registerModule(BeanUtils.instantiateClass(jdk7Module)); } catch (ClassNotFoundException ex) { // jackson-datatype-jdk7 not available @@ -737,7 +737,7 @@ private void registerWellKnownModulesIfAvailable(ObjectMapper objectMapper) { try { Class jdk8Module = (Class) ClassUtils.forName("com.fasterxml.jackson.datatype.jdk8.Jdk8Module", this.moduleClassLoader); - objectMapper.registerModule(BeanUtils.instantiate(jdk8Module)); + objectMapper.registerModule(BeanUtils.instantiateClass(jdk8Module)); } catch (ClassNotFoundException ex) { // jackson-datatype-jdk8 not available @@ -749,7 +749,7 @@ private void registerWellKnownModulesIfAvailable(ObjectMapper objectMapper) { try { Class javaTimeModule = (Class) ClassUtils.forName("com.fasterxml.jackson.datatype.jsr310.JavaTimeModule", this.moduleClassLoader); - objectMapper.registerModule(BeanUtils.instantiate(javaTimeModule)); + objectMapper.registerModule(BeanUtils.instantiateClass(javaTimeModule)); } catch (ClassNotFoundException ex) { // jackson-datatype-jsr310 not available @@ -761,7 +761,7 @@ private void registerWellKnownModulesIfAvailable(ObjectMapper objectMapper) { try { Class jodaModule = (Class) ClassUtils.forName("com.fasterxml.jackson.datatype.joda.JodaModule", this.moduleClassLoader); - objectMapper.registerModule(BeanUtils.instantiate(jodaModule)); + objectMapper.registerModule(BeanUtils.instantiateClass(jodaModule)); } catch (ClassNotFoundException ex) { // jackson-datatype-joda not available @@ -773,7 +773,7 @@ private void registerWellKnownModulesIfAvailable(ObjectMapper objectMapper) { try { Class kotlinModule = (Class) ClassUtils.forName("com.fasterxml.jackson.module.kotlin.KotlinModule", this.moduleClassLoader); - objectMapper.registerModule(BeanUtils.instantiate(kotlinModule)); + objectMapper.registerModule(BeanUtils.instantiateClass(kotlinModule)); } catch (ClassNotFoundException ex) { // jackson-module-kotlin not available diff --git a/spring-web/src/main/java/org/springframework/http/converter/protobuf/ProtobufHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/protobuf/ProtobufHttpMessageConverter.java index c73e6129139..9f023882dea 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/protobuf/ProtobufHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/protobuf/ProtobufHttpMessageConverter.java @@ -5,7 +5,7 @@ * 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 + * 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, @@ -39,7 +39,6 @@ import org.springframework.http.converter.HttpMessageNotWritableException; import org.springframework.util.FileCopyUtils; - /** * An {@code HttpMessageConverter} that reads and writes {@link com.google.protobuf.Message}s * using Google Protocol Buffers. diff --git a/spring-web/src/main/java/org/springframework/http/converter/xml/SourceHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/xml/SourceHttpMessageConverter.java index e9875107b60..8b4112591ba 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/xml/SourceHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/xml/SourceHttpMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -175,9 +175,8 @@ private DOMSource readDOMSource(InputStream body) throws IOException { } catch (NullPointerException ex) { if (!isSupportDtd()) { - throw new HttpMessageNotReadableException("NPE while unmarshalling. " + - "This can happen on JDK 1.6 due to the presence of DTD " + - "declarations, which are disabled.", ex); + throw new HttpMessageNotReadableException("NPE while unmarshalling: " + + "This can happen due to the presence of DTD declarations which are disabled.", ex); } throw ex; } @@ -191,14 +190,14 @@ private DOMSource readDOMSource(InputStream body) throws IOException { private SAXSource readSAXSource(InputStream body) throws IOException { try { - XMLReader reader = XMLReaderFactory.createXMLReader(); - reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd()); - reader.setFeature("http://xml.org/sax/features/external-general-entities", isProcessExternalEntities()); + XMLReader xmlReader = XMLReaderFactory.createXMLReader(); + xmlReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd()); + xmlReader.setFeature("http://xml.org/sax/features/external-general-entities", isProcessExternalEntities()); if (!isProcessExternalEntities()) { - reader.setEntityResolver(NO_OP_ENTITY_RESOLVER); + xmlReader.setEntityResolver(NO_OP_ENTITY_RESOLVER); } byte[] bytes = StreamUtils.copyToByteArray(body); - return new SAXSource(reader, new InputSource(new ByteArrayInputStream(bytes))); + return new SAXSource(xmlReader, new InputSource(new ByteArrayInputStream(bytes))); } catch (SAXException ex) { throw new HttpMessageNotReadableException("Could not parse document: " + ex.getMessage(), ex); diff --git a/spring-web/src/main/java/org/springframework/web/context/support/ServletContextResource.java b/spring-web/src/main/java/org/springframework/web/context/support/ServletContextResource.java index a05b782f8cc..a13890b0453 100644 --- a/spring-web/src/main/java/org/springframework/web/context/support/ServletContextResource.java +++ b/spring-web/src/main/java/org/springframework/web/context/support/ServletContextResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -77,6 +77,7 @@ public ServletContextResource(ServletContext servletContext, String path) { this.path = pathToUse; } + /** * Return the ServletContext for this resource. */ @@ -91,7 +92,6 @@ public final String getPath() { return this.path; } - /** * This implementation checks {@code ServletContext.getResource}. * @see javax.servlet.ServletContext#getResource(String) diff --git a/spring-web/src/main/java/org/springframework/web/util/HierarchicalUriComponents.java b/spring-web/src/main/java/org/springframework/web/util/HierarchicalUriComponents.java index 3cc6f52498c..2aceca89a5e 100644 --- a/spring-web/src/main/java/org/springframework/web/util/HierarchicalUriComponents.java +++ b/spring-web/src/main/java/org/springframework/web/util/HierarchicalUriComponents.java @@ -61,6 +61,7 @@ final class HierarchicalUriComponents extends UriComponents { private final boolean encoded; + /** * Package-private constructor. All arguments are optional, and can be {@code null}. * @param scheme the scheme diff --git a/spring-web/src/main/java/org/springframework/web/util/HtmlUtils.java b/spring-web/src/main/java/org/springframework/web/util/HtmlUtils.java index 62109456269..3546efd1305 100644 --- a/spring-web/src/main/java/org/springframework/web/util/HtmlUtils.java +++ b/spring-web/src/main/java/org/springframework/web/util/HtmlUtils.java @@ -74,7 +74,7 @@ public static String htmlEscape(String input) { * http://www.w3.org/TR/html4/sgml/entities.html * * @param input the (unescaped) input string - * @param encoding The name of a supported {@link java.nio.charset.Charset charset} + * @param encoding the name of a supported {@link java.nio.charset.Charset charset} * @return the escaped string * @since 4.1.2 */ @@ -125,7 +125,7 @@ public static String htmlEscapeDecimal(String input) { * http://www.w3.org/TR/html4/sgml/entities.html * * @param input the (unescaped) input string - * @param encoding The name of a supported {@link java.nio.charset.Charset charset} + * @param encoding the name of a supported {@link java.nio.charset.Charset charset} * @return the escaped string * @since 4.1.2 */ @@ -177,7 +177,7 @@ public static String htmlEscapeHex(String input) { * http://www.w3.org/TR/html4/sgml/entities.html * * @param input the (unescaped) input string - * @param encoding The name of a supported {@link java.nio.charset.Charset charset} + * @param encoding the name of a supported {@link java.nio.charset.Charset charset} * @return the escaped string * @since 4.1.2 */ diff --git a/spring-web/src/main/java/org/springframework/web/util/UrlPathHelper.java b/spring-web/src/main/java/org/springframework/web/util/UrlPathHelper.java index 1a7a4fe3221..a1cd6bf63b5 100644 --- a/spring-web/src/main/java/org/springframework/web/util/UrlPathHelper.java +++ b/spring-web/src/main/java/org/springframework/web/util/UrlPathHelper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -179,8 +179,8 @@ public String getPathWithinServletMapping(HttpServletRequest request) { String sanitizedPathWithinApp = getSanitizedPath(pathWithinApp); String path; - // if the app container sanitized the servletPath, check against the sanitized version - if (servletPath.indexOf(sanitizedPathWithinApp) != -1) { + // If the app container sanitized the servletPath, check against the sanitized version + if (servletPath.contains(sanitizedPathWithinApp)) { path = getRemainingPath(sanitizedPathWithinApp, servletPath, false); } else { @@ -485,8 +485,8 @@ protected String determineEncoding(HttpServletRequest request) { * @return the updated URI string */ public String removeSemicolonContent(String requestUri) { - return this.removeSemicolonContent ? - removeSemicolonContentInternal(requestUri) : removeJsessionid(requestUri); + return (this.removeSemicolonContent ? + removeSemicolonContentInternal(requestUri) : removeJsessionid(requestUri)); } private String removeSemicolonContentInternal(String requestUri) { diff --git a/spring-web/src/test/java/org/springframework/http/converter/feed/AtomFeedHttpMessageConverterTests.java b/spring-web/src/test/java/org/springframework/http/converter/feed/AtomFeedHttpMessageConverterTests.java index bcaa76b98cf..54d83074854 100644 --- a/spring-web/src/test/java/org/springframework/http/converter/feed/AtomFeedHttpMessageConverterTests.java +++ b/spring-web/src/test/java/org/springframework/http/converter/feed/AtomFeedHttpMessageConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -42,13 +42,13 @@ */ public class AtomFeedHttpMessageConverterTests { + private static final Charset UTF_8 = Charset.forName("UTF-8"); + private AtomFeedHttpMessageConverter converter; - private Charset utf8; @Before public void setUp() { - utf8 = Charset.forName("UTF-8"); converter = new AtomFeedHttpMessageConverter(); XMLUnit.setIgnoreWhitespace(true); } @@ -56,20 +56,20 @@ public void setUp() { @Test public void canRead() { assertTrue(converter.canRead(Feed.class, new MediaType("application", "atom+xml"))); - assertTrue(converter.canRead(Feed.class, new MediaType("application", "atom+xml", utf8))); + assertTrue(converter.canRead(Feed.class, new MediaType("application", "atom+xml", UTF_8))); } @Test public void canWrite() { assertTrue(converter.canWrite(Feed.class, new MediaType("application", "atom+xml"))); - assertTrue(converter.canWrite(Feed.class, new MediaType("application", "atom+xml", Charset.forName("UTF-8")))); + assertTrue(converter.canWrite(Feed.class, new MediaType("application", "atom+xml", UTF_8))); } @Test public void read() throws IOException { InputStream is = getClass().getResourceAsStream("atom.xml"); MockHttpInputMessage inputMessage = new MockHttpInputMessage(is); - inputMessage.getHeaders().setContentType(new MediaType("application", "atom+xml", utf8)); + inputMessage.getHeaders().setContentType(new MediaType("application", "atom+xml", UTF_8)); Feed result = converter.read(Feed.class, inputMessage); assertEquals("title", result.getTitle()); assertEquals("subtitle", result.getSubtitle().getValue()); @@ -106,12 +106,12 @@ public void write() throws IOException, SAXException { MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); converter.write(feed, null, outputMessage); - assertEquals("Invalid content-type", new MediaType("application", "atom+xml", utf8), + assertEquals("Invalid content-type", new MediaType("application", "atom+xml", UTF_8), outputMessage.getHeaders().getContentType()); String expected = "" + "title" + "id1title1" + "id2title2"; - assertXMLEqual(expected, outputMessage.getBodyAsString(utf8)); + assertXMLEqual(expected, outputMessage.getBodyAsString(UTF_8)); } @Test diff --git a/spring-web/src/test/java/org/springframework/http/converter/feed/RssChannelHttpMessageConverterTests.java b/spring-web/src/test/java/org/springframework/http/converter/feed/RssChannelHttpMessageConverterTests.java index 16fcfc00f22..a8ceeb3bcf8 100644 --- a/spring-web/src/test/java/org/springframework/http/converter/feed/RssChannelHttpMessageConverterTests.java +++ b/spring-web/src/test/java/org/springframework/http/converter/feed/RssChannelHttpMessageConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -42,34 +42,35 @@ */ public class RssChannelHttpMessageConverterTests { + private static final Charset UTF_8 = Charset.forName("UTF-8"); + private RssChannelHttpMessageConverter converter; - private Charset utf8; @Before public void setUp() { - utf8 = Charset.forName("UTF-8"); converter = new RssChannelHttpMessageConverter(); XMLUnit.setIgnoreWhitespace(true); } + @Test public void canRead() { assertTrue(converter.canRead(Channel.class, new MediaType("application", "rss+xml"))); - assertTrue(converter.canRead(Channel.class, new MediaType("application", "rss+xml", utf8))); + assertTrue(converter.canRead(Channel.class, new MediaType("application", "rss+xml", UTF_8))); } @Test public void canWrite() { assertTrue(converter.canWrite(Channel.class, new MediaType("application", "rss+xml"))); - assertTrue(converter.canWrite(Channel.class, new MediaType("application", "rss+xml", Charset.forName("UTF-8")))); + assertTrue(converter.canWrite(Channel.class, new MediaType("application", "rss+xml", UTF_8))); } @Test public void read() throws IOException { InputStream is = getClass().getResourceAsStream("rss.xml"); MockHttpInputMessage inputMessage = new MockHttpInputMessage(is); - inputMessage.getHeaders().setContentType(new MediaType("application", "rss+xml", utf8)); + inputMessage.getHeaders().setContentType(new MediaType("application", "rss+xml", UTF_8)); Channel result = converter.read(Channel.class, inputMessage); assertEquals("title", result.getTitle()); assertEquals("http://example.com", result.getLink()); @@ -106,14 +107,14 @@ public void write() throws IOException, SAXException { MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); converter.write(channel, null, outputMessage); - assertEquals("Invalid content-type", new MediaType("application", "rss+xml", utf8), + assertEquals("Invalid content-type", new MediaType("application", "rss+xml", UTF_8), outputMessage.getHeaders().getContentType()); String expected = "" + "titlehttp://example.comdescription" + "title1" + "title2" + ""; - assertXMLEqual(expected, outputMessage.getBodyAsString(utf8)); + assertXMLEqual(expected, outputMessage.getBodyAsString(UTF_8)); } @Test diff --git a/spring-web/src/test/java/org/springframework/remoting/jaxws/JaxWsSupportTests.java b/spring-web/src/test/java/org/springframework/remoting/jaxws/JaxWsSupportTests.java index 3a887601381..1376734fdbc 100644 --- a/spring-web/src/test/java/org/springframework/remoting/jaxws/JaxWsSupportTests.java +++ b/spring-web/src/test/java/org/springframework/remoting/jaxws/JaxWsSupportTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 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. @@ -127,7 +127,7 @@ private void doTestJaxWsPortAccess(Object... features) throws Exception { } catch (BeanCreationException ex) { if ("exporter".equals(ex.getBeanName()) && ex.getRootCause() instanceof ClassNotFoundException) { - // ignore - probably running on JDK < 1.6 without the JAX-WS impl present + // ignore - probably running on JDK without the JAX-WS impl present } else { throw ex; @@ -146,7 +146,7 @@ public static class ServiceAccessor { public OrderService myService; - @WebServiceRef(value=OrderServiceService.class, wsdlLocation = "http://localhost:9999/OrderService?wsdl") + @WebServiceRef(value = OrderServiceService.class, wsdlLocation = "http://localhost:9999/OrderService?wsdl") public void setMyService(OrderService myService) { this.myService = myService; } diff --git a/spring-web/src/test/resources/org/springframework/web/util/HtmlCharacterEntityReferences.dtd b/spring-web/src/test/resources/org/springframework/web/util/HtmlCharacterEntityReferences.dtd index 31aa2524bff..86e8cbab0ce 100644 --- a/spring-web/src/test/resources/org/springframework/web/util/HtmlCharacterEntityReferences.dtd +++ b/spring-web/src/test/resources/org/springframework/web/util/HtmlCharacterEntityReferences.dtd @@ -1,11 +1,9 @@ - - - - + http://www.w3.org/TR/html4/charset.html. --> + - + + customizer-ref="tracingCustomizer"/> ---- If you are not using the Spring namespace support, you can still use the @@ -7821,13 +7820,19 @@ If you are not using the Spring namespace support, you can still use the - + ---- +[NOTE] +==== +As of Spring Framework 4.3.3, you may also specify a Groovy `CompilationCustomizer` type +such as an `ImportCustomizer` in the same place as Spring's `GroovyObjectCustomizer`. +==== + [[dynamic-language-beans-bsh]] From e6cefdca25200b8ff7f1f8827538dd9a7a22831c Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 17 Aug 2016 16:59:16 +0200 Subject: [PATCH 0104/1274] RootBeanDefinition accepts ResolvableType for target type hint Issue: SPR-14580 (cherry picked from commit 4b06b60) --- .../AbstractAutowireCapableBeanFactory.java | 5 +- ...ricTypeAwareAutowireCandidateResolver.java | 31 +++++++++--- .../factory/support/RootBeanDefinition.java | 37 ++++++++++---- ...wiredAnnotationBeanPostProcessorTests.java | 50 +++++++++++++++++++ 4 files changed, 105 insertions(+), 18 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java index f8bd3818830..3c2f4ddbd34 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java @@ -624,10 +624,11 @@ protected Class predictBeanType(String beanName, RootBeanDefinition mbd, Clas protected Class determineTargetType(String beanName, RootBeanDefinition mbd, Class... typesToMatch) { Class targetType = mbd.getTargetType(); if (targetType == null) { - targetType = (mbd.getFactoryMethodName() != null ? getTypeForFactoryMethod(beanName, mbd, typesToMatch) : + targetType = (mbd.getFactoryMethodName() != null ? + getTypeForFactoryMethod(beanName, mbd, typesToMatch) : resolveBeanClass(mbd, beanName, typesToMatch)); if (ObjectUtils.isEmpty(typesToMatch) || getTempClassLoader() == null) { - mbd.setTargetType(targetType); + mbd.resolvedTargetType = targetType; } } return targetType; diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java index 65554ce9b57..cba9e9770ab 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 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. @@ -74,21 +74,31 @@ protected boolean checkGenericTypeMatch(BeanDefinitionHolder bdHolder, Dependenc // No generic type -> we know it's a Class type-match, so no need to check again. return true; } + ResolvableType targetType = null; + boolean cacheType = false; RootBeanDefinition rbd = null; if (bdHolder.getBeanDefinition() instanceof RootBeanDefinition) { rbd = (RootBeanDefinition) bdHolder.getBeanDefinition(); } if (rbd != null) { - // First, check factory method return type, if applicable - targetType = getReturnTypeForFactoryMethod(rbd, descriptor); + targetType = rbd.targetType; if (targetType == null) { - RootBeanDefinition dbd = getResolvedDecoratedDefinition(rbd); - if (dbd != null) { - targetType = getReturnTypeForFactoryMethod(dbd, descriptor); + cacheType = true; + // First, check factory method return type, if applicable + targetType = getReturnTypeForFactoryMethod(rbd, descriptor); + if (targetType == null) { + RootBeanDefinition dbd = getResolvedDecoratedDefinition(rbd); + if (dbd != null) { + targetType = dbd.targetType; + if (targetType == null) { + targetType = getReturnTypeForFactoryMethod(dbd, descriptor); + } + } } } } + if (targetType == null) { // Regular case: straight bean instance, with BeanFactory available. if (this.beanFactory != null) { @@ -106,7 +116,14 @@ protected boolean checkGenericTypeMatch(BeanDefinitionHolder bdHolder, Dependenc } } } - if (targetType == null || (descriptor.fallbackMatchAllowed() && targetType.hasUnresolvableGenerics())) { + + if (targetType == null) { + return true; + } + if (cacheType) { + rbd.targetType = targetType; + } + if (descriptor.fallbackMatchAllowed() && targetType.hasUnresolvableGenerics()) { return true; } // Full check for complex generic type match... diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java index 91ad5d8e96c..aa33bcef659 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java @@ -25,6 +25,7 @@ import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.config.ConstructorArgumentValues; +import org.springframework.core.ResolvableType; import org.springframework.util.Assert; /** @@ -48,22 +49,26 @@ @SuppressWarnings("serial") public class RootBeanDefinition extends AbstractBeanDefinition { - boolean allowCaching = true; - private BeanDefinitionHolder decoratedDefinition; - private volatile Class targetType; + boolean allowCaching = true; + + volatile ResolvableType targetType; boolean isFactoryMethodUnique = false; + /** Package-visible field for caching the determined Class of a given bean definition */ + volatile Class resolvedTargetType; + + /** Package-visible field for caching the return type of a generically typed factory method */ + volatile Class resolvedFactoryMethodReturnType; + + /** Common lock for the four constructor fields below */ final Object constructorArgumentLock = new Object(); /** Package-visible field for caching the resolved constructor or factory method */ Object resolvedConstructorOrFactoryMethod; - /** Package-visible field for caching the return type of a generically typed factory method */ - volatile Class resolvedFactoryMethodReturnType; - /** Package-visible field that marks the constructor arguments as resolved */ boolean constructorArgumentsResolved = false; @@ -73,6 +78,7 @@ public class RootBeanDefinition extends AbstractBeanDefinition { /** Package-visible field for caching partly prepared constructor arguments */ Object[] preparedConstructorArguments; + /** Common lock for the two post-processing fields below */ final Object postProcessingLock = new Object(); /** Package-visible field that indicates MergedBeanDefinitionPostProcessor having been applied */ @@ -171,8 +177,8 @@ public RootBeanDefinition(String beanClassName, ConstructorArgumentValues cargs, */ public RootBeanDefinition(RootBeanDefinition original) { super(original); - this.allowCaching = original.allowCaching; this.decoratedDefinition = original.decoratedDefinition; + this.allowCaching = original.allowCaching; this.targetType = original.targetType; this.isFactoryMethodUnique = original.isFactoryMethodUnique; } @@ -213,19 +219,32 @@ public BeanDefinitionHolder getDecoratedDefinition() { return this.decoratedDefinition; } + /** + * Specify a generics-containing target type of this bean definition, if known in advance. + * @since 4.3.3 + */ + public void setTargetType(ResolvableType targetType) { + this.targetType = targetType; + } + /** * Specify the target type of this bean definition, if known in advance. + * @since 3.2.2 */ public void setTargetType(Class targetType) { - this.targetType = targetType; + this.targetType = (targetType != null ? ResolvableType.forClass(targetType) : null); } /** * Return the target type of this bean definition, if known * (either specified in advance or resolved on first instantiation). + * @since 3.2.2 */ public Class getTargetType() { - return this.targetType; + if (this.resolvedTargetType != null) { + return this.resolvedTargetType; + } + return (this.targetType != null ? this.targetType.resolve() : null); } /** diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java index 2a4afd0a9d0..96e7e07a0f8 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java @@ -41,6 +41,7 @@ import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoUniqueBeanDefinitionException; @@ -2047,6 +2048,24 @@ public void testGenericsBasedInjectionIntoTypeVariableSelectingBestMatchAgainstF assertSame(bean2, bean1.gi2); } + @Test + public void testGenericsBasedInjectionWithBeanDefinitionTargetResolvableType() throws Exception { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver()); + AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); + bpp.setBeanFactory(bf); + bf.addBeanPostProcessor(bpp); + RootBeanDefinition bd1 = new RootBeanDefinition(GenericInterface2Bean.class); + bd1.setTargetType(ResolvableType.forClassWithGenerics(GenericInterface2Bean.class, String.class)); + bf.registerBeanDefinition("bean1", bd1); + RootBeanDefinition bd2 = new RootBeanDefinition(GenericInterface2Bean.class); + bd2.setTargetType(ResolvableType.forClassWithGenerics(GenericInterface2Bean.class, Integer.class)); + bf.registerBeanDefinition("bean2", bd2); + bf.registerBeanDefinition("bean3", new RootBeanDefinition(MultiGenericFieldInjection.class)); + + assertEquals("bean1 a bean2 123", bf.getBean("bean3").toString()); + } + @Test public void testCircularTypeReference() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); @@ -3139,6 +3158,37 @@ public String doSomethingMoreGeneric(Object o) { } + public static class GenericInterface2Bean implements GenericInterface2, BeanNameAware { + + private String name; + + @Override + public void setBeanName(String name) { + this.name = name; + } + + @Override + public String doSomethingMoreGeneric(K o) { + return this.name + " " + o; + } + } + + + public static class MultiGenericFieldInjection { + + @Autowired + private GenericInterface2 stringBean; + + @Autowired + private GenericInterface2 integerBean; + + @Override + public String toString() { + return this.stringBean.doSomethingMoreGeneric("a") + " " + this.integerBean.doSomethingMoreGeneric(123); + } + } + + @SuppressWarnings("rawtypes") public static class PlainGenericInterface2Impl implements GenericInterface2 { From 7b11fa18a19d903992ad1d153ed59f3d2feab7f5 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 17 Aug 2016 17:13:28 +0200 Subject: [PATCH 0105/1274] Revised NoSuchBeanDefinitionException message for proper array class names Issue: SPR-14595 (cherry picked from commit 022b013) --- .../NoSuchBeanDefinitionException.java | 8 ++++--- .../BeanFactoryAnnotationUtils.java | 22 +++++++++++++------ .../support/DefaultListableBeanFactory.java | 4 ++-- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/NoSuchBeanDefinitionException.java b/spring-beans/src/main/java/org/springframework/beans/factory/NoSuchBeanDefinitionException.java index 106fa0cafc9..1f3f8d36286 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/NoSuchBeanDefinitionException.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/NoSuchBeanDefinitionException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -17,6 +17,7 @@ package org.springframework.beans.factory; import org.springframework.beans.BeansException; +import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; /** @@ -74,7 +75,7 @@ public NoSuchBeanDefinitionException(Class type) { * @param message detailed message describing the problem */ public NoSuchBeanDefinitionException(Class type, String message) { - super("No qualifying bean of type [" + type.getName() + "] is defined: " + message); + super("No qualifying bean of type [" + ClassUtils.getQualifiedName(type) + "] is defined: " + message); this.beanType = type; } @@ -85,7 +86,8 @@ public NoSuchBeanDefinitionException(Class type, String message) { * @param message detailed message describing the problem */ public NoSuchBeanDefinitionException(Class type, String dependencyDescription, String message) { - super("No qualifying bean of type [" + type.getName() + "] found for dependency" + + super("No qualifying bean" + (!StringUtils.hasLength(dependencyDescription) ? + " of type [" + ClassUtils.getQualifiedName(type) + "]" : "") + " found for dependency" + (StringUtils.hasLength(dependencyDescription) ? " [" + dependencyDescription + "]" : "") + ": " + message); this.beanType = type; diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/BeanFactoryAnnotationUtils.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/BeanFactoryAnnotationUtils.java index 65901004885..acc87d64950 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/BeanFactoryAnnotationUtils.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/BeanFactoryAnnotationUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -18,27 +18,30 @@ import java.lang.reflect.Method; +import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.NoUniqueBeanDefinitionException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.AutowireCandidateQualifier; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; /** * Convenience methods performing bean lookups related to annotations, for example * Spring's {@link Qualifier @Qualifier} annotation. * - * @author Chris Beams * @author Juergen Hoeller + * @author Chris Beams * @since 3.1.2 * @see BeanFactoryUtils */ -public class BeanFactoryAnnotationUtils { +public abstract class BeanFactoryAnnotationUtils { /** * Obtain a bean of type {@code T} from the given {@code BeanFactory} declaring a @@ -48,9 +51,16 @@ public class BeanFactoryAnnotationUtils { * @param beanType the type of bean to retrieve * @param qualifier the qualifier for selecting between multiple bean matches * @return the matching bean of type {@code T} (never {@code null}) + * @throws NoUniqueBeanDefinitionException if multiple matching beans of type {@code T} found * @throws NoSuchBeanDefinitionException if no matching bean of type {@code T} found + * @throws BeansException if the bean could not be created + * @see BeanFactory#getBean(Class) */ - public static T qualifiedBeanOfType(BeanFactory beanFactory, Class beanType, String qualifier) { + public static T qualifiedBeanOfType(BeanFactory beanFactory, Class beanType, String qualifier) + throws BeansException { + + Assert.notNull(beanFactory, "BeanFactory must not be null"); + if (beanFactory instanceof ConfigurableListableBeanFactory) { // Full qualifier matching supported. return qualifiedBeanOfType((ConfigurableListableBeanFactory) beanFactory, beanType, qualifier); @@ -74,7 +84,6 @@ else if (beanFactory.containsBean(qualifier)) { * @param beanType the type of bean to retrieve * @param qualifier the qualifier for selecting between multiple bean matches * @return the matching bean of type {@code T} (never {@code null}) - * @throws NoSuchBeanDefinitionException if no matching bean of type {@code T} found */ private static T qualifiedBeanOfType(ConfigurableListableBeanFactory bf, Class beanType, String qualifier) { String[] candidateBeans = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(bf, beanType); @@ -82,8 +91,7 @@ private static T qualifiedBeanOfType(ConfigurableListableBeanFactory bf, Cla for (String beanName : candidateBeans) { if (isQualifierMatch(qualifier, beanName, bf)) { if (matchingBean != null) { - throw new NoSuchBeanDefinitionException(qualifier, "No unique " + beanType.getSimpleName() + - " bean found for qualifier '" + qualifier + "'"); + throw new NoUniqueBeanDefinitionException(beanType, matchingBean, beanName); } matchingBean = beanName; } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index 11591c79bf8..724a9ab829f 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -1430,7 +1430,7 @@ private void raiseNoMatchingBeanFound( checkBeanNotOfRequiredType(type, descriptor); throw new NoSuchBeanDefinitionException(type, dependencyDescription, - "expected at least 1 bean which qualifies as autowire candidate for this dependency. " + + "expected at least 1 bean which qualifies as autowire candidate. " + "Dependency annotations: " + ObjectUtils.nullSafeToString(descriptor.getAnnotations())); } @@ -1682,7 +1682,7 @@ public Object getOrderSource(Object obj) { sources.add(factoryMethod); } Class targetType = beanDefinition.getTargetType(); - if (targetType != null && !targetType.equals(obj.getClass())) { + if (targetType != null && targetType != obj.getClass()) { sources.add(targetType); } return sources.toArray(new Object[sources.size()]); From c926ec477a6aabad1b34c42f2d5857ab416d8b0c Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 17 Aug 2016 17:40:26 +0200 Subject: [PATCH 0106/1274] Polishing --- .../support/CronSequenceGenerator.java | 6 ++---- .../config/ScriptBeanDefinitionParser.java | 21 +++++++++---------- .../AbstractJasperReportsView.java | 4 ++-- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/scheduling/support/CronSequenceGenerator.java b/spring-context/src/main/java/org/springframework/scheduling/support/CronSequenceGenerator.java index b709f0d8b90..09d14360a4d 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/support/CronSequenceGenerator.java +++ b/spring-context/src/main/java/org/springframework/scheduling/support/CronSequenceGenerator.java @@ -116,7 +116,7 @@ public Date next(Date date) { /* The plan: - 1 Round up to the next whole second + 1 Start with whole second (rounding up if necessary) 2 If seconds match move on, otherwise find the next match: 2.1 If next match is in the next minute then roll forwards @@ -128,8 +128,6 @@ public Date next(Date date) { 4 If hour matches move on, otherwise find the next match 4.1 If next match is in the next day then roll forwards, 4.2 Reset the minutes and seconds and go to 2 - - ... */ Calendar calendar = new GregorianCalendar(); @@ -428,7 +426,7 @@ public int hashCode() { @Override public String toString() { - return (getClass().getSimpleName() + ": " + this.expression); + return getClass().getSimpleName() + ": " + this.expression; } } diff --git a/spring-context/src/main/java/org/springframework/scripting/config/ScriptBeanDefinitionParser.java b/spring-context/src/main/java/org/springframework/scripting/config/ScriptBeanDefinitionParser.java index 83d72a9d224..bd4aa553560 100644 --- a/spring-context/src/main/java/org/springframework/scripting/config/ScriptBeanDefinitionParser.java +++ b/spring-context/src/main/java/org/springframework/scripting/config/ScriptBeanDefinitionParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,17 +35,17 @@ /** * BeanDefinitionParser implementation for the '{@code }', - * '{@code }' and '{@code }' tags. + * '{@code }' and '{@code }' tags. * Allows for objects written using dynamic languages to be easily exposed with * the {@link org.springframework.beans.factory.BeanFactory}. * - *

The script for each object can be specified either as a reference to the Resource - * containing it (using the '{@code script-source}' attribute) or inline in the XML configuration - * itself (using the '{@code inline-script}' attribute. + *

The script for each object can be specified either as a reference to the + * resource containing it (using the '{@code script-source}' attribute) or inline + * in the XML configuration itself (using the '{@code inline-script}' attribute. * - *

By default, dynamic objects created with these tags are not refreshable. - * To enable refreshing, specify the refresh check delay for each object (in milliseconds) using the - * '{@code refresh-check-delay}' attribute. + *

By default, dynamic objects created with these tags are not + * refreshable. To enable refreshing, specify the refresh check delay for each + * object (in milliseconds) using the '{@code refresh-check-delay}' attribute. * * @author Rob Harrop * @author Rod Johnson @@ -176,14 +176,13 @@ else if (beanDefinitionDefaults.getDestroyMethodName() != null) { // Attach any refresh metadata. String refreshCheckDelay = element.getAttribute(REFRESH_CHECK_DELAY_ATTRIBUTE); if (StringUtils.hasText(refreshCheckDelay)) { - bd.setAttribute(ScriptFactoryPostProcessor.REFRESH_CHECK_DELAY_ATTRIBUTE, new Long(refreshCheckDelay)); + bd.setAttribute(ScriptFactoryPostProcessor.REFRESH_CHECK_DELAY_ATTRIBUTE, Long.valueOf(refreshCheckDelay)); } // Attach any proxy target class metadata. String proxyTargetClass = element.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE); if (StringUtils.hasText(proxyTargetClass)) { - Boolean flag = new Boolean(proxyTargetClass); - bd.setAttribute(ScriptFactoryPostProcessor.PROXY_TARGET_CLASS_ATTRIBUTE, flag); + bd.setAttribute(ScriptFactoryPostProcessor.PROXY_TARGET_CLASS_ATTRIBUTE, Boolean.valueOf(proxyTargetClass)); } // Add constructor arguments. diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/jasperreports/AbstractJasperReportsView.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/jasperreports/AbstractJasperReportsView.java index 1acbcdfc4aa..6276269eb6e 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/jasperreports/AbstractJasperReportsView.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/jasperreports/AbstractJasperReportsView.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -390,7 +390,7 @@ else if ("false".equals(str)) { else if (str.length() > 0 && Character.isDigit(str.charAt(0))) { // Looks like a number... let's try. try { - return new Integer(str); + return Integer.valueOf(str); } catch (NumberFormatException ex) { // OK, then let's keep it as a String value. From 1932a9d729ab469b645b9f4b569e9cde90346b96 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 17 Aug 2016 20:43:41 +0200 Subject: [PATCH 0107/1274] Polishing (cherry picked from commit de91b1a) --- .../tests/aop/interceptor/NopInterceptor.java | 23 +++++------ .../sample/beans/SerializablePerson.java | 31 +++++++-------- .../tests/sample/beans/TestBean.java | 4 +- .../annotation/AsyncExecutionTests.java | 1 + .../core/style/ToStringCreatorTests.java | 39 +++++++++---------- .../util/MethodInvokerTests.java | 14 ++++++- .../jdbc/object/GenericSqlQueryTests.java | 8 ++-- .../object/GenericSqlQueryTests-context.xml | 4 +- ...b2CollectionHttpMessageConverterTests.java | 9 +---- ...entNegotiationManagerFactoryBeanTests.java | 3 +- 10 files changed, 66 insertions(+), 70 deletions(-) diff --git a/spring-aop/src/test/java/org/springframework/tests/aop/interceptor/NopInterceptor.java b/spring-aop/src/test/java/org/springframework/tests/aop/interceptor/NopInterceptor.java index 95dff09cc04..de49c8af7fc 100644 --- a/spring-aop/src/test/java/org/springframework/tests/aop/interceptor/NopInterceptor.java +++ b/spring-aop/src/test/java/org/springframework/tests/aop/interceptor/NopInterceptor.java @@ -1,6 +1,5 @@ - /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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,28 +28,22 @@ public class NopInterceptor implements MethodInterceptor { private int count; - /** - * @see org.aopalliance.intercept.MethodInterceptor#invoke(MethodInvocation) - */ + @Override public Object invoke(MethodInvocation invocation) throws Throwable { increment(); return invocation.proceed(); } - public int getCount() { - return this.count; - } - protected void increment() { - ++count; + this.count++; } - @Override - public int hashCode() { - return 0; + public int getCount() { + return this.count; } + @Override public boolean equals(Object other) { if (!(other instanceof NopInterceptor)) { @@ -62,5 +55,9 @@ public boolean equals(Object other) { return this.count == ((NopInterceptor) other).count; } + @Override + public int hashCode() { + return NopInterceptor.class.hashCode(); + } } diff --git a/spring-aop/src/test/java/org/springframework/tests/sample/beans/SerializablePerson.java b/spring-aop/src/test/java/org/springframework/tests/sample/beans/SerializablePerson.java index 805dabd41ad..bfa856144a5 100644 --- a/spring-aop/src/test/java/org/springframework/tests/sample/beans/SerializablePerson.java +++ b/spring-aop/src/test/java/org/springframework/tests/sample/beans/SerializablePerson.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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,31 +28,29 @@ @SuppressWarnings("serial") public class SerializablePerson implements Person, Serializable { - private static final long serialVersionUID = 1L; - - private String name; private int age; + @Override - public int getAge() { - return age; + public String getName() { + return name; } @Override - public void setAge(int age) { - this.age = age; + public void setName(String name) { + this.name = name; } @Override - public String getName() { - return name; + public int getAge() { + return age; } @Override - public void setName(String name) { - this.name = name; + public void setAge(int age) { + this.age = age; } @Override @@ -63,10 +61,6 @@ public Object echo(Object o) throws Throwable { return o; } - @Override - public int hashCode() { - return 0; - } @Override public boolean equals(Object other) { @@ -77,4 +71,9 @@ public boolean equals(Object other) { return p.age == age && ObjectUtils.nullSafeEquals(name, p.name); } + @Override + public int hashCode() { + return SerializablePerson.class.hashCode(); + } + } diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/TestBean.java b/spring-beans/src/test/java/org/springframework/tests/sample/beans/TestBean.java index da243abbc93..f1ee1d4df5d 100644 --- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/TestBean.java +++ b/spring-beans/src/test/java/org/springframework/tests/sample/beans/TestBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -223,7 +223,7 @@ public void setSpouse(ITestBean spouse) { @Override public ITestBean[] getSpouses() { - return (spouse != null ? new ITestBean[]{spouse} : null); + return (spouse != null ? new ITestBean[] {spouse} : null); } public String getTouchy() { diff --git a/spring-context/src/test/java/org/springframework/scheduling/annotation/AsyncExecutionTests.java b/spring-context/src/test/java/org/springframework/scheduling/annotation/AsyncExecutionTests.java index 0d55ec7a20c..afc56975e01 100644 --- a/spring-context/src/test/java/org/springframework/scheduling/annotation/AsyncExecutionTests.java +++ b/spring-context/src/test/java/org/springframework/scheduling/annotation/AsyncExecutionTests.java @@ -124,6 +124,7 @@ public void asyncMethodsThroughInterface() throws Exception { context.registerBeanDefinition("autoProxyCreator", new RootBeanDefinition(DefaultAdvisorAutoProxyCreator.class)); context.registerBeanDefinition("asyncAdvisor", new RootBeanDefinition(AsyncAnnotationAdvisor.class)); context.refresh(); + SimpleInterface asyncTest = context.getBean("asyncTest", SimpleInterface.class); asyncTest.doNothing(5); asyncTest.doSomething(10); diff --git a/spring-core/src/test/java/org/springframework/core/style/ToStringCreatorTests.java b/spring-core/src/test/java/org/springframework/core/style/ToStringCreatorTests.java index dbafa1bbf79..f5f68df7075 100644 --- a/spring-core/src/test/java/org/springframework/core/style/ToStringCreatorTests.java +++ b/spring-core/src/test/java/org/springframework/core/style/ToStringCreatorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -33,7 +33,6 @@ /** * @author Keith Donald */ -@SuppressWarnings({ "rawtypes", "unchecked" }) public class ToStringCreatorTests { private SomeObject s1, s2, s3; @@ -63,20 +62,20 @@ public String toString() { @Test public void defaultStyleMap() { - final Map map = getMap(); + final Map map = getMap(); Object stringy = new Object() { @Override public String toString() { return new ToStringCreator(this).append("familyFavoriteSport", map).toString(); } }; - assertEquals("[ToStringCreatorTests.4@" + ObjectUtils.getIdentityHexString(stringy) - + " familyFavoriteSport = map['Keri' -> 'Softball', 'Scot' -> 'Fishing', 'Keith' -> 'Flag Football']]", + assertEquals("[ToStringCreatorTests.4@" + ObjectUtils.getIdentityHexString(stringy) + + " familyFavoriteSport = map['Keri' -> 'Softball', 'Scot' -> 'Fishing', 'Keith' -> 'Flag Football']]", stringy.toString()); } - private Map getMap() { - Map map = new LinkedHashMap(3); + private Map getMap() { + Map map = new LinkedHashMap<>(); map.put("Keri", "Softball"); map.put("Scot", "Fishing"); map.put("Keith", "Flag Football"); @@ -85,22 +84,22 @@ private Map getMap() { @Test public void defaultStyleArray() { - SomeObject[] array = new SomeObject[] { s1, s2, s3 }; + SomeObject[] array = new SomeObject[] {s1, s2, s3}; String str = new ToStringCreator(array).toString(); - assertEquals("[@" + ObjectUtils.getIdentityHexString(array) - + " array[A, B, C]]", str); + assertEquals("[@" + ObjectUtils.getIdentityHexString(array) + + " array[A, B, C]]", str); } @Test public void primitiveArrays() { - int[] integers = new int[] { 0, 1, 2, 3, 4 }; + int[] integers = new int[] {0, 1, 2, 3, 4}; String str = new ToStringCreator(integers).toString(); assertEquals("[@" + ObjectUtils.getIdentityHexString(integers) + " array[0, 1, 2, 3, 4]]", str); } @Test public void appendList() { - List list = new ArrayList(); + List list = new ArrayList<>(); list.add(s1); list.add(s2); list.add(s3); @@ -111,28 +110,26 @@ public void appendList() { @Test public void appendSet() { - Set set = new LinkedHashSet<>(3); + Set set = new LinkedHashSet<>(); set.add(s1); set.add(s2); set.add(s3); String str = new ToStringCreator(this).append("myLetters", set).toString(); - assertEquals("[ToStringCreatorTests@" + ObjectUtils.getIdentityHexString(this) + " myLetters = set[A, B, C]]", - str); + assertEquals("[ToStringCreatorTests@" + ObjectUtils.getIdentityHexString(this) + " myLetters = set[A, B, C]]", str); } @Test public void appendClass() { String str = new ToStringCreator(this).append("myClass", this.getClass()).toString(); - assertEquals("[ToStringCreatorTests@" + ObjectUtils.getIdentityHexString(this) - + " myClass = ToStringCreatorTests]", str); + assertEquals("[ToStringCreatorTests@" + ObjectUtils.getIdentityHexString(this) + + " myClass = ToStringCreatorTests]", str); } @Test public void appendMethod() throws Exception { - String str = new ToStringCreator(this).append("myMethod", this.getClass().getMethod("appendMethod")) - .toString(); - assertEquals("[ToStringCreatorTests@" + ObjectUtils.getIdentityHexString(this) - + " myMethod = appendMethod@ToStringCreatorTests]", str); + String str = new ToStringCreator(this).append("myMethod", this.getClass().getMethod("appendMethod")).toString(); + assertEquals("[ToStringCreatorTests@" + ObjectUtils.getIdentityHexString(this) + + " myMethod = appendMethod@ToStringCreatorTests]", str); } diff --git a/spring-core/src/test/java/org/springframework/util/MethodInvokerTests.java b/spring-core/src/test/java/org/springframework/util/MethodInvokerTests.java index 4800142dfa0..b90bf3b6dc7 100644 --- a/spring-core/src/test/java/org/springframework/util/MethodInvokerTests.java +++ b/spring-core/src/test/java/org/springframework/util/MethodInvokerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -79,7 +79,7 @@ public void stringWithMethodInvoker() throws Exception { MethodInvoker methodInvoker = new MethodInvoker(); methodInvoker.setTargetObject(new Greeter()); methodInvoker.setTargetMethod("greet"); - methodInvoker.setArguments(new Object[] { new String("no match") }); + methodInvoker.setArguments(new Object[] {"no match"}); exception.expect(NoSuchMethodException.class); methodInvoker.prepare(); @@ -199,6 +199,7 @@ public static String supertypes2(Collection c, List l, String s, String s2 } } + @SuppressWarnings("unused") public static class Greeter { @@ -223,13 +224,17 @@ private String greet(Regular regular) { } } + private interface Greetable { + String getGreeting(); } + private interface Person extends Greetable { } + private static class Purchaser implements Greetable { @Override @@ -238,6 +243,7 @@ public String getGreeting() { } } + private static class Shopper extends Purchaser implements Person { @Override @@ -246,6 +252,7 @@ public String getGreeting() { } } + private static class Salesman implements Person { @Override @@ -254,6 +261,7 @@ public String getGreeting() { } } + private static class Customer extends Shopper { @Override @@ -262,6 +270,7 @@ public String getGreeting() { } } + private static class Regular extends Customer { private String name; @@ -276,6 +285,7 @@ public String getGreeting() { } } + private static class VIP extends Regular { public VIP(String name) { diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/object/GenericSqlQueryTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/object/GenericSqlQueryTests.java index 5a90e703910..e7d18462ecb 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/object/GenericSqlQueryTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/object/GenericSqlQueryTests.java @@ -29,8 +29,6 @@ import org.junit.Before; import org.junit.Test; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.core.io.ClassPathResource; @@ -44,12 +42,12 @@ * @author Thomas Risberg * @author Juergen Hoeller */ -public class GenericSqlQueryTests { +public class GenericSqlQueryTests { private static final String SELECT_ID_FORENAME_NAMED_PARAMETERS_PARSED = "select id, forename from custmr where id = ? and country = ?"; - private BeanFactory beanFactory; + private DefaultListableBeanFactory beanFactory; private Connection connection; @@ -61,7 +59,7 @@ public class GenericSqlQueryTests { @Before public void setUp() throws Exception { this.beanFactory = new DefaultListableBeanFactory(); - new XmlBeanDefinitionReader((BeanDefinitionRegistry) this.beanFactory).loadBeanDefinitions( + new XmlBeanDefinitionReader(this.beanFactory).loadBeanDefinitions( new ClassPathResource("org/springframework/jdbc/object/GenericSqlQueryTests-context.xml")); DataSource dataSource = mock(DataSource.class); this.connection = mock(Connection.class); diff --git a/spring-jdbc/src/test/resources/org/springframework/jdbc/object/GenericSqlQueryTests-context.xml b/spring-jdbc/src/test/resources/org/springframework/jdbc/object/GenericSqlQueryTests-context.xml index 719502060c2..4bb3358773a 100644 --- a/spring-jdbc/src/test/resources/org/springframework/jdbc/object/GenericSqlQueryTests-context.xml +++ b/spring-jdbc/src/test/resources/org/springframework/jdbc/object/GenericSqlQueryTests-context.xml @@ -1,7 +1,7 @@ @@ -47,7 +47,7 @@ - + diff --git a/spring-web/src/test/java/org/springframework/http/converter/xml/Jaxb2CollectionHttpMessageConverterTests.java b/spring-web/src/test/java/org/springframework/http/converter/xml/Jaxb2CollectionHttpMessageConverterTests.java index 3af300d77a9..7f2c6442ae3 100644 --- a/spring-web/src/test/java/org/springframework/http/converter/xml/Jaxb2CollectionHttpMessageConverterTests.java +++ b/spring-web/src/test/java/org/springframework/http/converter/xml/Jaxb2CollectionHttpMessageConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -83,7 +83,6 @@ public void canRead() throws Exception { public void readXmlRootElementList() throws Exception { String content = ""; MockHttpInputMessage inputMessage = new MockHttpInputMessage(content.getBytes("UTF-8")); - List result = (List) converter.read(rootElementListType, null, inputMessage); assertEquals("Invalid result", 2, result.size()); @@ -96,7 +95,6 @@ public void readXmlRootElementList() throws Exception { public void readXmlRootElementSet() throws Exception { String content = ""; MockHttpInputMessage inputMessage = new MockHttpInputMessage(content.getBytes("UTF-8")); - Set result = (Set) converter.read(rootElementSetType, null, inputMessage); assertEquals("Invalid result", 2, result.size()); @@ -109,7 +107,6 @@ public void readXmlRootElementSet() throws Exception { public void readXmlTypeList() throws Exception { String content = ""; MockHttpInputMessage inputMessage = new MockHttpInputMessage(content.getBytes("UTF-8")); - List result = (List) converter.read(typeListType, null, inputMessage); assertEquals("Invalid result", 2, result.size()); @@ -122,7 +119,6 @@ public void readXmlTypeList() throws Exception { public void readXmlTypeSet() throws Exception { String content = ""; MockHttpInputMessage inputMessage = new MockHttpInputMessage(content.getBytes("UTF-8")); - Set result = (Set) converter.read(typeSetType, null, inputMessage); assertEquals("Invalid result", 2, result.size()); @@ -133,7 +129,6 @@ public void readXmlTypeSet() throws Exception { @Test @SuppressWarnings("unchecked") public void readXmlRootElementExternalEntityDisabled() throws Exception { - Resource external = new ClassPathResource("external.txt", getClass()); String content = "\n" + @@ -142,7 +137,6 @@ public void readXmlRootElementExternalEntityDisabled() throws Exception { MockHttpInputMessage inputMessage = new MockHttpInputMessage(content.getBytes("UTF-8")); converter = new Jaxb2CollectionHttpMessageConverter>() { - @Override protected XMLInputFactory createXmlInputFactory() { XMLInputFactory inputFactory = super.createXmlInputFactory(); @@ -164,7 +158,6 @@ protected XMLInputFactory createXmlInputFactory() { @Test @SuppressWarnings("unchecked") public void readXmlRootElementExternalEntityEnabled() throws Exception { - Resource external = new ClassPathResource("external.txt", getClass()); String content = "\n" + diff --git a/spring-web/src/test/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBeanTests.java b/spring-web/src/test/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBeanTests.java index baabc764498..33b9dca29f1 100644 --- a/spring-web/src/test/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBeanTests.java +++ b/spring-web/src/test/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBeanTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.web.accept; import java.util.Collections; From 188e5327eeed73c32d03366877012e23c0d79de4 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 17 Aug 2016 21:10:37 +0200 Subject: [PATCH 0108/1274] Removed duplicate NoHandlerFoundException entry Issue: SPR-14598 (cherry picked from commit e9f48a4) --- src/asciidoc/web-mvc.adoc | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/asciidoc/web-mvc.adoc b/src/asciidoc/web-mvc.adoc index 65f5bdf77ef..20a6dac8df4 100644 --- a/src/asciidoc/web-mvc.adoc +++ b/src/asciidoc/web-mvc.adoc @@ -3991,7 +3991,8 @@ config). Listed below are some of the exceptions handled by this resolver and th corresponding status codes: |=== -| Exception| HTTP Status Code +| Exception +| HTTP Status Code | `BindException` | 400 (Bad Request) @@ -4017,6 +4018,9 @@ corresponding status codes: | `MethodArgumentNotValidException` | 400 (Bad Request) +| `MissingPathVariableException` +| 500 (Internal Server Error) + | `MissingServletRequestParameterException` | 400 (Bad Request) @@ -4031,12 +4035,6 @@ corresponding status codes: | `TypeMismatchException` | 400 (Bad Request) - -| `MissingPathVariableException` -| 500 (Internal Server Error) - -| `NoHandlerFoundException` -| 404 (Not Found) |=== The `DefaultHandlerExceptionResolver` works transparently by setting the status of the From af53f3d6cf7285e53f135dd3930f242f806f8cec Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 17 Aug 2016 21:35:32 +0200 Subject: [PATCH 0109/1274] Upgrade to Gradle 2.14.1 Issue: SPR-14570 --- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.jar | Bin 53556 -> 53324 bytes gradle/wrapper/gradle-wrapper.properties | 4 ++-- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 303af1cec2e..99e6076512e 100644 --- a/build.gradle +++ b/build.gradle @@ -1358,7 +1358,7 @@ configure(rootProject) { task wrapper(type: Wrapper) { description = "Generates gradlew[.bat] scripts" - gradleVersion = "2.13" + gradleVersion = "2.14.1" doLast() { def gradleOpts = "-XX:MaxMetaspaceSize=1024m -Xmx1024m" diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index ca78035ef0501d802d4fc55381ef2d5c3ce0ec6e..3baa851b28c65f87dd36a6748e1a85cf360c1301 100644 GIT binary patch delta 4035 zcmZWr2{=^i8$Sk7jD3U|jeXyyBw1Q)iAY5hSu(OsLc%5HqO?%t>vl^_DGJFp6Qv}L z!Hlhy7G$Vwm8E2j|CuvA?w$KT&vTydJHPY$z3=Xb( zRVR!Qj`JjtTF8Ur6Hai=+W2l6 zAE{4Be2m9Exw zYP#2U?`$aflAhrH&Pvhv8-?cQ*;=N@{zxQ6#)TXU_6tdzVv)i^j`>AwGC#U+>+!kN z#62A{$L?hAyUusVsJL(3h8gMA6Z!YlL552>F)Z=tnW8g{xgu3T&yLmS)SJr7E}GcV zH~3##n{m}PMMEVc`_i!Y?q?L7*ItQitxrYKQSWH!S6JlF*?xv2p|;iwA~OL!CIO11 z)NV8=oWJcDtvv9M)m=Fi`L=xB(VB;Z#zK>#;Fr%UJ|=ece+{Y+pZ;EvO$r#*CLf}8 zwOKJbZDXRw^=ri*oe5`?63Q@w`faqRLZzBp=8fqFHg?~QN7EFeiyDkblxhK3v2yHG>hZ0FT-)QzB{r7=@FP8vkvDgyephy z%)$Hl4O8^c_oc3Z@9#wjuJX62Xc~FdS+h%Q=8P#tM5EKh#Bsy9BK@v%T*l|jPZc*a z(dv%v$*uD_Q$o%9^ftY=Kyl%HLtd3>Q!0#Fn`(y2;^$Wr=#uh@k z?ltm^Sccwbv?o2oc2ILhTb>y-bEU*?;wZXKV)*a-H~SXM%fc%rbTM_EMk)JxN4h_% z>LlJ|ldETP&wdD<;a@Cd@0_6~8;HNrXbWg;v+9gImwbILO)I^>EIDXf{eRoU=%f(IiwBbn{xZt7VZ`<7^r&2J@Z4p8 z?VRtSt}waLN0Q=oZ7obXv;K34MenQEjkOA+4pk*1hdymA%Tn!Uv2^7UJ!8zYw1-{7 zf|c*51YT7L6R~bt%=OEdB&-m@(>+@DKB~K0Jy~8|9~xh@D|~M0hkTtTv38o2>(Q== zn@u`bOs38Xth_+!t1feibW!lg-`zfHV0i*`H3|~d@X%>B>SFsDVpTxp?z?lb{(%v6 zgPyFvpQqKPWS9*wv7Xc}1~Dz%o~eAhGP*95vg45F{9kMEGN}_*wuWX-KNl($6(7}H zOFrDR-Xy_$@QW06Fboe7<4I{a z{J==HCZz&6vUQ>LyFwAA)A_Iyp#&*8@D;kXwr11`kXP42sU*x&5Y z2h69!<0ROZiCQV94t6`0ot|00d0M{u9R?p|Nqc`;#j*sGc+LBe!MoTIje7=}m-dv; zKnIl8OXp9n+%0h{mmZt7`$YEf9MoEt*GW^dc>d|~+OyK@b8>=h`cv>f>&J}M$8pjz zz4S|yhhJ5oV@+jtW>01nviUw*kPc6-5bPZr&&k1^xmI&ZC^G7e}(!lG~-;Z(-B z4OwXRN)xIgr{K|Pb;IlnW^t-1z}@%`+Lyd z{ockq*Y0^q%TXPmQkG7TtG?xl;HNAKUwPEKl{L=}OUzUj=|{&kH(5VW-d_KtV3Qf{ zo&1Q1r8|R;&d`*Xmvg)v5tN<}-VD%so5>D4dqTx2emGKuX_8ynL1S-~3B!HtnC*Q_ z>J(`^jh7QdOiGzjc1>DUZj{BRyJiJL1H`de!ows~lJKPP^3pO5bivYIm< zosy4BxU|tD;Mh=n=~trSuv%xbXi5Cw_txvKiavIG6(6#ecn@6aOZ;AG%^$!uZgx)Xx$ z`_9UHbbERI5d8MZN=>^!`_*7|3T47kOfi7%UQ{Gr;^`ulPV38m?)*N@Lb~Gh{>EJX z`FzNDen({EMir?%zEMTO#&+C9&Xcqk*L1^v_1HDv|Dm=ic&y;=hA-NMD@&yph9g`Z z=EdulFCPLB923y+)8mI*s+-?w6sH6F{D%>smAHfe)b_w(Zl@*$@FBlkNO)mM&VX>C zvK4J82zn3SF*blas#;di3TkoGAeVnJ#++Y_LsN-k?#~j0petxlo{Y&Nico*W6$&Dz z3jdtr+AK(ZErcKmXPTgt|{ zzt&YA;MxJytz1r5^)oI~{J@pV;d=!5(VPgym+0;?aAl-GUY&;Oa!o(|yOMk;8UE!9 z9#DK@%zMKF-cQztapnhX27+^fJOaO42$Fz)ZOw(hjzk6;x;Xx0>%R&S9!33cMPQ^K zkreP>A>gaW@_pgl>-7F>GANmN=q4!D#E<$dS_#_gjq#kI2fuh6Erv<&;a7!+{0C5eNK6hy+6m5(tw**G3=~40U4=Xn`)L z#EFaN&dcNTh#XfdCk?!QdkJm8qORf*IJZF_G5G+7eyl|xmvstYv`G{hihXCb+Do|n zs&Wvd#bcHq7%?^=(hvp~g|kMaISf@Knz!wevDe|%L4$ALyjhZU{_R!>%rj&Va)?q$ zCXEk*_VPoJDUXkzt_YbThXy`wav-=-6cJ?kJ7DF7A;?i2f|Pj#kUJu9<`V{$jZz4C zdEbJGyi>x%2VC<}2P>OIl{kyxpKS_);L$EWw4`buK=4%nJDWr~YYlieVNpb15Z|PT zd|rh=fiK9sEe{g>kjx?Q91O4V2T4(?z^qvsfn1v5niCNyvsn{?YG6n-5Qem~m?}sZ zL#pV>ZE!Xm;R!@o4uUgAq^ zkHWf*Ag7f(&W{IQYqDYEVX$^HkM2b@h+sJ(x_TBCB@qYMaau^$Ir+&Ud3D@|1H1Kq zW%^9!3;^`~DGBhtaxQaD6N}vl=1lv|~}Rci`v@mPc3hr2Ob*IGkd5*79aEu@vOB|IQ|?i>QDd zz*VRe=a@r=V*On9^9Y=k z7?(j(OhABKYdAqK$2J4$?pw?7{iu)@S{kY%Vty;ZjQLt{)?9pNNCjAlGgP%XT zzHdThH8VyxCpRc$F&&5kK}~9Pil+97PgoT!ytXM)^`|8`W>#0ZU359Se;?=UjN>7#bs&lg9MhO_yiB_4yG)WX)AzJ$@fe zv5!$p)M0g>D{Go_w*T=b)&ox)5r0S1i?N;vX3b^GJ|?i1=3?tZRTD`~bEaFORg-I0 zi(c55LOl@L5^`{GANf(@5p}!p!lJ>1-5sGP%9WINfh8^>LeT=AbX6t_#T!s{CbmMbD0NrxgL zdgh-IDRt9icH$N9&NFQ*Ti#|<>%K@#sGPj#x!UQd z57#fMub4X6DP&bxE#_&{e3nH;Kew*J;X~}1(c2W5sZQ82miqNZ16=sY158KRvE9Q# z`(jg})Redz}9iP@DIfUE}xgjv*t@7~-FI-e-s2Ecj}te)WG9b|iPe6&X_{&_Z;qojPmO-oaJJ(y*<-bw3o)9jKNb$T9oOu{?Yxs@Ldb(4TmVsDr)k9Qjh*l9@y3G0d zoWHz;tM8$mvxfB7C`P{fqh6;FrJ7^(V0qKZADF|9AN2%;Nv70Sk83MgeGB*M28#!& zj+F^lXykQ$TC}r{bMaf#4#^jdhkQ4lfBN&NZ`@dGbn3)f?`?Ib&7x)!_1;n9OtA&@ zg7s!r8qpmEk3y!Ve%mG2Dmc8hD2owJ&L|e%=stVo_J=(>{k>Yz0d@Qv=k?=XFDy@e ziO}0GXt+RUsc8i+zG*Bv7|Zy0jDPx>hbHyt?pw{nzGup&OM3nsn`)1fR1Hj#4(W?T zQBuaf4R37=t}&|`{eBW#qV6iv5)vCUNYC6J>C&e6p}afno1$xKh|A`r9vr#s)E&~O z>|5*!#irTHIIBBlXHnQN89vkac-7l#^iaLaowb(|?_|WD#V8*g-@KUHQ+eWLpQz?} zs`5SlCf(y-H~zG?weNh;bB!XWA#}0uw7O7g!D)TmuFi8C#j^q9FmAekM@;D%mD1;w zMU}-+f00nKHx<+Iwt+?$jGR=7oIK@V``ONiPw?%tyKj0eFk0{3-c%_y=rQf+<)ouT zuRjF46P$frO=_kShF8yQ)DBHOSa01@E<+XB79nNpaO|)Bz;?iUPlw`X^Q&icXQr9t z0wII1Lzvcw^;DCr)PnQ($}gDWhzi!kRgCzX;TNwJJ+hm9Zd6s;m;Pacb!4+?#-eTT zjja<(cH1oH4D9sdL&cmEYW?CzYPPAWu1FqVntMtHy~e#gZe}cjOC9HaA2-4&(`?hJ zp)s*1_}s(KOOYSZF}+yxMjD!@h-j zrkd{Qe)AXbHX}02#xXBVM-)9UsAmDsW%BO(NQz~KPMNH@pVfbVt$C5+@S;|^*8lq9 zi)Q}O>5mkazNvn%bW=DG!dwt9Xm;NAzMD$RQHs~04II98@$00*jKoA1{pHRiqxVMg zH{P~exEd+=`gl5dndMUoeVc*ek7LdQ)wR`L3SB+svPrqo2koO{n;1^#MmER)%#7Yk zp`=@i@9dIqdRBJB%Lq(u<2P;Y+i$OZ# zn`MNNj+*JxYEjNn+utd1GFJeCszgB=zZ?)@idMSpy~91B`J4s0C$xyGvycdf-Ix8! z!ydjB*n(gWb$!Rq4qLSjOJ@s9n|5})W-GgE%we&k%P~Zg?4V0=*43?~Y^b78TtXs6 zMD2Acx*NvS!Ji%k!Z`Y01NDSu$&2m2ovt*cOD3MK42DuD3Ut ze7A@!>T?@nr)F!E(_T&EGvvZ3Ee1)!;+lQ!+Hqn>zt1o$b+%ZE3K|hrtenpHS+{Qb z*fF%}-Y6-j{Jz82?YYr|NrZ}-ps?zU3zKHHb9U2{V*lKl;HiR8Pfb3!t<3fO9nI+g z4exgdfcN={0F`U}-f=saV*rJiXaOW4q*E78;zK$kk%ruk{HY!cf-F#%x#YR{7lH`V za=x33WTg~y1+6aKmPAaSbLY9pyPL1M9d=z8*I>Qw^xtz5R zo^p}T51qK2JvD$>L-Q$8X*-hgWhpn0~C{^8XcxuHC;1gxuC;WuvWDJ+7|aq0!2= zxsMbJ3N6j|>xv2t7#n!+s34MIz$t4`${^rraQNUbKOH;kf4n%h*9-!R0lqQh5ayaE z1k@CKwZwxxPmosv9C$)N%cH=J1{|sScF&%!zlFM@G4-0CIiuWu1UV0fR5rv zOt$p|gnb-_yf6rqQ?H`Pd1r{D?t@GW1TDi8(1*wN-=`{GNC+Vs3Mm4)2Ck%90|Bid ziol1O1i?O0DUJV`hlS56_Q(}}2r^m)LAw7HgWIb3Wsuvj9f?^GtxI=7!yp`&#KRfD zA>tx3SfJR5MaVq2Z65M*S%jP`D+PquT=M4O>Q@9Q2%1)dAVnT>GYKKHNg{B`^QOML zk$@)YBj)w`azMU`J6X*p0$O`B0v~EpLdJ4dF2A?}PLzo(1gZ1Lo0}rUoO%I3Z<0rn zJplXf+>5|iCO7};OafZT8G&+TMZi&}GQzwJ!y2v#Y=;s8ZA?iL&vph!TE`PD0jw}7 zikyo6XK#Wacu>Eds(73yuxVCt=a4ukC*7P!jN8Ef;?Vb_&JMbt^ zVRD==w|x3z8|>`}FP7dfXU>9Jc!8(Q+mT^VJdUIm!p2GXN9G~*c!SsNYr#QpA+Vo? zMaT{&tK@IPic9duzKw^>@IlBHK19&g$|cv9*ih17asnLQh=*M7hmb#n3xdaN0ie)A z5%$;QnT%EmJma5dWYq1w=9xhCbCGP-mAaGOb?+3M|FD VlO;32{9`4s@u|3g=o3z${{R5)&u9Pu diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index fc60887c40d..19e5389604f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Jun 15 12:59:30 CEST 2016 +#Wed Aug 17 21:21:18 CEST 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.13-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-bin.zip From 6eab5622c719df46f6b90df4e3a1581a3c908fc9 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 17 Aug 2016 22:12:47 +0200 Subject: [PATCH 0110/1274] Renamed setDefaultPersistenceUnitRootLocation to setPersistenceUnitRootLocation (cherry picked from commit f1ab37c) --- .../jpa/LocalContainerEntityManagerFactoryBean.java | 11 +++++++---- .../DefaultPersistenceUnitManager.java | 2 ++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/LocalContainerEntityManagerFactoryBean.java b/spring-orm/src/main/java/org/springframework/orm/jpa/LocalContainerEntityManagerFactoryBean.java index a0a0a51302a..0ad57e01e62 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/LocalContainerEntityManagerFactoryBean.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/LocalContainerEntityManagerFactoryBean.java @@ -144,14 +144,15 @@ public void setPersistenceUnitName(String persistenceUnitName) { } /** - * Set the default persistence unit root location, to be applied - * if no unit-specific persistence unit root could be determined. - *

NOTE: Only applied if no external PersistenceUnitManager specified. + * Set a persistence unit root location for the default persistence unit. *

Default is "classpath:", that is, the root of the current classpath * (nearest root directory). To be overridden if unit-specific resolution * does not work and the classpath root is not appropriate either. + *

NOTE: Only applied if no external PersistenceUnitManager specified. + * @since 4.3.3 + * @see DefaultPersistenceUnitManager#setDefaultPersistenceUnitRootLocation */ - public void setDefaultPersistenceUnitRootLocation(String defaultPersistenceUnitRootLocation) { + public void setPersistenceUnitRootLocation(String defaultPersistenceUnitRootLocation) { this.internalPersistenceUnitManager.setDefaultPersistenceUnitRootLocation(defaultPersistenceUnitRootLocation); } @@ -214,6 +215,7 @@ public void setMappingResources(String... mappingResources) { * Specify the JPA 2.0 shared cache mode for this persistence unit, * overriding a value in {@code persistence.xml} if set. *

NOTE: Only applied if no external PersistenceUnitManager specified. + * @since 4.0 * @see javax.persistence.spi.PersistenceUnitInfo#getSharedCacheMode() * @see #setPersistenceUnitManager */ @@ -225,6 +227,7 @@ public void setSharedCacheMode(SharedCacheMode sharedCacheMode) { * Specify the JPA 2.0 validation mode for this persistence unit, * overriding a value in {@code persistence.xml} if set. *

NOTE: Only applied if no external PersistenceUnitManager specified. + * @since 4.0 * @see javax.persistence.spi.PersistenceUnitInfo#getValidationMode() * @see #setPersistenceUnitManager */ diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/DefaultPersistenceUnitManager.java b/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/DefaultPersistenceUnitManager.java index bae326c44f6..a8a11cbb84f 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/DefaultPersistenceUnitManager.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/DefaultPersistenceUnitManager.java @@ -261,6 +261,7 @@ public void setMappingResources(String... mappingResources) { /** * Specify the JPA 2.0 shared cache mode for all of this manager's persistence * units, overriding any value in {@code persistence.xml} if set. + * @since 4.0 * @see javax.persistence.spi.PersistenceUnitInfo#getSharedCacheMode() */ public void setSharedCacheMode(SharedCacheMode sharedCacheMode) { @@ -270,6 +271,7 @@ public void setSharedCacheMode(SharedCacheMode sharedCacheMode) { /** * Specify the JPA 2.0 validation mode for all of this manager's persistence * units, overriding any value in {@code persistence.xml} if set. + * @since 4.0 * @see javax.persistence.spi.PersistenceUnitInfo#getValidationMode() */ public void setValidationMode(ValidationMode validationMode) { From 66dcc4b6db71e91d662b5e6c8b5002ff507d3374 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 17 Aug 2016 22:29:21 +0200 Subject: [PATCH 0111/1274] Upgrade to Caffeine 2.3.2 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 99e6076512e..24a535414c5 100644 --- a/build.gradle +++ b/build.gradle @@ -32,7 +32,7 @@ configure(allprojects) { project -> version = qualifyVersionIfNecessary(version) ext.aspectjVersion = "1.8.9" - ext.caffeineVersion = "2.3.1" + ext.caffeineVersion = "2.3.2" ext.eclipselinkVersion = "2.4.2" ext.ehcacheVersion = "2.10.2" ext.ehcachejcacheVersion = "1.0.1" From da56758054e7f1001eae86e9ae0108c789cf9fe6 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 17 Aug 2016 22:31:13 +0200 Subject: [PATCH 0112/1274] Updated note on CompilationCustomizer and CompilerConfiguration Issue: SPR-14585 --- src/asciidoc/integration.adoc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/asciidoc/integration.adoc b/src/asciidoc/integration.adoc index 31a34ea0e35..db6cce28f0e 100644 --- a/src/asciidoc/integration.adoc +++ b/src/asciidoc/integration.adoc @@ -7829,8 +7829,9 @@ If you are not using the Spring namespace support, you can still use the [NOTE] ==== -As of Spring Framework 4.3.3, you may also specify a Groovy `CompilationCustomizer` type -such as an `ImportCustomizer` in the same place as Spring's `GroovyObjectCustomizer`. +As of Spring Framework 4.3.3, you may also specify a Groovy `CompilationCustomizer` +(such as an `ImportCustomizer`) or even a full Groovy `CompilerConfiguration` object +in the same place as Spring's `GroovyObjectCustomizer`. ==== From 8d7db8e450abf1123f7b4dc2c3e0969b40e376fa Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 18 Aug 2016 09:05:47 +0200 Subject: [PATCH 0113/1274] DelegatingWebMvcConfiguration properly delegates extendHandlerExceptionResolvers Also fixes the declared visibility of configurePathMatch and configureAsyncSupport. Issue: SPR-14599 (cherry picked from commit d2e3a1a) --- .../DelegatingWebMvcConfiguration.java | 61 ++++--- .../WebMvcConfigurationSupport.java | 28 +-- .../config/annotation/WebMvcConfigurer.java | 169 +++++++++--------- .../annotation/WebMvcConfigurerAdapter.java | 50 +++--- .../annotation/WebMvcConfigurerComposite.java | 104 ++++++----- .../DelegatingWebMvcConfigurationTests.java | 23 ++- 6 files changed, 218 insertions(+), 217 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.java index 084acd90c6f..2c181c594b4 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -22,6 +22,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.format.FormatterRegistry; import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.util.CollectionUtils; import org.springframework.validation.MessageCodesResolver; import org.springframework.validation.Validator; import org.springframework.web.method.support.HandlerMethodArgumentResolver; @@ -29,7 +30,7 @@ import org.springframework.web.servlet.HandlerExceptionResolver; /** - * A sub-class of {@code WebMvcConfigurationSupport} that detects and delegates + * A subclass of {@code WebMvcConfigurationSupport} that detects and delegates * to all beans of type {@link WebMvcConfigurer} allowing them to customize the * configuration provided by {@code WebMvcConfigurationSupport}. This is the * class actually imported by {@link EnableWebMvc @EnableWebMvc}. @@ -45,16 +46,15 @@ public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport { @Autowired(required = false) public void setConfigurers(List configurers) { - if (configurers == null || configurers.isEmpty()) { - return; + if (!CollectionUtils.isEmpty(configurers)) { + this.configurers.addWebMvcConfigurers(configurers); } - this.configurers.addWebMvcConfigurers(configurers); } @Override - protected void addInterceptors(InterceptorRegistry registry) { - this.configurers.addInterceptors(registry); + protected void configurePathMatch(PathMatchConfigurer configurer) { + this.configurers.configurePathMatch(configurer); } @Override @@ -63,23 +63,23 @@ protected void configureContentNegotiation(ContentNegotiationConfigurer configur } @Override - public void configureAsyncSupport(AsyncSupportConfigurer configurer) { + protected void configureAsyncSupport(AsyncSupportConfigurer configurer) { this.configurers.configureAsyncSupport(configurer); } @Override - public void configurePathMatch(PathMatchConfigurer configurer) { - this.configurers.configurePathMatch(configurer); + protected void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { + this.configurers.configureDefaultServletHandling(configurer); } @Override - protected void addViewControllers(ViewControllerRegistry registry) { - this.configurers.addViewControllers(registry); + protected void addFormatters(FormatterRegistry registry) { + this.configurers.addFormatters(registry); } @Override - protected void configureViewResolvers(ViewResolverRegistry registry) { - this.configurers.configureViewResolvers(registry); + protected void addInterceptors(InterceptorRegistry registry) { + this.configurers.addInterceptors(registry); } @Override @@ -88,8 +88,18 @@ protected void addResourceHandlers(ResourceHandlerRegistry registry) { } @Override - protected void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { - this.configurers.configureDefaultServletHandling(configurer); + protected void addCorsMappings(CorsRegistry registry) { + this.configurers.addCorsMappings(registry); + } + + @Override + protected void addViewControllers(ViewControllerRegistry registry) { + this.configurers.addViewControllers(registry); + } + + @Override + protected void configureViewResolvers(ViewResolverRegistry registry) { + this.configurers.configureViewResolvers(registry); } @Override @@ -113,8 +123,13 @@ protected void extendMessageConverters(List> converters) } @Override - protected void addFormatters(FormatterRegistry registry) { - this.configurers.addFormatters(registry); + protected void configureHandlerExceptionResolvers(List exceptionResolvers) { + this.configurers.configureHandlerExceptionResolvers(exceptionResolvers); + } + + @Override + protected void extendHandlerExceptionResolvers(List exceptionResolvers) { + this.configurers.extendHandlerExceptionResolvers(exceptionResolvers); } @Override @@ -127,14 +142,4 @@ protected MessageCodesResolver getMessageCodesResolver() { return this.configurers.getMessageCodesResolver(); } - @Override - protected void configureHandlerExceptionResolvers(List exceptionResolvers) { - this.configurers.configureHandlerExceptionResolvers(exceptionResolvers); - } - - @Override - protected void addCorsMappings(CorsRegistry registry) { - this.configurers.addCorsMappings(registry); - } - } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java index 3a114972154..6d5ab799626 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java @@ -315,7 +315,7 @@ protected PathMatchConfigurer getPathMatchConfigurer() { * @see PathMatchConfigurer * @since 4.0.3 */ - public void configurePathMatch(PathMatchConfigurer configurer) { + protected void configurePathMatch(PathMatchConfigurer configurer) { } /** @@ -531,6 +531,13 @@ protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer return initializer; } + /** + * Override this method to configure asynchronous request processing options. + * @see AsyncSupportConfigurer + */ + protected void configureAsyncSupport(AsyncSupportConfigurer configurer) { + } + /** * Return a {@link FormattingConversionService} for use with annotated * controller methods and the {@code spring:eval} JSP tag. @@ -543,6 +550,12 @@ public FormattingConversionService mvcConversionService() { return conversionService; } + /** + * Override this method to add custom {@link Converter}s and {@link Formatter}s. + */ + protected void addFormatters(FormatterRegistry registry) { + } + /** * Return a global {@link Validator} instance for example for validating * {@code @ModelAttribute} and {@code @RequestBody} method arguments. @@ -762,19 +775,6 @@ else if (gsonPresent) { } } - /** - * Override this method to add custom {@link Converter}s and {@link Formatter}s. - */ - protected void addFormatters(FormatterRegistry registry) { - } - - /** - * Override this method to configure asynchronous request processing options. - * @see AsyncSupportConfigurer - */ - public void configureAsyncSupport(AsyncSupportConfigurer configurer) { - } - /** * Return an instance of {@link CompositeUriComponentsContributor} for use with * {@link org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder}. diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.java index 6ab3eb410bb..ca00aec989d 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.java @@ -46,51 +46,6 @@ */ public interface WebMvcConfigurer { - /** - * Add {@link Converter}s and {@link Formatter}s in addition to the ones - * registered by default. - */ - void addFormatters(FormatterRegistry registry); - - /** - * Configure the {@link HttpMessageConverter}s to use for reading or writing - * to the body of the request or response. If no converters are added, a - * default list of converters is registered. - *

Note that adding converters to the list, turns off - * default converter registration. To simply add a converter without impacting - * default registration, consider using the method - * {@link #extendMessageConverters(java.util.List)} instead. - * @param converters initially an empty list of converters - */ - void configureMessageConverters(List> converters); - - /** - * A hook for extending or modifying the list of converters after it has been - * configured. This may be useful for example to allow default converters to - * be registered and then insert a custom converter through this method. - * @param converters the list of configured converters to extend. - * @since 4.1.3 - */ - void extendMessageConverters(List> converters); - - /** - * Provide a custom {@link Validator} instead of the one created by default. - * The default implementation, assuming JSR-303 is on the classpath, is: - * {@link org.springframework.validation.beanvalidation.OptionalValidatorFactoryBean}. - * Leave the return value as {@code null} to keep the default. - */ - Validator getValidator(); - - /** - * Configure content negotiation options. - */ - void configureContentNegotiation(ContentNegotiationConfigurer configurer); - - /** - * Configure asynchronous request handling options. - */ - void configureAsyncSupport(AsyncSupportConfigurer configurer); - /** * Helps with configuring HandlerMappings path matching options such as trailing slash match, * suffix registration, path matcher and path helper. @@ -105,40 +60,28 @@ public interface WebMvcConfigurer { void configurePathMatch(PathMatchConfigurer configurer); /** - * Add resolvers to support custom controller method argument types. - *

This does not override the built-in support for resolving handler - * method arguments. To customize the built-in support for argument - * resolution, configure {@link RequestMappingHandlerAdapter} directly. - * @param argumentResolvers initially an empty list + * Configure content negotiation options. */ - void addArgumentResolvers(List argumentResolvers); + void configureContentNegotiation(ContentNegotiationConfigurer configurer); /** - * Add handlers to support custom controller method return value types. - *

Using this option does not override the built-in support for handling - * return values. To customize the built-in support for handling return - * values, configure RequestMappingHandlerAdapter directly. - * @param returnValueHandlers initially an empty list + * Configure asynchronous request handling options. */ - void addReturnValueHandlers(List returnValueHandlers); + void configureAsyncSupport(AsyncSupportConfigurer configurer); /** - * Configure the {@link HandlerExceptionResolver}s to handle unresolved - * controller exceptions. If no resolvers are added to the list, default - * exception resolvers are added instead. - * @param exceptionResolvers initially an empty list + * Configure a handler to delegate unhandled requests by forwarding to the + * Servlet container's "default" servlet. A common use case for this is when + * the {@link DispatcherServlet} is mapped to "/" thus overriding the + * Servlet container's default handling of static resources. */ - void configureHandlerExceptionResolvers(List exceptionResolvers); + void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer); /** - * A hook for extending or modifying the list of - * {@link HandlerExceptionResolver}s after it has been configured. This may - * be useful for example to allow default resolvers to be registered and then - * insert a custom one through this method. - * @param exceptionResolvers the list of configured resolvers to extend. - * @since 4.3 + * Add {@link Converter}s and {@link Formatter}s in addition to the ones + * registered by default. */ - void extendHandlerExceptionResolvers(List exceptionResolvers); + void addFormatters(FormatterRegistry registry); /** * Add Spring MVC lifecycle interceptors for pre- and post-processing of @@ -155,11 +98,17 @@ public interface WebMvcConfigurer { void addInterceptors(InterceptorRegistry registry); /** - * Provide a custom {@link MessageCodesResolver} for building message codes - * from data binding and validation error codes. Leave the return value as - * {@code null} to keep the default. + * Add handlers to serve static resources such as images, js, and, css + * files from specific locations under web application root, the classpath, + * and others. */ - MessageCodesResolver getMessageCodesResolver(); + void addResourceHandlers(ResourceHandlerRegistry registry); + + /** + * Configure cross origin requests processing. + * @since 4.2 + */ + void addCorsMappings(CorsRegistry registry); /** * Configure simple automated controllers pre-configured with the response @@ -178,24 +127,74 @@ public interface WebMvcConfigurer { void configureViewResolvers(ViewResolverRegistry registry); /** - * Add handlers to serve static resources such as images, js, and, css - * files from specific locations under web application root, the classpath, - * and others. + * Add resolvers to support custom controller method argument types. + *

This does not override the built-in support for resolving handler + * method arguments. To customize the built-in support for argument + * resolution, configure {@link RequestMappingHandlerAdapter} directly. + * @param argumentResolvers initially an empty list */ - void addResourceHandlers(ResourceHandlerRegistry registry); + void addArgumentResolvers(List argumentResolvers); /** - * Configure a handler to delegate unhandled requests by forwarding to the - * Servlet container's "default" servlet. A common use case for this is when - * the {@link DispatcherServlet} is mapped to "/" thus overriding the - * Servlet container's default handling of static resources. + * Add handlers to support custom controller method return value types. + *

Using this option does not override the built-in support for handling + * return values. To customize the built-in support for handling return + * values, configure RequestMappingHandlerAdapter directly. + * @param returnValueHandlers initially an empty list */ - void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer); + void addReturnValueHandlers(List returnValueHandlers); /** - * Configure cross origin requests processing. - * @since 4.2 + * Configure the {@link HttpMessageConverter}s to use for reading or writing + * to the body of the request or response. If no converters are added, a + * default list of converters is registered. + *

Note that adding converters to the list, turns off + * default converter registration. To simply add a converter without impacting + * default registration, consider using the method + * {@link #extendMessageConverters(java.util.List)} instead. + * @param converters initially an empty list of converters */ - void addCorsMappings(CorsRegistry registry); + void configureMessageConverters(List> converters); + + /** + * A hook for extending or modifying the list of converters after it has been + * configured. This may be useful for example to allow default converters to + * be registered and then insert a custom converter through this method. + * @param converters the list of configured converters to extend. + * @since 4.1.3 + */ + void extendMessageConverters(List> converters); + + /** + * Configure the {@link HandlerExceptionResolver}s to handle unresolved + * controller exceptions. If no resolvers are added to the list, default + * exception resolvers are added instead. + * @param exceptionResolvers initially an empty list + */ + void configureHandlerExceptionResolvers(List exceptionResolvers); + + /** + * A hook for extending or modifying the list of {@link HandlerExceptionResolver}s + * after it has been configured. This may be useful for example to allow default + * resolvers to be registered and then insert a custom one through this method. + * @param exceptionResolvers the list of configured resolvers to extend + * @since 4.3 + */ + void extendHandlerExceptionResolvers(List exceptionResolvers); + + /** + * Provide a custom {@link Validator} instead of the one created by default. + * The default implementation, assuming JSR-303 is on the classpath, is: + * {@link org.springframework.validation.beanvalidation.OptionalValidatorFactoryBean}. + * Leave the return value as {@code null} to keep the default. + */ + Validator getValidator(); + + /** + * Provide a custom {@link MessageCodesResolver} for building message codes + * from data binding and validation error codes. Leave the return value as + * {@code null} to keep the default. + */ + MessageCodesResolver getMessageCodesResolver(); } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerAdapter.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerAdapter.java index 1c9e3e0c7e5..3d1c28845e3 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerAdapter.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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 @@ /** * An implementation of {@link WebMvcConfigurer} with empty methods allowing - * sub-classes to override only the methods they're interested in. + * subclasses to override only the methods they're interested in. * * @author Rossen Stoyanchev * @since 3.1 @@ -40,7 +40,7 @@ public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer { *

This implementation is empty. */ @Override - public void addFormatters(FormatterRegistry registry) { + public void configurePathMatch(PathMatchConfigurer configurer) { } /** @@ -48,7 +48,7 @@ public void addFormatters(FormatterRegistry registry) { *

This implementation is empty. */ @Override - public void configureMessageConverters(List> converters) { + public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { } /** @@ -56,16 +56,15 @@ public void configureMessageConverters(List> converters) *

This implementation is empty. */ @Override - public void extendMessageConverters(List> converters) { + public void configureAsyncSupport(AsyncSupportConfigurer configurer) { } /** * {@inheritDoc} - *

This implementation returns {@code null} + *

This implementation is empty. */ @Override - public Validator getValidator() { - return null; + public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { } /** @@ -73,7 +72,7 @@ public Validator getValidator() { *

This implementation is empty. */ @Override - public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { + public void addFormatters(FormatterRegistry registry) { } /** @@ -81,7 +80,7 @@ public void configureContentNegotiation(ContentNegotiationConfigurer configurer) *

This implementation is empty. */ @Override - public void configureAsyncSupport(AsyncSupportConfigurer configurer) { + public void addInterceptors(InterceptorRegistry registry) { } /** @@ -89,7 +88,7 @@ public void configureAsyncSupport(AsyncSupportConfigurer configurer) { *

This implementation is empty. */ @Override - public void configurePathMatch(PathMatchConfigurer configurer) { + public void addResourceHandlers(ResourceHandlerRegistry registry) { } /** @@ -97,7 +96,7 @@ public void configurePathMatch(PathMatchConfigurer configurer) { *

This implementation is empty. */ @Override - public void addArgumentResolvers(List argumentResolvers) { + public void addCorsMappings(CorsRegistry registry) { } /** @@ -105,7 +104,7 @@ public void addArgumentResolvers(List argumentRes *

This implementation is empty. */ @Override - public void addReturnValueHandlers(List returnValueHandlers) { + public void addViewControllers(ViewControllerRegistry registry) { } /** @@ -113,7 +112,7 @@ public void addReturnValueHandlers(List returnV *

This implementation is empty. */ @Override - public void configureHandlerExceptionResolvers(List exceptionResolvers) { + public void configureViewResolvers(ViewResolverRegistry registry) { } /** @@ -121,7 +120,7 @@ public void configureHandlerExceptionResolvers(List ex *

This implementation is empty. */ @Override - public void extendHandlerExceptionResolvers(List exceptionResolvers) { + public void addArgumentResolvers(List argumentResolvers) { } /** @@ -129,8 +128,7 @@ public void extendHandlerExceptionResolvers(List excep *

This implementation is empty. */ @Override - public MessageCodesResolver getMessageCodesResolver() { - return null; + public void addReturnValueHandlers(List returnValueHandlers) { } /** @@ -138,7 +136,7 @@ public MessageCodesResolver getMessageCodesResolver() { *

This implementation is empty. */ @Override - public void addInterceptors(InterceptorRegistry registry) { + public void configureMessageConverters(List> converters) { } /** @@ -146,7 +144,7 @@ public void addInterceptors(InterceptorRegistry registry) { *

This implementation is empty. */ @Override - public void addViewControllers(ViewControllerRegistry registry) { + public void extendMessageConverters(List> converters) { } /** @@ -154,7 +152,7 @@ public void addViewControllers(ViewControllerRegistry registry) { *

This implementation is empty. */ @Override - public void configureViewResolvers(ViewResolverRegistry registry) { + public void configureHandlerExceptionResolvers(List exceptionResolvers) { } /** @@ -162,23 +160,25 @@ public void configureViewResolvers(ViewResolverRegistry registry) { *

This implementation is empty. */ @Override - public void addResourceHandlers(ResourceHandlerRegistry registry) { + public void extendHandlerExceptionResolvers(List exceptionResolvers) { } /** * {@inheritDoc} - *

This implementation is empty. + *

This implementation returns {@code null}. */ @Override - public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { + public Validator getValidator() { + return null; } /** * {@inheritDoc} - *

This implementation is empty. + *

This implementation returns {@code null}. */ @Override - public void addCorsMappings(CorsRegistry registry) { + public MessageCodesResolver getMessageCodesResolver() { + return null; } } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerComposite.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerComposite.java index 1b6d6e53cb7..c2b62ee2d3c 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerComposite.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurerComposite.java @@ -21,6 +21,7 @@ import org.springframework.format.FormatterRegistry; import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.util.CollectionUtils; import org.springframework.validation.MessageCodesResolver; import org.springframework.validation.Validator; import org.springframework.web.method.support.HandlerMethodArgumentResolver; @@ -37,16 +38,18 @@ class WebMvcConfigurerComposite implements WebMvcConfigurer { private final List delegates = new ArrayList(); + public void addWebMvcConfigurers(List configurers) { - if (configurers != null) { + if (!CollectionUtils.isEmpty(configurers)) { this.delegates.addAll(configurers); } } + @Override - public void addFormatters(FormatterRegistry registry) { + public void configurePathMatch(PathMatchConfigurer configurer) { for (WebMvcConfigurer delegate : this.delegates) { - delegate.addFormatters(registry); + delegate.configurePathMatch(configurer); } } @@ -65,131 +68,126 @@ public void configureAsyncSupport(AsyncSupportConfigurer configurer) { } @Override - public void configurePathMatch(PathMatchConfigurer configurer) { + public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { for (WebMvcConfigurer delegate : this.delegates) { - delegate.configurePathMatch(configurer); + delegate.configureDefaultServletHandling(configurer); } } @Override - public void configureMessageConverters(List> converters) { + public void addFormatters(FormatterRegistry registry) { for (WebMvcConfigurer delegate : this.delegates) { - delegate.configureMessageConverters(converters); + delegate.addFormatters(registry); } } @Override - public void extendMessageConverters(List> converters) { + public void addInterceptors(InterceptorRegistry registry) { for (WebMvcConfigurer delegate : this.delegates) { - delegate.extendMessageConverters(converters); + delegate.addInterceptors(registry); } } @Override - public void addArgumentResolvers(List argumentResolvers) { + public void addResourceHandlers(ResourceHandlerRegistry registry) { for (WebMvcConfigurer delegate : this.delegates) { - delegate.addArgumentResolvers(argumentResolvers); + delegate.addResourceHandlers(registry); } } @Override - public void addReturnValueHandlers(List returnValueHandlers) { + public void addCorsMappings(CorsRegistry registry) { for (WebMvcConfigurer delegate : this.delegates) { - delegate.addReturnValueHandlers(returnValueHandlers); + delegate.addCorsMappings(registry); } } @Override - public void configureHandlerExceptionResolvers(List exceptionResolvers) { + public void addViewControllers(ViewControllerRegistry registry) { for (WebMvcConfigurer delegate : this.delegates) { - delegate.configureHandlerExceptionResolvers(exceptionResolvers); + delegate.addViewControllers(registry); } } @Override - public void extendHandlerExceptionResolvers(List exceptionResolvers) { + public void configureViewResolvers(ViewResolverRegistry registry) { for (WebMvcConfigurer delegate : this.delegates) { - delegate.configureHandlerExceptionResolvers(exceptionResolvers); + delegate.configureViewResolvers(registry); } } @Override - public void addInterceptors(InterceptorRegistry registry) { + public void addArgumentResolvers(List argumentResolvers) { for (WebMvcConfigurer delegate : this.delegates) { - delegate.addInterceptors(registry); + delegate.addArgumentResolvers(argumentResolvers); } } @Override - public void addViewControllers(ViewControllerRegistry registry) { + public void addReturnValueHandlers(List returnValueHandlers) { for (WebMvcConfigurer delegate : this.delegates) { - delegate.addViewControllers(registry); + delegate.addReturnValueHandlers(returnValueHandlers); } } @Override - public void configureViewResolvers(ViewResolverRegistry registry) { + public void configureMessageConverters(List> converters) { for (WebMvcConfigurer delegate : this.delegates) { - delegate.configureViewResolvers(registry); + delegate.configureMessageConverters(converters); } } @Override - public void addResourceHandlers(ResourceHandlerRegistry registry) { + public void extendMessageConverters(List> converters) { for (WebMvcConfigurer delegate : this.delegates) { - delegate.addResourceHandlers(registry); + delegate.extendMessageConverters(converters); } } @Override - public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { + public void configureHandlerExceptionResolvers(List exceptionResolvers) { for (WebMvcConfigurer delegate : this.delegates) { - delegate.configureDefaultServletHandling(configurer); + delegate.configureHandlerExceptionResolvers(exceptionResolvers); + } + } + + @Override + public void extendHandlerExceptionResolvers(List exceptionResolvers) { + for (WebMvcConfigurer delegate : this.delegates) { + delegate.extendHandlerExceptionResolvers(exceptionResolvers); } } @Override public Validator getValidator() { - List candidates = new ArrayList(); + Validator selected = null; for (WebMvcConfigurer configurer : this.delegates) { Validator validator = configurer.getValidator(); if (validator != null) { - candidates.add(validator); + if (selected != null) { + throw new IllegalStateException("No unique Validator found: {" + + selected + ", " + validator + "}"); + } + selected = validator; } } - return selectSingleInstance(candidates, Validator.class); - } - - @Override - public void addCorsMappings(CorsRegistry registry) { - for (WebMvcConfigurer delegate : this.delegates) { - delegate.addCorsMappings(registry); - } - } - - private T selectSingleInstance(List instances, Class instanceType) { - if (instances.size() > 1) { - throw new IllegalStateException( - "Only one [" + instanceType + "] was expected but multiple instances were provided: " + instances); - } - else if (instances.size() == 1) { - return instances.get(0); - } - else { - return null; - } + return selected; } @Override public MessageCodesResolver getMessageCodesResolver() { - List candidates = new ArrayList(); + MessageCodesResolver selected = null; for (WebMvcConfigurer configurer : this.delegates) { MessageCodesResolver messageCodesResolver = configurer.getMessageCodesResolver(); if (messageCodesResolver != null) { - candidates.add(messageCodesResolver); + if (selected != null) { + throw new IllegalStateException("No unique MessageCodesResolver found: {" + + selected + ", " + messageCodesResolver + "}"); + } + selected = messageCodesResolver; } } - return selectSingleInstance(candidates, MessageCodesResolver.class); + return selected; } } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfigurationTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfigurationTests.java index 258b67a9a22..d0c58268453 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfigurationTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -17,7 +17,7 @@ package org.springframework.web.servlet.config.annotation; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Collections; import java.util.List; import org.junit.Before; @@ -89,10 +89,10 @@ public void setUp() { delegatingConfig = new DelegatingWebMvcConfiguration(); } + @Test public void requestMappingHandlerAdapter() throws Exception { - - delegatingConfig.setConfigurers(Arrays.asList(webMvcConfigurer)); + delegatingConfig.setConfigurers(Collections.singletonList(webMvcConfigurer)); RequestMappingHandlerAdapter adapter = delegatingConfig.requestMappingHandlerAdapter(); ConfigurableWebBindingInitializer initializer = (ConfigurableWebBindingInitializer) adapter.getWebBindingInitializer(); @@ -141,7 +141,7 @@ public void extendMessageConverters(List> converters) { public void getCustomValidator() { given(webMvcConfigurer.getValidator()).willReturn(new LocalValidatorFactoryBean()); - delegatingConfig.setConfigurers(Arrays.asList(webMvcConfigurer)); + delegatingConfig.setConfigurers(Collections.singletonList(webMvcConfigurer)); delegatingConfig.mvcValidator(); verify(webMvcConfigurer).getValidator(); @@ -151,7 +151,7 @@ public void getCustomValidator() { public void getCustomMessageCodesResolver() { given(webMvcConfigurer.getMessageCodesResolver()).willReturn(new DefaultMessageCodesResolver()); - delegatingConfig.setConfigurers(Arrays.asList(webMvcConfigurer)); + delegatingConfig.setConfigurers(Collections.singletonList(webMvcConfigurer)); delegatingConfig.getMessageCodesResolver(); verify(webMvcConfigurer).getMessageCodesResolver(); @@ -159,8 +159,7 @@ public void getCustomMessageCodesResolver() { @Test public void handlerExceptionResolver() throws Exception { - - delegatingConfig.setConfigurers(Arrays.asList(webMvcConfigurer)); + delegatingConfig.setConfigurers(Collections.singletonList(webMvcConfigurer)); delegatingConfig.handlerExceptionResolver(); verify(webMvcConfigurer).configureMessageConverters(converters.capture()); @@ -186,7 +185,7 @@ public void configureHandlerExceptionResolvers(List ex delegatingConfig.setConfigurers(configurers); HandlerExceptionResolverComposite composite = - (HandlerExceptionResolverComposite) delegatingConfig.handlerExceptionResolver(); + (HandlerExceptionResolverComposite) delegatingConfig.handlerExceptionResolver(); assertEquals("Only one custom converter is expected", 1, composite.getExceptionResolvers().size()); } @@ -200,9 +199,9 @@ public void configurePathMatch() throws Exception { @Override public void configurePathMatch(PathMatchConfigurer configurer) { configurer.setUseRegisteredSuffixPatternMatch(true) - .setUseTrailingSlashMatch(false) - .setUrlPathHelper(pathHelper) - .setPathMatcher(pathMatcher); + .setUseTrailingSlashMatch(false) + .setUrlPathHelper(pathHelper) + .setPathMatcher(pathMatcher); } }); delegatingConfig.setConfigurers(configurers); From 27f830f345023041e016abb66d4bfe39eb7940b4 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 18 Aug 2016 10:28:31 +0200 Subject: [PATCH 0114/1274] Polishing --- .../AbstractMessageBrokerConfiguration.java | 2 +- .../WebMvcConfigurationSupport.java | 71 +++++++++---------- 2 files changed, 33 insertions(+), 40 deletions(-) diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/config/AbstractMessageBrokerConfiguration.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/config/AbstractMessageBrokerConfiguration.java index c9c9be60201..4e08188c435 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/config/AbstractMessageBrokerConfiguration.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/config/AbstractMessageBrokerConfiguration.java @@ -438,7 +438,7 @@ else if (ClassUtils.isPresent("javax.validation.Validator", getClass().getClassL catch (Throwable ex) { throw new BeanInitializationException("Could not find default validator class", ex); } - validator = (Validator) BeanUtils.instantiate(clazz); + validator = (Validator) BeanUtils.instantiateClass(clazz); } else { validator = new Validator() { diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java index 6d5ab799626..10cc5bdac0e 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java @@ -17,6 +17,7 @@ package org.springframework.web.servlet.config.annotation; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -233,6 +234,7 @@ public ServletContext getServletContext() { return this.servletContext; } + /** * Return a {@link RequestMappingHandlerMapping} ordered at 0 for mapping * requests to annotated controllers. @@ -255,11 +257,13 @@ public RequestMappingHandlerMapping requestMappingHandlerMapping() { if (configurer.isUseTrailingSlashMatch() != null) { handlerMapping.setUseTrailingSlashMatch(configurer.isUseTrailingSlashMatch()); } - if (configurer.getPathMatcher() != null) { - handlerMapping.setPathMatcher(configurer.getPathMatcher()); + UrlPathHelper pathHelper = configurer.getUrlPathHelper(); + if (pathHelper != null) { + handlerMapping.setUrlPathHelper(pathHelper); } - if (configurer.getUrlPathHelper() != null) { - handlerMapping.setUrlPathHelper(configurer.getUrlPathHelper()); + PathMatcher pathMatcher = configurer.getPathMatcher(); + if (pathMatcher != null) { + handlerMapping.setPathMatcher(pathMatcher); } return handlerMapping; @@ -339,7 +343,7 @@ public ContentNegotiationManager mvcContentNegotiationManager() { } protected Map getDefaultMediaTypes() { - Map map = new HashMap(); + Map map = new HashMap(4); if (romePresent) { map.put("atom", MediaType.APPLICATION_ATOM_XML); map.put("rss", MediaType.valueOf("application/rss+xml")); @@ -487,18 +491,14 @@ public RequestMappingHandlerAdapter requestMappingHandlerAdapter() { adapter.setCustomReturnValueHandlers(getReturnValueHandlers()); if (jackson2Present) { - List requestBodyAdvices = new ArrayList(); - requestBodyAdvices.add(new JsonViewRequestBodyAdvice()); - adapter.setRequestBodyAdvice(requestBodyAdvices); - - List> responseBodyAdvices = new ArrayList>(); - responseBodyAdvices.add(new JsonViewResponseBodyAdvice()); - adapter.setResponseBodyAdvice(responseBodyAdvices); + adapter.setRequestBodyAdvice( + Collections.singletonList(new JsonViewRequestBodyAdvice())); + adapter.setResponseBodyAdvice( + Collections.>singletonList(new JsonViewResponseBodyAdvice())); } AsyncSupportConfigurer configurer = new AsyncSupportConfigurer(); configureAsyncSupport(configurer); - if (configurer.getTaskExecutor() != null) { adapter.setTaskExecutor(configurer.getTaskExecutor()); } @@ -531,6 +531,13 @@ protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer return initializer; } + /** + * Override this method to provide a custom {@link MessageCodesResolver}. + */ + protected MessageCodesResolver getMessageCodesResolver() { + return null; + } + /** * Override this method to configure asynchronous request processing options. * @see AsyncSupportConfigurer @@ -580,7 +587,7 @@ public Validator mvcValidator() { catch (LinkageError ex) { throw new BeanInitializationException("Could not load default validator class", ex); } - validator = (Validator) BeanUtils.instantiate(clazz); + validator = (Validator) BeanUtils.instantiateClass(clazz); } else { validator = new NoOpValidator(); @@ -589,6 +596,13 @@ public Validator mvcValidator() { return validator; } + /** + * Override this method to provide a custom {@link Validator}. + */ + protected Validator getValidator() { + return null; + } + /** * Return a global {@link PathMatcher} instance for path matching * patterns in {@link HandlerMapping}s. @@ -615,26 +629,8 @@ public PathMatcher mvcPathMatcher() { */ @Bean public UrlPathHelper mvcUrlPathHelper() { - if (getPathMatchConfigurer().getUrlPathHelper() != null) { - return getPathMatchConfigurer().getUrlPathHelper(); - } - else { - return new UrlPathHelper(); - } - } - - /** - * Override this method to provide a custom {@link Validator}. - */ - protected Validator getValidator() { - return null; - } - - /** - * Override this method to provide a custom {@link MessageCodesResolver}. - */ - protected MessageCodesResolver getMessageCodesResolver() { - return null; + UrlPathHelper pathHelper = getPathMatchConfigurer().getUrlPathHelper(); + return (pathHelper != null ? pathHelper : new UrlPathHelper()); } /** @@ -817,11 +813,9 @@ public SimpleControllerHandlerAdapter simpleControllerHandlerAdapter() { public HandlerExceptionResolver handlerExceptionResolver() { List exceptionResolvers = new ArrayList(); configureHandlerExceptionResolvers(exceptionResolvers); - if (exceptionResolvers.isEmpty()) { addDefaultHandlerExceptionResolvers(exceptionResolvers); } - extendHandlerExceptionResolvers(exceptionResolvers); HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite(); composite.setOrder(0); @@ -871,9 +865,8 @@ protected final void addDefaultHandlerExceptionResolvers(List> interceptors = new ArrayList>(); - interceptors.add(new JsonViewResponseBodyAdvice()); - exceptionHandlerResolver.setResponseBodyAdvice(interceptors); + exceptionHandlerResolver.setResponseBodyAdvice( + Collections.>singletonList(new JsonViewResponseBodyAdvice())); } exceptionHandlerResolver.setApplicationContext(this.applicationContext); exceptionHandlerResolver.afterPropertiesSet(); From 5222489a017c7a47214868efa0c8018ff91f8541 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 18 Aug 2016 12:31:21 +0200 Subject: [PATCH 0115/1274] Various @since tags (and varargs on setInterceptors) (cherry picked from commit aac0e63) --- .../web/util/UriComponentsBuilder.java | 3 + .../WebMvcConfigurationSupport.java | 83 +++++++++++-------- .../config/annotation/WebMvcConfigurer.java | 1 + .../handler/AbstractHandlerMapping.java | 4 +- 4 files changed, 54 insertions(+), 37 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java b/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java index 74d0105e1b1..44eb0a07e49 100644 --- a/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java +++ b/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java @@ -124,6 +124,7 @@ protected UriComponentsBuilder() { /** * Create a deep copy of the given UriComponentsBuilder. * @param other the other builder to copy from + * @since 4.1.3 */ protected UriComponentsBuilder(UriComponentsBuilder other) { this.scheme = other.scheme; @@ -603,6 +604,7 @@ public UriComponentsBuilder queryParam(String name, Object... values) { * Add the given query parameters. * @param params the params * @return this UriComponentsBuilder + * @since 4.0 */ public UriComponentsBuilder queryParams(MultiValueMap params) { if (params != null) { @@ -632,6 +634,7 @@ public UriComponentsBuilder replaceQueryParam(String name, Object... values) { * Set the query parameter values overriding all existing query values. * @param params the query parameter name * @return this UriComponentsBuilder + * @since 4.2 */ public UriComponentsBuilder replaceQueryParams(MultiValueMap params) { this.queryParams.clear(); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java index 10cc5bdac0e..1510a539f97 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java @@ -217,6 +217,10 @@ public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } + /** + * Return the associated Spring {@link ApplicationContext}. + * @since 4.2 + */ public ApplicationContext getApplicationContext() { return this.applicationContext; } @@ -230,6 +234,10 @@ public void setServletContext(ServletContext servletContext) { this.servletContext = servletContext; } + /** + * Return the associated {@link javax.servlet.ServletContext}. + * @since 4.2 + */ public ServletContext getServletContext() { return this.servletContext; } @@ -270,8 +278,9 @@ public RequestMappingHandlerMapping requestMappingHandlerMapping() { } /** - * Protected method for plugging in a custom sub-class of + * Protected method for plugging in a custom subclass of * {@link RequestMappingHandlerMapping}. + * @since 4.0 */ protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() { return new RequestMappingHandlerMapping(); @@ -322,6 +331,32 @@ protected PathMatchConfigurer getPathMatchConfigurer() { protected void configurePathMatch(PathMatchConfigurer configurer) { } + /** + * Return a global {@link PathMatcher} instance for path matching + * patterns in {@link HandlerMapping}s. + * This instance can be configured using the {@link PathMatchConfigurer} + * in {@link #configurePathMatch(PathMatchConfigurer)}. + * @since 4.1 + */ + @Bean + public PathMatcher mvcPathMatcher() { + PathMatcher pathMatcher = getPathMatchConfigurer().getPathMatcher(); + return (pathMatcher != null ? pathMatcher : new AntPathMatcher()); + } + + /** + * Return a global {@link UrlPathHelper} instance for path matching + * patterns in {@link HandlerMapping}s. + * This instance can be configured using the {@link PathMatchConfigurer} + * in {@link #configurePathMatch(PathMatchConfigurer)}. + * @since 4.1 + */ + @Bean + public UrlPathHelper mvcUrlPathHelper() { + UrlPathHelper pathHelper = getPathMatchConfigurer().getUrlPathHelper(); + return (pathHelper != null ? pathHelper : new UrlPathHelper()); + } + /** * Return a {@link ContentNegotiationManager} instance to use to determine * requested {@linkplain MediaType media types} in a given request. @@ -419,8 +454,7 @@ public HandlerMapping resourceHandlerMapping() { if (handlerMapping != null) { handlerMapping.setPathMatcher(mvcPathMatcher()); handlerMapping.setUrlPathHelper(mvcUrlPathHelper()); - handlerMapping.setInterceptors(new HandlerInterceptor[] { - new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider())}); + handlerMapping.setInterceptors(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider())); handlerMapping.setCorsConfigurations(getCorsConfigurations()); } else { @@ -436,6 +470,10 @@ public HandlerMapping resourceHandlerMapping() { protected void addResourceHandlers(ResourceHandlerRegistry registry) { } + /** + * A {@link ResourceUrlProvider} bean for use with the MVC dispatcher. + * @since 4.1 + */ @Bean public ResourceUrlProvider mvcResourceUrlProvider() { ResourceUrlProvider urlProvider = new ResourceUrlProvider(); @@ -512,8 +550,9 @@ public RequestMappingHandlerAdapter requestMappingHandlerAdapter() { } /** - * Protected method for plugging in a custom sub-class of + * Protected method for plugging in a custom subclass of * {@link RequestMappingHandlerAdapter}. + * @since 4.3 */ protected RequestMappingHandlerAdapter createRequestMappingHandlerAdapter() { return new RequestMappingHandlerAdapter(); @@ -603,41 +642,12 @@ protected Validator getValidator() { return null; } - /** - * Return a global {@link PathMatcher} instance for path matching - * patterns in {@link HandlerMapping}s. - * This instance can be configured using the {@link PathMatchConfigurer} - * in {@link #configurePathMatch(PathMatchConfigurer)}. - * @since 4.1 - */ - @Bean - public PathMatcher mvcPathMatcher() { - if (getPathMatchConfigurer().getPathMatcher() != null) { - return getPathMatchConfigurer().getPathMatcher(); - } - else { - return new AntPathMatcher(); - } - } - - /** - * Return a global {@link UrlPathHelper} instance for path matching - * patterns in {@link HandlerMapping}s. - * This instance can be configured using the {@link PathMatchConfigurer} - * in {@link #configurePathMatch(PathMatchConfigurer)}. - * @since 4.1 - */ - @Bean - public UrlPathHelper mvcUrlPathHelper() { - UrlPathHelper pathHelper = getPathMatchConfigurer().getUrlPathHelper(); - return (pathHelper != null ? pathHelper : new UrlPathHelper()); - } - /** * Provide access to the shared custom argument resolvers used by the * {@link RequestMappingHandlerAdapter} and the * {@link ExceptionHandlerExceptionResolver}. This method cannot be * overridden, use {@link #addArgumentResolvers(List)} instead. + * @since 4.3 */ protected final List getArgumentResolvers() { if (this.argumentResolvers == null) { @@ -666,6 +676,7 @@ protected void addArgumentResolvers(List argument * {@link RequestMappingHandlerAdapter} and the * {@link ExceptionHandlerExceptionResolver}. This method cannot be * overridden, use {@link #addReturnValueHandlers(List)} instead. + * @since 4.3 */ protected final List getReturnValueHandlers() { if (this.returnValueHandlers == null) { @@ -774,6 +785,7 @@ else if (gsonPresent) { /** * Return an instance of {@link CompositeUriComponentsContributor} for use with * {@link org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder}. + * @since 4.0 */ @Bean public CompositeUriComponentsContributor mvcUriComponentsContributor() { @@ -880,8 +892,9 @@ protected final void addDefaultHandlerExceptionResolvers(List Date: Fri, 19 Aug 2016 13:52:39 +0200 Subject: [PATCH 0116/1274] Document support for `@ManagedBean` Issue: SPR-14600 --- src/asciidoc/core-beans.adoc | 18 ++++++++++-------- src/asciidoc/testing.adoc | 1 + 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/asciidoc/core-beans.adoc b/src/asciidoc/core-beans.adoc index 8af1139edaf..b6f74bd7bd9 100644 --- a/src/asciidoc/core-beans.adoc +++ b/src/asciidoc/core-beans.adoc @@ -5856,9 +5856,10 @@ you should use the `@Named` annotation as follows: [[beans-named]] -=== @Named: a standard equivalent to the @Component annotation +=== @Named and @ManagedBean: standard equivalents to the @Component annotation -Instead of `@Component`, `@javax.inject.Named` may be used as follows: +Instead of `@Component`, `@javax.inject.Named` or `javax.annotation.ManagedBean` may be +used as follows: [source,java,indent=0] [subs="verbatim,quotes"] @@ -5866,7 +5867,7 @@ Instead of `@Component`, `@javax.inject.Named` may be used as follows: import javax.inject.Inject; import javax.inject.Named; - @Named("movieListener") + @Named("movieListener") // @ManagedBean("movieListener") could be used as well public class SimpleMovieLister { private MovieFinder movieFinder; @@ -5903,8 +5904,8 @@ It is very common to use `@Component` without specifying a name for the componen } ---- -When using `@Named`, it is possible to use component scanning in the exact same way -as when using Spring annotations: +When using `@Named` or `@ManagedBean`, it is possible to use component scanning in the +exact same way as when using Spring annotations: [source,java,indent=0] [subs="verbatim,quotes"] @@ -5918,8 +5919,9 @@ as when using Spring annotations: [NOTE] ==== -In contrast to `@Component`, the JSR-330 `@Named` annotation is not composable. -Please use Spring's stereotype model for building custom component annotations. +In contrast to `@Component`, the JSR-330 `@Named` and the JSR-250 `ManagedBean` +annotations are not composable. Please use Spring's stereotype model for building custom +component annotations. ==== @@ -5940,7 +5942,7 @@ features are not available as shown in the table below: | `@Inject` has no 'required' attribute; can be used with Java 8's `Optional` instead. | @Component -| @Named +| @Named / @ManagedBean | JSR-330 does not provide a composable model, just a way to identify named components. | @Scope("singleton") diff --git a/src/asciidoc/testing.adoc b/src/asciidoc/testing.adoc index aa9e7dc81a4..3efed67525b 100644 --- a/src/asciidoc/testing.adoc +++ b/src/asciidoc/testing.adoc @@ -852,6 +852,7 @@ tests and can be used anywhere in the Spring Framework. * `@Autowired` * `@Qualifier` * `@Resource` (javax.annotation) _if JSR-250 is present_ +* `@ManagedBean` (javax.annotation) _if JSR-250 is present_ * `@Inject` (javax.inject) _if JSR-330 is present_ * `@Named` (javax.inject) _if JSR-330 is present_ * `@PersistenceContext` (javax.persistence) _if JPA is present_ From 9044706796bb34fac66c1c3df3bd4569b624f344 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Fri, 19 Aug 2016 14:22:21 +0200 Subject: [PATCH 0117/1274] Remove reference to PayloadApplicationEvent Issue: SPR-14594 --- src/asciidoc/core-beans.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/asciidoc/core-beans.adoc b/src/asciidoc/core-beans.adoc index b6f74bd7bd9..3f525dc1338 100644 --- a/src/asciidoc/core-beans.adoc +++ b/src/asciidoc/core-beans.adoc @@ -8014,8 +8014,8 @@ Essentially, this is the standard __Observer__ design pattern. As of Spring 4.2, the event infrastructure has been significantly improved and offer an <> as well as the ability to publish any arbitrary event, that is an object that does not necessarily -extend from `ApplicationEvent`. When such an object is published we wrap it in a -`PayloadApplicationEvent` for you. +extend from `ApplicationEvent`. When such an object is published we wrap it in an +event for you. ==== Spring provides the following standard events: From 7135bc2dc24e5198f77e21c407c6882ce63d6bfb Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 23 Aug 2016 13:20:02 +0200 Subject: [PATCH 0118/1274] Reintroduced MessageMethodArgumentResolver default constructor Issue: SPR-14616 (cherry picked from commit c4fff6d) --- .../MessageMethodArgumentResolver.java | 25 ++++---- .../MessageMethodArgumentResolverTests.java | 57 +++++++++++++++---- 2 files changed, 62 insertions(+), 20 deletions(-) diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/MessageMethodArgumentResolver.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/MessageMethodArgumentResolver.java index 03b9ba06814..a388946ffb0 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/MessageMethodArgumentResolver.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/MessageMethodArgumentResolver.java @@ -26,7 +26,6 @@ import org.springframework.messaging.converter.SmartMessageConverter; import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver; import org.springframework.messaging.support.MessageBuilder; -import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; @@ -38,6 +37,7 @@ * * @author Rossen Stoyanchev * @author Stephane Nicoll + * @author Juergen Hoeller * @since 4.0 */ public class MessageMethodArgumentResolver implements HandlerMethodArgumentResolver { @@ -46,12 +46,18 @@ public class MessageMethodArgumentResolver implements HandlerMethodArgumentResol /** - * Create a new instance with the given {@link MessageConverter}. - * @param converter the MessageConverter to use (required) - * @since 4.1 + * Create a default resolver instance without message conversion. + */ + public MessageMethodArgumentResolver() { + this(null); + } + + /** + * Create a resolver instance with the given {@link MessageConverter}. + * @param converter the MessageConverter to use (may be {@code null}) + * @since 4.3 */ public MessageMethodArgumentResolver(MessageConverter converter) { - Assert.notNull(converter, "MessageConverter must not be null"); this.converter = converter; } @@ -63,7 +69,6 @@ public boolean supportsParameter(MethodParameter parameter) { @Override public Object resolveArgument(MethodParameter parameter, Message message) throws Exception { - Class targetMessageType = parameter.getParameterType(); Class targetPayloadType = getPayloadType(parameter); @@ -117,20 +122,20 @@ else if (payload instanceof String) { } private Object convertPayload(Message message, MethodParameter parameter, Class targetPayloadType) { - Object result; + Object result = null; if (this.converter instanceof SmartMessageConverter) { SmartMessageConverter smartConverter = (SmartMessageConverter) this.converter; result = smartConverter.fromMessage(message, targetPayloadType, parameter); } - else { + else if (this.converter != null) { result = this.converter.fromMessage(message, targetPayloadType); } if (result == null) { String actual = ClassUtils.getQualifiedName(targetPayloadType); String expected = ClassUtils.getQualifiedName(message.getPayload().getClass()); - throw new MessageConversionException(message, "No converter found to convert payload " + - "type [" + actual + "] to expected payload type [" + expected + "]."); + throw new MessageConversionException(message, "No converter found to convert payload type [" + + actual + "] to expected payload type [" + expected + "]"); } return result; } diff --git a/spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/MessageMethodArgumentResolverTests.java b/spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/MessageMethodArgumentResolverTests.java index 159207c18ab..792910f7b42 100644 --- a/spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/MessageMethodArgumentResolverTests.java +++ b/spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/MessageMethodArgumentResolverTests.java @@ -33,14 +33,13 @@ import org.springframework.messaging.support.MessageBuilder; import static org.junit.Assert.*; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; /** - * Unit tests for - * {@link org.springframework.messaging.handler.annotation.support.MessageMethodArgumentResolver}. + * Unit tests for {@link MessageMethodArgumentResolver}. * * @author Stephane Nicoll + * @author Juergen Hoeller */ public class MessageMethodArgumentResolverTests { @@ -56,10 +55,8 @@ public class MessageMethodArgumentResolverTests { @Before public void setup() throws Exception { - this.method = MessageMethodArgumentResolverTests.class.getDeclaredMethod("handle", - Message.class, Message.class, Message.class, Message.class, - ErrorMessage.class); + Message.class, Message.class, Message.class, Message.class, ErrorMessage.class); this.converter = mock(MessageConverter.class); this.resolver = new MessageMethodArgumentResolver(this.converter); @@ -85,7 +82,7 @@ public void resolveWithMatchingPayloadType() throws Exception { } @Test - public void resolveWithPayloadTypeSubClass() throws Exception { + public void resolveWithPayloadTypeSubclass() throws Exception { Message message = MessageBuilder.withPayload(123).build(); MethodParameter parameter = new MethodParameter(this.method, 2); @@ -155,7 +152,7 @@ public void resolveWithPayloadTypeOutOfBound() throws Exception { } @Test - public void resolveMessageSubClassMatch() throws Exception { + public void resolveMessageSubclassMatch() throws Exception { ErrorMessage message = new ErrorMessage(new UnsupportedOperationException()); MethodParameter parameter = new MethodParameter(this.method, 4); @@ -164,7 +161,7 @@ public void resolveMessageSubClassMatch() throws Exception { } @Test - public void resolveWithMessageSubClassAndPayloadWildcard() throws Exception { + public void resolveWithMessageSubclassAndPayloadWildcard() throws Exception { ErrorMessage message = new ErrorMessage(new UnsupportedOperationException()); MethodParameter parameter = new MethodParameter(this.method, 0); @@ -185,6 +182,46 @@ public void resolveWithWrongMessageType() throws Exception { assertSame(message, this.resolver.resolveArgument(parameter, message)); } + @Test + public void resolveWithPayloadTypeAsWildcardAndNoConverter() throws Exception { + this.resolver = new MessageMethodArgumentResolver(); + + Message message = MessageBuilder.withPayload("test").build(); + MethodParameter parameter = new MethodParameter(this.method, 0); + + assertTrue(this.resolver.supportsParameter(parameter)); + assertSame(message, this.resolver.resolveArgument(parameter, message)); + } + + @Test + public void resolveWithConversionNeededButNoConverter() throws Exception { + this.resolver = new MessageMethodArgumentResolver(); + + Message message = MessageBuilder.withPayload("test").build(); + MethodParameter parameter = new MethodParameter(this.method, 1); + + assertTrue(this.resolver.supportsParameter(parameter)); + thrown.expect(MessageConversionException.class); + thrown.expectMessage(Integer.class.getName()); + thrown.expectMessage(String.class.getName()); + this.resolver.resolveArgument(parameter, message); + } + + @Test + public void resolveWithConversionEmptyPayloadButNoConverter() throws Exception { + this.resolver = new MessageMethodArgumentResolver(); + + Message message = MessageBuilder.withPayload("").build(); + MethodParameter parameter = new MethodParameter(this.method, 1); + + assertTrue(this.resolver.supportsParameter(parameter)); + thrown.expect(MessageConversionException.class); + thrown.expectMessage("the payload is empty"); + thrown.expectMessage(Integer.class.getName()); + thrown.expectMessage(String.class.getName()); + this.resolver.resolveArgument(parameter, message); + } + @SuppressWarnings("unused") private void handle( From f735d12247e81bc36a36d5f5ca716a0d05e240f4 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 23 Aug 2016 13:21:40 +0200 Subject: [PATCH 0119/1274] UnsatisfiedDependencyException avoids duplicate nested exception message Issue: SPR-14607 (cherry picked from commit 93d2287) --- .../beans/factory/UnsatisfiedDependencyException.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/UnsatisfiedDependencyException.java b/spring-beans/src/main/java/org/springframework/beans/factory/UnsatisfiedDependencyException.java index 0403abfc7a9..666415dc642 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/UnsatisfiedDependencyException.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/UnsatisfiedDependencyException.java @@ -18,6 +18,7 @@ import org.springframework.beans.BeansException; import org.springframework.util.ClassUtils; +import org.springframework.util.StringUtils; /** * Exception thrown when a bean depends on other beans or simple properties @@ -46,7 +47,7 @@ public UnsatisfiedDependencyException( super(resourceDescription, beanName, "Unsatisfied dependency expressed through bean property '" + propertyName + "'" + - (msg != null ? ": " + msg : "")); + (StringUtils.hasLength(msg) ? ": " + msg : "")); } /** @@ -59,7 +60,7 @@ public UnsatisfiedDependencyException( public UnsatisfiedDependencyException( String resourceDescription, String beanName, String propertyName, BeansException ex) { - this(resourceDescription, beanName, propertyName, (ex != null ? ex.getMessage() : "")); + this(resourceDescription, beanName, propertyName, ""); initCause(ex); } @@ -74,7 +75,9 @@ public UnsatisfiedDependencyException( public UnsatisfiedDependencyException( String resourceDescription, String beanName, InjectionPoint injectionPoint, String msg) { - super(resourceDescription, beanName, "Unsatisfied dependency expressed through " + injectionPoint + ": " + msg); + super(resourceDescription, beanName, + "Unsatisfied dependency expressed through " + injectionPoint + + (StringUtils.hasLength(msg) ? ": " + msg : "")); this.injectionPoint = injectionPoint; } @@ -89,7 +92,7 @@ public UnsatisfiedDependencyException( public UnsatisfiedDependencyException( String resourceDescription, String beanName, InjectionPoint injectionPoint, BeansException ex) { - this(resourceDescription, beanName, injectionPoint, (ex != null ? ex.getMessage() : "")); + this(resourceDescription, beanName, injectionPoint, ""); initCause(ex); } From 1e8065d04053ab629f7a06adcb20fc4d6b63db29 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 24 Aug 2016 11:40:18 +0200 Subject: [PATCH 0120/1274] Unit test for empty Access-Control-Request-Headers (Chrome 52) Includes optimized method/header resolution in CorsConfiguration. Issue: SPR-14617 (cherry picked from commit d047174) --- .../org/springframework/http/HttpHeaders.java | 4 +- .../web/cors/CorsConfiguration.java | 93 +++++++---- .../web/cors/DefaultCorsProcessorTests.java | 154 +++++++++++------- .../handler/AbstractHandlerMapping.java | 13 +- 4 files changed, 161 insertions(+), 103 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/HttpHeaders.java b/spring-web/src/main/java/org/springframework/http/HttpHeaders.java index 0ba9a02aaf4..53e4881bf5f 100644 --- a/spring-web/src/main/java/org/springframework/http/HttpHeaders.java +++ b/spring-web/src/main/java/org/springframework/http/HttpHeaders.java @@ -546,8 +546,8 @@ public List getAccessControlRequestHeaders() { /** * Set the (new) value of the {@code Access-Control-Request-Method} request header. */ - public void setAccessControlRequestMethod(HttpMethod requestedMethod) { - set(ACCESS_CONTROL_REQUEST_METHOD, requestedMethod.name()); + public void setAccessControlRequestMethod(HttpMethod requestMethod) { + set(ACCESS_CONTROL_REQUEST_METHOD, requestMethod.name()); } /** diff --git a/spring-web/src/main/java/org/springframework/web/cors/CorsConfiguration.java b/spring-web/src/main/java/org/springframework/web/cors/CorsConfiguration.java index 016a355be49..95d1134d7c2 100644 --- a/spring-web/src/main/java/org/springframework/web/cors/CorsConfiguration.java +++ b/spring-web/src/main/java/org/springframework/web/cors/CorsConfiguration.java @@ -5,7 +5,7 @@ * 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 + * 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, @@ -21,6 +21,7 @@ import java.util.List; import org.springframework.http.HttpMethod; +import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -30,6 +31,7 @@ * * @author Sebastien Deleuze * @author Rossen Stoyanchev + * @author Juergen Hoeller * @author Sam Brannen * @since 4.2 * @see CORS W3C recommendation @@ -41,10 +43,22 @@ public class CorsConfiguration { */ public static final String ALL = "*"; + private static final List DEFAULT_METHODS; + + static { + List rawMethods = new ArrayList(2); + rawMethods.add(HttpMethod.GET); + rawMethods.add(HttpMethod.HEAD); + DEFAULT_METHODS = Collections.unmodifiableList(rawMethods); + } + + private List allowedOrigins; private List allowedMethods; + private List resolvedMethods = DEFAULT_METHODS; + private List allowedHeaders; private List exposedHeaders; @@ -67,6 +81,7 @@ public CorsConfiguration() { public CorsConfiguration(CorsConfiguration other) { this.allowedOrigins = other.allowedOrigins; this.allowedMethods = other.allowedMethods; + this.resolvedMethods = other.resolvedMethods; this.allowedHeaders = other.allowedHeaders; this.exposedHeaders = other.exposedHeaders; this.allowCredentials = other.allowCredentials; @@ -86,10 +101,10 @@ public CorsConfiguration combine(CorsConfiguration other) { return this; } CorsConfiguration config = new CorsConfiguration(this); - config.setAllowedOrigins(combine(this.getAllowedOrigins(), other.getAllowedOrigins())); - config.setAllowedMethods(combine(this.getAllowedMethods(), other.getAllowedMethods())); - config.setAllowedHeaders(combine(this.getAllowedHeaders(), other.getAllowedHeaders())); - config.setExposedHeaders(combine(this.getExposedHeaders(), other.getExposedHeaders())); + config.setAllowedOrigins(combine(getAllowedOrigins(), other.getAllowedOrigins())); + config.setAllowedMethods(combine(getAllowedMethods(), other.getAllowedMethods())); + config.setAllowedHeaders(combine(getAllowedHeaders(), other.getAllowedHeaders())); + config.setExposedHeaders(combine(getExposedHeaders(), other.getExposedHeaders())); Boolean allowCredentials = other.getAllowCredentials(); if (allowCredentials != null) { config.setAllowCredentials(allowCredentials); @@ -137,7 +152,7 @@ public List getAllowedOrigins() { */ public void addAllowedOrigin(String origin) { if (this.allowedOrigins == null) { - this.allowedOrigins = new ArrayList(); + this.allowedOrigins = new ArrayList(4); } this.allowedOrigins.add(origin); } @@ -146,16 +161,29 @@ public void addAllowedOrigin(String origin) { * Set the HTTP methods to allow, e.g. {@code "GET"}, {@code "POST"}, * {@code "PUT"}, etc. *

The special value {@code "*"} allows all methods. - *

If not set, only {@code "GET"} is allowed. + *

If not set, only {@code "GET"} and {@code "HEAD"} are allowed. *

By default this is not set. */ public void setAllowedMethods(List allowedMethods) { this.allowedMethods = (allowedMethods != null ? new ArrayList(allowedMethods) : null); + if (!CollectionUtils.isEmpty(allowedMethods)) { + this.resolvedMethods = new ArrayList(allowedMethods.size()); + for (String method : allowedMethods) { + if (ALL.equals(method)) { + this.resolvedMethods = null; + break; + } + this.resolvedMethods.add(HttpMethod.resolve(method)); + } + } + else { + this.resolvedMethods = DEFAULT_METHODS; + } } /** * Return the allowed HTTP methods, possibly {@code null} in which case - * only {@code "GET"} is allowed. + * only {@code "GET"} and {@code "HEAD"} allowed. * @see #addAllowedMethod(HttpMethod) * @see #addAllowedMethod(String) * @see #setAllowedMethods(List) @@ -179,9 +207,16 @@ public void addAllowedMethod(HttpMethod method) { public void addAllowedMethod(String method) { if (StringUtils.hasText(method)) { if (this.allowedMethods == null) { - this.allowedMethods = new ArrayList(); + this.allowedMethods = new ArrayList(4); + this.resolvedMethods = new ArrayList(4); } this.allowedMethods.add(method); + if (ALL.equals(method)) { + this.resolvedMethods = null; + } + else if (this.resolvedMethods != null) { + this.resolvedMethods.add(HttpMethod.resolve(method)); + } } } @@ -213,7 +248,7 @@ public List getAllowedHeaders() { */ public void addAllowedHeader(String allowedHeader) { if (this.allowedHeaders == null) { - this.allowedHeaders = new ArrayList(); + this.allowedHeaders = new ArrayList(4); } this.allowedHeaders.add(allowedHeader); } @@ -230,7 +265,7 @@ public void setExposedHeaders(List exposedHeaders) { if (exposedHeaders != null && exposedHeaders.contains(ALL)) { throw new IllegalArgumentException("'*' is not a valid exposed header value"); } - this.exposedHeaders = (exposedHeaders == null ? null : new ArrayList(exposedHeaders)); + this.exposedHeaders = (exposedHeaders != null ? new ArrayList(exposedHeaders) : null); } /** @@ -333,27 +368,10 @@ public List checkHttpMethod(HttpMethod requestMethod) { if (requestMethod == null) { return null; } - List allowedMethods = - (this.allowedMethods != null ? this.allowedMethods : new ArrayList()); - if (allowedMethods.contains(ALL)) { + if (this.resolvedMethods == null) { return Collections.singletonList(requestMethod); } - if (allowedMethods.isEmpty()) { - allowedMethods.add(HttpMethod.GET.name()); - allowedMethods.add(HttpMethod.HEAD.name()); - } - List result = new ArrayList(allowedMethods.size()); - boolean allowed = false; - for (String method : allowedMethods) { - if (requestMethod.matches(method)) { - allowed = true; - } - HttpMethod resolved = HttpMethod.resolve(method); - if (resolved != null) { - result.add(resolved); - } - } - return (allowed ? result : null); + return (this.resolvedMethods.contains(requestMethod) ? this.resolvedMethods : null); } /** @@ -376,14 +394,19 @@ public List checkHeaders(List requestHeaders) { } boolean allowAnyHeader = this.allowedHeaders.contains(ALL); - List result = new ArrayList(); + List result = new ArrayList(requestHeaders.size()); for (String requestHeader : requestHeaders) { if (StringUtils.hasText(requestHeader)) { requestHeader = requestHeader.trim(); - for (String allowedHeader : this.allowedHeaders) { - if (allowAnyHeader || requestHeader.equalsIgnoreCase(allowedHeader)) { - result.add(requestHeader); - break; + if (allowAnyHeader) { + result.add(requestHeader); + } + else { + for (String allowedHeader : this.allowedHeaders) { + if (requestHeader.equalsIgnoreCase(allowedHeader)) { + result.add(requestHeader); + break; + } } } } diff --git a/spring-web/src/test/java/org/springframework/web/cors/DefaultCorsProcessorTests.java b/spring-web/src/test/java/org/springframework/web/cors/DefaultCorsProcessorTests.java index 30a93e30856..ba666056ccf 100644 --- a/spring-web/src/test/java/org/springframework/web/cors/DefaultCorsProcessorTests.java +++ b/spring-web/src/test/java/org/springframework/web/cors/DefaultCorsProcessorTests.java @@ -16,6 +16,8 @@ package org.springframework.web.cors; +import javax.servlet.http.HttpServletResponse; + import org.junit.Before; import org.junit.Test; @@ -24,8 +26,6 @@ import org.springframework.mock.web.test.MockHttpServletRequest; import org.springframework.mock.web.test.MockHttpServletResponse; -import javax.servlet.http.HttpServletResponse; - import static org.junit.Assert.*; /** @@ -33,6 +33,7 @@ * * @author Sebastien Deleuze * @author Rossen Stoyanchev + * @author Juergen Hoeller */ public class DefaultCorsProcessorTests { @@ -56,22 +57,25 @@ public void setup() { this.processor = new DefaultCorsProcessor(); } + @Test public void actualRequestWithOriginHeader() throws Exception { this.request.setMethod(HttpMethod.GET.name()); this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); - this.processor.processRequest(this.conf, request, response); + + this.processor.processRequest(this.conf, this.request, this.response); assertFalse(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); - assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus()); + assertEquals(HttpServletResponse.SC_FORBIDDEN, this.response.getStatus()); } @Test public void actualRequestWithOriginHeaderAndNullConfig() throws Exception { this.request.setMethod(HttpMethod.GET.name()); this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); - this.processor.processRequest(null, request, response); + + this.processor.processRequest(null, this.request, this.response); assertFalse(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); - assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + assertEquals(HttpServletResponse.SC_OK, this.response.getStatus()); } @Test @@ -79,12 +83,13 @@ public void actualRequestWithOriginHeaderAndAllowedOrigin() throws Exception { this.request.setMethod(HttpMethod.GET.name()); this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); this.conf.addAllowedOrigin("*"); - this.processor.processRequest(this.conf, request, response); + + this.processor.processRequest(this.conf, this.request, this.response); assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); - assertEquals("*", response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); + assertEquals("*", this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); assertFalse(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_MAX_AGE)); assertFalse(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS)); - assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + assertEquals(HttpServletResponse.SC_OK, this.response.getStatus()); } @Test @@ -95,12 +100,13 @@ public void actualRequestCredentials() throws Exception { this.conf.addAllowedOrigin("http://domain2.com"); this.conf.addAllowedOrigin("http://domain3.com"); this.conf.setAllowCredentials(true); - this.processor.processRequest(this.conf, request, response); + + this.processor.processRequest(this.conf, this.request, this.response); assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); - assertEquals("http://domain2.com", response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); + assertEquals("http://domain2.com", this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)); - assertEquals("true", response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)); - assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + assertEquals("true", this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)); + assertEquals(HttpServletResponse.SC_OK, this.response.getStatus()); } @Test @@ -109,12 +115,13 @@ public void actualRequestCredentialsWithOriginWildcard() throws Exception { this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); this.conf.addAllowedOrigin("*"); this.conf.setAllowCredentials(true); - this.processor.processRequest(this.conf, request, response); + + this.processor.processRequest(this.conf, this.request, this.response); assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); - assertEquals("http://domain2.com", response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); + assertEquals("http://domain2.com", this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)); - assertEquals("true", response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)); - assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + assertEquals("true", this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)); + assertEquals(HttpServletResponse.SC_OK, this.response.getStatus()); } @Test @@ -122,9 +129,10 @@ public void actualRequestCaseInsensitiveOriginMatch() throws Exception { this.request.setMethod(HttpMethod.GET.name()); this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); this.conf.addAllowedOrigin("http://DOMAIN2.com"); - this.processor.processRequest(this.conf, request, response); + + this.processor.processRequest(this.conf, this.request, this.response); assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); - assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + assertEquals(HttpServletResponse.SC_OK, this.response.getStatus()); } @Test @@ -134,13 +142,14 @@ public void actualRequestExposedHeaders() throws Exception { this.conf.addExposedHeader("header1"); this.conf.addExposedHeader("header2"); this.conf.addAllowedOrigin("http://domain2.com"); - this.processor.processRequest(this.conf, request, response); + + this.processor.processRequest(this.conf, this.request, this.response); assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); - assertEquals("http://domain2.com", response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); + assertEquals("http://domain2.com", this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS)); assertTrue(this.response.getHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS).contains("header1")); assertTrue(this.response.getHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS).contains("header2")); - assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + assertEquals(HttpServletResponse.SC_OK, this.response.getStatus()); } @Test @@ -149,8 +158,9 @@ public void preflightRequestAllOriginsAllowed() throws Exception { this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET"); this.conf.addAllowedOrigin("*"); - this.processor.processRequest(this.conf, request, response); - assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + + this.processor.processRequest(this.conf, this.request, this.response); + assertEquals(HttpServletResponse.SC_OK, this.response.getStatus()); } @Test @@ -159,8 +169,9 @@ public void preflightRequestWrongAllowedMethod() throws Exception { this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "DELETE"); this.conf.addAllowedOrigin("*"); - this.processor.processRequest(this.conf, request, response); - assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus()); + + this.processor.processRequest(this.conf, this.request, this.response); + assertEquals(HttpServletResponse.SC_FORBIDDEN, this.response.getStatus()); } @Test @@ -169,18 +180,20 @@ public void preflightRequestMatchedAllowedMethod() throws Exception { this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET"); this.conf.addAllowedOrigin("*"); - this.processor.processRequest(this.conf, request, response); - assertEquals(HttpServletResponse.SC_OK, response.getStatus()); - assertEquals("GET,HEAD", response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS)); + + this.processor.processRequest(this.conf, this.request, this.response); + assertEquals(HttpServletResponse.SC_OK, this.response.getStatus()); + assertEquals("GET,HEAD", this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS)); } @Test public void preflightRequestTestWithOriginButWithoutOtherHeaders() throws Exception { this.request.setMethod(HttpMethod.OPTIONS.name()); this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); - this.processor.processRequest(this.conf, request, response); + + this.processor.processRequest(this.conf, this.request, this.response); assertFalse(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); - assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus()); + assertEquals(HttpServletResponse.SC_FORBIDDEN, this.response.getStatus()); } @Test @@ -188,112 +201,134 @@ public void preflightRequestWithoutRequestMethod() throws Exception { this.request.setMethod(HttpMethod.OPTIONS.name()); this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1"); - this.processor.processRequest(this.conf, request, response); + + this.processor.processRequest(this.conf, this.request, this.response); assertFalse(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); - assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus()); + assertEquals(HttpServletResponse.SC_FORBIDDEN, this.response.getStatus()); } @Test public void preflightRequestWithRequestAndMethodHeaderButNoConfig() throws Exception { this.request.setMethod(HttpMethod.OPTIONS.name()); this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); - this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1"); this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET"); - this.processor.processRequest(this.conf, request, response); + this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1"); + + this.processor.processRequest(this.conf, this.request, this.response); assertFalse(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); - assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus()); + assertEquals(HttpServletResponse.SC_FORBIDDEN, this.response.getStatus()); } @Test public void preflightRequestValidRequestAndConfig() throws Exception { this.request.setMethod(HttpMethod.OPTIONS.name()); this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); - this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1"); this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET"); + this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1"); this.conf.addAllowedOrigin("*"); this.conf.addAllowedMethod("GET"); this.conf.addAllowedMethod("PUT"); this.conf.addAllowedHeader("header1"); this.conf.addAllowedHeader("header2"); - this.processor.processRequest(this.conf, request, response); + + this.processor.processRequest(this.conf, this.request, this.response); assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); - assertEquals("*", response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); + assertEquals("*", this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS)); - assertEquals("GET,PUT", response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS)); + assertEquals("GET,PUT", this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS)); assertFalse(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_MAX_AGE)); - assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + assertEquals(HttpServletResponse.SC_OK, this.response.getStatus()); } @Test public void preflightRequestCredentials() throws Exception { this.request.setMethod(HttpMethod.OPTIONS.name()); this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); - this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1"); this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET"); + this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1"); this.conf.addAllowedOrigin("http://domain1.com"); this.conf.addAllowedOrigin("http://domain2.com"); this.conf.addAllowedOrigin("http://domain3.com"); this.conf.addAllowedHeader("Header1"); this.conf.setAllowCredentials(true); - this.processor.processRequest(this.conf, request, response); + + this.processor.processRequest(this.conf, this.request, this.response); assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); - assertEquals("http://domain2.com", response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); + assertEquals("http://domain2.com", this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)); - assertEquals("true", response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)); - assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + assertEquals("true", this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)); + assertEquals(HttpServletResponse.SC_OK, this.response.getStatus()); } @Test public void preflightRequestCredentialsWithOriginWildcard() throws Exception { this.request.setMethod(HttpMethod.OPTIONS.name()); this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); - this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1"); this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET"); + this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1"); this.conf.addAllowedOrigin("http://domain1.com"); this.conf.addAllowedOrigin("*"); this.conf.addAllowedOrigin("http://domain3.com"); this.conf.addAllowedHeader("Header1"); this.conf.setAllowCredentials(true); - this.processor.processRequest(this.conf, request, response); + + this.processor.processRequest(this.conf, this.request, this.response); assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); - assertEquals("http://domain2.com", response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); - assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + assertEquals("http://domain2.com", this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); + assertEquals(HttpServletResponse.SC_OK, this.response.getStatus()); } @Test public void preflightRequestAllowedHeaders() throws Exception { this.request.setMethod(HttpMethod.OPTIONS.name()); this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); - this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1, Header2"); this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET"); + this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1, Header2"); this.conf.addAllowedHeader("Header1"); this.conf.addAllowedHeader("Header2"); this.conf.addAllowedHeader("Header3"); this.conf.addAllowedOrigin("http://domain2.com"); - this.processor.processRequest(this.conf, request, response); + + this.processor.processRequest(this.conf, this.request, this.response); assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS)); assertTrue(this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS).contains("Header1")); assertTrue(this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS).contains("Header2")); assertFalse(this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS).contains("Header3")); - assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + assertEquals(HttpServletResponse.SC_OK, this.response.getStatus()); } @Test public void preflightRequestAllowsAllHeaders() throws Exception { this.request.setMethod(HttpMethod.OPTIONS.name()); this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); - this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1, Header2"); this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET"); + this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1, Header2"); this.conf.addAllowedHeader("*"); this.conf.addAllowedOrigin("http://domain2.com"); - this.processor.processRequest(this.conf, request, response); + + this.processor.processRequest(this.conf, this.request, this.response); assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS)); assertTrue(this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS).contains("Header1")); assertTrue(this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS).contains("Header2")); assertFalse(this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS).contains("*")); - assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + assertEquals(HttpServletResponse.SC_OK, this.response.getStatus()); + } + + @Test + public void preflightRequestWithEmptyHeaders() throws Exception { + this.request.setMethod(HttpMethod.OPTIONS.name()); + this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); + this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET"); + this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, ""); + this.conf.addAllowedHeader("*"); + this.conf.addAllowedOrigin("http://domain2.com"); + + this.processor.processRequest(this.conf, this.request, this.response); + assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); + assertFalse(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS)); + assertEquals(HttpServletResponse.SC_OK, this.response.getStatus()); } @Test @@ -302,9 +337,10 @@ public void preflightRequestWithNullConfig() throws Exception { this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET"); this.conf.addAllowedOrigin("*"); - this.processor.processRequest(null, request, response); + + this.processor.processRequest(null, this.request, this.response); assertFalse(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); - assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus()); + assertEquals(HttpServletResponse.SC_FORBIDDEN, this.response.getStatus()); } -} \ No newline at end of file +} diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMapping.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMapping.java index 5ef9d4cdeff..1117cb6bcf6 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMapping.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMapping.java @@ -64,8 +64,7 @@ * @see #setInterceptors * @see org.springframework.web.servlet.HandlerInterceptor */ -public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport - implements HandlerMapping, Ordered { +public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered { private int order = Integer.MAX_VALUE; // default: same as non-Ordered @@ -236,6 +235,7 @@ public Map getCorsConfigurations() { return this.corsConfigSource.getCorsConfigurations(); } + /** * Initializes the interceptors. * @see #extendInterceptors(java.util.List) @@ -339,6 +339,7 @@ protected final MappedInterceptor[] getMappedInterceptors() { return (count > 0 ? mappedInterceptors.toArray(new MappedInterceptor[count]) : null); } + /** * Look up a handler for the given request, falling back to the default * handler if no specific one is found. @@ -480,9 +481,7 @@ public PreFlightHandler(CorsConfiguration config) { } @Override - public void handleRequest(HttpServletRequest request, HttpServletResponse response) - throws IOException { - + public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws IOException { corsProcessor.processRequest(this.config, request, response); } @@ -502,8 +501,8 @@ public CorsInterceptor(CorsConfiguration config) { } @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, - Object handler) throws Exception { + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) + throws Exception { return corsProcessor.processRequest(this.config, request, response); } From 184285ab277bd0836b4e5336754ed459339d88c1 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 24 Aug 2016 13:01:54 +0200 Subject: [PATCH 0121/1274] Polishing --- .../beans/factory/config/NamedBeanHolder.java | 9 ++++++++- .../org/springframework/web/cors/CorsConfiguration.java | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/NamedBeanHolder.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/NamedBeanHolder.java index b950aef98db..04e5e39a372 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/NamedBeanHolder.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/NamedBeanHolder.java @@ -17,6 +17,7 @@ package org.springframework.beans.factory.config; import org.springframework.beans.factory.NamedBean; +import org.springframework.util.Assert; /** * A simple holder for a given bean name plus bean instance. @@ -34,20 +35,26 @@ public class NamedBeanHolder implements NamedBean { /** * Create a new holder for the given bean name plus instance. + * @param beanName the name of the bean + * @param beanInstance the corresponding bean instance */ public NamedBeanHolder(String beanName, T beanInstance) { + Assert.notNull(beanName, "Bean name must not be null"); this.beanName = beanName; this.beanInstance = beanInstance; } + /** + * Return the name of the bean (never {@code null}). + */ @Override public String getBeanName() { return this.beanName; } /** - * Return the corresponding bean instance. + * Return the corresponding bean instance (can be {@code null}). */ public T getBeanInstance() { return this.beanInstance; diff --git a/spring-web/src/main/java/org/springframework/web/cors/CorsConfiguration.java b/spring-web/src/main/java/org/springframework/web/cors/CorsConfiguration.java index 95d1134d7c2..d774012e517 100644 --- a/spring-web/src/main/java/org/springframework/web/cors/CorsConfiguration.java +++ b/spring-web/src/main/java/org/springframework/web/cors/CorsConfiguration.java @@ -286,7 +286,7 @@ public void addExposedHeader(String exposedHeader) { throw new IllegalArgumentException("'*' is not a valid exposed header value"); } if (this.exposedHeaders == null) { - this.exposedHeaders = new ArrayList(); + this.exposedHeaders = new ArrayList(4); } this.exposedHeaders.add(exposedHeader); } From 6d86437369c04435237e446d3aeddd79a6e0b494 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 24 Aug 2016 14:31:02 +0200 Subject: [PATCH 0122/1274] TransactionAspectSupport stores given PlatformTransactionManager instance as strong reference Issue: SPR-14609 (cherry picked from commit 951ac5e) --- .../interceptor/TransactionAspectSupport.java | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java b/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java index 7f196227ab9..ff8faac6f18 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java +++ b/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java @@ -128,6 +128,8 @@ public static TransactionStatus currentTransactionStatus() throws NoTransactionE private String transactionManagerBeanName; + private PlatformTransactionManager transactionManager; + private TransactionAttributeSource transactionAttributeSource; private BeanFactory beanFactory; @@ -158,16 +160,14 @@ protected final String getTransactionManagerBeanName() { * @see #setTransactionManagerBeanName */ public void setTransactionManager(PlatformTransactionManager transactionManager) { - if (transactionManager != null) { - this.transactionManagerCache.put(DEFAULT_TRANSACTION_MANAGER_KEY, transactionManager); - } + this.transactionManager = transactionManager; } /** * Return the default transaction manager, or {@code null} if unknown. */ public PlatformTransactionManager getTransactionManager() { - return this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY); + return this.transactionManager; } /** @@ -240,11 +240,11 @@ protected final BeanFactory getBeanFactory() { */ @Override public void afterPropertiesSet() { - if (getTransactionManager() == null && this.beanFactory == null) { + if (getTransactionManager() == null && getBeanFactory() == null) { throw new IllegalStateException( "Setting the property 'transactionManager' or running in a BeanFactory is required"); } - if (this.transactionAttributeSource == null) { + if (getTransactionAttributeSource() == null) { throw new IllegalStateException( "Either 'transactionAttributeSource' or 'transactionAttributes' is required: " + "If there are no transactional methods, then don't use a transaction aspect."); @@ -363,9 +363,12 @@ else if (StringUtils.hasText(this.transactionManagerBeanName)) { else { PlatformTransactionManager defaultTransactionManager = getTransactionManager(); if (defaultTransactionManager == null) { - defaultTransactionManager = this.beanFactory.getBean(PlatformTransactionManager.class); - this.transactionManagerCache.putIfAbsent( - DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager); + defaultTransactionManager = this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY); + if (defaultTransactionManager == null) { + defaultTransactionManager = this.beanFactory.getBean(PlatformTransactionManager.class); + this.transactionManagerCache.putIfAbsent( + DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager); + } } return defaultTransactionManager; } @@ -567,6 +570,7 @@ protected final class TransactionInfo { public TransactionInfo(PlatformTransactionManager transactionManager, TransactionAttribute transactionAttribute, String joinpointIdentification) { + this.transactionManager = transactionManager; this.transactionAttribute = transactionAttribute; this.joinpointIdentification = joinpointIdentification; From ab686732d0702daf36b52d9e2c4e23b77511c7fa Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 24 Aug 2016 15:05:43 +0200 Subject: [PATCH 0123/1274] Refined exception message Issue: SPR-14609 --- .../transaction/interceptor/TransactionAspectSupport.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java b/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java index ff8faac6f18..f0c2b847833 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java +++ b/spring-tx/src/main/java/org/springframework/transaction/interceptor/TransactionAspectSupport.java @@ -240,9 +240,10 @@ protected final BeanFactory getBeanFactory() { */ @Override public void afterPropertiesSet() { - if (getTransactionManager() == null && getBeanFactory() == null) { + if (getTransactionManager() == null && this.beanFactory == null) { throw new IllegalStateException( - "Setting the property 'transactionManager' or running in a BeanFactory is required"); + "Set the 'transactionManager' property or make sure to run within a BeanFactory " + + "containing a PlatformTransactionManager bean!"); } if (getTransactionAttributeSource() == null) { throw new IllegalStateException( From 0735e9ba98821f9c204ab0005aab6d9ac24dd740 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 24 Aug 2016 20:12:01 +0200 Subject: [PATCH 0124/1274] Avoid unnecessary String concatenation in StompSubProtocolHandler Issue: SPR-14624 (cherry picked from commit 56b197b) --- .../messaging/StompSubProtocolHandler.java | 56 +++++++++++-------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/messaging/StompSubProtocolHandler.java b/spring-websocket/src/main/java/org/springframework/web/socket/messaging/StompSubProtocolHandler.java index ead37f43f98..a56a4e06281 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/messaging/StompSubProtocolHandler.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/messaging/StompSubProtocolHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -296,8 +296,10 @@ else if (StompCommand.UNSUBSCRIBE.equals(headerAccessor.getCommand())) { } } catch (Throwable ex) { - logger.error("Failed to send client message to application via MessageChannel" + - " in session " + session.getId() + ". Sending STOMP ERROR to client.", ex); + if (logger.isErrorEnabled()) { + logger.error("Failed to send client message to application via MessageChannel" + + " in session " + session.getId() + ". Sending STOMP ERROR to client.", ex); + } handleError(session, ex, message); } } @@ -316,7 +318,7 @@ private void handleError(WebSocketSession session, Throwable ex, Message } StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class); - Assert.notNull(accessor, "Expected STOMP headers"); + Assert.state(accessor != null, "Expected STOMP headers"); sendToClient(session, accessor, message.getPayload()); } @@ -365,7 +367,9 @@ private void publishEvent(ApplicationEvent event) { this.eventPublisher.publishEvent(event); } catch (Throwable ex) { - logger.error("Error publishing " + event, ex); + if (logger.isErrorEnabled()) { + logger.error("Error publishing " + event, ex); + } } } @@ -376,27 +380,29 @@ private void publishEvent(ApplicationEvent event) { @SuppressWarnings("unchecked") public void handleMessageToClient(WebSocketSession session, Message message) { if (!(message.getPayload() instanceof byte[])) { - logger.error("Expected byte[] payload. Ignoring " + message + "."); + if (logger.isErrorEnabled()) { + logger.error("Expected byte[] payload. Ignoring " + message + "."); + } return; } - StompHeaderAccessor stompAccessor = getStompHeaderAccessor(message); - StompCommand command = stompAccessor.getCommand(); + StompHeaderAccessor accessor = getStompHeaderAccessor(message); + StompCommand command = accessor.getCommand(); if (StompCommand.MESSAGE.equals(command)) { - if (stompAccessor.getSubscriptionId() == null) { + if (accessor.getSubscriptionId() == null && logger.isWarnEnabled()) { logger.warn("No STOMP \"subscription\" header in " + message); } - String origDestination = stompAccessor.getFirstNativeHeader(SimpMessageHeaderAccessor.ORIGINAL_DESTINATION); + String origDestination = accessor.getFirstNativeHeader(SimpMessageHeaderAccessor.ORIGINAL_DESTINATION); if (origDestination != null) { - stompAccessor = toMutableAccessor(stompAccessor, message); - stompAccessor.removeNativeHeader(SimpMessageHeaderAccessor.ORIGINAL_DESTINATION); - stompAccessor.setDestination(origDestination); + accessor = toMutableAccessor(accessor, message); + accessor.removeNativeHeader(SimpMessageHeaderAccessor.ORIGINAL_DESTINATION); + accessor.setDestination(origDestination); } } else if (StompCommand.CONNECTED.equals(command)) { this.stats.incrementConnectedCount(); - stompAccessor = afterStompSessionConnected(message, stompAccessor, session); + accessor = afterStompSessionConnected(message, accessor, session); if (this.eventPublisher != null && StompCommand.CONNECTED.equals(command)) { try { SimpAttributes simpAttributes = new SimpAttributes(session.getId(), session.getAttributes()); @@ -411,25 +417,21 @@ else if (StompCommand.CONNECTED.equals(command)) { } byte[] payload = (byte[]) message.getPayload(); - if (StompCommand.ERROR.equals(command) && getErrorHandler() != null) { Message errorMessage = getErrorHandler().handleErrorMessageToClient((Message) message); - stompAccessor = MessageHeaderAccessor.getAccessor(errorMessage, StompHeaderAccessor.class); - Assert.notNull(stompAccessor, "Expected STOMP headers"); + accessor = MessageHeaderAccessor.getAccessor(errorMessage, StompHeaderAccessor.class); + Assert.state(accessor != null, "Expected STOMP headers"); payload = errorMessage.getPayload(); } - - sendToClient(session, stompAccessor, payload); + sendToClient(session, accessor, payload); } private void sendToClient(WebSocketSession session, StompHeaderAccessor stompAccessor, byte[] payload) { StompCommand command = stompAccessor.getCommand(); try { byte[] bytes = this.stompEncoder.encode(stompAccessor.getMessageHeaders(), payload); - boolean useBinary = (payload.length > 0 && !(session instanceof SockJsSession) && MimeTypeUtils.APPLICATION_OCTET_STREAM.isCompatibleWith(stompAccessor.getContentType())); - if (useBinary) { session.sendMessage(new BinaryMessage(bytes)); } @@ -443,7 +445,9 @@ private void sendToClient(WebSocketSession session, StompHeaderAccessor stompAcc } catch (Throwable ex) { // Could be part of normal workflow (e.g. browser tab closed) - logger.debug("Failed to send WebSocket message to client in session " + session.getId(), ex); + if (logger.isDebugEnabled()) { + logger.debug("Failed to send WebSocket message to client in session " + session.getId(), ex); + } command = StompCommand.ERROR; } finally { @@ -500,9 +504,13 @@ else if (stompAccessor.getCommand() == null || StompCommand.SEND.equals(stompAcc private StompHeaderAccessor convertConnectAcktoStompConnected(StompHeaderAccessor connectAckHeaders) { String name = StompHeaderAccessor.CONNECT_MESSAGE_HEADER; Message message = (Message) connectAckHeaders.getHeader(name); - Assert.notNull(message, "Original STOMP CONNECT not found in " + connectAckHeaders); + if (message == null) { + throw new IllegalStateException("Original STOMP CONNECT not found in " + connectAckHeaders); + } + StompHeaderAccessor connectHeaders = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class); StompHeaderAccessor connectedHeaders = StompHeaderAccessor.create(StompCommand.CONNECTED); + Set acceptVersions = connectHeaders.getAcceptVersion(); if (acceptVersions.contains("1.2")) { connectedHeaders.setVersion("1.2"); @@ -513,6 +521,7 @@ else if (acceptVersions.contains("1.1")) { else if (!acceptVersions.isEmpty()) { throw new IllegalArgumentException("Unsupported STOMP version '" + acceptVersions + "'"); } + long[] heartbeat = (long[]) connectAckHeaders.getHeader(SimpMessageHeaderAccessor.HEART_BEAT_HEADER); if (heartbeat != null) { connectedHeaders.setHeartbeat(heartbeat[0], heartbeat[1]); @@ -520,6 +529,7 @@ else if (!acceptVersions.isEmpty()) { else { connectedHeaders.setHeartbeat(0, 0); } + return connectedHeaders; } From c2feedb7a2cf8d3b96ad0501b72df642c82f3aa0 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 24 Aug 2016 21:04:01 +0200 Subject: [PATCH 0125/1274] Revised assertions in StompHeaderAccessor Issue: SPR-14625 (cherry picked from commit f3f691c) --- .../simp/stomp/StompHeaderAccessor.java | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompHeaderAccessor.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompHeaderAccessor.java index 76971b265f6..8e38664934d 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompHeaderAccessor.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompHeaderAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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.messaging.simp.SimpMessageHeaderAccessor; import org.springframework.messaging.simp.SimpMessageType; import org.springframework.messaging.support.MessageHeaderAccessor; -import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; import org.springframework.util.MimeType; import org.springframework.util.MimeTypeUtils; import org.springframework.util.StringUtils; @@ -185,7 +185,9 @@ Map> getNativeHeaders() { } public StompCommand updateStompCommandAsClientMessage() { - Assert.state(SimpMessageType.MESSAGE.equals(getMessageType()), "Unexpected message type " + getMessage()); + if (getMessageType() != SimpMessageType.MESSAGE) { + throw new IllegalStateException("Unexpected message type " + getMessageType()); + } if (getCommand() == null) { setHeader(COMMAND_HEADER, StompCommand.SEND); } @@ -196,7 +198,9 @@ else if (!getCommand().equals(StompCommand.SEND)) { } public void updateStompCommandAsServerMessage() { - Assert.state(SimpMessageType.MESSAGE.equals(getMessageType()), "Unexpected message type " + getMessage()); + if (getMessageType() != SimpMessageType.MESSAGE) { + throw new IllegalStateException("Unexpected message type " + getMessageType()); + } StompCommand command = getCommand(); if ((command == null) || StompCommand.SEND.equals(command)) { setHeader(COMMAND_HEADER, StompCommand.MESSAGE); @@ -434,7 +438,10 @@ private String appendSession() { } private String appendPayload(Object payload) { - Assert.isInstanceOf(byte[].class, payload); + if (payload.getClass() != byte[].class) { + throw new IllegalStateException( + "Expected byte array payload but got: " + ClassUtils.getQualifiedName(payload.getClass())); + } byte[] bytes = (byte[]) payload; String contentType = (getContentType() != null ? " " + getContentType().toString() : ""); if (bytes.length == 0 || getContentType() == null || !isReadableContentType()) { From 74bf659c566508545981e97a4ce5cbba7984bdbb Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 24 Aug 2016 21:23:57 +0200 Subject: [PATCH 0126/1274] GenericApplicationContext picks up ClassLoader from custom ResourceLoader Issue: SPR-14626 (cherry picked from commit 405e74b) --- .../support/GenericApplicationContext.java | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/spring-context/src/main/java/org/springframework/context/support/GenericApplicationContext.java b/spring-context/src/main/java/org/springframework/context/support/GenericApplicationContext.java index aff0017ef91..754ceafe1d4 100644 --- a/spring-context/src/main/java/org/springframework/context/support/GenericApplicationContext.java +++ b/spring-context/src/main/java/org/springframework/context/support/GenericApplicationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -91,6 +91,8 @@ public class GenericApplicationContext extends AbstractApplicationContext implem private ResourceLoader resourceLoader; + private boolean customClassLoader = false; + private final AtomicBoolean refreshed = new AtomicBoolean(); @@ -198,6 +200,10 @@ public void setResourceLoader(ResourceLoader resourceLoader) { } + //--------------------------------------------------------------------- + // ResourceLoader / ResourcePatternResolver override if necessary + //--------------------------------------------------------------------- + /** * This implementation delegates to this context's ResourceLoader if set, * falling back to the default superclass behavior else. @@ -225,6 +231,20 @@ public Resource[] getResources(String locationPattern) throws IOException { return super.getResources(locationPattern); } + @Override + public void setClassLoader(ClassLoader classLoader) { + super.setClassLoader(classLoader); + this.customClassLoader = true; + } + + @Override + public ClassLoader getClassLoader() { + if (this.resourceLoader != null && !this.customClassLoader) { + return this.resourceLoader.getClassLoader(); + } + return super.getClassLoader(); + } + //--------------------------------------------------------------------- // Implementations of AbstractApplicationContext's template methods From a7849b2861eea7af18da9b0023bde5cfce2321da Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 24 Aug 2016 22:56:47 +0200 Subject: [PATCH 0127/1274] DefaultListableBeanFactory does not trigger early candidate creation ahead of primary bean selection Issue: SPR-14611 (cherry picked from commit c4fcdb6) --- .../support/DefaultListableBeanFactory.java | 182 +++++++++++------- .../DefaultListableBeanFactoryTests.java | 20 +- ...wiredAnnotationBeanPostProcessorTests.java | 5 +- .../core/annotation/OrderUtils.java | 8 +- 4 files changed, 124 insertions(+), 91 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index 724a9ab829f..fad8a3956d0 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -986,38 +986,47 @@ public NamedBeanHolder resolveNamedBean(Class requiredType) throws Bea return null; } + @SuppressWarnings("unchecked") private NamedBeanHolder resolveNamedBean(Class requiredType, Object... args) throws BeansException { Assert.notNull(requiredType, "Required type must not be null"); - String[] beanNames = getBeanNamesForType(requiredType); + String[] candidateNames = getBeanNamesForType(requiredType); - if (beanNames.length > 1) { - ArrayList autowireCandidates = new ArrayList(); - for (String beanName : beanNames) { + if (candidateNames.length > 1) { + List autowireCandidates = new ArrayList(candidateNames.length); + for (String beanName : candidateNames) { if (!containsBeanDefinition(beanName) || getBeanDefinition(beanName).isAutowireCandidate()) { autowireCandidates.add(beanName); } } if (!autowireCandidates.isEmpty()) { - beanNames = autowireCandidates.toArray(new String[autowireCandidates.size()]); + candidateNames = autowireCandidates.toArray(new String[autowireCandidates.size()]); } } - if (beanNames.length == 1) { - String beanName = beanNames[0]; + if (candidateNames.length == 1) { + String beanName = candidateNames[0]; return new NamedBeanHolder(beanName, getBean(beanName, requiredType, args)); } - else if (beanNames.length > 1) { - Map candidates = new LinkedHashMap(); - for (String beanName : beanNames) { - candidates.put(beanName, getBean(beanName, requiredType, args)); + else if (candidateNames.length > 1) { + Map candidates = new LinkedHashMap(candidateNames.length); + for (String candidateName : candidateNames) { + if (containsSingleton(candidateName)) { + candidates.put(candidateName, getBean(candidateName, requiredType, args)); + } + else { + candidates.put(candidateName, getType(candidateName)); + } } - String primaryCandidate = determinePrimaryCandidate(candidates, requiredType); - if (primaryCandidate != null) { - return new NamedBeanHolder(primaryCandidate, getBean(primaryCandidate, requiredType, args)); + String candidateName = determinePrimaryCandidate(candidates, requiredType); + if (candidateName == null) { + candidateName = determineHighestPriorityCandidate(candidates, requiredType); } - String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType); - if (priorityCandidate != null) { - return new NamedBeanHolder(priorityCandidate, getBean(priorityCandidate, requiredType, args)); + if (candidateName != null) { + Object beanInstance = candidates.get(candidateName); + if (beanInstance instanceof Class) { + beanInstance = getBean(candidateName, requiredType, args); + } + return new NamedBeanHolder(candidateName, (T) beanInstance); } throw new NoUniqueBeanDefinitionException(requiredType, candidates.keySet()); } @@ -1086,9 +1095,13 @@ public Object doResolveDependency(DependencyDescriptor descriptor, String beanNa } return null; } + + String autowiredBeanName; + Object instanceCandidate; + if (matchingBeans.size() > 1) { - String primaryBeanName = determineAutowireCandidate(matchingBeans, descriptor); - if (primaryBeanName == null) { + autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor); + if (autowiredBeanName == null) { if (descriptor.isRequired() || !indicatesMultipleBeans(type)) { return descriptor.resolveNotUnique(type, matchingBeans); } @@ -1099,17 +1112,20 @@ public Object doResolveDependency(DependencyDescriptor descriptor, String beanNa return null; } } - if (autowiredBeanNames != null) { - autowiredBeanNames.add(primaryBeanName); - } - return matchingBeans.get(primaryBeanName); + instanceCandidate = matchingBeans.get(autowiredBeanName); } - // We have exactly one match. - Map.Entry entry = matchingBeans.entrySet().iterator().next(); + else { + // We have exactly one match. + Map.Entry entry = matchingBeans.entrySet().iterator().next(); + autowiredBeanName = entry.getKey(); + instanceCandidate = entry.getValue(); + } + if (autowiredBeanNames != null) { - autowiredBeanNames.add(entry.getKey()); + autowiredBeanNames.add(autowiredBeanName); } - return entry.getValue(); + return (instanceCandidate instanceof Class ? + descriptor.resolveCandidate(autowiredBeanName, type, this) : instanceCandidate); } finally { ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint); @@ -1122,9 +1138,8 @@ private Object resolveMultipleBeans(DependencyDescriptor descriptor, String bean Class type = descriptor.getDependencyType(); if (type.isArray()) { Class componentType = type.getComponentType(); - DependencyDescriptor targetDesc = new DependencyDescriptor(descriptor); - targetDesc.increaseNestingLevel(); - Map matchingBeans = findAutowireCandidates(beanName, componentType, targetDesc); + Map matchingBeans = findAutowireCandidates(beanName, componentType, + new MultiElementDependencyDescriptor(descriptor)); if (matchingBeans.isEmpty()) { return null; } @@ -1143,9 +1158,8 @@ else if (Collection.class.isAssignableFrom(type) && type.isInterface()) { if (elementType == null) { return null; } - DependencyDescriptor targetDesc = new DependencyDescriptor(descriptor); - targetDesc.increaseNestingLevel(); - Map matchingBeans = findAutowireCandidates(beanName, elementType, targetDesc); + Map matchingBeans = findAutowireCandidates(beanName, elementType, + new MultiElementDependencyDescriptor(descriptor)); if (matchingBeans.isEmpty()) { return null; } @@ -1168,9 +1182,8 @@ else if (Map.class.isAssignableFrom(type) && type.isInterface()) { if (valueType == null) { return null; } - DependencyDescriptor targetDesc = new DependencyDescriptor(descriptor); - targetDesc.increaseNestingLevel(); - Map matchingBeans = findAutowireCandidates(beanName, valueType, targetDesc); + Map matchingBeans = findAutowireCandidates(beanName, valueType, + new MultiElementDependencyDescriptor(descriptor)); if (matchingBeans.isEmpty()) { return null; } @@ -1239,7 +1252,7 @@ protected Map findAutowireCandidates( } for (String candidateName : candidateNames) { if (!isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, descriptor)) { - result.put(candidateName, descriptor.resolveCandidate(candidateName, requiredType, this)); + addCandidateEntry(result, candidateName, descriptor, requiredType); } } if (result.isEmpty() && !indicatesMultipleBeans(requiredType)) { @@ -1247,14 +1260,14 @@ protected Map findAutowireCandidates( DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch(); for (String candidateName : candidateNames) { if (!isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, fallbackDescriptor)) { - result.put(candidateName, descriptor.resolveCandidate(candidateName, requiredType, this)); + addCandidateEntry(result, candidateName, descriptor, requiredType); } } if (result.isEmpty()) { // Consider self references before as a final pass for (String candidateName : candidateNames) { if (isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, fallbackDescriptor)) { - result.put(candidateName, descriptor.resolveCandidate(candidateName, requiredType, this)); + addCandidateEntry(result, candidateName, descriptor, requiredType); } } } @@ -1262,31 +1275,46 @@ protected Map findAutowireCandidates( return result; } + /** + * Add an entry to the candidate map: a bean instance if available or just the resolved + * type, preventing early bean initialization ahead of primary candidate selection. + */ + private void addCandidateEntry(Map candidates, String candidateName, + DependencyDescriptor descriptor, Class requiredType) { + + if (descriptor instanceof MultiElementDependencyDescriptor || containsSingleton(candidateName)) { + candidates.put(candidateName, descriptor.resolveCandidate(candidateName, requiredType, this)); + } + else { + candidates.put(candidateName, getType(candidateName)); + } + } + /** * Determine the autowire candidate in the given set of beans. *

Looks for {@code @Primary} and {@code @Priority} (in that order). - * @param candidateBeans a Map of candidate names and candidate instances + * @param candidates a Map of candidate names and candidate instances * that match the required type, as returned by {@link #findAutowireCandidates} * @param descriptor the target dependency to match against * @return the name of the autowire candidate, or {@code null} if none found */ - protected String determineAutowireCandidate(Map candidateBeans, DependencyDescriptor descriptor) { + protected String determineAutowireCandidate(Map candidates, DependencyDescriptor descriptor) { Class requiredType = descriptor.getDependencyType(); - String primaryCandidate = determinePrimaryCandidate(candidateBeans, requiredType); + String primaryCandidate = determinePrimaryCandidate(candidates, requiredType); if (primaryCandidate != null) { return primaryCandidate; } - String priorityCandidate = determineHighestPriorityCandidate(candidateBeans, requiredType); + String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType); if (priorityCandidate != null) { return priorityCandidate; } // Fallback - for (Map.Entry entry : candidateBeans.entrySet()) { - String candidateBeanName = entry.getKey(); + for (Map.Entry entry : candidates.entrySet()) { + String candidateName = entry.getKey(); Object beanInstance = entry.getValue(); if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) || - matchesBeanName(candidateBeanName, descriptor.getDependencyName())) { - return candidateBeanName; + matchesBeanName(candidateName, descriptor.getDependencyName())) { + return candidateName; } } return null; @@ -1294,15 +1322,15 @@ protected String determineAutowireCandidate(Map candidateBeans, /** * Determine the primary candidate in the given set of beans. - * @param candidateBeans a Map of candidate names and candidate instances - * that match the required type + * @param candidates a Map of candidate names and candidate instances + * (or candidate classes if not created yet) that match the required type * @param requiredType the target dependency type to match against * @return the name of the primary candidate, or {@code null} if none found * @see #isPrimary(String, Object) */ - protected String determinePrimaryCandidate(Map candidateBeans, Class requiredType) { + protected String determinePrimaryCandidate(Map candidates, Class requiredType) { String primaryBeanName = null; - for (Map.Entry entry : candidateBeans.entrySet()) { + for (Map.Entry entry : candidates.entrySet()) { String candidateBeanName = entry.getKey(); Object beanInstance = entry.getValue(); if (isPrimary(candidateBeanName, beanInstance)) { @@ -1310,8 +1338,8 @@ protected String determinePrimaryCandidate(Map candidateBeans, C boolean candidateLocal = containsBeanDefinition(candidateBeanName); boolean primaryLocal = containsBeanDefinition(primaryBeanName); if (candidateLocal && primaryLocal) { - throw new NoUniqueBeanDefinitionException(requiredType, candidateBeans.size(), - "more than one 'primary' bean found among candidates: " + candidateBeans.keySet()); + throw new NoUniqueBeanDefinitionException(requiredType, candidates.size(), + "more than one 'primary' bean found among candidates: " + candidates.keySet()); } else if (candidateLocal) { primaryBeanName = candidateBeanName; @@ -1326,29 +1354,30 @@ else if (candidateLocal) { } /** - * Determine the candidate with the highest priority in the given set of beans. As - * defined by the {@link org.springframework.core.Ordered} interface, the lowest - * value has the highest priority. - * @param candidateBeans a Map of candidate names and candidate instances - * that match the required type + * Determine the candidate with the highest priority in the given set of beans. + *

Based on {@code @javax.annotation.Priority}. As defined by the related + * {@link org.springframework.core.Ordered} interface, the lowest value has + * the highest priority. + * @param candidates a Map of candidate names and candidate instances + * (or candidate classes if not created yet) that match the required type * @param requiredType the target dependency type to match against * @return the name of the candidate with the highest priority, * or {@code null} if none found * @see #getPriority(Object) */ - protected String determineHighestPriorityCandidate(Map candidateBeans, Class requiredType) { + protected String determineHighestPriorityCandidate(Map candidates, Class requiredType) { String highestPriorityBeanName = null; Integer highestPriority = null; - for (Map.Entry entry : candidateBeans.entrySet()) { + for (Map.Entry entry : candidates.entrySet()) { String candidateBeanName = entry.getKey(); Object beanInstance = entry.getValue(); Integer candidatePriority = getPriority(beanInstance); if (candidatePriority != null) { if (highestPriorityBeanName != null) { if (candidatePriority.equals(highestPriority)) { - throw new NoUniqueBeanDefinitionException(requiredType, candidateBeans.size(), - "Multiple beans found with the same priority ('" + highestPriority + "') " + - "among candidates: " + candidateBeans.keySet()); + throw new NoUniqueBeanDefinitionException(requiredType, candidates.size(), + "Multiple beans found with the same priority ('" + highestPriority + + "') among candidates: " + candidates.keySet()); } else if (candidatePriority < highestPriority) { highestPriorityBeanName = candidateBeanName; @@ -1529,7 +1558,7 @@ private Object readResolve() { private class OptionalDependencyFactory { public Object createOptionalDependency(DependencyDescriptor descriptor, String beanName, final Object... args) { - DependencyDescriptor descriptorToUse = new DependencyDescriptor(descriptor) { + DependencyDescriptor descriptorToUse = new NestedDependencyDescriptor(descriptor) { @Override public boolean isRequired() { return false; @@ -1540,7 +1569,6 @@ public Object resolveCandidate(String beanName, Class requiredType, BeanFacto super.resolveCandidate(beanName, requiredType, beanFactory)); } }; - descriptorToUse.increaseNestingLevel(); return Optional.ofNullable(doResolveDependency(descriptorToUse, beanName, null, null)); } } @@ -1558,9 +1586,8 @@ private class DependencyObjectProvider implements ObjectProvider, Serial private final String beanName; public DependencyObjectProvider(DependencyDescriptor descriptor, String beanName) { - this.descriptor = new DependencyDescriptor(descriptor); - this.descriptor.increaseNestingLevel(); - this.optional = this.descriptor.getDependencyType().equals(javaUtilOptionalClass); + this.descriptor = new NestedDependencyDescriptor(descriptor); + this.optional = (this.descriptor.getDependencyType() == javaUtilOptionalClass); this.beanName = beanName; } @@ -1676,7 +1703,7 @@ public Object getOrderSource(Object obj) { if (beanDefinition == null) { return null; } - List sources = new ArrayList(); + List sources = new ArrayList(2); Method factoryMethod = beanDefinition.getResolvedFactoryMethod(); if (factoryMethod != null) { sources.add(factoryMethod); @@ -1699,4 +1726,21 @@ private RootBeanDefinition getRootBeanDefinition(String beanName) { } } + + private static class NestedDependencyDescriptor extends DependencyDescriptor { + + public NestedDependencyDescriptor(DependencyDescriptor original) { + super(original); + increaseNestingLevel(); + } + } + + + private static class MultiElementDependencyDescriptor extends NestedDependencyDescriptor { + + public MultiElementDependencyDescriptor(DependencyDescriptor original) { + super(original); + } + } + } diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java index 00e41497aae..71689a6a8a6 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java @@ -1435,12 +1435,14 @@ public void testGetBeanByTypeWithAmbiguity() { public void testGetBeanByTypeWithPrimary() throws Exception { DefaultListableBeanFactory lbf = new DefaultListableBeanFactory(); RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class); + bd1.setLazyInit(true); RootBeanDefinition bd2 = new RootBeanDefinition(TestBean.class); bd2.setPrimary(true); lbf.registerBeanDefinition("bd1", bd1); lbf.registerBeanDefinition("bd2", bd2); TestBean bean = lbf.getBean(TestBean.class); assertThat(bean.getBeanName(), equalTo("bd2")); + assertFalse(lbf.containsSingleton("bd1")); } @Test @@ -1760,24 +1762,6 @@ public void testAutowireBeanByTypeWithTwoMatches() { } } - @Test - public void testAutowireBeanByTypeWithTwoMatchesAndParameterNameDiscovery() { - DefaultListableBeanFactory lbf = new DefaultListableBeanFactory(); - RootBeanDefinition bd = new RootBeanDefinition(TestBean.class); - RootBeanDefinition bd2 = new RootBeanDefinition(TestBean.class); - lbf.registerBeanDefinition("test", bd); - lbf.registerBeanDefinition("spouse", bd2); - try { - lbf.autowire(DependenciesBean.class, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true); - fail("Should have thrown UnsatisfiedDependencyException"); - } - catch (UnsatisfiedDependencyException ex) { - // expected - assertTrue(ex.getMessage().contains("test")); - assertTrue(ex.getMessage().contains("spouse")); - } - } - @Test public void testAutowireBeanByTypeWithDependencyCheck() { DefaultListableBeanFactory lbf = new DefaultListableBeanFactory(); diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java index 96e7e07a0f8..21301e4cb1a 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java @@ -1146,12 +1146,15 @@ public void testSmartObjectFactoryInjectionWithTargetPrimary() { RootBeanDefinition tb1 = new RootBeanDefinition(TestBean.class); tb1.setPrimary(true); bf.registerBeanDefinition("testBean1", tb1); - bf.registerBeanDefinition("testBean2", new RootBeanDefinition(TestBean.class)); + RootBeanDefinition tb2 = new RootBeanDefinition(TestBean.class); + tb2.setLazyInit(true); + bf.registerBeanDefinition("testBean2", tb2); SmartObjectFactoryInjectionBean bean = (SmartObjectFactoryInjectionBean) bf.getBean("annotatedBean"); assertSame(bf.getBean("testBean1"), bean.getTestBean()); assertSame(bf.getBean("testBean1"), bean.getOptionalTestBean()); assertSame(bf.getBean("testBean1"), bean.getUniqueTestBean()); + assertFalse(bf.containsSingleton("testBean2")); bf.destroySingletons(); } diff --git a/spring-core/src/main/java/org/springframework/core/annotation/OrderUtils.java b/spring-core/src/main/java/org/springframework/core/annotation/OrderUtils.java index 350a7bdf6ff..5b2cf361fed 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/OrderUtils.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/OrderUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -48,9 +48,10 @@ public abstract class OrderUtils { /** * Return the order on the specified {@code type}. - *

Take care of {@link Order @Order} and {@code @javax.annotation.Priority}. + *

Takes care of {@link Order @Order} and {@code @javax.annotation.Priority}. * @param type the type to handle * @return the order value, or {@code null} if none can be found + * @see #getPriority(Class) */ public static Integer getOrder(Class type) { return getOrder(type, null); @@ -59,9 +60,10 @@ public static Integer getOrder(Class type) { /** * Return the order on the specified {@code type}, or the specified * default value if none can be found. - *

Take care of {@link Order @Order} and {@code @javax.annotation.Priority}. + *

Takes care of {@link Order @Order} and {@code @javax.annotation.Priority}. * @param type the type to handle * @return the priority value, or the specified default order if none can be found + * @see #getPriority(Class) */ public static Integer getOrder(Class type, Integer defaultOrder) { Order order = AnnotationUtils.findAnnotation(type, Order.class); From 026473280b5ece9c8d6b5807f930c43989a5ca8d Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 25 Aug 2016 00:10:06 +0200 Subject: [PATCH 0128/1274] Polishing --- .../beans/factory/support/DefaultListableBeanFactory.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index fad8a3956d0..4c5ca7c4962 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -1009,12 +1009,12 @@ private NamedBeanHolder resolveNamedBean(Class requiredType, Object... } else if (candidateNames.length > 1) { Map candidates = new LinkedHashMap(candidateNames.length); - for (String candidateName : candidateNames) { - if (containsSingleton(candidateName)) { - candidates.put(candidateName, getBean(candidateName, requiredType, args)); + for (String beanName : candidateNames) { + if (containsSingleton(beanName)) { + candidates.put(beanName, getBean(beanName, requiredType, args)); } else { - candidates.put(candidateName, getType(candidateName)); + candidates.put(beanName, getType(beanName)); } } String candidateName = determinePrimaryCandidate(candidates, requiredType); From 9b91b9db8cb65d5af50892578085c19145817973 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Thu, 25 Aug 2016 14:39:07 +0200 Subject: [PATCH 0129/1274] Add RFC5987 support for HTTP header field params This commit adds support for HTTP header field parameters encoding, as described in RFC5987. Note that the default implementation still relies on US-ASCII encoding, as the latest rfc7230 Section 3.2.4 says that: > Newly defined header fields SHOULD limit their field values to US-ASCII octets Issue: SPR-14547 Cherry-picked from: f2faf84f317f --- .../org/springframework/util/StringUtils.java | 42 +++++++++++++++++++ .../util/StringUtilsTests.java | 18 +++++++- .../org/springframework/http/HttpHeaders.java | 24 ++++++++++- .../http/HttpHeadersTests.java | 5 +++ 4 files changed, 86 insertions(+), 3 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/util/StringUtils.java b/spring-core/src/main/java/org/springframework/util/StringUtils.java index 6d470ae88e8..d7528106da4 100644 --- a/spring-core/src/main/java/org/springframework/util/StringUtils.java +++ b/spring-core/src/main/java/org/springframework/util/StringUtils.java @@ -16,6 +16,7 @@ package org.springframework.util; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -50,6 +51,7 @@ * @author Rick Evans * @author Arjen Poutsma * @author Sam Brannen + * @author Brian Clozel * @since 16 April 2001 */ public abstract class StringUtils { @@ -1193,4 +1195,44 @@ public static String arrayToCommaDelimitedString(Object[] arr) { return arrayToDelimitedString(arr, ","); } + /** + * Encode the given header field param as describe in the rfc5987. + * @param input the header field param + * @param charset the charset of the header field param string + * @return the encoded header field param + * @see rfc5987 + * @since 5.0 + */ + public static String encodeHttpHeaderFieldParam(String input, Charset charset) { + Assert.notNull(charset, "charset should not be null"); + if(Charset.forName("US-ASCII").equals(charset)) { + return input; + } + Assert.isTrue(Charset.forName("UTF-8").equals(charset) || Charset.forName("ISO-8859-1").equals(charset), + "charset should be UTF-8 or ISO-8859-1"); + final byte[] source = input.getBytes(charset); + final int len = source.length; + final StringBuilder sb = new StringBuilder(len << 1); + sb.append(charset.name()); + sb.append("''"); + for (byte b : source) { + if (isRFC5987AttrChar(b)) { + sb.append((char) b); + } + else { + sb.append('%'); + char hex1 = Character.toUpperCase(Character.forDigit((b >> 4) & 0xF, 16)); + char hex2 = Character.toUpperCase(Character.forDigit(b & 0xF, 16)); + sb.append(hex1); + sb.append(hex2); + } + } + return sb.toString(); + } + + private static boolean isRFC5987AttrChar(byte c) { + return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') + || c == '!' || c == '#' || c == '$' || c == '&' || c == '+' || c == '-' + || c == '.' || c == '^' || c == '_' || c == '`' || c == '|' || c == '~'; + } } diff --git a/spring-core/src/test/java/org/springframework/util/StringUtilsTests.java b/spring-core/src/test/java/org/springframework/util/StringUtilsTests.java index 22e979e455a..11d6e406cf1 100644 --- a/spring-core/src/test/java/org/springframework/util/StringUtilsTests.java +++ b/spring-core/src/test/java/org/springframework/util/StringUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,6 +16,7 @@ package org.springframework.util; +import java.nio.charset.Charset; import java.util.Arrays; import java.util.Locale; import java.util.Properties; @@ -700,4 +701,19 @@ public void testParseLocaleWithVariantContainingCountryCode() { assertEquals("Variant containing country code not extracted correctly", variant, locale.getVariant()); } + // SPR-14547 + @Test + public void encodeHttpHeaderFieldParam() { + String result = StringUtils.encodeHttpHeaderFieldParam("test.txt", Charset.forName("US-ASCII")); + assertEquals("test.txt", result); + + result = StringUtils.encodeHttpHeaderFieldParam("中文.txt", Charset.forName("UTF-8")); + assertEquals("UTF-8''%E4%B8%AD%E6%96%87.txt", result); + } + + @Test(expected = IllegalArgumentException.class) + public void encodeHttpHeaderFieldParamInvalidCharset() { + StringUtils.encodeHttpHeaderFieldParam("test", Charset.forName("UTF-16")); + } + } diff --git a/spring-web/src/main/java/org/springframework/http/HttpHeaders.java b/spring-web/src/main/java/org/springframework/http/HttpHeaders.java index 53e4881bf5f..b613cd43a9f 100644 --- a/spring-web/src/main/java/org/springframework/http/HttpHeaders.java +++ b/spring-web/src/main/java/org/springframework/http/HttpHeaders.java @@ -672,12 +672,32 @@ public List getConnection() { * @param filename the filename (may be {@code null}) */ public void setContentDispositionFormData(String name, String filename) { + setContentDispositionFormData(name, filename, null); + } + + /** + * Set the (new) value of the {@code Content-Disposition} header + * for {@code form-data}, optionally encoding the filename using the rfc5987. + *

Only the US-ASCII, UTF-8 and ISO-8859-1 charsets are supported. + * @param name the control name + * @param filename the filename (may be {@code null}) + * @param charset the charset used for the filename (may be {@code null}) + * @see rfc7230 Section 3.2.4 + * @since 5.0 + */ + public void setContentDispositionFormData(String name, String filename, Charset charset) { Assert.notNull(name, "'name' must not be null"); StringBuilder builder = new StringBuilder("form-data; name=\""); builder.append(name).append('\"'); if (filename != null) { - builder.append("; filename=\""); - builder.append(filename).append('\"'); + if(charset == null || Charset.forName("US-ASCII").equals(charset)) { + builder.append("; filename=\""); + builder.append(filename).append('\"'); + } + else { + builder.append("; filename*="); + builder.append(StringUtils.encodeHttpHeaderFieldParam(filename, charset)); + } } set(CONTENT_DISPOSITION, builder.toString()); } diff --git a/spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java b/spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java index 77c79da49d2..ac171d3a999 100644 --- a/spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java +++ b/spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java @@ -320,6 +320,11 @@ public void contentDisposition() { headers.setContentDispositionFormData("name", "filename"); assertEquals("Invalid Content-Disposition header", "form-data; name=\"name\"; filename=\"filename\"", headers.getFirst("Content-Disposition")); + + headers.setContentDispositionFormData("name", "中文.txt", Charset.forName("UTF-8")); + assertEquals("Invalid Content-Disposition header", + "form-data; name=\"name\"; filename*=UTF-8''%E4%B8%AD%E6%96%87.txt", + headers.getFirst("Content-Disposition")); } @Test // SPR-11917 From 696f6874190cd6d2e01db6a672e12e4e049913c7 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 26 Aug 2016 11:14:02 +0200 Subject: [PATCH 0130/1274] Moved encodeHttpHeaderFieldParam method to HttpHeaders itself (including tests) This commit also sets the test source encoding to UTF-8. Issue: SPR-14547 (cherry picked from commit a8f7f75) --- build.gradle | 2 + .../org/springframework/util/StringUtils.java | 43 +--------------- .../util/StringUtilsTests.java | 40 +++------------ .../org/springframework/http/HttpHeaders.java | 50 +++++++++++++++++-- .../http/HttpHeadersTests.java | 14 ++++++ .../servlet/view/groovy/GroovyMarkupView.java | 31 ++++++------ .../view/groovy/GroovyMarkupViewTests.java | 29 ++++------- .../web/servlet/view/groovy/i18n_es.tpl | 2 +- 8 files changed, 99 insertions(+), 112 deletions(-) diff --git a/build.gradle b/build.gradle index 24a535414c5..39402a82525 100644 --- a/build.gradle +++ b/build.gradle @@ -110,11 +110,13 @@ configure(allprojects) { project -> compileJava { sourceCompatibility = 1.6 targetCompatibility = 1.6 + options.encoding = 'UTF-8' } compileTestJava { sourceCompatibility = 1.8 targetCompatibility = 1.8 + options.encoding = 'UTF-8' options.compilerArgs += "-parameters" } diff --git a/spring-core/src/main/java/org/springframework/util/StringUtils.java b/spring-core/src/main/java/org/springframework/util/StringUtils.java index d7528106da4..121f17ad567 100644 --- a/spring-core/src/main/java/org/springframework/util/StringUtils.java +++ b/spring-core/src/main/java/org/springframework/util/StringUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,6 @@ package org.springframework.util; -import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -1195,44 +1194,4 @@ public static String arrayToCommaDelimitedString(Object[] arr) { return arrayToDelimitedString(arr, ","); } - /** - * Encode the given header field param as describe in the rfc5987. - * @param input the header field param - * @param charset the charset of the header field param string - * @return the encoded header field param - * @see rfc5987 - * @since 5.0 - */ - public static String encodeHttpHeaderFieldParam(String input, Charset charset) { - Assert.notNull(charset, "charset should not be null"); - if(Charset.forName("US-ASCII").equals(charset)) { - return input; - } - Assert.isTrue(Charset.forName("UTF-8").equals(charset) || Charset.forName("ISO-8859-1").equals(charset), - "charset should be UTF-8 or ISO-8859-1"); - final byte[] source = input.getBytes(charset); - final int len = source.length; - final StringBuilder sb = new StringBuilder(len << 1); - sb.append(charset.name()); - sb.append("''"); - for (byte b : source) { - if (isRFC5987AttrChar(b)) { - sb.append((char) b); - } - else { - sb.append('%'); - char hex1 = Character.toUpperCase(Character.forDigit((b >> 4) & 0xF, 16)); - char hex2 = Character.toUpperCase(Character.forDigit(b & 0xF, 16)); - sb.append(hex1); - sb.append(hex2); - } - } - return sb.toString(); - } - - private static boolean isRFC5987AttrChar(byte c) { - return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') - || c == '!' || c == '#' || c == '$' || c == '&' || c == '+' || c == '-' - || c == '.' || c == '^' || c == '_' || c == '`' || c == '|' || c == '~'; - } } diff --git a/spring-core/src/test/java/org/springframework/util/StringUtilsTests.java b/spring-core/src/test/java/org/springframework/util/StringUtilsTests.java index 11d6e406cf1..e63ec35575c 100644 --- a/spring-core/src/test/java/org/springframework/util/StringUtilsTests.java +++ b/spring-core/src/test/java/org/springframework/util/StringUtilsTests.java @@ -16,7 +16,6 @@ package org.springframework.util; -import java.nio.charset.Charset; import java.util.Arrays; import java.util.Locale; import java.util.Properties; @@ -628,8 +627,7 @@ public void testParseLocaleWithMultiSpecialCharactersInVariant() throws Exceptio assertEquals("Multi-valued variant portion of the Locale not extracted correctly.", variant, locale.getVariant()); } - // SPR-3671 - @Test + @Test // SPR-3671 public void testParseLocaleWithMultiValuedVariant() throws Exception { final String variant = "proper_northern"; final String localeString = "en_GB_" + variant; @@ -637,8 +635,7 @@ public void testParseLocaleWithMultiValuedVariant() throws Exception { assertEquals("Multi-valued variant portion of the Locale not extracted correctly.", variant, locale.getVariant()); } - // SPR-3671 - @Test + @Test // SPR-3671 public void testParseLocaleWithMultiValuedVariantUsingSpacesAsSeparators() throws Exception { final String variant = "proper northern"; final String localeString = "en GB " + variant; @@ -646,8 +643,7 @@ public void testParseLocaleWithMultiValuedVariantUsingSpacesAsSeparators() throw assertEquals("Multi-valued variant portion of the Locale not extracted correctly.", variant, locale.getVariant()); } - // SPR-3671 - @Test + @Test // SPR-3671 public void testParseLocaleWithMultiValuedVariantUsingMixtureOfUnderscoresAndSpacesAsSeparators() throws Exception { final String variant = "proper northern"; final String localeString = "en_GB_" + variant; @@ -655,8 +651,7 @@ public void testParseLocaleWithMultiValuedVariantUsingMixtureOfUnderscoresAndSpa assertEquals("Multi-valued variant portion of the Locale not extracted correctly.", variant, locale.getVariant()); } - // SPR-3671 - @Test + @Test // SPR-3671 public void testParseLocaleWithMultiValuedVariantUsingSpacesAsSeparatorsWithLotsOfLeadingWhitespace() throws Exception { final String variant = "proper northern"; final String localeString = "en GB " + variant; // lots of whitespace @@ -664,8 +659,7 @@ public void testParseLocaleWithMultiValuedVariantUsingSpacesAsSeparatorsWithLots assertEquals("Multi-valued variant portion of the Locale not extracted correctly.", variant, locale.getVariant()); } - // SPR-3671 - @Test + @Test // SPR-3671 public void testParseLocaleWithMultiValuedVariantUsingUnderscoresAsSeparatorsWithLotsOfLeadingWhitespace() throws Exception { final String variant = "proper_northern"; final String localeString = "en_GB_____" + variant; // lots of underscores @@ -673,8 +667,7 @@ public void testParseLocaleWithMultiValuedVariantUsingUnderscoresAsSeparatorsWit assertEquals("Multi-valued variant portion of the Locale not extracted correctly.", variant, locale.getVariant()); } - // SPR-7779 - @Test + @Test // SPR-7779 public void testParseLocaleWithInvalidCharacters() { try { StringUtils.parseLocaleString("%0D%0AContent-length:30%0D%0A%0D%0A%3Cscript%3Ealert%28123%29%3C/script%3E"); @@ -685,15 +678,13 @@ public void testParseLocaleWithInvalidCharacters() { } } - // SPR-9420 - @Test + @Test // SPR-9420 public void testParseLocaleWithSameLowercaseTokenForLanguageAndCountry() { assertEquals("tr_TR", StringUtils.parseLocaleString("tr_tr").toString()); assertEquals("bg_BG_vnt", StringUtils.parseLocaleString("bg_bg_vnt").toString()); } - // SPR-11806 - @Test + @Test // SPR-11806 public void testParseLocaleWithVariantContainingCountryCode() { String variant = "GBtest"; String localeString = "en_GB_" + variant; @@ -701,19 +692,4 @@ public void testParseLocaleWithVariantContainingCountryCode() { assertEquals("Variant containing country code not extracted correctly", variant, locale.getVariant()); } - // SPR-14547 - @Test - public void encodeHttpHeaderFieldParam() { - String result = StringUtils.encodeHttpHeaderFieldParam("test.txt", Charset.forName("US-ASCII")); - assertEquals("test.txt", result); - - result = StringUtils.encodeHttpHeaderFieldParam("中文.txt", Charset.forName("UTF-8")); - assertEquals("UTF-8''%E4%B8%AD%E6%96%87.txt", result); - } - - @Test(expected = IllegalArgumentException.class) - public void encodeHttpHeaderFieldParamInvalidCharset() { - StringUtils.encodeHttpHeaderFieldParam("test", Charset.forName("UTF-16")); - } - } diff --git a/spring-web/src/main/java/org/springframework/http/HttpHeaders.java b/spring-web/src/main/java/org/springframework/http/HttpHeaders.java index b613cd43a9f..9145aecfcd3 100644 --- a/spring-web/src/main/java/org/springframework/http/HttpHeaders.java +++ b/spring-web/src/main/java/org/springframework/http/HttpHeaders.java @@ -677,13 +677,14 @@ public void setContentDispositionFormData(String name, String filename) { /** * Set the (new) value of the {@code Content-Disposition} header - * for {@code form-data}, optionally encoding the filename using the rfc5987. + * for {@code form-data}, optionally encoding the filename using the RFC 5987. *

Only the US-ASCII, UTF-8 and ISO-8859-1 charsets are supported. * @param name the control name * @param filename the filename (may be {@code null}) * @param charset the charset used for the filename (may be {@code null}) - * @see rfc7230 Section 3.2.4 - * @since 5.0 + * @since 4.3.3 + * @see #setContentDispositionFormData(String, String) + * @see RFC 7230 Section 3.2.4 */ public void setContentDispositionFormData(String name, String filename, Charset charset) { Assert.notNull(name, "'name' must not be null"); @@ -696,7 +697,7 @@ public void setContentDispositionFormData(String name, String filename, Charset } else { builder.append("; filename*="); - builder.append(StringUtils.encodeHttpHeaderFieldParam(filename, charset)); + builder.append(encodeHeaderFieldParam(filename, charset)); } } set(CONTENT_DISPOSITION, builder.toString()); @@ -1302,4 +1303,45 @@ public static HttpHeaders readOnlyHttpHeaders(HttpHeaders headers) { return new HttpHeaders(headers, true); } + /** + * Encode the given header field param as describe in RFC 5987. + * @param input the header field param + * @param charset the charset of the header field param string + * @return the encoded header field param + * @see RFC 5987 + */ + static String encodeHeaderFieldParam(String input, Charset charset) { + Assert.notNull(input, "Input String should not be null"); + Assert.notNull(charset, "Charset should not be null"); + if (charset.name().equals("US-ASCII")) { + return input; + } + Assert.isTrue(charset.name().equals("UTF-8") || charset.name().equals("ISO-8859-1"), + "Charset should be UTF-8 or ISO-8859-1"); + byte[] source = input.getBytes(charset); + int len = source.length; + StringBuilder sb = new StringBuilder(len << 1); + sb.append(charset.name()); + sb.append("''"); + for (byte b : source) { + if (isRFC5987AttrChar(b)) { + sb.append((char) b); + } + else { + sb.append('%'); + char hex1 = Character.toUpperCase(Character.forDigit((b >> 4) & 0xF, 16)); + char hex2 = Character.toUpperCase(Character.forDigit(b & 0xF, 16)); + sb.append(hex1); + sb.append(hex2); + } + } + return sb.toString(); + } + + private static boolean isRFC5987AttrChar(byte c) { + return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + c == '!' || c == '#' || c == '$' || c == '&' || c == '+' || c == '-' || + c == '.' || c == '^' || c == '_' || c == '`' || c == '|' || c == '~'; + } + } diff --git a/spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java b/spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java index ac171d3a999..c8098be86be 100644 --- a/spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java +++ b/spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java @@ -409,4 +409,18 @@ public void accessControlRequestMethod() { assertEquals(HttpMethod.POST, headers.getAccessControlRequestMethod()); } + @Test // SPR-14547 + public void encodeHeaderFieldParam() { + String result = HttpHeaders.encodeHeaderFieldParam("test.txt", Charset.forName("US-ASCII")); + assertEquals("test.txt", result); + + result = HttpHeaders.encodeHeaderFieldParam("中文.txt", Charset.forName("UTF-8")); + assertEquals("UTF-8''%E4%B8%AD%E6%96%87.txt", result); + } + + @Test(expected = IllegalArgumentException.class) + public void encodeHeaderFieldParamInvalidCharset() { + HttpHeaders.encodeHeaderFieldParam("test", Charset.forName("UTF-16")); + } + } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/groovy/GroovyMarkupView.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/groovy/GroovyMarkupView.java index 38eda9811f4..e3007c34a8c 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/groovy/GroovyMarkupView.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/groovy/GroovyMarkupView.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -45,7 +45,7 @@ * @see GroovyMarkupViewResolver * @see GroovyMarkupConfigurer * @see - * Groovy Markup Template engine documentation + * Groovy Markup Template engine documentation */ public class GroovyMarkupView extends AbstractTemplateView { @@ -63,17 +63,6 @@ public void setTemplateEngine(MarkupTemplateEngine engine) { this.engine = engine; } - @Override - public boolean checkResource(Locale locale) throws Exception { - try { - this.engine.resolveTemplate(getUrl()); - } - catch (IOException exception) { - return false; - } - return true; - } - /** * Invoked at startup. * If no {@link #setTemplateEngine(MarkupTemplateEngine) templateEngine} has @@ -107,6 +96,17 @@ protected MarkupTemplateEngine autodetectMarkupTemplateEngine() throws BeansExce } + @Override + public boolean checkResource(Locale locale) throws Exception { + try { + this.engine.resolveTemplate(getUrl()); + } + catch (IOException ex) { + return false; + } + return true; + } + @Override protected void renderMergedTemplateModel(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception { @@ -125,8 +125,9 @@ protected Template getTemplate(String viewUrl) throws Exception { } catch (ClassNotFoundException ex) { Throwable cause = (ex.getCause() != null ? ex.getCause() : ex); - throw new NestedServletException("Could not find class while rendering Groovy Markup view with name '" + - getUrl() + "': " + ex.getMessage() + "'", cause); + throw new NestedServletException( + "Could not find class while rendering Groovy Markup view with name '" + + getUrl() + "': " + ex.getMessage() + "'", cause); } } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/groovy/GroovyMarkupViewTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/groovy/GroovyMarkupViewTests.java index 4c557b444a2..273e13ae578 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/groovy/GroovyMarkupViewTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/groovy/GroovyMarkupViewTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -15,9 +15,7 @@ */ package org.springframework.web.servlet.view.groovy; -import java.io.IOException; import java.io.Reader; -import java.util.Arrays; import java.util.HashMap; import java.util.Locale; import java.util.Map; @@ -27,7 +25,6 @@ import groovy.text.TemplateEngine; import groovy.text.markup.MarkupTemplateEngine; import groovy.text.markup.TemplateConfiguration; -import org.codehaus.groovy.control.CompilationFailedException; import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Test; @@ -57,6 +54,7 @@ public class GroovyMarkupViewTests { private ServletContext servletContext; + @Before public void setup() { this.webAppContext = mock(WebApplicationContext.class); @@ -64,6 +62,7 @@ public void setup() { this.servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.webAppContext); } + @Test public void missingGroovyMarkupConfig() throws Exception { GroovyMarkupView view = new GroovyMarkupView(); @@ -82,7 +81,6 @@ public void missingGroovyMarkupConfig() throws Exception { @Test public void customTemplateEngine() throws Exception { - GroovyMarkupView view = new GroovyMarkupView(); view.setTemplateEngine(new TestTemplateEngine()); view.setApplicationContext(this.webAppContext); @@ -95,9 +93,7 @@ public void customTemplateEngine() throws Exception { @Test public void detectTemplateEngine() throws Exception { - GroovyMarkupView view = new GroovyMarkupView(); - view.setTemplateEngine(new TestTemplateEngine()); view.setApplicationContext(this.webAppContext); @@ -109,35 +105,30 @@ public void detectTemplateEngine() throws Exception { @Test public void checkResource() throws Exception { - GroovyMarkupView view = createViewWithUrl("test.tpl"); assertTrue(view.checkResource(Locale.US)); } @Test public void checkMissingResource() throws Exception { - GroovyMarkupView view = createViewWithUrl("missing.tpl"); assertFalse(view.checkResource(Locale.US)); } @Test public void checkI18nResource() throws Exception { - GroovyMarkupView view = createViewWithUrl("i18n.tpl"); assertTrue(view.checkResource(Locale.FRENCH)); } @Test public void checkI18nResourceMissingLocale() throws Exception { - GroovyMarkupView view = createViewWithUrl("i18n.tpl"); assertTrue(view.checkResource(Locale.CHINESE)); } @Test public void renderMarkupTemplate() throws Exception { - Map model = new HashMap<>(); model.put("name", "Spring"); MockHttpServletResponse response = renderViewWithModel("test.tpl", model, Locale.US); @@ -155,7 +146,7 @@ public void renderI18nTemplate() throws Exception { assertEquals("

Include German

Hallo Spring

", response.getContentAsString()); response = renderViewWithModel("i18n.tpl", model, new Locale("es")); - assertEquals("

Include Default

¡hola Spring

", response.getContentAsString()); + assertEquals("

Include Default

Hola Spring

", response.getContentAsString()); } @Test @@ -166,30 +157,30 @@ public void renderLayoutTemplate() throws Exception { response.getContentAsString()); } - private MockHttpServletResponse renderViewWithModel(String viewUrl, Map model, Locale locale) throws Exception { + private MockHttpServletResponse renderViewWithModel(String viewUrl, Map model, Locale locale) throws Exception { GroovyMarkupView view = createViewWithUrl(viewUrl); MockHttpServletResponse response = new MockHttpServletResponse(); MockHttpServletRequest request = new MockHttpServletRequest(); - request.setPreferredLocales(Arrays.asList(locale)); + request.addPreferredLocale(locale); LocaleContextHolder.setLocale(locale); view.renderMergedTemplateModel(model, request, response); return response; } private GroovyMarkupView createViewWithUrl(String viewUrl) throws Exception { - AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(GroovyMarkupConfiguration.class); ctx.refresh(); GroovyMarkupView view = new GroovyMarkupView(); - view.setApplicationContext(ctx); view.setUrl(viewUrl); + view.setApplicationContext(ctx); view.afterPropertiesSet(); return view; } + public class TestTemplateEngine extends MarkupTemplateEngine { public TestTemplateEngine() { @@ -197,11 +188,12 @@ public TestTemplateEngine() { } @Override - public Template createTemplate(Reader reader) throws CompilationFailedException, ClassNotFoundException, IOException { + public Template createTemplate(Reader reader) { return null; } } + @Configuration static class GroovyMarkupConfiguration { @@ -212,4 +204,5 @@ public GroovyMarkupConfig groovyMarkupConfigurer() { return configurer; } } + } diff --git a/spring-webmvc/src/test/resources/org/springframework/web/servlet/view/groovy/i18n_es.tpl b/spring-webmvc/src/test/resources/org/springframework/web/servlet/view/groovy/i18n_es.tpl index 2d52884bde6..169951e0d69 100644 --- a/spring-webmvc/src/test/resources/org/springframework/web/servlet/view/groovy/i18n_es.tpl +++ b/spring-webmvc/src/test/resources/org/springframework/web/servlet/view/groovy/i18n_es.tpl @@ -1,2 +1,2 @@ include template: 'includes/include.tpl' -p('¡hola Spring') \ No newline at end of file +p('Hola Spring') \ No newline at end of file From 2a82b8fed94194d513822f9492053e677e9c8cb0 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 26 Aug 2016 13:11:39 +0200 Subject: [PATCH 0131/1274] Consistent use of Charset.forName over JDK 7 StandardCharsets in 4.x line --- .../SendToMethodReturnValueHandlerTests.java | 3 +- ...criptionMethodReturnValueHandlerTests.java | 5 +- .../samples/standalone/AsyncTests.java | 48 +++++++------------ .../web/servlet/config/MvcNamespaceTests.java | 25 +++++++--- .../view/script/ScriptTemplateViewTests.java | 20 ++++---- 5 files changed, 50 insertions(+), 51 deletions(-) diff --git a/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SendToMethodReturnValueHandlerTests.java b/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SendToMethodReturnValueHandlerTests.java index 5d831dcaf5f..e82dedee933 100644 --- a/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SendToMethodReturnValueHandlerTests.java +++ b/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SendToMethodReturnValueHandlerTests.java @@ -20,7 +20,6 @@ import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Method; import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; import java.security.Principal; import java.util.LinkedHashMap; import java.util.Map; @@ -539,7 +538,7 @@ public void jsonView() throws Exception { Message message = this.messageCaptor.getValue(); assertNotNull(message); - String bytes = new String((byte[]) message.getPayload(), StandardCharsets.UTF_8); + String bytes = new String((byte[]) message.getPayload(), Charset.forName("UTF-8")); assertEquals("{\"withView1\":\"with\"}", bytes); } diff --git a/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SubscriptionMethodReturnValueHandlerTests.java b/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SubscriptionMethodReturnValueHandlerTests.java index ceaa4f0f720..823887f075b 100644 --- a/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SubscriptionMethodReturnValueHandlerTests.java +++ b/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SubscriptionMethodReturnValueHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -18,7 +18,6 @@ import java.lang.reflect.Method; import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; import java.security.Principal; import com.fasterxml.jackson.annotation.JsonView; @@ -179,7 +178,7 @@ public void testJsonView() throws Exception { Message message = this.messageCaptor.getValue(); assertNotNull(message); - assertEquals("{\"withView1\":\"with\"}", new String((byte[]) message.getPayload(), StandardCharsets.UTF_8)); + assertEquals("{\"withView1\":\"with\"}", new String((byte[]) message.getPayload(), Charset.forName("UTF-8"))); } diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/AsyncTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/AsyncTests.java index c922297fd35..541401b86f9 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/AsyncTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/AsyncTests.java @@ -17,7 +17,7 @@ package org.springframework.test.web.servlet.samples.standalone; import java.io.StringWriter; -import java.nio.charset.StandardCharsets; +import java.nio.charset.Charset; import java.util.Collection; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; @@ -78,7 +78,7 @@ public void callable() throws Exception { public void streaming() throws Exception { this.mockMvc.perform(get("/1").param("streaming", "true")) .andExpect(request().asyncStarted()) - .andDo(r -> r.getAsyncResult()) // fetch async result similar to "asyncDispatch" builder + .andDo(MvcResult::getAsyncResult) // fetch async result similar to "asyncDispatch" builder .andExpect(status().isOk()) .andExpect(content().string("name=Joe")); } @@ -87,7 +87,7 @@ public void streaming() throws Exception { public void streamingSlow() throws Exception { this.mockMvc.perform(get("/1").param("streamingSlow", "true")) .andExpect(request().asyncStarted()) - .andDo(r -> r.getAsyncResult()) + .andDo(MvcResult::getAsyncResult) .andExpect(status().isOk()) .andExpect(content().string("name=Joe&someBoolean=true")); } @@ -96,7 +96,7 @@ public void streamingSlow() throws Exception { public void streamingJson() throws Exception { this.mockMvc.perform(get("/1").param("streamingJson", "true")) .andExpect(request().asyncStarted()) - .andDo(r -> r.getAsyncResult()) + .andDo(MvcResult::getAsyncResult) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) .andExpect(content().string("{\"name\":\"Joe\",\"someDouble\":0.5}")); @@ -129,10 +129,7 @@ public void deferredResultWithImmediateValue() throws Exception { .andExpect(content().string("{\"name\":\"Joe\",\"someDouble\":0.0,\"someBoolean\":false}")); } - /** - * SPR-13079 - */ - @Test + @Test // SPR-13079 public void deferredResultWithDelayedError() throws Exception { MvcResult mvcResult = this.mockMvc.perform(get("/1").param("deferredResultWithDelayedError", "true")) .andExpect(request().asyncStarted()) @@ -157,10 +154,7 @@ public void listenableFuture() throws Exception { .andExpect(content().string("{\"name\":\"Joe\",\"someDouble\":0.0,\"someBoolean\":false}")); } - /** - * SPR-12597 - */ - @Test + @Test // SPR-12597 public void completableFutureWithImmediateValue() throws Exception { MvcResult mvcResult = this.mockMvc.perform(get("/1").param("completableFutureWithImmediateValue", "true")) .andExpect(request().asyncStarted()) @@ -172,10 +166,7 @@ public void completableFutureWithImmediateValue() throws Exception { .andExpect(content().string("{\"name\":\"Joe\",\"someDouble\":0.0,\"someBoolean\":false}")); } - /** - * SPR-12735 - */ - @Test + @Test // SPR-12735 public void printAsyncResult() throws Exception { StringWriter writer = new StringWriter(); @@ -203,12 +194,9 @@ public void printAsyncResult() throws Exception { @RequestMapping(path = "/{id}", produces = "application/json") private static class AsyncController { - private final Collection> deferredResults = - new CopyOnWriteArrayList>(); - - private final Collection> futureTasks = - new CopyOnWriteArrayList>(); + private final Collection> deferredResults = new CopyOnWriteArrayList<>(); + private final Collection> futureTasks = new CopyOnWriteArrayList<>(); @RequestMapping(params = "callable") public Callable getCallable() { @@ -217,7 +205,7 @@ public Callable getCallable() { @RequestMapping(params = "streaming") public StreamingResponseBody getStreaming() { - return os -> os.write("name=Joe".getBytes()); + return os -> os.write("name=Joe".getBytes(Charset.forName("UTF-8"))); } @RequestMapping(params = "streamingSlow") @@ -226,7 +214,7 @@ public StreamingResponseBody getStreamingSlow() { os.write("name=Joe".getBytes()); try { Thread.sleep(200); - os.write("&someBoolean=true".getBytes()); + os.write("&someBoolean=true".getBytes(Charset.forName("UTF-8"))); } catch (InterruptedException e) { /* no-op */ @@ -237,26 +225,26 @@ public StreamingResponseBody getStreamingSlow() { @RequestMapping(params = "streamingJson") public ResponseEntity getStreamingJson() { return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON_UTF8) - .body(os -> os.write("{\"name\":\"Joe\",\"someDouble\":0.5}".getBytes(StandardCharsets.UTF_8))); + .body(os -> os.write("{\"name\":\"Joe\",\"someDouble\":0.5}".getBytes(Charset.forName("UTF-8")))); } @RequestMapping(params = "deferredResult") public DeferredResult getDeferredResult() { - DeferredResult deferredResult = new DeferredResult(); + DeferredResult deferredResult = new DeferredResult<>(); this.deferredResults.add(deferredResult); return deferredResult; } @RequestMapping(params = "deferredResultWithImmediateValue") public DeferredResult getDeferredResultWithImmediateValue() { - DeferredResult deferredResult = new DeferredResult(); + DeferredResult deferredResult = new DeferredResult<>(); deferredResult.setResult(new Person("Joe")); return deferredResult; } @RequestMapping(params = "deferredResultWithDelayedError") public DeferredResult getDeferredResultWithDelayedError() { - final DeferredResult deferredResult = new DeferredResult(); + final DeferredResult deferredResult = new DeferredResult<>(); new Thread() { public void run() { try { @@ -273,14 +261,14 @@ public void run() { @RequestMapping(params = "listenableFuture") public ListenableFuture getListenableFuture() { - ListenableFutureTask futureTask = new ListenableFutureTask(() -> new Person("Joe")); + ListenableFutureTask futureTask = new ListenableFutureTask<>(() -> new Person("Joe")); this.futureTasks.add(futureTask); return futureTask; } @RequestMapping(params = "completableFutureWithImmediateValue") public CompletableFuture getCompletableFutureWithImmediateValue() { - CompletableFuture future = new CompletableFuture(); + CompletableFuture future = new CompletableFuture<>(); future.complete(new Person("Joe")); return future; } @@ -291,7 +279,7 @@ public String errorHandler(Exception e) { return e.getMessage(); } - public void onMessage(String name) { + void onMessage(String name) { for (DeferredResult deferredResult : this.deferredResults) { deferredResult.setResult(new Person(name)); this.deferredResults.remove(deferredResult); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java index 859bf4eb27b..df6c1203146 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/MvcNamespaceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Method; -import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -145,8 +144,8 @@ import org.springframework.web.servlet.view.velocity.VelocityViewResolver; import org.springframework.web.util.UrlPathHelper; -import static org.hamcrest.CoreMatchers.*; -import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; /** @@ -823,7 +822,7 @@ public void testViewResolution() throws Exception { assertNotNull(scriptTemplateConfigurer); assertEquals("render", scriptTemplateConfigurer.getRenderFunction()); assertEquals(MediaType.TEXT_PLAIN_VALUE, scriptTemplateConfigurer.getContentType()); - assertEquals(StandardCharsets.ISO_8859_1, scriptTemplateConfigurer.getCharset()); + assertEquals("ISO-8859-1", scriptTemplateConfigurer.getCharset().name()); assertEquals("classpath:", scriptTemplateConfigurer.getResourceLoaderPath()); assertFalse(scriptTemplateConfigurer.isSharedEngine()); String[] scripts = { "org/springframework/web/servlet/view/script/nashorn/render.js" }; @@ -956,18 +955,21 @@ private void loadBeanDefinitions(String fileName, int expectedBeanCount) { public @interface IsoDate { } + @NumberFormat(style = NumberFormat.Style.PERCENT) @Target({ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) public @interface PercentNumber { } + @Validated(MyGroup.class) @Target({ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) public @interface MyValid { } + @Controller public static class TestController { @@ -978,13 +980,16 @@ public static class TestController { private boolean recordedValidationError; @RequestMapping - public void testBind(@RequestParam @IsoDate Date date, @RequestParam(required = false) @PercentNumber Double percent, @MyValid TestBean bean, BindingResult result) { + public void testBind(@RequestParam @IsoDate Date date, + @RequestParam(required = false) @PercentNumber Double percent, + @MyValid TestBean bean, BindingResult result) { this.date = date; this.percent = percent; this.recordedValidationError = (result.getErrorCount() == 1); } } + public static class TestValidator implements Validator { boolean validatorInvoked; @@ -1000,10 +1005,12 @@ public void validate(Object target, Errors errors) { } } + @Retention(RetentionPolicy.RUNTIME) public @interface MyGroup { } + private static class TestBean { @NotNull(groups = MyGroup.class) @@ -1020,6 +1027,7 @@ public void setField(String field) { } } + private static class TestMockServletContext extends MockServletContext { @Override @@ -1033,12 +1041,15 @@ public RequestDispatcher getNamedDispatcher(String path) { } } + public static class TestCallableProcessingInterceptor extends CallableProcessingInterceptorAdapter { } + public static class TestDeferredResultProcessingInterceptor extends DeferredResultProcessingInterceptorAdapter { } + public static class TestPathMatcher implements PathMatcher { @Override @@ -1077,9 +1088,11 @@ public String combine(String pattern1, String pattern2) { } } + public static class TestPathHelper extends UrlPathHelper { } + public static class TestCacheManager implements CacheManager { @Override diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/script/ScriptTemplateViewTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/script/ScriptTemplateViewTests.java index af4e8251d52..a337901e516 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/script/ScriptTemplateViewTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/script/ScriptTemplateViewTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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.web.servlet.view.script; -import java.nio.charset.StandardCharsets; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -86,7 +86,7 @@ public void detectScriptTemplateConfigWithEngine() { this.configurer.setRenderObject("Template"); this.configurer.setRenderFunction("render"); this.configurer.setContentType(MediaType.TEXT_PLAIN_VALUE); - this.configurer.setCharset(StandardCharsets.ISO_8859_1); + this.configurer.setCharset(Charset.forName("ISO-8859-1")); this.configurer.setSharedEngine(true); DirectFieldAccessor accessor = new DirectFieldAccessor(this.view); @@ -95,7 +95,7 @@ public void detectScriptTemplateConfigWithEngine() { assertEquals("Template", accessor.getPropertyValue("renderObject")); assertEquals("render", accessor.getPropertyValue("renderFunction")); assertEquals(MediaType.TEXT_PLAIN_VALUE, accessor.getPropertyValue("contentType")); - assertEquals(StandardCharsets.ISO_8859_1, accessor.getPropertyValue("charset")); + assertEquals(Charset.forName("ISO-8859-1"), accessor.getPropertyValue("charset")); assertEquals(true, accessor.getPropertyValue("sharedEngine")); } @@ -112,7 +112,7 @@ public void detectScriptTemplateConfigWithEngineName() { assertEquals("Template", accessor.getPropertyValue("renderObject")); assertEquals("render", accessor.getPropertyValue("renderFunction")); assertEquals(MediaType.TEXT_HTML_VALUE, accessor.getPropertyValue("contentType")); - assertEquals(StandardCharsets.UTF_8, accessor.getPropertyValue("charset")); + assertEquals(Charset.forName("UTF-8"), accessor.getPropertyValue("charset")); } @Test @@ -128,7 +128,7 @@ public void customEngineAndRenderFunction() throws Exception { DirectFieldAccessor accessor = new DirectFieldAccessor(this.view); assertNull(accessor.getPropertyValue("renderObject")); assertEquals("render", accessor.getPropertyValue("renderFunction")); - assertEquals(StandardCharsets.UTF_8, accessor.getPropertyValue("charset")); + assertEquals(Charset.forName("UTF-8"), accessor.getPropertyValue("charset")); } @Test @@ -252,19 +252,19 @@ public void contentType() throws Exception { this.view.render(model, request, response); assertEquals(MediaType.TEXT_HTML_VALUE + ";charset=" + - StandardCharsets.UTF_8, response.getHeader(HttpHeaders.CONTENT_TYPE)); + Charset.forName("UTF-8"), response.getHeader(HttpHeaders.CONTENT_TYPE)); response = new MockHttpServletResponse(); this.view.setContentType(MediaType.TEXT_PLAIN_VALUE); this.view.render(model, request, response); assertEquals(MediaType.TEXT_PLAIN_VALUE + ";charset=" + - StandardCharsets.UTF_8, response.getHeader(HttpHeaders.CONTENT_TYPE)); + Charset.forName("UTF-8"), response.getHeader(HttpHeaders.CONTENT_TYPE)); response = new MockHttpServletResponse(); - this.view.setCharset(StandardCharsets.ISO_8859_1); + this.view.setCharset(Charset.forName("ISO-8859-1")); this.view.render(model, request, response); assertEquals(MediaType.TEXT_PLAIN_VALUE + ";charset=" + - StandardCharsets.ISO_8859_1, response.getHeader(HttpHeaders.CONTENT_TYPE)); + Charset.forName("ISO-8859-1"), response.getHeader(HttpHeaders.CONTENT_TYPE)); } From 798d8668a48626003ef2d5aa52fe84e8ed3ffc55 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Fri, 26 Aug 2016 15:33:02 +0200 Subject: [PATCH 0132/1274] Fix server errors for invalid If-None-Match request headers HttpEntityMethodProcessor should not throw IllegalArgumentExceptions for invalid If-None-Match headers. For those cases, this commit makes sure that both `HttpEntityMethodProcessor` and `ServletWebRequest` have a consistent behavior and stop processing the request as conditional and leave the handler handle it. Issue: SPR-14559 --- .../ServletWebRequestHttpMethodsTests.java | 9 +++++ .../annotation/HttpEntityMethodProcessor.java | 36 ++++++++++--------- .../HttpEntityMethodProcessorMockTests.java | 15 ++++++++ 3 files changed, 44 insertions(+), 16 deletions(-) diff --git a/spring-web/src/test/java/org/springframework/web/context/request/ServletWebRequestHttpMethodsTests.java b/spring-web/src/test/java/org/springframework/web/context/request/ServletWebRequestHttpMethodsTests.java index a5a8b07ce5a..e5ff3c2c858 100644 --- a/spring-web/src/test/java/org/springframework/web/context/request/ServletWebRequestHttpMethodsTests.java +++ b/spring-web/src/test/java/org/springframework/web/context/request/ServletWebRequestHttpMethodsTests.java @@ -99,6 +99,15 @@ public void checkNotModifiedInvalidStatus() { assertEquals(dateFormat.format(epochTime), servletResponse.getHeader("Last-Modified")); } + @Test // SPR-14559 + public void checkNotModifiedInvalidIfNoneMatchHeader() { + String eTag = "\"etagvalue\""; + servletRequest.addHeader("If-None-Match", "missingquotes"); + assertFalse(request.checkNotModified(eTag)); + assertEquals(200, servletResponse.getStatus()); + assertEquals(eTag, servletResponse.getHeader("ETag")); + } + @Test public void checkNotModifiedHeaderAlreadySet() { long epochTime = currentDate.getTime(); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessor.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessor.java index 6545d4a8404..2f98f56cdd6 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessor.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessor.java @@ -223,24 +223,28 @@ private List getVaryRequestHeadersToAdd(HttpHeaders responseHeaders, Htt } private boolean isResourceNotModified(ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) { - List ifNoneMatch = inputMessage.getHeaders().getIfNoneMatch(); - long ifModifiedSince = inputMessage.getHeaders().getIfModifiedSince(); - String eTag = addEtagPadding(outputMessage.getHeaders().getETag()); - long lastModified = outputMessage.getHeaders().getLastModified(); boolean notModified = false; - - if (!ifNoneMatch.isEmpty() && (inputMessage.getHeaders().containsKey(HttpHeaders.IF_UNMODIFIED_SINCE) - || inputMessage.getHeaders().containsKey(HttpHeaders.IF_MATCH))) { - // invalid conditional request, do not process - } - else if (lastModified != -1 && StringUtils.hasLength(eTag)) { - notModified = isETagNotModified(ifNoneMatch, eTag) && isTimeStampNotModified(ifModifiedSince, lastModified); - } - else if (lastModified != -1) { - notModified = isTimeStampNotModified(ifModifiedSince, lastModified); + try { + long ifModifiedSince = inputMessage.getHeaders().getIfModifiedSince(); + String eTag = addEtagPadding(outputMessage.getHeaders().getETag()); + long lastModified = outputMessage.getHeaders().getLastModified(); + List ifNoneMatch = inputMessage.getHeaders().getIfNoneMatch(); + if (!ifNoneMatch.isEmpty() && (inputMessage.getHeaders().containsKey(HttpHeaders.IF_UNMODIFIED_SINCE) + || inputMessage.getHeaders().containsKey(HttpHeaders.IF_MATCH))) { + // invalid conditional request, do not process + } + else if (lastModified != -1 && StringUtils.hasLength(eTag)) { + notModified = isETagNotModified(ifNoneMatch, eTag) && isTimeStampNotModified(ifModifiedSince, lastModified); + } + else if (lastModified != -1) { + notModified = isTimeStampNotModified(ifModifiedSince, lastModified); + } + else if (StringUtils.hasLength(eTag)) { + notModified = isETagNotModified(ifNoneMatch, eTag); + } } - else if (StringUtils.hasLength(eTag)) { - notModified = isETagNotModified(ifNoneMatch, eTag); + catch (IllegalArgumentException exc) { + // invalid conditional request, do not process } return notModified; } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessorMockTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessorMockTests.java index 6acf1028752..65787c10ff1 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessorMockTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessorMockTests.java @@ -379,6 +379,21 @@ public void handleReturnValueEtag() throws Exception { assertEquals(etagValue, servletResponse.getHeader(HttpHeaders.ETAG)); } + @Test // SPR-14559 + public void handleReturnValueEtagInvalidIfNoneMatch() throws Exception { + String etagValue = "\"deadb33f8badf00d\""; + servletRequest.addHeader(HttpHeaders.IF_NONE_MATCH, "unquoted"); + HttpHeaders responseHeaders = new HttpHeaders(); + responseHeaders.set(HttpHeaders.ETAG, etagValue); + ResponseEntity returnValue = new ResponseEntity<>("body", responseHeaders, HttpStatus.OK); + + initStringMessageConversion(MediaType.TEXT_PLAIN); + processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest); + + assertTrue(mavContainer.isRequestHandled()); + assertEquals(HttpStatus.OK.value(), servletResponse.getStatus()); + } + @Test public void handleReturnValueETagAndLastModified() throws Exception { long currentTime = new Date().getTime(); From d09b0fe83af3d70a0333d6de53e2f1473ad85317 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Fri, 26 Aug 2016 16:33:41 +0200 Subject: [PATCH 0133/1274] Support target type in JsonPath assertions This change adds support for a target type in JsonPath assertions in Spring MVC Test. The existing assertValue(String expression, Object expectedValue) transparently falls back on using an alternative JsonPath API that allows specifying the target type to coerce to. There is also a new overloaded method assertValue(String expression, Matcher matcher, Class targetType) for use with Hamcrest matchers where the target type can be specified. Issue: SPR-14498 (cherry picked from commit 7fdb892) --- .../test/util/JsonPathExpectationsHelper.java | 66 ++++++++++++------- .../client/match/JsonPathRequestMatchers.java | 17 +++++ .../util/JsonPathExpectationsHelperTests.java | 17 +++-- 3 files changed, 69 insertions(+), 31 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/util/JsonPathExpectationsHelper.java b/spring-test/src/main/java/org/springframework/test/util/JsonPathExpectationsHelper.java index cbb74dc6e40..f6b44fdaeb5 100644 --- a/spring-test/src/main/java/org/springframework/test/util/JsonPathExpectationsHelper.java +++ b/spring-test/src/main/java/org/springframework/test/util/JsonPathExpectationsHelper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,11 +16,9 @@ package org.springframework.test.util; -import java.text.ParseException; import java.util.List; import java.util.Map; -import com.jayway.jsonpath.InvalidPathException; import com.jayway.jsonpath.JsonPath; import org.hamcrest.Matcher; @@ -71,18 +69,33 @@ public JsonPathExpectationsHelper(String expression, Object... args) { * @param matcher the matcher with which to assert the result */ @SuppressWarnings("unchecked") - public void assertValue(String content, Matcher matcher) throws ParseException { + public void assertValue(String content, Matcher matcher) { T value = (T) evaluateJsonPath(content); assertThat("JSON path \"" + this.expression + "\"", value, matcher); } + /** + * An overloaded variant of {@link #assertValue(String, Matcher)} that also + * accepts a target type for the resulting value. This can be useful for + * matching numbers reliably for example coercing an integer into a double. + * @param content the JSON content + * @param matcher the matcher with which to assert the result + * @param targetType a the expected type of the resulting value + * @since 4.3.3 + */ + @SuppressWarnings("unchecked") + public void assertValue(String content, Matcher matcher, Class targetType) { + T value = (T) evaluateJsonPath(content, targetType); + assertThat("JSON path \"" + this.expression + "\"", value, matcher); + } + /** * Evaluate the JSON path expression against the supplied {@code content} * and assert that the result is equal to the expected value. * @param content the JSON content * @param expectedValue the expected value */ - public void assertValue(String content, Object expectedValue) throws ParseException { + public void assertValue(String content, Object expectedValue) { Object actualValue = evaluateJsonPath(content); if ((actualValue instanceof List) && !(expectedValue instanceof List)) { @SuppressWarnings("rawtypes") @@ -96,8 +109,9 @@ public void assertValue(String content, Object expectedValue) throws ParseExcept actualValue = actualValueList.get(0); } else if (actualValue != null && expectedValue != null) { - assertEquals("At JSON path \"" + this.expression + "\", type of value", - expectedValue.getClass().getName(), actualValue.getClass().getName()); + if (!actualValue.getClass().equals(expectedValue.getClass())) { + actualValue = evaluateJsonPath(content, expectedValue.getClass()); + } } assertEquals("JSON path \"" + this.expression + "\"", expectedValue, actualValue); } @@ -108,7 +122,7 @@ else if (actualValue != null && expectedValue != null) { * @param content the JSON content * @since 4.2.1 */ - public void assertValueIsString(String content) throws ParseException { + public void assertValueIsString(String content) { Object value = assertExistsAndReturn(content); assertThat(failureReason("a string", value), value, instanceOf(String.class)); } @@ -119,7 +133,7 @@ public void assertValueIsString(String content) throws ParseException { * @param content the JSON content * @since 4.2.1 */ - public void assertValueIsBoolean(String content) throws ParseException { + public void assertValueIsBoolean(String content) { Object value = assertExistsAndReturn(content); assertThat(failureReason("a boolean", value), value, instanceOf(Boolean.class)); } @@ -130,7 +144,7 @@ public void assertValueIsBoolean(String content) throws ParseException { * @param content the JSON content * @since 4.2.1 */ - public void assertValueIsNumber(String content) throws ParseException { + public void assertValueIsNumber(String content) { Object value = assertExistsAndReturn(content); assertThat(failureReason("a number", value), value, instanceOf(Number.class)); } @@ -140,7 +154,7 @@ public void assertValueIsNumber(String content) throws ParseException { * and assert that the resulting value is an array. * @param content the JSON content */ - public void assertValueIsArray(String content) throws ParseException { + public void assertValueIsArray(String content) { Object value = assertExistsAndReturn(content); assertThat(failureReason("an array", value), value, instanceOf(List.class)); } @@ -151,7 +165,7 @@ public void assertValueIsArray(String content) throws ParseException { * @param content the JSON content * @since 4.2.1 */ - public void assertValueIsMap(String content) throws ParseException { + public void assertValueIsMap(String content) { Object value = assertExistsAndReturn(content); assertThat(failureReason("a map", value), value, instanceOf(Map.class)); } @@ -164,7 +178,7 @@ public void assertValueIsMap(String content) throws ParseException { * that the value at the given path is not empty. * @param content the JSON content */ - public void exists(String content) throws ParseException { + public void exists(String content) { assertExistsAndReturn(content); } @@ -176,7 +190,7 @@ public void exists(String content) throws ParseException { * that the value at the given path is empty. * @param content the JSON content */ - public void doesNotExist(String content) throws ParseException { + public void doesNotExist(String content) { Object value; try { value = evaluateJsonPath(content); @@ -189,7 +203,7 @@ public void doesNotExist(String content) throws ParseException { assertTrue(reason, ((List) value).isEmpty()); } else { - assertTrue(reason, value == null); + assertTrue(reason, (value == null)); } } @@ -200,7 +214,7 @@ public void doesNotExist(String content) throws ParseException { * {@link ObjectUtils#isEmpty(Object)}. * @param content the JSON content */ - public void assertValueIsEmpty(String content) throws ParseException { + public void assertValueIsEmpty(String content) { Object value = evaluateJsonPath(content); assertTrue(failureReason("an empty value", value), ObjectUtils.isEmpty(value)); } @@ -212,33 +226,37 @@ public void assertValueIsEmpty(String content) throws ParseException { * {@link ObjectUtils#isEmpty(Object)}. * @param content the JSON content */ - public void assertValueIsNotEmpty(String content) throws ParseException { + public void assertValueIsNotEmpty(String content) { Object value = evaluateJsonPath(content); assertTrue(failureReason("a non-empty value", value), !ObjectUtils.isEmpty(value)); } private String failureReason(String expectedDescription, Object value) { return String.format("Expected %s at JSON path \"%s\" but found: %s", expectedDescription, this.expression, - ObjectUtils.nullSafeToString(StringUtils.quoteIfString(value))); + ObjectUtils.nullSafeToString(StringUtils.quoteIfString(value))); } - private Object evaluateJsonPath(String content) throws ParseException { + private Object evaluateJsonPath(String content) { String message = "No value at JSON path \"" + this.expression + "\", exception: "; try { return this.jsonPath.read(content); } - catch (InvalidPathException ex) { + catch (Throwable ex) { throw new AssertionError(message + ex.getMessage()); } - catch (ArrayIndexOutOfBoundsException ex) { - throw new AssertionError(message + ex.getMessage()); + } + + private Object evaluateJsonPath(String content, Class targetType) { + String message = "No value at JSON path \"" + this.expression + "\", exception: "; + try { + return JsonPath.parse(content).read(this.expression, targetType); } - catch (IndexOutOfBoundsException ex) { + catch (Throwable ex) { throw new AssertionError(message + ex.getMessage()); } } - private Object assertExistsAndReturn(String content) throws ParseException { + private Object assertExistsAndReturn(String content) { Object value = evaluateJsonPath(content); String reason = "No value at JSON path \"" + this.expression + "\""; assertTrue(reason, value != null); diff --git a/spring-test/src/main/java/org/springframework/test/web/client/match/JsonPathRequestMatchers.java b/spring-test/src/main/java/org/springframework/test/web/client/match/JsonPathRequestMatchers.java index 2c3b7a99f94..f308ddc796d 100644 --- a/spring-test/src/main/java/org/springframework/test/web/client/match/JsonPathRequestMatchers.java +++ b/spring-test/src/main/java/org/springframework/test/web/client/match/JsonPathRequestMatchers.java @@ -69,6 +69,23 @@ protected void matchInternal(MockClientHttpRequest request) throws IOException, }; } + /** + * An overloaded variant of (@link {@link #value(Matcher)} that also + * accepts a target type for the resulting value that the matcher can work + * reliably against. This can be useful for matching numbers reliably for + * example coercing an integer into a double. + * @since 4.3.3 + */ + public RequestMatcher value(final Matcher matcher, final Class targetType) { + return new AbstractJsonPathRequestMatcher() { + @Override + protected void matchInternal(MockClientHttpRequest request) throws IOException, ParseException { + String body = request.getBodyAsString(); + JsonPathRequestMatchers.this.jsonPathHelper.assertValue(body, matcher, targetType); + } + }; + } + /** * Evaluate the JSON path expression against the request content and * assert that the result is equal to the supplied value. diff --git a/spring-test/src/test/java/org/springframework/test/util/JsonPathExpectationsHelperTests.java b/spring-test/src/test/java/org/springframework/test/util/JsonPathExpectationsHelperTests.java index ba44e894870..5fa61814cce 100644 --- a/spring-test/src/test/java/org/springframework/test/util/JsonPathExpectationsHelperTests.java +++ b/spring-test/src/test/java/org/springframework/test/util/JsonPathExpectationsHelperTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2015 the original author or authors. + * Copyright 2004-2016 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. @@ -20,7 +20,7 @@ import org.junit.Test; import org.junit.rules.ExpectedException; -import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.core.Is.*; /** * Unit tests for {@link JsonPathExpectationsHelper}. @@ -222,11 +222,14 @@ public void assertValue() throws Exception { new JsonPathExpectationsHelper("$.num").assertValue(CONTENT, 5); } - @Test - public void assertValueWithDifferentExpectedType() throws Exception { - exception.expect(AssertionError.class); - exception.expectMessage(equalTo("At JSON path \"$.num\", type of value expected: but was:")); - new JsonPathExpectationsHelper("$.num").assertValue(CONTENT, "5"); + @Test // SPR-14498 + public void assertValueWithNumberConversion() throws Exception { + new JsonPathExpectationsHelper("$.num").assertValue(CONTENT, 5.0); + } + + @Test // SPR-14498 + public void assertValueWithNumberConversionAndMatcher() throws Exception { + new JsonPathExpectationsHelper("$.num").assertValue(CONTENT, is(5.0), Double.class); } @Test From 52447efb97ae584a0242172c4b98a9767dc38e53 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 26 Aug 2016 17:29:00 +0200 Subject: [PATCH 0134/1274] Avoid getParameterType use with Oracle 12c driver by default Issue: SPR-14629 Issue: SPR-14574 Issue: SPR-14191 --- .../jdbc/core/StatementCreatorUtils.java | 28 ++++++++++++++----- .../jdbc/core/StatementCreatorUtilsTests.java | 3 +- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/StatementCreatorUtils.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/StatementCreatorUtils.java index c7b754d25ba..71a6c433fbe 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/StatementCreatorUtils.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/StatementCreatorUtils.java @@ -79,7 +79,7 @@ public abstract class StatementCreatorUtils { public static final String IGNORE_GETPARAMETERTYPE_PROPERTY_NAME = "spring.jdbc.getParameterType.ignore"; - static final boolean shouldIgnoreGetParameterType = SpringProperties.getFlag(IGNORE_GETPARAMETERTYPE_PROPERTY_NAME); + static final Boolean shouldIgnoreGetParameterType; static final Set driversWithNoSupportForGetParameterType = Collections.newSetFromMap(new ConcurrentHashMap(1)); @@ -89,6 +89,9 @@ public abstract class StatementCreatorUtils { private static final Map, Integer> javaTypeToSqlTypeMap = new HashMap, Integer>(32); static { + String propVal = SpringProperties.getProperty(IGNORE_GETPARAMETERTYPE_PROPERTY_NAME); + shouldIgnoreGetParameterType = (propVal != null ? Boolean.valueOf(propVal) : null); + javaTypeToSqlTypeMap.put(boolean.class, Types.BOOLEAN); javaTypeToSqlTypeMap.put(Boolean.class, Types.BOOLEAN); javaTypeToSqlTypeMap.put(byte.class, Types.TINYINT); @@ -246,18 +249,29 @@ private static void setNull(PreparedStatement ps, int paramIndex, int sqlType, S Integer sqlTypeToUse = null; DatabaseMetaData dbmd = null; String jdbcDriverName = null; - boolean checkGetParameterType = !shouldIgnoreGetParameterType; - if (checkGetParameterType && !driversWithNoSupportForGetParameterType.isEmpty()) { + boolean tryGetParameterType = true; + + if (shouldIgnoreGetParameterType == null) { try { dbmd = ps.getConnection().getMetaData(); jdbcDriverName = dbmd.getDriverName(); - checkGetParameterType = !driversWithNoSupportForGetParameterType.contains(jdbcDriverName); + tryGetParameterType = !driversWithNoSupportForGetParameterType.contains(jdbcDriverName); + if (tryGetParameterType && jdbcDriverName.startsWith("Oracle")) { + // Avoid getParameterType use with Oracle 12c driver by default: + // needs to be explicitly activated through spring.jdbc.getParameterType.ignore=false + tryGetParameterType = false; + driversWithNoSupportForGetParameterType.add(jdbcDriverName); + } } catch (Throwable ex) { logger.debug("Could not check connection metadata", ex); } } - if (checkGetParameterType) { + else { + tryGetParameterType = !shouldIgnoreGetParameterType; + } + + if (tryGetParameterType) { try { sqlTypeToUse = ps.getParameterMetaData().getParameterType(paramIndex); } @@ -267,6 +281,7 @@ private static void setNull(PreparedStatement ps, int paramIndex, int sqlType, S } } } + if (sqlTypeToUse == null) { // JDBC driver not compliant with JDBC 3.0 -> proceed with database-specific checks sqlTypeToUse = Types.NULL; @@ -277,8 +292,7 @@ private static void setNull(PreparedStatement ps, int paramIndex, int sqlType, S if (jdbcDriverName == null) { jdbcDriverName = dbmd.getDriverName(); } - if (checkGetParameterType && - !(jdbcDriverName.startsWith("Oracle") && dbmd.getDriverMajorVersion() >= 12)) { + if (shouldIgnoreGetParameterType == null) { // Register JDBC driver with no support for getParameterType, except for the // Oracle 12c driver where getParameterType fails for specific statements only // (so an exception thrown above does not indicate general lack of support). diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/core/StatementCreatorUtilsTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/core/StatementCreatorUtilsTests.java index 1c7cb2e39e8..799d8a5def9 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/core/StatementCreatorUtilsTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/core/StatementCreatorUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -103,7 +103,6 @@ public void testSetParameterValueWithNullAndGetParameterTypeWorking() throws SQL given(pmd.getParameterType(1)).willReturn(Types.SMALLINT); StatementCreatorUtils.setParameterValue(preparedStatement, 1, SqlTypeValue.TYPE_UNKNOWN, null, null); verify(pmd).getParameterType(1); - verify(preparedStatement, never()).getConnection(); verify(preparedStatement).setNull(1, Types.SMALLINT); assertTrue(StatementCreatorUtils.driversWithNoSupportForGetParameterType.isEmpty()); } From e828be96f02c7e2742be6d71ac092f4c623fe453 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 26 Aug 2016 17:29:16 +0200 Subject: [PATCH 0135/1274] Polishing --- .../MappingJackson2MessageConverterTests.java | 31 +++++++++++-------- .../core/GenericMessagingTemplateTests.java | 11 ++++--- ...jectToStringHttpMessageConverterTests.java | 9 +----- .../AsyncRestTemplateIntegrationTests.java | 15 +++------ .../client/RestTemplateIntegrationTests.java | 9 ++++-- ...uestPartServletServerHttpRequestTests.java | 25 +++++---------- .../RequestPartIntegrationTests.java | 19 ++++++------ 7 files changed, 53 insertions(+), 66 deletions(-) diff --git a/spring-messaging/src/test/java/org/springframework/messaging/converter/MappingJackson2MessageConverterTests.java b/spring-messaging/src/test/java/org/springframework/messaging/converter/MappingJackson2MessageConverterTests.java index edc5b5a70bd..59f18e304cc 100644 --- a/spring-messaging/src/test/java/org/springframework/messaging/converter/MappingJackson2MessageConverterTests.java +++ b/spring-messaging/src/test/java/org/springframework/messaging/converter/MappingJackson2MessageConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -199,6 +199,20 @@ public void toMessageJsonView() throws Exception { } + + @JsonView(MyJacksonView1.class) + public JacksonViewBean jsonViewResponse() { + JacksonViewBean bean = new JacksonViewBean(); + bean.setWithView1("with"); + bean.setWithView2("with"); + bean.setWithoutView("with"); + return bean; + } + + public void jsonViewPayload(@JsonView(MyJacksonView2.class) JacksonViewBean payload) { + } + + public static class MyBean { private String string; @@ -262,9 +276,12 @@ public void setArray(String[] array) { } } + public interface MyJacksonView1 {}; + public interface MyJacksonView2 {}; + public static class JacksonViewBean { @JsonView(MyJacksonView1.class) @@ -300,16 +317,4 @@ public void setWithoutView(String withoutView) { } } - @JsonView(MyJacksonView1.class) - public JacksonViewBean jsonViewResponse() { - JacksonViewBean bean = new JacksonViewBean(); - bean.setWithView1("with"); - bean.setWithView2("with"); - bean.setWithoutView("with"); - return bean; - } - - public void jsonViewPayload(@JsonView(MyJacksonView2.class) JacksonViewBean payload) { - } - } diff --git a/spring-messaging/src/test/java/org/springframework/messaging/core/GenericMessagingTemplateTests.java b/spring-messaging/src/test/java/org/springframework/messaging/core/GenericMessagingTemplateTests.java index 44baf9dc55b..fdd7ef946fe 100644 --- a/spring-messaging/src/test/java/org/springframework/messaging/core/GenericMessagingTemplateTests.java +++ b/spring-messaging/src/test/java/org/springframework/messaging/core/GenericMessagingTemplateTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -66,7 +66,6 @@ public void setup() { @Test public void sendAndReceive() { - SubscribableChannel channel = new ExecutorSubscribableChannel(this.executor); channel.subscribe(new MessageHandler() { @Override @@ -82,7 +81,6 @@ public void handleMessage(Message message) throws MessagingException { @Test public void sendAndReceiveTimeout() throws InterruptedException { - final AtomicReference failure = new AtomicReference(); final CountDownLatch latch = new CountDownLatch(1); @@ -118,8 +116,9 @@ public void handleMessage(Message message) throws MessagingException { assertNull(this.template.convertSendAndReceive(channel, "request", String.class)); assertTrue(latch.await(1000, TimeUnit.MILLISECONDS)); - if (failure.get() != null) { - throw new AssertionError(failure.get()); + Throwable ex = failure.get(); + if (ex != null) { + throw new AssertionError(ex); } } @@ -138,6 +137,7 @@ public void convertAndSendWithSimpMessageHeaders() { assertFalse(accessor.isMutable()); } + private class TestDestinationResolver implements DestinationResolver { @Override @@ -145,4 +145,5 @@ public MessageChannel resolveDestination(String name) throws DestinationResoluti return messageChannel; } } + } diff --git a/spring-web/src/test/java/org/springframework/http/converter/ObjectToStringHttpMessageConverterTests.java b/spring-web/src/test/java/org/springframework/http/converter/ObjectToStringHttpMessageConverterTests.java index f7e05e60f43..0b4bcbc78be 100644 --- a/spring-web/src/test/java/org/springframework/http/converter/ObjectToStringHttpMessageConverterTests.java +++ b/spring-web/src/test/java/org/springframework/http/converter/ObjectToStringHttpMessageConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -123,28 +123,21 @@ public void writeAcceptCharsetTurnedOff() throws IOException { @Test public void read() throws IOException { MockHttpServletRequest request = new MockHttpServletRequest(); - request.setContentType(MediaType.TEXT_PLAIN_VALUE); Short shortValue = Short.valueOf((short) 781); - request.setContent(shortValue.toString().getBytes( StringHttpMessageConverter.DEFAULT_CHARSET)); - assertEquals(shortValue, this.converter.read(Short.class, new ServletServerHttpRequest(request))); Float floatValue = Float.valueOf(123); - request.setCharacterEncoding("UTF-16"); request.setContent(floatValue.toString().getBytes("UTF-16")); - assertEquals(floatValue, this.converter.read(Float.class, new ServletServerHttpRequest(request))); Long longValue = Long.valueOf(55819182821331L); - request.setCharacterEncoding("UTF-8"); request.setContent(longValue.toString().getBytes("UTF-8")); - assertEquals(longValue, this.converter.read(Long.class, new ServletServerHttpRequest(request))); } diff --git a/spring-web/src/test/java/org/springframework/web/client/AsyncRestTemplateIntegrationTests.java b/spring-web/src/test/java/org/springframework/web/client/AsyncRestTemplateIntegrationTests.java index 258a9cc48f4..724df72c639 100644 --- a/spring-web/src/test/java/org/springframework/web/client/AsyncRestTemplateIntegrationTests.java +++ b/spring-web/src/test/java/org/springframework/web/client/AsyncRestTemplateIntegrationTests.java @@ -50,14 +50,7 @@ import org.springframework.util.concurrent.ListenableFuture; import org.springframework.util.concurrent.ListenableFutureCallback; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; /** * @author Arjen Poutsma @@ -185,7 +178,7 @@ public void headForHeadersCallbackWithLambdas() throws Exception { @Test public void postForLocation() throws Exception { HttpHeaders entityHeaders = new HttpHeaders(); - entityHeaders.setContentType(new MediaType("text", "plain", Charset.forName("ISO-8859-15"))); + entityHeaders.setContentType(new MediaType("text", "plain", Charset.forName("ISO-8859-1"))); HttpEntity entity = new HttpEntity<>(helloWorld, entityHeaders); Future locationFuture = template.postForLocation(baseUrl + "/{method}", entity, "post"); URI location = locationFuture.get(); @@ -195,7 +188,7 @@ public void postForLocation() throws Exception { @Test public void postForLocationCallback() throws Exception { HttpHeaders entityHeaders = new HttpHeaders(); - entityHeaders.setContentType(new MediaType("text", "plain", Charset.forName("ISO-8859-15"))); + entityHeaders.setContentType(new MediaType("text", "plain", Charset.forName("ISO-8859-1"))); HttpEntity entity = new HttpEntity<>(helloWorld, entityHeaders); final URI expected = new URI(baseUrl + "/post/1"); ListenableFuture locationFuture = template.postForLocation(baseUrl + "/{method}", entity, "post"); @@ -215,7 +208,7 @@ public void onFailure(Throwable ex) { @Test public void postForLocationCallbackWithLambdas() throws Exception { HttpHeaders entityHeaders = new HttpHeaders(); - entityHeaders.setContentType(new MediaType("text", "plain", Charset.forName("ISO-8859-15"))); + entityHeaders.setContentType(new MediaType("text", "plain", Charset.forName("ISO-8859-1"))); HttpEntity entity = new HttpEntity<>(helloWorld, entityHeaders); final URI expected = new URI(baseUrl + "/post/1"); ListenableFuture locationFuture = template.postForLocation(baseUrl + "/{method}", entity, "post"); diff --git a/spring-web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java b/spring-web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java index d5f00fc2548..9d9b654c820 100644 --- a/spring-web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java +++ b/spring-web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java @@ -27,6 +27,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.annotation.JsonView; import org.junit.Test; import org.springframework.core.ParameterizedTypeReference; @@ -44,8 +45,6 @@ import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; -import com.fasterxml.jackson.annotation.JsonView; - import static org.junit.Assert.*; /** @@ -264,9 +263,12 @@ public void jsonPostForObjectWithJacksonTypeInfoList() throws URISyntaxException assertTrue(content.contains("\"type\":\"bar\"")); } + public interface MyJacksonView1 {}; + public interface MyJacksonView2 {}; + public static class MySampleBean { @JsonView(MyJacksonView1.class) @@ -311,6 +313,7 @@ public void setWithout(String without) { } } + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") public static class ParentClass { @@ -332,6 +335,7 @@ public void setParentProperty(String parentProperty) { } } + @JsonTypeName("foo") public static class Foo extends ParentClass { @@ -343,6 +347,7 @@ public Foo(String parentProperty) { } } + @JsonTypeName("bar") public static class Bar extends ParentClass { diff --git a/spring-web/src/test/java/org/springframework/web/multipart/support/RequestPartServletServerHttpRequestTests.java b/spring-web/src/test/java/org/springframework/web/multipart/support/RequestPartServletServerHttpRequestTests.java index f821dab48c1..a9306cae0ec 100644 --- a/spring-web/src/test/java/org/springframework/web/multipart/support/RequestPartServletServerHttpRequestTests.java +++ b/spring-web/src/test/java/org/springframework/web/multipart/support/RequestPartServletServerHttpRequestTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -18,7 +18,6 @@ import java.net.URI; import java.nio.charset.Charset; - import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; @@ -33,9 +32,7 @@ import org.springframework.util.FileCopyUtils; import org.springframework.web.multipart.MultipartFile; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.*; /** * @author Rossen Stoyanchev @@ -89,9 +86,7 @@ public void getBody() throws Exception { assertArrayEquals(bytes, result); } - // SPR-13317 - - @Test + @Test // SPR-13317 public void getBodyWithWrappedRequest() throws Exception { byte[] bytes = "content".getBytes("UTF-8"); MultipartFile part = new MockMultipartFile("part", "", "application/json", bytes); @@ -103,12 +98,9 @@ public void getBodyWithWrappedRequest() throws Exception { assertArrayEquals(bytes, result); } - // SPR-13096 - - @Test + @Test // SPR-13096 public void getBodyViaRequestParameter() throws Exception { MockMultipartHttpServletRequest mockRequest = new MockMultipartHttpServletRequest() { - @Override public HttpHeaders getMultipartHeaders(String paramOrFileName) { HttpHeaders headers = new HttpHeaders(); @@ -116,10 +108,10 @@ public HttpHeaders getMultipartHeaders(String paramOrFileName) { return headers; } }; + byte[] bytes = {(byte) 0xC4}; - mockRequest.setParameter("part", new String(bytes, Charset.forName("iso-8859-1"))); + mockRequest.setParameter("part", new String(bytes, Charset.forName("ISO-8859-1"))); ServerHttpRequest request = new RequestPartServletServerHttpRequest(mockRequest, "part"); - byte[] result = FileCopyUtils.copyToByteArray(request.getBody()); assertArrayEquals(bytes, result); } @@ -127,7 +119,6 @@ public HttpHeaders getMultipartHeaders(String paramOrFileName) { @Test public void getBodyViaRequestParameterWithRequestEncoding() throws Exception { MockMultipartHttpServletRequest mockRequest = new MockMultipartHttpServletRequest() { - @Override public HttpHeaders getMultipartHeaders(String paramOrFileName) { HttpHeaders headers = new HttpHeaders(); @@ -135,11 +126,11 @@ public HttpHeaders getMultipartHeaders(String paramOrFileName) { return headers; } }; + byte[] bytes = {(byte) 0xC4}; - mockRequest.setParameter("part", new String(bytes, Charset.forName("iso-8859-1"))); + mockRequest.setParameter("part", new String(bytes, Charset.forName("ISO-8859-1"))); mockRequest.setCharacterEncoding("iso-8859-1"); ServerHttpRequest request = new RequestPartServletServerHttpRequest(mockRequest, "part"); - byte[] result = FileCopyUtils.copyToByteArray(request.getBody()); assertArrayEquals(bytes, result); } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestPartIntegrationTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestPartIntegrationTests.java index 97f42ee8e15..731c44034ed 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestPartIntegrationTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestPartIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,7 +30,6 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; - import org.junit.AfterClass; import org.junit.Assert; import org.junit.Before; @@ -116,6 +115,13 @@ public static void startServer() throws Exception { baseUrl = "http://localhost:" + connector.getLocalPort(); } + @AfterClass + public static void stopServer() throws Exception { + if (server != null) { + server.stop(); + } + } + @Before public void setUp() { ByteArrayHttpMessageConverter emptyBodyConverter = new ByteArrayHttpMessageConverter(); @@ -134,13 +140,6 @@ public void setUp() { restTemplate.setMessageConverters(Collections.singletonList(converter)); } - @AfterClass - public static void stopServer() throws Exception { - if (server != null) { - server.stop(); - } - } - @Test public void commonsMultipartResolver() throws Exception { @@ -172,7 +171,7 @@ public void standardMultipartResolverWithEncodedFileName() throws Exception { RequestEntity requestEntity = RequestEntity.post(new URI(baseUrl + "/standard-resolver/spr13319")) .contentType(new MediaType(MediaType.MULTIPART_FORM_DATA, params)) - .body(content.getBytes(Charset.forName("us-ascii"))); + .body(content.getBytes(Charset.forName("US-ASCII"))); ByteArrayHttpMessageConverter converter = new ByteArrayHttpMessageConverter(); converter.setSupportedMediaTypes(Collections.singletonList(MediaType.MULTIPART_FORM_DATA)); From 430180aa96693d26c17aaef20d6934e0e1f26bfa Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 26 Aug 2016 18:33:13 +0200 Subject: [PATCH 0136/1274] Polishing --- .../SendToMethodReturnValueHandlerTests.java | 115 ++++++++++-------- .../samples/standalone/AsyncTests.java | 14 +-- .../AsyncRestTemplateIntegrationTests.java | 1 - .../client/RestTemplateIntegrationTests.java | 4 +- .../view/script/ScriptTemplateViewTests.java | 7 +- 5 files changed, 75 insertions(+), 66 deletions(-) diff --git a/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SendToMethodReturnValueHandlerTests.java b/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SendToMethodReturnValueHandlerTests.java index e82dedee933..75a1e0f1bb4 100644 --- a/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SendToMethodReturnValueHandlerTests.java +++ b/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SendToMethodReturnValueHandlerTests.java @@ -42,7 +42,9 @@ import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.converter.MappingJackson2MessageConverter; import org.springframework.messaging.converter.StringMessageConverter; +import org.springframework.messaging.handler.DestinationPatternsMessageCondition; import org.springframework.messaging.handler.annotation.SendTo; +import org.springframework.messaging.handler.annotation.support.DestinationVariableMethodArgumentResolver; import org.springframework.messaging.simp.SimpMessageHeaderAccessor; import org.springframework.messaging.simp.SimpMessageSendingOperations; import org.springframework.messaging.simp.SimpMessagingTemplate; @@ -54,9 +56,6 @@ import static org.junit.Assert.*; import static org.mockito.BDDMockito.*; -import static org.springframework.messaging.handler.DestinationPatternsMessageCondition.*; -import static org.springframework.messaging.handler.annotation.support.DestinationVariableMethodArgumentResolver.*; -import static org.springframework.messaging.support.MessageHeaderAccessor.*; /** * Test fixture for {@link SendToMethodReturnValueHandlerTests}. @@ -357,7 +356,8 @@ public void testHeadersToSend() throws Exception { verify(messagingTemplate).convertAndSend(eq("/topic/dest"), eq(PAYLOAD), captor.capture()); MessageHeaders headers = captor.getValue(); - SimpMessageHeaderAccessor accessor = getAccessor(headers, SimpMessageHeaderAccessor.class); + SimpMessageHeaderAccessor accessor = + MessageHeaderAccessor.getAccessor(headers, SimpMessageHeaderAccessor.class); assertNotNull(accessor); assertTrue(accessor.isMutable()); assertEquals("sess1", accessor.getSessionId()); @@ -399,7 +399,7 @@ public void sendToWithDestinationPlaceholders() throws Exception { SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.create(); accessor.setSessionId(sessionId); accessor.setSubscriptionId("sub1"); - accessor.setHeader(DESTINATION_TEMPLATE_VARIABLES_HEADER, vars); + accessor.setHeader(DestinationVariableMethodArgumentResolver.DESTINATION_TEMPLATE_VARIABLES_HEADER, vars); Message message = MessageBuilder.createMessage(PAYLOAD, accessor.getMessageHeaders()); this.handler.handleReturnValue(PAYLOAD, this.sendToWithPlaceholdersReturnType, message); @@ -549,7 +549,7 @@ private Message createMessage(String sessId, String subsId, String destPrefix headerAccessor.setSubscriptionId(subsId); if (dest != null && destPrefix != null) { headerAccessor.setDestination(destPrefix + dest); - headerAccessor.setHeader(LOOKUP_DESTINATION_HEADER, dest); + headerAccessor.setHeader(DestinationPatternsMessageCondition.LOOKUP_DESTINATION_HEADER, dest); } if (user != null) { headerAccessor.setUser(user); @@ -563,83 +563,55 @@ private SimpMessageHeaderAccessor getCapturedAccessor(int index) { } - private static class TestUser implements Principal { - - public String getName() { - return "joe"; - } - - public boolean implies(Subject subject) { - return false; - } - } - - private static class UniqueUser extends TestUser implements DestinationUserNameProvider { - - @Override - public String getDestinationUserName() { - return "Me myself and I"; - } - } - - @SendTo - @Retention(RetentionPolicy.RUNTIME) - @interface MySendTo { - - @AliasFor(annotation = SendTo.class, attribute = "value") - String[] dest(); - } - - @SendToUser - @Retention(RetentionPolicy.RUNTIME) - @interface MySendToUser { - - @AliasFor(annotation = SendToUser.class, attribute = "destinations") - String[] dest(); - } - - @SuppressWarnings("unused") String handleNoAnnotations() { return PAYLOAD; } - @SendTo @SuppressWarnings("unused") + @SendTo + @SuppressWarnings("unused") String handleAndSendToDefaultDestination() { return PAYLOAD; } - @SendTo({"/dest1", "/dest2"}) @SuppressWarnings("unused") + @SendTo({"/dest1", "/dest2"}) + @SuppressWarnings("unused") String handleAndSendTo() { return PAYLOAD; } - @SendTo("/topic/chat.message.filtered.{roomName}") @SuppressWarnings("unused") + @SendTo("/topic/chat.message.filtered.{roomName}") + @SuppressWarnings("unused") String handleAndSendToWithPlaceholders() { return PAYLOAD; } - @SendToUser @SuppressWarnings("unused") + @SendToUser + @SuppressWarnings("unused") String handleAndSendToUserDefaultDestination() { return PAYLOAD; } - @SendToUser(broadcast = false) @SuppressWarnings("unused") + @SendToUser(broadcast = false) + @SuppressWarnings("unused") String handleAndSendToUserDefaultDestinationSingleSession() { return PAYLOAD; } - @SendToUser({"/dest1", "/dest2"}) @SuppressWarnings("unused") + @SendToUser({"/dest1", "/dest2"}) + @SuppressWarnings("unused") String handleAndSendToUser() { return PAYLOAD; } - @SendToUser(destinations = { "/dest1", "/dest2" }, broadcast = false) @SuppressWarnings("unused") + @SendToUser(destinations = { "/dest1", "/dest2" }, broadcast = false) + @SuppressWarnings("unused") String handleAndSendToUserSingleSession() { return PAYLOAD; } - @JsonView(MyJacksonView1.class) @SuppressWarnings("unused") + @JsonView(MyJacksonView1.class) + @SuppressWarnings("unused") JacksonViewBean handleAndSendToJsonView() { JacksonViewBean payload = new JacksonViewBean(); payload.setWithView1("with"); @@ -649,6 +621,45 @@ JacksonViewBean handleAndSendToJsonView() { } + private static class TestUser implements Principal { + + public String getName() { + return "joe"; + } + + public boolean implies(Subject subject) { + return false; + } + } + + + private static class UniqueUser extends TestUser implements DestinationUserNameProvider { + + @Override + public String getDestinationUserName() { + return "Me myself and I"; + } + } + + + @SendTo + @Retention(RetentionPolicy.RUNTIME) + @interface MySendTo { + + @AliasFor(annotation = SendTo.class, attribute = "value") + String[] dest(); + } + + + @SendToUser + @Retention(RetentionPolicy.RUNTIME) + @interface MySendToUser { + + @AliasFor(annotation = SendToUser.class, attribute = "destinations") + String[] dest(); + } + + @MySendTo(dest = "/dest-default") @SuppressWarnings("unused") private static class SendToTestBean { @@ -667,6 +678,7 @@ String handleAndSendToOverride() { } } + @MySendToUser(dest = "/dest-default") @SuppressWarnings("unused") private static class SendToUserTestBean { @@ -685,6 +697,7 @@ String handleAndSendToOverride() { } } + @MySendToUser(dest = "/dest-default") @SuppressWarnings("unused") private static class SendToUserWithSendToOverrideTestBean { @@ -701,8 +714,10 @@ String handleAndSendToOverride() { private interface MyJacksonView1 {} + private interface MyJacksonView2 {} + @SuppressWarnings("unused") private static class JacksonViewBean { diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/AsyncTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/AsyncTests.java index 541401b86f9..6dd110507cd 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/AsyncTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/AsyncTests.java @@ -70,7 +70,7 @@ public void callable() throws Exception { this.mockMvc.perform(asyncDispatch(mvcResult)) .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)) .andExpect(content().string("{\"name\":\"Joe\",\"someDouble\":0.0,\"someBoolean\":false}")); } @@ -98,7 +98,7 @@ public void streamingJson() throws Exception { .andExpect(request().asyncStarted()) .andDo(MvcResult::getAsyncResult) .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)) .andExpect(content().string("{\"name\":\"Joe\",\"someDouble\":0.5}")); } @@ -112,7 +112,7 @@ public void deferredResult() throws Exception { this.mockMvc.perform(asyncDispatch(mvcResult)) .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)) .andExpect(content().string("{\"name\":\"Joe\",\"someDouble\":0.0,\"someBoolean\":false}")); } @@ -125,7 +125,7 @@ public void deferredResultWithImmediateValue() throws Exception { this.mockMvc.perform(asyncDispatch(mvcResult)) .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)) .andExpect(content().string("{\"name\":\"Joe\",\"someDouble\":0.0,\"someBoolean\":false}")); } @@ -150,7 +150,7 @@ public void listenableFuture() throws Exception { this.mockMvc.perform(asyncDispatch(mvcResult)) .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)) .andExpect(content().string("{\"name\":\"Joe\",\"someDouble\":0.0,\"someBoolean\":false}")); } @@ -162,7 +162,7 @@ public void completableFutureWithImmediateValue() throws Exception { this.mockMvc.perform(asyncDispatch(mvcResult)) .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)) .andExpect(content().string("{\"name\":\"Joe\",\"someDouble\":0.0,\"someBoolean\":false}")); } @@ -183,7 +183,7 @@ public void printAsyncResult() throws Exception { this.mockMvc.perform(asyncDispatch(mvcResult)) .andDo(print(writer)) .andExpect(status().isOk()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)) .andExpect(content().string("{\"name\":\"Joe\",\"someDouble\":0.0,\"someBoolean\":false}")); assertTrue(writer.toString().contains("Async started = false")); diff --git a/spring-web/src/test/java/org/springframework/web/client/AsyncRestTemplateIntegrationTests.java b/spring-web/src/test/java/org/springframework/web/client/AsyncRestTemplateIntegrationTests.java index 724df72c639..f3aac5602c2 100644 --- a/spring-web/src/test/java/org/springframework/web/client/AsyncRestTemplateIntegrationTests.java +++ b/spring-web/src/test/java/org/springframework/web/client/AsyncRestTemplateIntegrationTests.java @@ -630,7 +630,6 @@ public ListenableFuture intercept(HttpRequest request, byte[ AsyncClientHttpRequestExecution execution) throws IOException { request = new HttpRequestWrapper(request) { - @Override public URI getURI() { try { diff --git a/spring-web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java b/spring-web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java index 9d9b654c820..71f5c4a326f 100644 --- a/spring-web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java +++ b/spring-web/src/test/java/org/springframework/web/client/RestTemplateIntegrationTests.java @@ -240,9 +240,7 @@ public void jsonPostForObjectWithJacksonView() throws URISyntaxException { assertFalse(s.contains("\"without\":\"without\"")); } - // SPR-12123 - - @Test + @Test // SPR-12123 public void serverPort() { String s = template.getForObject("http://localhost:{port}/get", String.class, port); assertEquals("Invalid content", helloWorld, s); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/script/ScriptTemplateViewTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/script/ScriptTemplateViewTests.java index a337901e516..a652d7e9826 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/script/ScriptTemplateViewTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/script/ScriptTemplateViewTests.java @@ -67,16 +67,16 @@ public void setup() { this.view = new ScriptTemplateView(); } + @Test public void missingScriptTemplateConfig() throws Exception { try { this.view.setApplicationContext(new StaticApplicationContext()); + fail("Should have thrown ApplicationContextException"); } catch (ApplicationContextException ex) { assertTrue(ex.getMessage().contains("ScriptTemplateConfig")); - return; } - fail(); } @Test @@ -158,7 +158,6 @@ public void nonInvocableScriptEngine() throws Exception { } catch (IllegalArgumentException ex) { assertThat(ex.getMessage(), containsString("instance")); - return; } } @@ -199,9 +198,7 @@ public void engineSetterAndNonSharedEngine() { } catch (IllegalArgumentException ex) { assertThat(ex.getMessage(), containsString("sharedEngine")); - return; } - fail(); } @Test // SPR-14210 From 5a004c3b2a336157ac4fb94e61ab22f42b1b76e9 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 26 Aug 2016 20:04:23 +0200 Subject: [PATCH 0137/1274] LiveBeansView exposes aliases as well Issue: SPR-14632 (cherry picked from commit 57cb7c7) --- .../context/support/LiveBeansView.java | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/support/LiveBeansView.java b/spring-context/src/main/java/org/springframework/context/support/LiveBeansView.java index b77e604634b..abac5b2711d 100644 --- a/spring-context/src/main/java/org/springframework/context/support/LiveBeansView.java +++ b/spring-context/src/main/java/org/springframework/context/support/LiveBeansView.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -165,6 +165,9 @@ protected String generateJson(Set contexts) { result.append(",\n"); } result.append("{\n\"bean\": \"").append(beanName).append("\",\n"); + result.append("\"aliases\": "); + appendArray(result, bf.getAliases(beanName)); + result.append(",\n"); String scope = bd.getScope(); if (!StringUtils.hasText(scope)) { scope = BeanDefinition.SCOPE_SINGLETON; @@ -178,16 +181,9 @@ protected String generateJson(Set contexts) { result.append("\"type\": null,\n"); } result.append("\"resource\": \"").append(getEscapedResourceDescription(bd)).append("\",\n"); - result.append("\"dependencies\": ["); - String[] dependencies = bf.getDependenciesForBean(beanName); - if (dependencies.length > 0) { - result.append("\""); - } - result.append(StringUtils.arrayToDelimitedString(dependencies, "\", \"")); - if (dependencies.length > 0) { - result.append("\""); - } - result.append("]\n}"); + result.append("\"dependencies\": "); + appendArray(result, bf.getDependenciesForBean(beanName)); + result.append("\n}"); elementAppended = true; } } @@ -241,4 +237,16 @@ else if (character == '"') { return result.toString(); } + private void appendArray(StringBuilder result, String[] arr) { + result.append('['); + if (arr.length > 0) { + result.append('\"'); + } + result.append(StringUtils.arrayToDelimitedString(arr, "\", \"")); + if (arr.length > 0) { + result.append('\"'); + } + result.append(']'); + } + } From fe404628e917a2efc0cf771a24dad50ca73618dc Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Mon, 29 Aug 2016 16:27:24 -0400 Subject: [PATCH 0138/1274] Fix media type regression in resource handling Issue: SPR-14577 --- ...MappingMediaTypeFileExtensionResolver.java | 4 ++ .../resource/ResourceHttpRequestHandler.java | 54 ++++++++----------- .../ResourceHttpRequestHandlerTests.java | 44 +++++++-------- .../resource/ResourceUrlProviderTests.java | 1 + 4 files changed, 50 insertions(+), 53 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/accept/MappingMediaTypeFileExtensionResolver.java b/spring-web/src/main/java/org/springframework/web/accept/MappingMediaTypeFileExtensionResolver.java index 49c38d6dd34..9c518c4eb43 100644 --- a/spring-web/src/main/java/org/springframework/web/accept/MappingMediaTypeFileExtensionResolver.java +++ b/spring-web/src/main/java/org/springframework/web/accept/MappingMediaTypeFileExtensionResolver.java @@ -67,6 +67,10 @@ public MappingMediaTypeFileExtensionResolver(Map mediaTypes) } + public Map getMediaTypes() { + return this.mediaTypes; + } + protected List getAllMediaTypes() { return new ArrayList(this.mediaTypes.values()); } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java index 823fec222cb..4e4cc695300 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java @@ -19,7 +19,9 @@ import java.io.IOException; import java.net.URLDecoder; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletResponse; @@ -51,6 +53,7 @@ import org.springframework.web.accept.ContentNegotiationManager; import org.springframework.web.accept.ContentNegotiationManagerFactoryBean; import org.springframework.web.accept.PathExtensionContentNegotiationStrategy; +import org.springframework.web.accept.ServletPathExtensionContentNegotiationStrategy; import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; @@ -111,7 +114,9 @@ public class ResourceHttpRequestHandler extends WebContentGenerator private ContentNegotiationManager contentNegotiationManager; - private final ContentNegotiationManagerFactoryBean cnmFactoryBean = new ContentNegotiationManagerFactoryBean(); + private ServletPathExtensionContentNegotiationStrategy pathExtensionStrategy; + + private ServletContext servletContext; private CorsConfiguration corsConfiguration; @@ -254,7 +259,7 @@ public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { @Override protected void initServletContext(ServletContext servletContext) { - this.cnmFactoryBean.setServletContext(servletContext); + this.servletContext = servletContext; } @@ -268,16 +273,13 @@ public void afterPropertiesSet() throws Exception { this.resourceResolvers.add(new PathResourceResolver()); } initAllowedLocations(); - if (this.contentNegotiationManager == null) { - this.cnmFactoryBean.afterPropertiesSet(); - this.contentNegotiationManager = this.cnmFactoryBean.getObject(); - } if (this.resourceHttpMessageConverter == null) { this.resourceHttpMessageConverter = new ResourceHttpMessageConverter(); } if (this.resourceRegionHttpMessageConverter == null) { this.resourceRegionHttpMessageConverter = new ResourceRegionHttpMessageConverter(); } + this.pathExtensionStrategy = initPathExtensionStrategy(); } /** @@ -300,6 +302,19 @@ protected void initAllowedLocations() { } } + protected ServletPathExtensionContentNegotiationStrategy initPathExtensionStrategy() { + Map mediaTypes = null; + if (getContentNegotiationManager() != null) { + PathExtensionContentNegotiationStrategy strategy = + getContentNegotiationManager().getStrategy(PathExtensionContentNegotiationStrategy.class); + if (strategy != null) { + mediaTypes = new HashMap(strategy.getMediaTypes()); + } + } + return new ServletPathExtensionContentNegotiationStrategy(this.servletContext, mediaTypes); + } + + /** * Processes a resource request. *

Checks for the existence of the requested resource in the configured list of locations. @@ -512,32 +527,7 @@ protected boolean isInvalidPath(String path) { */ @SuppressWarnings("deprecation") protected MediaType getMediaType(HttpServletRequest request, Resource resource) { - // For backwards compatibility - MediaType mediaType = getMediaType(resource); - if (mediaType != null) { - return mediaType; - } - - Class clazz = PathExtensionContentNegotiationStrategy.class; - PathExtensionContentNegotiationStrategy strategy = this.contentNegotiationManager.getStrategy(clazz); - if (strategy != null) { - mediaType = strategy.getMediaTypeForResource(resource); - } - - if (mediaType == null) { - ServletWebRequest webRequest = new ServletWebRequest(request); - try { - List mediaTypes = getContentNegotiationManager().resolveMediaTypes(webRequest); - if (!mediaTypes.isEmpty()) { - mediaType = mediaTypes.get(0); - } - } - catch (HttpMediaTypeNotAcceptableException ex) { - // Ignore - } - } - - return mediaType; + return this.pathExtensionStrategy.getMediaTypeForResource(resource); } /** diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandlerTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandlerTests.java index c78f75739ef..e835aff8c15 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandlerTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandlerTests.java @@ -248,37 +248,38 @@ public void getResourceWithRegisteredMediaType() throws Exception { ContentNegotiationManager manager = factory.getObject(); List paths = Collections.singletonList(new ClassPathResource("test/", getClass())); - this.handler = new ResourceHttpRequestHandler(); - this.handler.setLocations(paths); - this.handler.setContentNegotiationManager(manager); - this.handler.afterPropertiesSet(); + ResourceHttpRequestHandler handler = new ResourceHttpRequestHandler(); + handler.setServletContext(new MockServletContext()); + handler.setLocations(paths); + handler.setContentNegotiationManager(manager); + handler.afterPropertiesSet(); this.request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, "foo.css"); - this.handler.handleRequest(this.request, this.response); + handler.handleRequest(this.request, this.response); assertEquals("foo/bar", this.response.getContentType()); assertEquals("h1 { color:red; }", this.response.getContentAsString()); } - @Test // SPR-13658 - public void getResourceWithRegisteredMediaTypeDefaultStrategy() throws Exception { + @Test // SPR-14577 + public void getMediaTypeWithFavorPathExtensionOff() throws Exception { ContentNegotiationManagerFactoryBean factory = new ContentNegotiationManagerFactoryBean(); factory.setFavorPathExtension(false); - factory.setDefaultContentType(new MediaType("foo", "bar")); factory.afterPropertiesSet(); ContentNegotiationManager manager = factory.getObject(); List paths = Collections.singletonList(new ClassPathResource("test/", getClass())); - this.handler = new ResourceHttpRequestHandler(); - this.handler.setLocations(paths); - this.handler.setContentNegotiationManager(manager); - this.handler.afterPropertiesSet(); + ResourceHttpRequestHandler handler = new ResourceHttpRequestHandler(); + handler.setServletContext(new MockServletContext()); + handler.setLocations(paths); + handler.setContentNegotiationManager(manager); + handler.afterPropertiesSet(); - this.request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, "foo.css"); - this.handler.handleRequest(this.request, this.response); + this.request.addHeader("Accept", "application/json,text/plain,*/*"); + this.request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, "foo.html"); + handler.handleRequest(this.request, this.response); - assertEquals("foo/bar", this.response.getContentType()); - assertEquals("h1 { color:red; }", this.response.getContentAsString()); + assertEquals("text/html", this.response.getContentType()); } @Test // SPR-14368 @@ -297,13 +298,13 @@ public String getVirtualServerName() { }; List paths = Collections.singletonList(new ClassPathResource("test/", getClass())); - this.handler = new ResourceHttpRequestHandler(); - this.handler.setServletContext(servletContext); - this.handler.setLocations(paths); - this.handler.afterPropertiesSet(); + ResourceHttpRequestHandler handler = new ResourceHttpRequestHandler(); + handler.setServletContext(servletContext); + handler.setLocations(paths); + handler.afterPropertiesSet(); this.request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, "foo.css"); - this.handler.handleRequest(this.request, this.response); + handler.handleRequest(this.request, this.response); assertEquals("foo/bar", this.response.getContentType()); assertEquals("h1 { color:red; }", this.response.getContentAsString()); @@ -412,6 +413,7 @@ public void initAllowedLocationsWithExplicitConfiguration() throws Exception { ResourceHttpRequestHandler handler = new ResourceHttpRequestHandler(); handler.setResourceResolvers(Collections.singletonList(pathResolver)); + handler.setServletContext(new MockServletContext()); handler.setLocations(Arrays.asList(location1, location2)); handler.afterPropertiesSet(); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceUrlProviderTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceUrlProviderTests.java index fbb222c01b7..85d4e669cfe 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceUrlProviderTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceUrlProviderTests.java @@ -58,6 +58,7 @@ public class ResourceUrlProviderTests { public void setUp() throws Exception { this.locations.add(new ClassPathResource("test/", getClass())); this.locations.add(new ClassPathResource("testalternatepath/", getClass())); + this.handler.setServletContext(new MockServletContext()); this.handler.setLocations(locations); this.handler.afterPropertiesSet(); this.handlerMap.put("/resources/**", this.handler); From 198a74d793f18f246c3385b732a71a1bd7ff6ed3 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Mon, 29 Aug 2016 18:30:17 -0400 Subject: [PATCH 0139/1274] Support receipt on DISCONNECT with simple broker Issue: SPR-14568 --- .../simp/SimpMessageHeaderAccessor.java | 2 ++ .../broker/SimpleBrokerMessageHandler.java | 9 +++-- .../SimpleBrokerMessageHandlerTests.java | 23 +++++++++--- .../messaging/StompSubProtocolHandler.java | 22 ++++++++++-- .../StompSubProtocolHandlerTests.java | 36 ++++++++++++++++++- 5 files changed, 81 insertions(+), 11 deletions(-) diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/SimpMessageHeaderAccessor.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/SimpMessageHeaderAccessor.java index 7e3d7f12b69..5bec0672bb1 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/SimpMessageHeaderAccessor.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/SimpMessageHeaderAccessor.java @@ -65,6 +65,8 @@ public class SimpMessageHeaderAccessor extends NativeMessageHeaderAccessor { public static final String CONNECT_MESSAGE_HEADER = "simpConnectMessage"; + public static final String DISCONNECT_MESSAGE_HEADER = "simpDisconnectMessage"; + public static final String HEART_BEAT_HEADER = "simpHeartbeat"; diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/broker/SimpleBrokerMessageHandler.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/broker/SimpleBrokerMessageHandler.java index f58d7e7f927..eef8f689b20 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/broker/SimpleBrokerMessageHandler.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/broker/SimpleBrokerMessageHandler.java @@ -275,7 +275,7 @@ else if (SimpMessageType.CONNECT.equals(messageType)) { } else if (SimpMessageType.DISCONNECT.equals(messageType)) { logMessage(message); - handleDisconnect(sessionId, user); + handleDisconnect(sessionId, user, message); } else if (SimpMessageType.SUBSCRIBE.equals(messageType)) { logMessage(message); @@ -310,12 +310,15 @@ private void initHeaders(SimpMessageHeaderAccessor accessor) { } } - private void handleDisconnect(String sessionId, Principal user) { + private void handleDisconnect(String sessionId, Principal user, Message origMessage) { this.sessions.remove(sessionId); this.subscriptionRegistry.unregisterAllSubscriptions(sessionId); SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.create(SimpMessageType.DISCONNECT_ACK); accessor.setSessionId(sessionId); accessor.setUser(user); + if (origMessage != null) { + accessor.setHeader(SimpMessageHeaderAccessor.DISCONNECT_MESSAGE_HEADER, origMessage); + } initHeaders(accessor); Message message = MessageBuilder.createMessage(EMPTY_PAYLOAD, accessor.getMessageHeaders()); getClientOutboundChannel().send(message); @@ -432,7 +435,7 @@ public void run() { long now = System.currentTimeMillis(); for (SessionInfo info : sessions.values()) { if (info.getReadInterval() > 0 && (now - info.getLastReadTime()) > info.getReadInterval()) { - handleDisconnect(info.getSessiondId(), info.getUser()); + handleDisconnect(info.getSessiondId(), info.getUser(), null); } if (info.getWriteInterval() > 0 && (now - info.getLastWriteTime()) > info.getWriteInterval()) { SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.create(SimpMessageType.HEARTBEAT); diff --git a/spring-messaging/src/test/java/org/springframework/messaging/simp/broker/SimpleBrokerMessageHandlerTests.java b/spring-messaging/src/test/java/org/springframework/messaging/simp/broker/SimpleBrokerMessageHandlerTests.java index cc969cc5c7c..86d8b4f0fe7 100644 --- a/spring-messaging/src/test/java/org/springframework/messaging/simp/broker/SimpleBrokerMessageHandlerTests.java +++ b/spring-messaging/src/test/java/org/springframework/messaging/simp/broker/SimpleBrokerMessageHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,9 +16,6 @@ package org.springframework.messaging.simp.broker; -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; - import java.security.Principal; import java.util.Collections; import java.util.List; @@ -41,6 +38,21 @@ import org.springframework.messaging.support.MessageBuilder; import org.springframework.scheduling.TaskScheduler; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + /** * Unit tests for SimpleBrokerMessageHandler. * @@ -72,7 +84,7 @@ public class SimpleBrokerMessageHandlerTests { public void setup() { MockitoAnnotations.initMocks(this); this.messageHandler = new SimpleBrokerMessageHandler(this.clientInboundChannel, - this.clientOutboundChannel, this.brokerChannel, Collections.emptyList()); + this.clientOutboundChannel, this.brokerChannel, Collections.emptyList()); } @@ -130,6 +142,7 @@ public void subcribeDisconnectPublish() { Message captured = this.messageCaptor.getAllValues().get(0); assertEquals(SimpMessageType.DISCONNECT_ACK, SimpMessageHeaderAccessor.getMessageType(captured.getHeaders())); + assertSame(message, captured.getHeaders().get(SimpMessageHeaderAccessor.DISCONNECT_MESSAGE_HEADER)); assertEquals(sess1, SimpMessageHeaderAccessor.getSessionId(captured.getHeaders())); assertEquals("joe", SimpMessageHeaderAccessor.getUser(captured.getHeaders()).getName()); diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/messaging/StompSubProtocolHandler.java b/spring-websocket/src/main/java/org/springframework/web/socket/messaging/StompSubProtocolHandler.java index a56a4e06281..98a3d65aead 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/messaging/StompSubProtocolHandler.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/messaging/StompSubProtocolHandler.java @@ -34,6 +34,7 @@ import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; +import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.simp.SimpAttributes; import org.springframework.messaging.simp.SimpAttributesContextHolder; import org.springframework.messaging.simp.SimpMessageHeaderAccessor; @@ -479,8 +480,15 @@ else if (accessor instanceof SimpMessageHeaderAccessor) { stompAccessor = convertConnectAcktoStompConnected(stompAccessor); } else if (SimpMessageType.DISCONNECT_ACK.equals(messageType)) { - stompAccessor = StompHeaderAccessor.create(StompCommand.ERROR); - stompAccessor.setMessage("Session closed."); + String receipt = getDisconnectReceipt(stompAccessor); + if (receipt != null) { + stompAccessor = StompHeaderAccessor.create(StompCommand.RECEIPT); + stompAccessor.setReceiptId(receipt); + } + else { + stompAccessor = StompHeaderAccessor.create(StompCommand.ERROR); + stompAccessor.setMessage("Session closed."); + } } else if (SimpMessageType.HEARTBEAT.equals(messageType)) { stompAccessor = StompHeaderAccessor.createForHeartbeat(); @@ -533,6 +541,16 @@ else if (!acceptVersions.isEmpty()) { return connectedHeaders; } + private String getDisconnectReceipt(SimpMessageHeaderAccessor simpHeaders) { + String name = StompHeaderAccessor.DISCONNECT_MESSAGE_HEADER; + Message message = (Message) simpHeaders.getHeader(name); + if (message != null) { + StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class); + return accessor.getReceipt(); + } + return null; + } + protected StompHeaderAccessor toMutableAccessor(StompHeaderAccessor headerAccessor, Message message) { return (headerAccessor.isMutable() ? headerAccessor : StompHeaderAccessor.wrap(message)); } diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/messaging/StompSubProtocolHandlerTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/messaging/StompSubProtocolHandlerTests.java index 22214eafab3..1c388c08d0e 100644 --- a/spring-websocket/src/test/java/org/springframework/web/socket/messaging/StompSubProtocolHandlerTests.java +++ b/spring-websocket/src/test/java/org/springframework/web/socket/messaging/StompSubProtocolHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -169,6 +169,40 @@ public void handleMessageToClientWithSimpConnectAckDefaultHeartBeat() { "user-name:joe\n" + "\n" + "\u0000", actual.getPayload()); } + @Test + public void handleMessageToClientWithSimpDisconnectAck() { + + StompHeaderAccessor accessor = StompHeaderAccessor.create(StompCommand.DISCONNECT); + Message connectMessage = MessageBuilder.createMessage(EMPTY_PAYLOAD, accessor.getMessageHeaders()); + + SimpMessageHeaderAccessor ackAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.DISCONNECT_ACK); + ackAccessor.setHeader(SimpMessageHeaderAccessor.DISCONNECT_MESSAGE_HEADER, connectMessage); + Message ackMessage = MessageBuilder.createMessage(EMPTY_PAYLOAD, ackAccessor.getMessageHeaders()); + this.protocolHandler.handleMessageToClient(this.session, ackMessage); + + assertEquals(1, this.session.getSentMessages().size()); + TextMessage actual = (TextMessage) this.session.getSentMessages().get(0); + assertEquals("ERROR\n" + "message:Session closed.\n" + "content-length:0\n" + + "\n\u0000", actual.getPayload()); + } + + @Test + public void handleMessageToClientWithSimpDisconnectAckAndReceipt() { + + StompHeaderAccessor accessor = StompHeaderAccessor.create(StompCommand.DISCONNECT); + accessor.setReceipt("message-123"); + Message connectMessage = MessageBuilder.createMessage(EMPTY_PAYLOAD, accessor.getMessageHeaders()); + + SimpMessageHeaderAccessor ackAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.DISCONNECT_ACK); + ackAccessor.setHeader(SimpMessageHeaderAccessor.DISCONNECT_MESSAGE_HEADER, connectMessage); + Message ackMessage = MessageBuilder.createMessage(EMPTY_PAYLOAD, ackAccessor.getMessageHeaders()); + this.protocolHandler.handleMessageToClient(this.session, ackMessage); + + assertEquals(1, this.session.getSentMessages().size()); + TextMessage actual = (TextMessage) this.session.getSentMessages().get(0); + assertEquals("RECEIPT\n" + "receipt-id:message-123\n" + "\n\u0000", actual.getPayload()); + } + @Test public void handleMessageToClientWithSimpHeartbeat() { From ca09dcbe89c24b8b1bb60b35f91d6afa163f3c4e Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Mon, 29 Aug 2016 19:48:02 -0400 Subject: [PATCH 0140/1274] Polish media type change in ResourceHttpRequestHandler --- .../resource/ResourceHttpRequestHandler.java | 32 ++++++++----------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java index 4e4cc695300..9c7d45b8df0 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java @@ -48,10 +48,8 @@ import org.springframework.util.ObjectUtils; import org.springframework.util.ResourceUtils; import org.springframework.util.StringUtils; -import org.springframework.web.HttpMediaTypeNotAcceptableException; import org.springframework.web.HttpRequestHandler; import org.springframework.web.accept.ContentNegotiationManager; -import org.springframework.web.accept.ContentNegotiationManagerFactoryBean; import org.springframework.web.accept.PathExtensionContentNegotiationStrategy; import org.springframework.web.accept.ServletPathExtensionContentNegotiationStrategy; import org.springframework.web.context.request.ServletWebRequest; @@ -216,17 +214,10 @@ public ResourceRegionHttpMessageConverter getResourceRegionHttpMessageConverter( } /** - * Configure a {@code ContentNegotiationManager} to determine the media types - * for resources being served. If the manager contains a path - * extension strategy it will be used to look up the file extension - * of resources being served via - * {@link PathExtensionContentNegotiationStrategy#getMediaTypeForResource - * getMediaTypeForResource}. If that fails the check is then expanded - * to use any configured content negotiation strategy against the request. - *

By default a {@link ContentNegotiationManagerFactoryBean} with default - * settings is used to create the manager. See the Javadoc of - * {@code ContentNegotiationManagerFactoryBean} for details - * @param contentNegotiationManager the manager to use + * Configure a {@code ContentNegotiationManager} to help determine the + * media types for resources being served. If the manager contains a path + * extension strategy it will be checked for registered file extension. + * @param contentNegotiationManager the manager in use * @since 4.3 */ public void setContentNegotiationManager(ContentNegotiationManager contentNegotiationManager) { @@ -234,7 +225,7 @@ public void setContentNegotiationManager(ContentNegotiationManager contentNegoti } /** - * Return the specified content negotiation manager. + * Return the configured content negotiation manager. * @since 4.3 */ public ContentNegotiationManager getContentNegotiationManager() { @@ -516,17 +507,20 @@ protected boolean isInvalidPath(String path) { /** * Determine the media type for the given request and the resource matched - * to it. This implementation first tries to determine the MediaType based - * strictly on the file extension of the Resource via - * {@link PathExtensionContentNegotiationStrategy#getMediaTypeForResource} - * and then expands to check against the request via - * {@link ContentNegotiationManager#resolveMediaTypes}. + * to it. This implementation tries to determine the MediaType based on the + * file extension of the Resource via + * {@link ServletPathExtensionContentNegotiationStrategy#getMediaTypeForResource}. * @param request the current request * @param resource the resource to check * @return the corresponding media type, or {@code null} if none found */ @SuppressWarnings("deprecation") protected MediaType getMediaType(HttpServletRequest request, Resource resource) { + // For backwards compatibility + MediaType mediaType = getMediaType(resource); + if (mediaType != null) { + return mediaType; + } return this.pathExtensionStrategy.getMediaTypeForResource(resource); } From 5075dd4dfabd95dbc74bba0f5808b8338c7e6a92 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Mon, 29 Aug 2016 14:12:32 -0400 Subject: [PATCH 0141/1274] Harden synchronization around SockJS heartbeats Create an explicit heartbeat task with an experiration flag so that it can be cancelled reliably vs relying on the ScheduledFutureTask cancel method which may return true even if the task is already running. Issue: SPR-14356 --- .../session/AbstractSockJsSession.java | 109 +++++++++--------- .../transport/session/SockJsSessionTests.java | 2 +- 2 files changed, 54 insertions(+), 57 deletions(-) diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractSockJsSession.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractSockJsSession.java index f2fbadeef5f..b1db14696b2 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractSockJsSession.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractSockJsSession.java @@ -27,11 +27,10 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.core.NestedCheckedException; import org.springframework.util.Assert; import org.springframework.web.socket.CloseStatus; @@ -106,9 +105,11 @@ private enum State {NEW, OPEN, CLOSED} private volatile long timeLastActive = this.timeCreated; - private volatile ScheduledFuture heartbeatTask; + private ScheduledFuture heartbeatFuture; + + private HeartbeatTask heartbeatTask; - private final Lock heartbeatLock = new ReentrantLock(); + private final Object heartbeatLock = new Object(); private volatile boolean heartbeatDisabled; @@ -249,15 +250,10 @@ public void disableHeartbeat() { } public void sendHeartbeat() throws SockJsTransportFailureException { - if (isActive()) { - if (heartbeatLock.tryLock()) { - try { - writeFrame(SockJsFrame.heartbeatFrame()); - scheduleHeartbeat(); - } - finally { - heartbeatLock.unlock(); - } + synchronized (this.heartbeatLock) { + if (isActive() && !this.heartbeatDisabled) { + writeFrame(SockJsFrame.heartbeatFrame()); + scheduleHeartbeat(); } } } @@ -266,56 +262,33 @@ protected void scheduleHeartbeat() { if (this.heartbeatDisabled) { return; } - - Assert.state(this.config.getTaskScheduler() != null, "Expected SockJS TaskScheduler"); - cancelHeartbeat(); - if (!isActive()) { - return; - } - - Date time = new Date(System.currentTimeMillis() + this.config.getHeartbeatTime()); - this.heartbeatTask = this.config.getTaskScheduler().schedule(new Runnable() { - public void run() { - try { - sendHeartbeat(); - } - catch (Throwable ex) { - // ignore - } - } - }, time); - if (logger.isTraceEnabled()) { - logger.trace("Scheduled heartbeat in session " + getId()); - } - } - - protected void cancelHeartbeat() { - try { - ScheduledFuture task = this.heartbeatTask; - this.heartbeatTask = null; - if (task == null || task.isCancelled()) { + synchronized (this.heartbeatLock) { + cancelHeartbeat(); + if (!isActive()) { return; } - + Date time = new Date(System.currentTimeMillis() + this.config.getHeartbeatTime()); + this.heartbeatTask = new HeartbeatTask(); + this.heartbeatFuture = this.config.getTaskScheduler().schedule(this.heartbeatTask, time); if (logger.isTraceEnabled()) { - logger.trace("Cancelling heartbeat in session " + getId()); - } - if (task.cancel(false)) { - return; + logger.trace("Scheduled heartbeat in session " + getId()); } + } + } - if (logger.isTraceEnabled()) { - logger.trace("Failed to cancel heartbeat, acquiring heartbeat write lock."); + protected void cancelHeartbeat() { + synchronized (this.heartbeatLock) { + if (this.heartbeatFuture != null) { + if (logger.isTraceEnabled()) { + logger.trace("Cancelling heartbeat in session " + getId()); + } + this.heartbeatFuture.cancel(false); + this.heartbeatFuture = null; } - this.heartbeatLock.lock(); - - if (logger.isTraceEnabled()) { - logger.trace("Releasing heartbeat lock."); + if (this.heartbeatTask != null) { + this.heartbeatTask.cancel(); + this.heartbeatTask = null; } - this.heartbeatLock.unlock(); - } - catch (Throwable ex) { - logger.debug("Failure while cancelling heartbeat in session " + getId(), ex); } } @@ -465,4 +438,28 @@ public String toString() { return getClass().getSimpleName() + "[id=" + getId() + "]"; } + + private class HeartbeatTask implements Runnable { + + private boolean expired; + + @Override + public void run() { + synchronized (heartbeatLock) { + if (!this.expired) { + try { + sendHeartbeat(); + } + finally { + this.expired = true; + } + } + } + } + + void cancel() { + this.expired = true; + } + } + } diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/session/SockJsSessionTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/session/SockJsSessionTests.java index 8741ef315e4..e09ff565c82 100644 --- a/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/session/SockJsSessionTests.java +++ b/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/session/SockJsSessionTests.java @@ -270,6 +270,7 @@ public void scheduleHeartbeatNotActive() throws Exception { @Test public void sendHeartbeatWhenDisabled() throws Exception { this.session.disableHeartbeat(); + this.session.setActive(true); this.session.sendHeartbeat(); assertEquals(Collections.emptyList(), this.session.getSockJsFramesWritten()); @@ -292,7 +293,6 @@ public void scheduleAndCancelHeartbeat() throws Exception { this.session.cancelHeartbeat(); - verify(task).isCancelled(); verify(task).cancel(false); verifyNoMoreInteractions(task); } From 815a3ad0de7c22134f25c3dd0738d6363d0728d2 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 30 Aug 2016 12:57:35 -0400 Subject: [PATCH 0142/1274] Relax ServletContext check for resource handling This is a follow-up on commit fe4046 relaxing the expectation that a ServletContext is present. Instead we check defensively and fall back on PathExtensionContentNegotiationStrategy which can use JAF. Issue: SPR-14577 --- .../resource/ResourceHttpRequestHandler.java | 17 ++++++++++++----- .../ResourceHandlerRegistryTests.java | 2 ++ .../ResourceHttpRequestHandlerTests.java | 6 ++++++ 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java index 9c7d45b8df0..d73ae890b58 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java @@ -32,6 +32,7 @@ import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.core.io.Resource; import org.springframework.core.io.support.ResourceRegion; import org.springframework.http.HttpHeaders; @@ -91,7 +92,7 @@ * @since 3.0.4 */ public class ResourceHttpRequestHandler extends WebContentGenerator - implements HttpRequestHandler, InitializingBean, CorsConfigurationSource { + implements HttpRequestHandler, InitializingBean, SmartInitializingSingleton, CorsConfigurationSource { // Servlet 3.1 setContentLengthLong(long) available? private static final boolean contentLengthLongAvailable = @@ -112,7 +113,7 @@ public class ResourceHttpRequestHandler extends WebContentGenerator private ContentNegotiationManager contentNegotiationManager; - private ServletPathExtensionContentNegotiationStrategy pathExtensionStrategy; + private PathExtensionContentNegotiationStrategy pathExtensionStrategy; private ServletContext servletContext; @@ -270,7 +271,6 @@ public void afterPropertiesSet() throws Exception { if (this.resourceRegionHttpMessageConverter == null) { this.resourceRegionHttpMessageConverter = new ResourceRegionHttpMessageConverter(); } - this.pathExtensionStrategy = initPathExtensionStrategy(); } /** @@ -293,7 +293,12 @@ protected void initAllowedLocations() { } } - protected ServletPathExtensionContentNegotiationStrategy initPathExtensionStrategy() { + @Override + public void afterSingletonsInstantiated() { + this.pathExtensionStrategy = initPathExtensionStrategy(); + } + + protected PathExtensionContentNegotiationStrategy initPathExtensionStrategy() { Map mediaTypes = null; if (getContentNegotiationManager() != null) { PathExtensionContentNegotiationStrategy strategy = @@ -302,7 +307,9 @@ protected ServletPathExtensionContentNegotiationStrategy initPathExtensionStrate mediaTypes = new HashMap(strategy.getMediaTypes()); } } - return new ServletPathExtensionContentNegotiationStrategy(this.servletContext, mediaTypes); + return (getServletContext() != null) ? + new ServletPathExtensionContentNegotiationStrategy(getServletContext(), mediaTypes) : + new PathExtensionContentNegotiationStrategy(mediaTypes); } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistryTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistryTests.java index 66965ed569a..32e2f64366c 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistryTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistryTests.java @@ -79,6 +79,8 @@ public void mapPathToLocation() throws Exception { request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, "/testStylesheet.css"); ResourceHttpRequestHandler handler = getHandler("/resources/**"); + handler.afterPropertiesSet(); + handler.afterSingletonsInstantiated(); handler.handleRequest(request, this.response); assertEquals("test stylesheet content", this.response.getContentAsString()); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandlerTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandlerTests.java index e835aff8c15..1a5b74f55b7 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandlerTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandlerTests.java @@ -81,6 +81,7 @@ public void setUp() throws Exception { this.handler.setCacheSeconds(3600); this.handler.setServletContext(new TestServletContext()); this.handler.afterPropertiesSet(); + this.handler.afterSingletonsInstantiated(); this.request = new MockHttpServletRequest("GET", ""); this.response = new MockHttpServletResponse(); @@ -147,6 +148,7 @@ public void getVersionedResource() throws Exception { .addFixedVersionStrategy("versionString", "/**"); this.handler.setResourceResolvers(Arrays.asList(versionResolver, new PathResourceResolver())); this.handler.afterPropertiesSet(); + this.handler.afterSingletonsInstantiated(); this.request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, "versionString/foo.css"); this.handler.handleRequest(this.request, this.response); @@ -253,6 +255,7 @@ public void getResourceWithRegisteredMediaType() throws Exception { handler.setLocations(paths); handler.setContentNegotiationManager(manager); handler.afterPropertiesSet(); + handler.afterSingletonsInstantiated(); this.request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, "foo.css"); handler.handleRequest(this.request, this.response); @@ -274,6 +277,7 @@ public void getMediaTypeWithFavorPathExtensionOff() throws Exception { handler.setLocations(paths); handler.setContentNegotiationManager(manager); handler.afterPropertiesSet(); + handler.afterSingletonsInstantiated(); this.request.addHeader("Accept", "application/json,text/plain,*/*"); this.request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, "foo.html"); @@ -302,6 +306,7 @@ public String getVirtualServerName() { handler.setServletContext(servletContext); handler.setLocations(paths); handler.afterPropertiesSet(); + handler.afterSingletonsInstantiated(); this.request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, "foo.css"); handler.handleRequest(this.request, this.response); @@ -416,6 +421,7 @@ public void initAllowedLocationsWithExplicitConfiguration() throws Exception { handler.setServletContext(new MockServletContext()); handler.setLocations(Arrays.asList(location1, location2)); handler.afterPropertiesSet(); + handler.afterSingletonsInstantiated(); Resource[] locations = pathResolver.getAllowedLocations(); assertEquals(1, locations.length); From 8e98177fb33597a21c43077a805375045279cd01 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 29 Aug 2016 11:54:21 +0200 Subject: [PATCH 0143/1274] Avoid collection lookups in StompCommand Issue: SPR-14636 (cherry picked from commit 899ebd8) --- .../messaging/simp/stomp/StompCommand.java | 85 +++++++-------- .../simp/stomp/StompCommandTests.java | 100 ++++++++++++++++++ 2 files changed, 141 insertions(+), 44 deletions(-) create mode 100644 spring-messaging/src/test/java/org/springframework/messaging/simp/stomp/StompCommandTests.java diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompCommand.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompCommand.java index 5e5b2930907..8f0fd34bb35 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompCommand.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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,79 +16,76 @@ package org.springframework.messaging.simp.stomp; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - import org.springframework.messaging.simp.SimpMessageType; /** * Represents a STOMP command. * * @author Rossen Stoyanchev + * @author Juergen Hoeller * @since 4.0 */ public enum StompCommand { // client - CONNECT, - STOMP, - DISCONNECT, - SUBSCRIBE, - UNSUBSCRIBE, - SEND, - ACK, - NACK, - BEGIN, - COMMIT, - ABORT, + STOMP(SimpMessageType.CONNECT), + CONNECT(SimpMessageType.CONNECT), + DISCONNECT(SimpMessageType.DISCONNECT), + SUBSCRIBE(SimpMessageType.SUBSCRIBE, true, true, false), + UNSUBSCRIBE(SimpMessageType.UNSUBSCRIBE, false, true, false), + SEND(SimpMessageType.MESSAGE, true, false, true), + ACK(SimpMessageType.OTHER), + NACK(SimpMessageType.OTHER), + BEGIN(SimpMessageType.OTHER), + COMMIT(SimpMessageType.OTHER), + ABORT(SimpMessageType.OTHER), // server - CONNECTED, - MESSAGE, - RECEIPT, - ERROR; - - - private static Map messageTypes = new HashMap(); - static { - messageTypes.put(StompCommand.CONNECT, SimpMessageType.CONNECT); - messageTypes.put(StompCommand.STOMP, SimpMessageType.CONNECT); - messageTypes.put(StompCommand.SEND, SimpMessageType.MESSAGE); - messageTypes.put(StompCommand.MESSAGE, SimpMessageType.MESSAGE); - messageTypes.put(StompCommand.SUBSCRIBE, SimpMessageType.SUBSCRIBE); - messageTypes.put(StompCommand.UNSUBSCRIBE, SimpMessageType.UNSUBSCRIBE); - messageTypes.put(StompCommand.DISCONNECT, SimpMessageType.DISCONNECT); - } + CONNECTED(SimpMessageType.OTHER), + RECEIPT(SimpMessageType.OTHER), + MESSAGE(SimpMessageType.MESSAGE, true, true, true), + ERROR(SimpMessageType.OTHER, false, false, true); + - private static Collection destinationRequired = Arrays.asList(SEND, SUBSCRIBE, MESSAGE); - private static Collection subscriptionIdRequired = Arrays.asList(SUBSCRIBE, UNSUBSCRIBE, MESSAGE); - private static Collection contentLengthRequired = Arrays.asList(SEND, MESSAGE, ERROR); - private static Collection bodyAllowed = Arrays.asList(SEND, MESSAGE, ERROR); + private final SimpMessageType messageType; + private final boolean destination; + + private final boolean subscriptionId; + + private final boolean body; + + + StompCommand(SimpMessageType messageType) { + this(messageType, false, false, false); + } + + StompCommand(SimpMessageType messageType, boolean destination, boolean subscriptionId, boolean body) { + this.messageType = messageType; + this.destination = destination; + this.subscriptionId = subscriptionId; + this.body = body; + } public SimpMessageType getMessageType() { - SimpMessageType type = messageTypes.get(this); - return (type != null) ? type : SimpMessageType.OTHER; + return this.messageType; } public boolean requiresDestination() { - return destinationRequired.contains(this); + return this.destination; } public boolean requiresSubscriptionId() { - return subscriptionIdRequired.contains(this); + return this.subscriptionId; } public boolean requiresContentLength() { - return contentLengthRequired.contains(this); + return this.body; } public boolean isBodyAllowed() { - return bodyAllowed.contains(this); + return this.body; } } - diff --git a/spring-messaging/src/test/java/org/springframework/messaging/simp/stomp/StompCommandTests.java b/spring-messaging/src/test/java/org/springframework/messaging/simp/stomp/StompCommandTests.java new file mode 100644 index 00000000000..7768421ad1a --- /dev/null +++ b/spring-messaging/src/test/java/org/springframework/messaging/simp/stomp/StompCommandTests.java @@ -0,0 +1,100 @@ +/* + * Copyright 2002-2016 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.messaging.simp.stomp; + +import java.util.Arrays; +import java.util.Collection; +import java.util.EnumMap; +import java.util.Map; + +import org.junit.Test; + +import org.springframework.messaging.simp.SimpMessageType; + +import static org.junit.Assert.*; + +/** + * @author Juergen Hoeller + */ +public class StompCommandTests { + + private static final Collection destinationRequired = + Arrays.asList(StompCommand.SEND, StompCommand.SUBSCRIBE, StompCommand.MESSAGE); + + private static final Collection subscriptionIdRequired = + Arrays.asList(StompCommand.SUBSCRIBE, StompCommand.UNSUBSCRIBE, StompCommand.MESSAGE); + + private static final Collection contentLengthRequired = + Arrays.asList(StompCommand.SEND, StompCommand.MESSAGE, StompCommand.ERROR); + + private static final Collection bodyAllowed = + Arrays.asList(StompCommand.SEND, StompCommand.MESSAGE, StompCommand.ERROR); + + private static final Map messageTypes = + new EnumMap<>(StompCommand.class); + + static { + messageTypes.put(StompCommand.STOMP, SimpMessageType.CONNECT); + messageTypes.put(StompCommand.CONNECT, SimpMessageType.CONNECT); + messageTypes.put(StompCommand.DISCONNECT, SimpMessageType.DISCONNECT); + messageTypes.put(StompCommand.SUBSCRIBE, SimpMessageType.SUBSCRIBE); + messageTypes.put(StompCommand.UNSUBSCRIBE, SimpMessageType.UNSUBSCRIBE); + messageTypes.put(StompCommand.SEND, SimpMessageType.MESSAGE); + messageTypes.put(StompCommand.MESSAGE, SimpMessageType.MESSAGE); + } + + + @Test + public void getMessageType() throws Exception { + for (StompCommand stompCommand : StompCommand.values()) { + SimpMessageType simp = messageTypes.get(stompCommand); + if (simp == null) { + simp = SimpMessageType.OTHER; + } + assertSame(simp, stompCommand.getMessageType()); + } + } + + @Test + public void requiresDestination() throws Exception { + for (StompCommand stompCommand : StompCommand.values()) { + assertEquals(destinationRequired.contains(stompCommand), stompCommand.requiresDestination()); + } + } + + @Test + public void requiresSubscriptionId() throws Exception { + for (StompCommand stompCommand : StompCommand.values()) { + assertEquals(subscriptionIdRequired.contains(stompCommand), stompCommand.requiresSubscriptionId()); + } + } + + @Test + public void requiresContentLength() throws Exception { + for (StompCommand stompCommand : StompCommand.values()) { + assertEquals(contentLengthRequired.contains(stompCommand), stompCommand.requiresContentLength()); + } + } + + @Test + public void isBodyAllowed() throws Exception { + for (StompCommand stompCommand : StompCommand.values()) { + assertEquals(bodyAllowed.contains(stompCommand), stompCommand.isBodyAllowed()); + } + } + +} From 05f74b42186243e3f90bb87555ff75fa3a29b9ee Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 30 Aug 2016 12:57:46 +0200 Subject: [PATCH 0144/1274] CommonsMultipartResolver explicitly converts FileSizeLimitExceededException Issue: SPR-14638 (cherry picked from commit 58ffca7) --- .../web/multipart/commons/CommonsMultipartResolver.java | 7 +++++-- .../portlet/multipart/CommonsPortletMultipartResolver.java | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/multipart/commons/CommonsMultipartResolver.java b/spring-web/src/main/java/org/springframework/web/multipart/commons/CommonsMultipartResolver.java index 22960e3811c..f4226d5afee 100644 --- a/spring-web/src/main/java/org/springframework/web/multipart/commons/CommonsMultipartResolver.java +++ b/spring-web/src/main/java/org/springframework/web/multipart/commons/CommonsMultipartResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -161,8 +161,11 @@ protected MultipartParsingResult parseRequest(HttpServletRequest request) throws catch (FileUploadBase.SizeLimitExceededException ex) { throw new MaxUploadSizeExceededException(fileUpload.getSizeMax(), ex); } + catch (FileUploadBase.FileSizeLimitExceededException ex) { + throw new MaxUploadSizeExceededException(fileUpload.getFileSizeMax(), ex); + } catch (FileUploadException ex) { - throw new MultipartException("Could not parse multipart servlet request", ex); + throw new MultipartException("Failed to parse multipart servlet request", ex); } } diff --git a/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/multipart/CommonsPortletMultipartResolver.java b/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/multipart/CommonsPortletMultipartResolver.java index 2644f2c8450..2c95b02e350 100644 --- a/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/multipart/CommonsPortletMultipartResolver.java +++ b/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/multipart/CommonsPortletMultipartResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -156,8 +156,11 @@ protected MultipartParsingResult parseRequest(ActionRequest request) throws Mult catch (FileUploadBase.SizeLimitExceededException ex) { throw new MaxUploadSizeExceededException(fileUpload.getSizeMax(), ex); } + catch (FileUploadBase.FileSizeLimitExceededException ex) { + throw new MaxUploadSizeExceededException(fileUpload.getFileSizeMax(), ex); + } catch (FileUploadException ex) { - throw new MultipartException("Could not parse multipart portlet request", ex); + throw new MultipartException("Failed to parse multipart portlet request", ex); } } From 3b91dec46241d8ef21af34e5c621de2570d6b234 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 30 Aug 2016 20:50:02 +0200 Subject: [PATCH 0145/1274] ApplicationListenerMethodAdapter resolves order on construction Issue: SPR-14642 (cherry picked from commit 58fa63f) --- .../ApplicationListenerMethodAdapter.java | 93 +++++++++---------- ...ionListenerMethodTransactionalAdapter.java | 19 ++-- 2 files changed, 48 insertions(+), 64 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/event/ApplicationListenerMethodAdapter.java b/spring-context/src/main/java/org/springframework/context/event/ApplicationListenerMethodAdapter.java index a57eb0c6120..48de2bffe97 100644 --- a/spring-context/src/main/java/org/springframework/context/event/ApplicationListenerMethodAdapter.java +++ b/spring-context/src/main/java/org/springframework/context/event/ApplicationListenerMethodAdapter.java @@ -16,7 +16,6 @@ package org.springframework.context.event; -import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.UndeclaredThrowableException; @@ -53,6 +52,7 @@ * evaluated prior to invoking the underlying method. * * @author Stephane Nicoll + * @author Juergen Hoeller * @author Sam Brannen * @since 4.2 */ @@ -70,26 +70,58 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe private final List declaredEventTypes; + private final String condition; + + private final int order; + private final AnnotatedElementKey methodKey; private ApplicationContext applicationContext; private EventExpressionEvaluator evaluator; - private String condition; - - private EventListener eventListener; - public ApplicationListenerMethodAdapter(String beanName, Class targetClass, Method method) { this.beanName = beanName; this.method = method; this.targetClass = targetClass; this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); - this.declaredEventTypes = resolveDeclaredEventTypes(); - this.methodKey = new AnnotatedElementKey(this.method, this.targetClass); + + EventListener ann = AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class); + this.declaredEventTypes = resolveDeclaredEventTypes(method, ann); + this.condition = (ann != null ? ann.condition() : null); + this.order = resolveOrder(method); + + this.methodKey = new AnnotatedElementKey(method, targetClass); + } + + + private List resolveDeclaredEventTypes(Method method, EventListener ann) { + int count = method.getParameterTypes().length; + if (count > 1) { + throw new IllegalStateException( + "Maximum one parameter is allowed for event listener method: " + method); + } + if (ann != null && ann.classes().length > 0) { + List types = new ArrayList(ann.classes().length); + for (Class eventType : ann.classes()) { + types.add(ResolvableType.forClass(eventType)); + } + return types; + } + else { + if (count == 0) { + throw new IllegalStateException( + "Event parameter is mandatory for event listener method: " + method); + } + return Collections.singletonList(ResolvableType.forMethodParameter(method, 0)); + } } + private int resolveOrder(Method method) { + Order ann = AnnotatedElementUtils.findMergedAnnotation(method, Order.class); + return (ann != null ? ann.value() : 0); + } /** * Initialize this instance. @@ -128,8 +160,7 @@ public boolean supportsSourceType(Class sourceType) { @Override public int getOrder() { - Order order = getMethodAnnotation(Order.class); - return (order != null ? order.value() : 0); + return this.order; } @@ -164,8 +195,8 @@ protected Object[] resolveArguments(ApplicationEvent event) { if (this.method.getParameterTypes().length == 0) { return new Object[0]; } - if (!ApplicationEvent.class.isAssignableFrom(declaredEventType.getRawClass()) - && event instanceof PayloadApplicationEvent) { + if (!ApplicationEvent.class.isAssignableFrom(declaredEventType.getRawClass()) && + event instanceof PayloadApplicationEvent) { return new Object[] {((PayloadApplicationEvent) event).getPayload()}; } else { @@ -212,10 +243,6 @@ private boolean shouldHandle(ApplicationEvent event, Object[] args) { return true; } - protected A getMethodAnnotation(Class annotationType) { - return AnnotatedElementUtils.findMergedAnnotation(this.method, annotationType); - } - /** * Invoke the event listener method with the given argument values. */ @@ -253,13 +280,6 @@ protected Object getTargetBean() { return this.applicationContext.getBean(this.beanName); } - protected EventListener getEventListener() { - if (this.eventListener == null) { - this.eventListener = AnnotatedElementUtils.findMergedAnnotation(this.method, EventListener.class); - } - return this.eventListener; - } - /** * Return the condition to use. *

Matches the {@code condition} attribute of the {@link EventListener} @@ -267,12 +287,6 @@ protected EventListener getEventListener() { * is meta-annotated with {@code @EventListener}. */ protected String getCondition() { - if (this.condition == null) { - EventListener eventListener = AnnotatedElementUtils.findMergedAnnotation(this.method, EventListener.class); - if (eventListener != null) { - this.condition = eventListener.condition(); - } - } return this.condition; } @@ -346,29 +360,6 @@ private ResolvableType getResolvableType(ApplicationEvent event) { return null; } - private List resolveDeclaredEventTypes() { - int count = this.method.getParameterTypes().length; - if (count > 1) { - throw new IllegalStateException( - "Maximum one parameter is allowed for event listener method: " + this.method); - } - EventListener ann = getEventListener(); - if (ann != null && ann.classes().length > 0) { - List types = new ArrayList(); - for (Class eventType : ann.classes()) { - types.add(ResolvableType.forClass(eventType)); - } - return types; - } - else { - if (count == 0) { - throw new IllegalStateException( - "Event parameter is mandatory for event listener method: " + this.method); - } - return Collections.singletonList(ResolvableType.forMethodParameter(this.method, 0)); - } - } - @Override public String toString() { diff --git a/spring-tx/src/main/java/org/springframework/transaction/event/ApplicationListenerMethodTransactionalAdapter.java b/spring-tx/src/main/java/org/springframework/transaction/event/ApplicationListenerMethodTransactionalAdapter.java index 23db045521f..5998522c692 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/event/ApplicationListenerMethodTransactionalAdapter.java +++ b/spring-tx/src/main/java/org/springframework/transaction/event/ApplicationListenerMethodTransactionalAdapter.java @@ -18,9 +18,6 @@ import java.lang.reflect.Method; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - import org.springframework.context.ApplicationEvent; import org.springframework.context.event.ApplicationListenerMethodAdapter; import org.springframework.context.event.EventListener; @@ -41,14 +38,13 @@ * bean of type {@link TransactionalEventListenerFactory} is required. * * @author Stephane Nicoll + * @author Juergen Hoeller * @since 4.2 * @see ApplicationListenerMethodAdapter * @see TransactionalEventListener */ class ApplicationListenerMethodTransactionalAdapter extends ApplicationListenerMethodAdapter { - protected final Log logger = LogFactory.getLog(getClass()); - private final TransactionalEventListener annotation; @@ -56,7 +52,7 @@ public ApplicationListenerMethodTransactionalAdapter(String beanName, Class t super(beanName, targetClass, method); this.annotation = AnnotatedElementUtils.findMergedAnnotation(method, TransactionalEventListener.class); if (this.annotation == null) { - throw new IllegalStateException("No TransactionalEventListener annotation found on '" + method + "'"); + throw new IllegalStateException("No TransactionalEventListener annotation found on method: " + method); } } @@ -68,15 +64,13 @@ public void onApplicationEvent(ApplicationEvent event) { TransactionSynchronizationManager.registerSynchronization(transactionSynchronization); } else if (this.annotation.fallbackExecution()) { - if (this.annotation.phase() == TransactionPhase.AFTER_ROLLBACK) { - logger.warn("Processing '" + event + "' as a fallback execution on AFTER_ROLLBACK phase."); + if (this.annotation.phase() == TransactionPhase.AFTER_ROLLBACK && logger.isWarnEnabled()) { + logger.warn("Processing " + event + " as a fallback execution on AFTER_ROLLBACK phase"); } processEvent(event); } - else { - if (logger.isDebugEnabled()) { - logger.debug("No transaction is running, skipping '" + event + "' for '" + this + "'"); - } + else if (logger.isDebugEnabled()) { + logger.debug("No transaction is running - skipping " + event); } } @@ -85,7 +79,6 @@ private TransactionSynchronization createTransactionSynchronization(ApplicationE } - private static class TransactionSynchronizationEventAdapter extends TransactionSynchronizationAdapter { private final ApplicationListenerMethodAdapter listener; From d8f7347000408363c5e91a778d73eb164d71925d Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 30 Aug 2016 23:56:58 +0200 Subject: [PATCH 0146/1274] Consistent comma splitting without regex overhead Issue: SPR-14635 (cherry picked from commit 03609c1) --- .../springframework/util/MimeTypeUtils.java | 2 +- .../simp/stomp/StompHeaderAccessor.java | 6 +- .../messaging/simp/stomp/StompHeaders.java | 25 ++--- .../htmlunit/HtmlUnitRequestBuilder.java | 20 ++-- .../org/springframework/http/HttpHeaders.java | 17 +-- .../org/springframework/http/HttpRange.java | 2 +- .../org/springframework/http/MediaType.java | 2 +- .../web/util/UriComponentsBuilder.java | 15 +-- .../web/socket/WebSocketExtension.java | 105 +++++++++--------- .../AbstractTyrusRequestUpgradeStrategy.java | 2 +- 10 files changed, 98 insertions(+), 98 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/util/MimeTypeUtils.java b/spring-core/src/main/java/org/springframework/util/MimeTypeUtils.java index a0ad5cc0953..7ca497a5f59 100644 --- a/spring-core/src/main/java/org/springframework/util/MimeTypeUtils.java +++ b/spring-core/src/main/java/org/springframework/util/MimeTypeUtils.java @@ -277,7 +277,7 @@ public static List parseMimeTypes(String mimeTypes) { if (!StringUtils.hasLength(mimeTypes)) { return Collections.emptyList(); } - String[] tokens = mimeTypes.split(",\\s*"); + String[] tokens = StringUtils.tokenizeToStringArray(mimeTypes, ","); List result = new ArrayList(tokens.length); for (String token : tokens) { result.add(parseMimeType(token)); diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompHeaderAccessor.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompHeaderAccessor.java index 8e38664934d..dc467a9f7b6 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompHeaderAccessor.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompHeaderAccessor.java @@ -228,10 +228,10 @@ public boolean isHeartbeat() { public long[] getHeartbeat() { String rawValue = getFirstNativeHeader(STOMP_HEARTBEAT_HEADER); - if (!StringUtils.hasText(rawValue)) { + String[] rawValues = StringUtils.split(rawValue, ","); + if (rawValues == null) { return Arrays.copyOf(DEFAULT_HEARTBEAT, 2); } - String[] rawValues = StringUtils.commaDelimitedListToStringArray(rawValue); return new long[] {Long.valueOf(rawValues[0]), Long.valueOf(rawValues[1])}; } @@ -297,7 +297,7 @@ public void setContentLength(int contentLength) { } public void setHeartbeat(long cx, long cy) { - setNativeHeader(STOMP_HEARTBEAT_HEADER, StringUtils.arrayToCommaDelimitedString(new Object[]{cx, cy})); + setNativeHeader(STOMP_HEARTBEAT_HEADER, cx + "," + cy); } public void setAck(String ack) { diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompHeaders.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompHeaders.java index c6abad4581c..4c6e252142d 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompHeaders.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompHeaders.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -223,9 +223,14 @@ public String getPasscode() { * Applies to the CONNECT and CONNECTED frames. */ public void setHeartbeat(long[] heartbeat) { - Assert.notNull(heartbeat); + if (heartbeat == null || heartbeat.length != 2) { + throw new IllegalArgumentException("Heart-beat array must be of length 2, not " + + (heartbeat != null ? heartbeat.length : "null")); + } String value = heartbeat[0] + "," + heartbeat[1]; - Assert.isTrue(heartbeat[0] >= 0 && heartbeat[1] >= 0, "Heart-beat values cannot be negative: " + value); + if (heartbeat[0] < 0 || heartbeat[1] < 0) { + throw new IllegalArgumentException("Heart-beat values cannot be negative: " + value); + } set(HEARTBEAT, value); } @@ -234,10 +239,10 @@ public void setHeartbeat(long[] heartbeat) { */ public long[] getHeartbeat() { String rawValue = getFirst(HEARTBEAT); - if (!StringUtils.hasText(rawValue)) { + String[] rawValues = StringUtils.split(rawValue, ","); + if (rawValues == null) { return null; } - String[] rawValues = StringUtils.commaDelimitedListToStringArray(rawValue); return new long[] {Long.valueOf(rawValues[0]), Long.valueOf(rawValues[1])}; } @@ -497,14 +502,8 @@ public Set>> entrySet() { @Override public boolean equals(Object other) { - if (this == other) { - return true; - } - if (!(other instanceof StompHeaders)) { - return false; - } - StompHeaders otherHeaders = (StompHeaders) other; - return this.headers.equals(otherHeaders.headers); + return (this == other || (other instanceof StompHeaders && + this.headers.equals(((StompHeaders) other).headers))); } @Override diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/HtmlUnitRequestBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/HtmlUnitRequestBuilder.java index 142fceb1423..7bab3e442f6 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/HtmlUnitRequestBuilder.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/HtmlUnitRequestBuilder.java @@ -50,6 +50,7 @@ import org.springframework.test.web.servlet.request.RequestPostProcessor; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; @@ -198,7 +199,8 @@ private void parent(MockHttpServletRequest request, RequestBuilder parent) { * {@link HttpServletRequest#getContextPath()} which states it can be * an empty string, or it must start with a "/" and not end with a "/". * @param contextPath a valid contextPath - * @throws IllegalArgumentException if the contextPath is not a valid {@link HttpServletRequest#getContextPath()} + * @throws IllegalArgumentException if the contextPath is not a valid + * {@link HttpServletRequest#getContextPath()} */ public void setContextPath(String contextPath) { MockMvcWebConnection.validateContextPath(contextPath); @@ -211,9 +213,9 @@ public void setForwardPostProcessor(RequestPostProcessor forwardPostProcessor) { private void authType(MockHttpServletRequest request) { String authorization = header("Authorization"); - if (authorization != null) { - String[] authzParts = authorization.split(": "); - request.setAuthType(authzParts[0]); + String[] authSplit = StringUtils.split(authorization, ": "); + if (authSplit != null) { + request.setAuthType(authSplit[0]); } } @@ -263,8 +265,8 @@ private void cookies(MockHttpServletRequest request) { while (tokens.hasMoreTokens()) { String cookieName = tokens.nextToken().trim(); if (!tokens.hasMoreTokens()) { - throw new IllegalArgumentException("Expected value for cookie name '" + cookieName - + "'. Full cookie was " + cookieHeaderValue); + throw new IllegalArgumentException("Expected value for cookie name '" + cookieName + + "'. Full cookie was " + cookieHeaderValue); } String cookieValue = tokens.nextToken().trim(); processCookie(request, cookies, new Cookie(cookieName, cookieValue)); @@ -352,9 +354,9 @@ private void locales(MockHttpServletRequest request) { request.addPreferredLocale(Locale.getDefault()); } else { - String[] locales = locale.split(", "); - for (int i = locales.length - 1; i >= 0; i--) { - request.addPreferredLocale(parseLocale(locales[i])); + String[] tokens = StringUtils.tokenizeToStringArray(locale, ","); + for (int i = tokens.length - 1; i >= 0; i--) { + request.addPreferredLocale(parseLocale(tokens[i])); } } } diff --git a/spring-web/src/main/java/org/springframework/http/HttpHeaders.java b/spring-web/src/main/java/org/springframework/http/HttpHeaders.java index 9145aecfcd3..a13b9bb6965 100644 --- a/spring-web/src/main/java/org/springframework/http/HttpHeaders.java +++ b/spring-web/src/main/java/org/springframework/http/HttpHeaders.java @@ -474,7 +474,7 @@ public List getAccessControlAllowMethods() { List result = new ArrayList(); String value = getFirst(ACCESS_CONTROL_ALLOW_METHODS); if (value != null) { - String[] tokens = StringUtils.tokenizeToStringArray(value, ",", true, true); + String[] tokens = StringUtils.tokenizeToStringArray(value, ","); for (String token : tokens) { HttpMethod resolved = HttpMethod.resolve(token); if (resolved != null) { @@ -578,10 +578,10 @@ public void setAcceptCharset(List acceptableCharsets) { * as specified by the {@code Accept-Charset} header. */ public List getAcceptCharset() { - List result = new ArrayList(); String value = getFirst(ACCEPT_CHARSET); if (value != null) { - String[] tokens = value.split(",\\s*"); + String[] tokens = StringUtils.tokenizeToStringArray(value, ","); + List result = new ArrayList(tokens.length); for (String token : tokens) { int paramIdx = token.indexOf(';'); String charsetName; @@ -595,8 +595,11 @@ public List getAcceptCharset() { result.add(Charset.forName(charsetName)); } } + return result; + } + else { + return Collections.emptyList(); } - return result; } /** @@ -615,8 +618,8 @@ public void setAllow(Set allowedMethods) { public Set getAllow() { String value = getFirst(ALLOW); if (!StringUtils.isEmpty(value)) { - List result = new LinkedList(); - String[] tokens = value.split(",\\s*"); + String[] tokens = StringUtils.tokenizeToStringArray(value, ","); + List result = new ArrayList(tokens.length); for (String token : tokens) { HttpMethod resolved = HttpMethod.resolve(token); if (resolved != null) { @@ -691,7 +694,7 @@ public void setContentDispositionFormData(String name, String filename, Charset StringBuilder builder = new StringBuilder("form-data; name=\""); builder.append(name).append('\"'); if (filename != null) { - if(charset == null || Charset.forName("US-ASCII").equals(charset)) { + if (charset == null || charset.name().equals("US-ASCII")) { builder.append("; filename=\""); builder.append(filename).append('\"'); } diff --git a/spring-web/src/main/java/org/springframework/http/HttpRange.java b/spring-web/src/main/java/org/springframework/http/HttpRange.java index a6ba6dea651..e3dbb8ca16a 100644 --- a/spring-web/src/main/java/org/springframework/http/HttpRange.java +++ b/spring-web/src/main/java/org/springframework/http/HttpRange.java @@ -132,7 +132,7 @@ public static List parseRanges(String ranges) { } ranges = ranges.substring(BYTE_RANGE_PREFIX.length()); - String[] tokens = ranges.split(",\\s*"); + String[] tokens = StringUtils.tokenizeToStringArray(ranges, ","); List result = new ArrayList(tokens.length); for (String token : tokens) { result.add(parseRange(token)); diff --git a/spring-web/src/main/java/org/springframework/http/MediaType.java b/spring-web/src/main/java/org/springframework/http/MediaType.java index 924c1e4cb1d..3fad7788bb4 100644 --- a/spring-web/src/main/java/org/springframework/http/MediaType.java +++ b/spring-web/src/main/java/org/springframework/http/MediaType.java @@ -438,7 +438,7 @@ public static List parseMediaTypes(String mediaTypes) { if (!StringUtils.hasLength(mediaTypes)) { return Collections.emptyList(); } - String[] tokens = mediaTypes.split(",\\s*"); + String[] tokens = StringUtils.tokenizeToStringArray(mediaTypes, ","); List result = new ArrayList(tokens.length); for (String token : tokens) { result.add(parseMediaType(token)); diff --git a/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java b/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java index 44eb0a07e49..b86889a3eff 100644 --- a/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java +++ b/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java @@ -673,7 +673,7 @@ public UriComponentsBuilder fragment(String fragment) { UriComponentsBuilder adaptFromForwardedHeaders(HttpHeaders headers) { String forwardedHeader = headers.getFirst("Forwarded"); if (StringUtils.hasText(forwardedHeader)) { - String forwardedToUse = StringUtils.commaDelimitedListToStringArray(forwardedHeader)[0]; + String forwardedToUse = StringUtils.tokenizeToStringArray(forwardedHeader, ",")[0]; Matcher matcher = FORWARDED_HOST_PATTERN.matcher(forwardedToUse); if (matcher.find()) { host(matcher.group(1).trim()); @@ -686,10 +686,9 @@ UriComponentsBuilder adaptFromForwardedHeaders(HttpHeaders headers) { else { String hostHeader = headers.getFirst("X-Forwarded-Host"); if (StringUtils.hasText(hostHeader)) { - String[] hosts = StringUtils.commaDelimitedListToStringArray(hostHeader); - String hostToUse = hosts[0]; - if (hostToUse.contains(":")) { - String[] hostAndPort = StringUtils.split(hostToUse, ":"); + String hostToUse = StringUtils.tokenizeToStringArray(hostHeader, ",")[0]; + String[] hostAndPort = StringUtils.split(hostToUse, ":"); + if (hostAndPort != null) { host(hostAndPort[0]); port(Integer.parseInt(hostAndPort[1])); } @@ -701,14 +700,12 @@ UriComponentsBuilder adaptFromForwardedHeaders(HttpHeaders headers) { String portHeader = headers.getFirst("X-Forwarded-Port"); if (StringUtils.hasText(portHeader)) { - String[] ports = StringUtils.commaDelimitedListToStringArray(portHeader); - port(Integer.parseInt(ports[0])); + port(Integer.parseInt(StringUtils.tokenizeToStringArray(portHeader, ",")[0])); } String protocolHeader = headers.getFirst("X-Forwarded-Proto"); if (StringUtils.hasText(protocolHeader)) { - String[] protocols = StringUtils.commaDelimitedListToStringArray(protocolHeader); - scheme(protocols[0]); + scheme(StringUtils.tokenizeToStringArray(protocolHeader, ",")[0]); } } diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/WebSocketExtension.java b/spring-websocket/src/main/java/org/springframework/web/socket/WebSocketExtension.java index 4ec0fe803b3..817371c1638 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/WebSocketExtension.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/WebSocketExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -44,6 +44,7 @@ * e.g. extensions "foo, bar" will be executed as "bar(foo(message))".

* * @author Brian Clozel + * @author Juergen Hoeller * @since 4.0 * @see WebSocket Protocol Extensions, RFC 6455 - Section 9 */ @@ -68,54 +69,90 @@ public WebSocketExtension(String name) { * @param parameters the parameters */ public WebSocketExtension(String name, Map parameters) { - Assert.hasLength(name, "extension name must not be empty"); + Assert.hasLength(name, "Extension name must not be empty"); this.name = name; if (!CollectionUtils.isEmpty(parameters)) { - Map m = new LinkedCaseInsensitiveMap(parameters.size(), Locale.ENGLISH); - m.putAll(parameters); - this.parameters = Collections.unmodifiableMap(m); + Map map = new LinkedCaseInsensitiveMap(parameters.size(), Locale.ENGLISH); + map.putAll(parameters); + this.parameters = Collections.unmodifiableMap(map); } else { this.parameters = Collections.emptyMap(); } } + /** - * @return the name of the extension + * Return the name of the extension (never {@code null) or empty}. */ public String getName() { return this.name; } /** - * @return the parameters of the extension, never {@code null} + * Return the parameters of the extension (never {@code null}). */ public Map getParameters() { return this.parameters; } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + WebSocketExtension otherExt = (WebSocketExtension) other; + return (this.name.equals(otherExt.name) && this.parameters.equals(otherExt.parameters)); + } + + @Override + public int hashCode() { + return this.name.hashCode() * 31 + this.parameters.hashCode(); + } + + @Override + public String toString() { + StringBuilder str = new StringBuilder(); + str.append(this.name); + for (Map.Entry entry : this.parameters.entrySet()) { + str.append(';'); + str.append(entry.getKey()); + str.append('='); + str.append(entry.getValue()); + } + return str.toString(); + } + + /** * Parse the given, comma-separated string into a list of {@code WebSocketExtension} objects. - *

This method can be used to parse a "Sec-WebSocket-Extension" extensions. + *

This method can be used to parse a "Sec-WebSocket-Extension" header. * @param extensions the string to parse * @return the list of extensions * @throws IllegalArgumentException if the string cannot be parsed */ public static List parseExtensions(String extensions) { - if (extensions == null || !StringUtils.hasText(extensions)) { - return Collections.emptyList(); - } - else { - List result = new ArrayList(); - for (String token : extensions.split(",")) { + if (StringUtils.hasText(extensions)) { + String[] tokens = StringUtils.tokenizeToStringArray(extensions, ","); + List result = new ArrayList(tokens.length); + for (String token : StringUtils.tokenizeToStringArray(extensions, ",")) { result.add(parseExtension(token)); } return result; } + else { + return Collections.emptyList(); + } } private static WebSocketExtension parseExtension(String extension) { - Assert.doesNotContain(extension, ",", "Expected a single extension value: " + extension); + if (extension.contains(",")) { + throw new IllegalArgumentException("Expected single extension value: [" + extension + "]"); + } String[] parts = StringUtils.tokenizeToStringArray(extension, ";"); String name = parts[0].trim(); @@ -136,42 +173,4 @@ private static WebSocketExtension parseExtension(String extension) { return new WebSocketExtension(name, parameters); } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if ((o == null) || (getClass() != o.getClass())) { - return false; - } - WebSocketExtension that = (WebSocketExtension) o; - if (!name.equals(that.name)) { - return false; - } - if (!parameters.equals(that.parameters)) { - return false; - } - return true; - } - - @Override - public int hashCode() { - int result = name.hashCode(); - result = 31 * result + parameters.hashCode(); - return result; - } - - @Override - public String toString() { - StringBuilder str = new StringBuilder(); - str.append(this.name); - for (String param : parameters.keySet()) { - str.append(';'); - str.append(param); - str.append('='); - str.append(this.parameters.get(param)); - } - return str.toString(); - } - } diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractTyrusRequestUpgradeStrategy.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractTyrusRequestUpgradeStrategy.java index 35886efc724..9d62b4c573d 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractTyrusRequestUpgradeStrategy.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractTyrusRequestUpgradeStrategy.java @@ -76,7 +76,7 @@ public abstract class AbstractTyrusRequestUpgradeStrategy extends AbstractStanda @Override public String[] getSupportedVersions() { - return StringUtils.commaDelimitedListToStringArray(Version.getSupportedWireProtocolVersions()); + return StringUtils.tokenizeToStringArray(Version.getSupportedWireProtocolVersions(), ","); } protected List getInstalledExtensions(WebSocketContainer container) { From acbb2544bf90a85c9d42f9e2c71f750e6499cd53 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 30 Aug 2016 23:57:11 +0200 Subject: [PATCH 0147/1274] Polishing (cherry picked from commit 4ef428d) --- .../AnnotationDrivenEventListenerTests.java | 10 ++++++++-- .../context/event/test/EventCollector.java | 14 +++++++++++--- .../core/env/AbstractEnvironment.java | 19 +++++++++---------- .../web/filter/HttpPutFormContentFilter.java | 15 +++++++++------ .../standard/ServerEndpointRegistration.java | 5 ++--- .../TomcatRequestUpgradeStrategy.java | 5 ++--- 6 files changed, 41 insertions(+), 27 deletions(-) diff --git a/spring-context/src/test/java/org/springframework/context/event/AnnotationDrivenEventListenerTests.java b/spring-context/src/test/java/org/springframework/context/event/AnnotationDrivenEventListenerTests.java index 89738660adb..438c126f8ad 100644 --- a/spring-context/src/test/java/org/springframework/context/event/AnnotationDrivenEventListenerTests.java +++ b/spring-context/src/test/java/org/springframework/context/event/AnnotationDrivenEventListenerTests.java @@ -77,7 +77,7 @@ public class AnnotationDrivenEventListenerTests { private EventCollector eventCollector; - private CountDownLatch countDownLatch; // 1 call by default + private CountDownLatch countDownLatch; // 1 call by default @After @@ -93,16 +93,23 @@ public void simpleEventJavaConfig() { load(TestEventListener.class); TestEvent event = new TestEvent(this, "test"); TestEventListener listener = this.context.getBean(TestEventListener.class); + this.eventCollector.assertNoEventReceived(listener); this.context.publishEvent(event); this.eventCollector.assertEvent(listener, event); this.eventCollector.assertTotalEventsCount(1); + + this.eventCollector.clear(); + this.context.publishEvent(event); + this.eventCollector.assertEvent(listener, event); + this.eventCollector.assertTotalEventsCount(1); } @Test public void simpleEventXmlConfig() { this.context = new ClassPathXmlApplicationContext( "org/springframework/context/event/simple-event-configuration.xml"); + TestEvent event = new TestEvent(this, "test"); TestEventListener listener = this.context.getBean(TestEventListener.class); this.eventCollector = getEventCollector(this.context); @@ -116,7 +123,6 @@ public void simpleEventXmlConfig() { @Test public void metaAnnotationIsDiscovered() { load(MetaAnnotationListenerTestBean.class); - MetaAnnotationListenerTestBean bean = this.context.getBean(MetaAnnotationListenerTestBean.class); this.eventCollector.assertNoEventReceived(bean); diff --git a/spring-context/src/test/java/org/springframework/context/event/test/EventCollector.java b/spring-context/src/test/java/org/springframework/context/event/test/EventCollector.java index 9df254c247c..cbbee976e92 100644 --- a/spring-context/src/test/java/org/springframework/context/event/test/EventCollector.java +++ b/spring-context/src/test/java/org/springframework/context/event/test/EventCollector.java @@ -30,6 +30,7 @@ * Test utility to collect and assert events. * * @author Stephane Nicoll + * @author Juergen Hoeller */ @Component public class EventCollector { @@ -73,7 +74,7 @@ public void assertNoEventReceived(Identifiable listener) { */ public void assertEvent(String listenerId, Object... events) { List actual = this.content.getOrDefault(listenerId, Collections.emptyList()); - assertEquals("wrong number of events", events.length, actual.size()); + assertEquals("Wrong number of events", events.length, actual.size()); for (int i = 0; i < events.length; i++) { assertEquals("Wrong event at index " + i, events[i], actual.get(i)); } @@ -97,8 +98,15 @@ public void assertTotalEventsCount(int number) { for (Map.Entry> entry : this.content.entrySet()) { actual += entry.getValue().size(); } - assertEquals("Wrong number of total events (" + this.content.size() + ") " + - "registered listener(s)", number, actual); + assertEquals("Wrong number of total events (" + this.content.size() + + ") registered listener(s)", number, actual); + } + + /** + * Clear the collected events, allowing for reuse of the collector. + */ + public void clear() { + this.content.clear(); } } diff --git a/spring-core/src/main/java/org/springframework/core/env/AbstractEnvironment.java b/spring-core/src/main/java/org/springframework/core/env/AbstractEnvironment.java index 25268f848db..356a7a91437 100644 --- a/spring-core/src/main/java/org/springframework/core/env/AbstractEnvironment.java +++ b/spring-core/src/main/java/org/springframework/core/env/AbstractEnvironment.java @@ -31,9 +31,6 @@ import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; -import static java.lang.String.*; -import static org.springframework.util.StringUtils.*; - /** * Abstract base class for {@link Environment} implementations. Supports the notion of * reserved default profile names and enables specifying active and default profiles @@ -124,7 +121,7 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment { public AbstractEnvironment() { customizePropertySources(this.propertySources); if (this.logger.isDebugEnabled()) { - this.logger.debug(format( + this.logger.debug(String.format( "Initialized %s with PropertySources %s", getClass().getSimpleName(), this.propertySources)); } } @@ -242,7 +239,8 @@ protected Set doGetActiveProfiles() { if (this.activeProfiles.isEmpty()) { String profiles = getProperty(ACTIVE_PROFILES_PROPERTY_NAME); if (StringUtils.hasText(profiles)) { - setActiveProfiles(commaDelimitedListToStringArray(trimAllWhitespace(profiles))); + setActiveProfiles(StringUtils.commaDelimitedListToStringArray( + StringUtils.trimAllWhitespace(profiles))); } } return this.activeProfiles; @@ -264,7 +262,7 @@ public void setActiveProfiles(String... profiles) { @Override public void addActiveProfile(String profile) { if (this.logger.isDebugEnabled()) { - this.logger.debug(format("Activating profile '%s'", profile)); + this.logger.debug(String.format("Activating profile '%s'", profile)); } validateProfile(profile); doGetActiveProfiles(); @@ -296,7 +294,8 @@ protected Set doGetDefaultProfiles() { if (this.defaultProfiles.equals(getReservedDefaultProfiles())) { String profiles = getProperty(DEFAULT_PROFILES_PROPERTY_NAME); if (StringUtils.hasText(profiles)) { - setDefaultProfiles(commaDelimitedListToStringArray(trimAllWhitespace(profiles))); + setDefaultProfiles(StringUtils.commaDelimitedListToStringArray( + StringUtils.trimAllWhitespace(profiles))); } } return this.defaultProfiles; @@ -393,7 +392,7 @@ protected String getSystemAttribute(String attributeName) { } catch (AccessControlException ex) { if (logger.isInfoEnabled()) { - logger.info(format("Caught AccessControlException when accessing system " + + logger.info(String.format("Caught AccessControlException when accessing system " + "environment variable [%s]; its value will be returned [null]. Reason: %s", attributeName, ex.getMessage())); } @@ -434,7 +433,7 @@ protected String getSystemAttribute(String attributeName) { } catch (AccessControlException ex) { if (logger.isInfoEnabled()) { - logger.info(format("Caught AccessControlException when accessing system " + + logger.info(String.format("Caught AccessControlException when accessing system " + "property [%s]; its value will be returned [null]. Reason: %s", attributeName, ex.getMessage())); } @@ -575,7 +574,7 @@ public String resolveRequiredPlaceholders(String text) throws IllegalArgumentExc @Override public String toString() { - return format("%s {activeProfiles=%s, defaultProfiles=%s, propertySources=%s}", + return String.format("%s {activeProfiles=%s, defaultProfiles=%s, propertySources=%s}", getClass().getSimpleName(), this.activeProfiles, this.defaultProfiles, this.propertySources); } diff --git a/spring-web/src/main/java/org/springframework/web/filter/HttpPutFormContentFilter.java b/spring-web/src/main/java/org/springframework/web/filter/HttpPutFormContentFilter.java index 74664a9cca0..8d672b5a10c 100644 --- a/spring-web/src/main/java/org/springframework/web/filter/HttpPutFormContentFilter.java +++ b/spring-web/src/main/java/org/springframework/web/filter/HttpPutFormContentFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -61,6 +61,7 @@ public class HttpPutFormContentFilter extends OncePerRequestFilter { private final FormHttpMessageConverter formConverter = new AllEncompassingFormHttpMessageConverter(); + /** * The default character set to use for reading form data. */ @@ -68,6 +69,7 @@ public void setCharset(Charset charset) { this.formConverter.setCharset(charset); } + @Override protected void doFilterInternal(final HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { @@ -104,29 +106,30 @@ private boolean isFormContentType(HttpServletRequest request) { } } + private static class HttpPutFormContentRequestWrapper extends HttpServletRequestWrapper { private MultiValueMap formParameters; public HttpPutFormContentRequestWrapper(HttpServletRequest request, MultiValueMap parameters) { super(request); - this.formParameters = (parameters != null) ? parameters : new LinkedMultiValueMap(); + this.formParameters = (parameters != null ? parameters : new LinkedMultiValueMap()); } @Override public String getParameter(String name) { String queryStringValue = super.getParameter(name); String formValue = this.formParameters.getFirst(name); - return (queryStringValue != null) ? queryStringValue : formValue; + return (queryStringValue != null ? queryStringValue : formValue); } @Override public Map getParameterMap() { Map result = new LinkedHashMap(); - Enumeration names = this.getParameterNames(); + Enumeration names = getParameterNames(); while (names.hasMoreElements()) { String name = names.nextElement(); - result.put(name, this.getParameterValues(name)); + result.put(name, getParameterValues(name)); } return result; } @@ -150,7 +153,7 @@ else if (queryStringValues == null) { return formValues.toArray(new String[formValues.size()]); } else { - List result = new ArrayList(); + List result = new ArrayList(queryStringValues.length + formValues.size()); result.addAll(Arrays.asList(queryStringValues)); result.addAll(formValues); return result.toArray(new String[result.size()]); diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/ServerEndpointRegistration.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/ServerEndpointRegistration.java index 5e066de54e7..4b2afc5f03d 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/ServerEndpointRegistration.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/ServerEndpointRegistration.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -107,8 +107,7 @@ public String getPath() { @Override public Class getEndpointClass() { - return (this.endpoint != null) ? - this.endpoint.getClass() : ((Class) this.endpointProvider.getHandlerType()); + return (this.endpoint != null ? this.endpoint.getClass() : this.endpointProvider.getHandlerType()); } public Endpoint getEndpoint() { diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/TomcatRequestUpgradeStrategy.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/TomcatRequestUpgradeStrategy.java index 9ae54f9924c..0948aec5b73 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/TomcatRequestUpgradeStrategy.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/TomcatRequestUpgradeStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -17,7 +17,6 @@ package org.springframework.web.socket.server.standard; import java.io.IOException; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; @@ -65,7 +64,7 @@ public void upgradeInternal(ServerHttpRequest request, ServerHttpResponse respon Map pathParams = Collections. emptyMap(); ServerEndpointRegistration endpointConfig = new ServerEndpointRegistration(path, endpoint); - endpointConfig.setSubprotocols(Arrays.asList(selectedProtocol)); + endpointConfig.setSubprotocols(Collections.singletonList(selectedProtocol)); endpointConfig.setExtensions(selectedExtensions); try { From ee5143b54b394f51211ce272fca01c1d83b03f84 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 31 Aug 2016 00:45:34 +0200 Subject: [PATCH 0148/1274] Upgrade to Jackson 2.8.2 and Netty 4.1.5 --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 39402a82525..1cc79601155 100644 --- a/build.gradle +++ b/build.gradle @@ -52,7 +52,7 @@ configure(allprojects) { project -> ext.hsqldbVersion = "2.3.4" ext.httpasyncVersion = "4.1.2" ext.httpclientVersion = "4.5.2" - ext.jackson2Version = "2.8.1" + ext.jackson2Version = "2.8.2" ext.jasperreportsVersion = "6.2.1" // our tests fail with JR-internal NPEs against 6.2.2 and higher ext.javamailVersion = "1.5.5" ext.jettyVersion = "9.3.11.v20160721" @@ -61,7 +61,7 @@ configure(allprojects) { project -> ext.jtaVersion = "1.2" ext.junitVersion = "4.12" ext.log4jVersion = "1.2.17" - ext.nettyVersion = "4.1.4.Final" + ext.nettyVersion = "4.1.5.Final" ext.okhttpVersion = "2.7.5" ext.okhttp3Version = "3.4.1" ext.openjpaVersion = "2.4.1" From 37670924f684bb6e9f63b9830c0a02a59b8fc3db Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 31 Aug 2016 01:53:03 +0200 Subject: [PATCH 0149/1274] Polishing --- .../htmlunit/HtmlUnitRequestBuilder.java | 25 +++++++++---------- ...ionListenerMethodTransactionalAdapter.java | 15 ++++++----- .../org/springframework/http/HttpRange.java | 10 ++++---- .../org/springframework/http/MediaType.java | 5 ++++ .../web/socket/WebSocketExtension.java | 2 +- 5 files changed, 32 insertions(+), 25 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/HtmlUnitRequestBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/HtmlUnitRequestBuilder.java index 7bab3e442f6..763be2d527f 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/HtmlUnitRequestBuilder.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/HtmlUnitRequestBuilder.java @@ -441,20 +441,19 @@ public boolean isMergeEnabled() { @Override public Object merge(Object parent) { - if (parent == null) { - return this; - } - if (parent instanceof MockHttpServletRequestBuilder) { - MockHttpServletRequestBuilder copiedParent = MockMvcRequestBuilders.get("/"); - copiedParent.merge(parent); - this.parentBuilder = copiedParent; - } else if (parent instanceof RequestBuilder) { - this.parentBuilder = (RequestBuilder) parent; - } - if (parent instanceof SmartRequestBuilder) { - this.parentPostProcessor = (SmartRequestBuilder) parent; + if (parent instanceof RequestBuilder) { + if (parent instanceof MockHttpServletRequestBuilder) { + MockHttpServletRequestBuilder copiedParent = MockMvcRequestBuilders.get("/"); + copiedParent.merge(parent); + this.parentBuilder = copiedParent; + } + else { + this.parentBuilder = (RequestBuilder) parent; + } + if (parent instanceof SmartRequestBuilder) { + this.parentPostProcessor = (SmartRequestBuilder) parent; + } } - return this; } diff --git a/spring-tx/src/main/java/org/springframework/transaction/event/ApplicationListenerMethodTransactionalAdapter.java b/spring-tx/src/main/java/org/springframework/transaction/event/ApplicationListenerMethodTransactionalAdapter.java index 5998522c692..a33b07bc63d 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/event/ApplicationListenerMethodTransactionalAdapter.java +++ b/spring-tx/src/main/java/org/springframework/transaction/event/ApplicationListenerMethodTransactionalAdapter.java @@ -32,10 +32,10 @@ * an event to a {@link TransactionalEventListener} annotated method. Supports * the exact same features as any regular {@link EventListener} annotated method * but is aware of the transactional context of the event publisher. - *

- * Processing of {@link TransactionalEventListener} is enabled automatically when - * Spring's transaction management is enabled. For other cases, registering a - * bean of type {@link TransactionalEventListenerFactory} is required. + * + *

Processing of {@link TransactionalEventListener} is enabled automatically + * when Spring's transaction management is enabled. For other cases, registering + * a bean of type {@link TransactionalEventListenerFactory} is required. * * @author Stephane Nicoll * @author Juergen Hoeller @@ -69,8 +69,11 @@ else if (this.annotation.fallbackExecution()) { } processEvent(event); } - else if (logger.isDebugEnabled()) { - logger.debug("No transaction is running - skipping " + event); + else { + // No transactional event execution at all + if (logger.isDebugEnabled()) { + logger.debug("No transaction is active - skipping " + event); + } } } diff --git a/spring-web/src/main/java/org/springframework/http/HttpRange.java b/spring-web/src/main/java/org/springframework/http/HttpRange.java index e3dbb8ca16a..c11a57a008d 100644 --- a/spring-web/src/main/java/org/springframework/http/HttpRange.java +++ b/spring-web/src/main/java/org/springframework/http/HttpRange.java @@ -56,8 +56,8 @@ public abstract class HttpRange { public ResourceRegion toResourceRegion(Resource resource) { // Don't try to determine contentLength on InputStreamResource - cannot be read afterwards... // Note: custom InputStreamResource subclasses could provide a pre-calculated content length! - Assert.isTrue(InputStreamResource.class != resource.getClass(), - "Can't convert an InputStreamResource to a ResourceRegion"); + Assert.isTrue(resource.getClass() != InputStreamResource.class, + "Cannot convert an InputStreamResource to a ResourceRegion"); try { long contentLength = resource.contentLength(); Assert.isTrue(contentLength > 0, "Resource content length should be > 0"); @@ -163,12 +163,12 @@ else if (dashIdx == 0) { } /** - * Convert each {@code HttpRange} into a {@code ResourceRegion}, - * selecting the appropriate segment of the given {@code Resource} - * using the HTTP Range information. + * Convert each {@code HttpRange} into a {@code ResourceRegion}, selecting the + * appropriate segment of the given {@code Resource} using HTTP Range information. * @param ranges the list of ranges * @param resource the resource to select the regions from * @return the list of regions for the given resource + * @since 4.3 */ public static List toResourceRegions(List ranges, Resource resource) { if (CollectionUtils.isEmpty(ranges)) { diff --git a/spring-web/src/main/java/org/springframework/http/MediaType.java b/spring-web/src/main/java/org/springframework/http/MediaType.java index 3fad7788bb4..f2ffba68b54 100644 --- a/spring-web/src/main/java/org/springframework/http/MediaType.java +++ b/spring-web/src/main/java/org/springframework/http/MediaType.java @@ -113,11 +113,13 @@ public class MediaType extends MimeType implements Serializable { /** * Public constant media type for {@code application/pdf}. + * @since 4.3 */ public final static MediaType APPLICATION_PDF; /** * A String equivalent of {@link MediaType#APPLICATION_PDF}. + * @since 4.3 */ public final static String APPLICATION_PDF_VALUE = "application/pdf"; @@ -193,11 +195,13 @@ public class MediaType extends MimeType implements Serializable { /** * Public constant media type for {@code text/markdown}. + * @since 4.3 */ public final static MediaType TEXT_MARKDOWN; /** * A String equivalent of {@link MediaType#TEXT_MARKDOWN}. + * @since 4.3 */ public final static String TEXT_MARKDOWN_VALUE = "text/markdown"; @@ -295,6 +299,7 @@ public MediaType(String type, String subtype, double qualityValue) { * @param other the other media type * @param charset the character set * @throws IllegalArgumentException if any of the parameters contain illegal characters + * @since 4.3 */ public MediaType(MediaType other, Charset charset) { super(other, charset); diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/WebSocketExtension.java b/spring-websocket/src/main/java/org/springframework/web/socket/WebSocketExtension.java index 817371c1638..e43a1d2a82a 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/WebSocketExtension.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/WebSocketExtension.java @@ -139,7 +139,7 @@ public static List parseExtensions(String extensions) { if (StringUtils.hasText(extensions)) { String[] tokens = StringUtils.tokenizeToStringArray(extensions, ","); List result = new ArrayList(tokens.length); - for (String token : StringUtils.tokenizeToStringArray(extensions, ",")) { + for (String token : tokens) { result.add(parseExtension(token)); } return result; From d26421fe3b7c42fb47bc438a22d2514a385252d0 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Wed, 31 Aug 2016 10:43:11 +0200 Subject: [PATCH 0150/1274] Fix missing ResourceHttpRequestHandler init in registry Issue: SPR-14577 Cherry-picked from: 7a88776329 --- .../servlet/config/annotation/ResourceHandlerRegistry.java | 1 + .../config/annotation/ResourceHandlerRegistryTests.java | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistry.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistry.java index 81f04456e0d..93853ed5680 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistry.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistry.java @@ -142,6 +142,7 @@ protected AbstractHandlerMapping getHandlerMapping() { handler.setContentNegotiationManager(this.contentNegotiationManager); try { handler.afterPropertiesSet(); + handler.afterSingletonsInstantiated(); } catch (Exception ex) { throw new BeanInitializationException("Failed to init ResourceHttpRequestHandler", ex); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistryTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistryTests.java index 32e2f64366c..83c032fb6ca 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistryTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -79,8 +79,6 @@ public void mapPathToLocation() throws Exception { request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, "/testStylesheet.css"); ResourceHttpRequestHandler handler = getHandler("/resources/**"); - handler.afterPropertiesSet(); - handler.afterSingletonsInstantiated(); handler.handleRequest(request, this.response); assertEquals("test stylesheet content", this.response.getContentAsString()); From c30290b43c8a860ee778bd75e7e772a32b909ff5 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 31 Aug 2016 14:43:39 +0200 Subject: [PATCH 0151/1274] @PathVariable supports 'required' attribute (for model attribute methods) Issue: SPR-14646 (cherry picked from commit e08b1b7) --- .../web/bind/annotation/PathVariable.java | 25 ++++- .../PathVariableMethodArgumentResolver.java | 17 ++-- ...thVariableMethodArgumentResolverTests.java | 94 ++++++++++++++++--- 3 files changed, 111 insertions(+), 25 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/bind/annotation/PathVariable.java b/spring-web/src/main/java/org/springframework/web/bind/annotation/PathVariable.java index 2d64574d0ec..21aa61049f2 100644 --- a/spring-web/src/main/java/org/springframework/web/bind/annotation/PathVariable.java +++ b/spring-web/src/main/java/org/springframework/web/bind/annotation/PathVariable.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -22,6 +22,8 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.springframework.core.annotation.AliasFor; + /** * Annotation which indicates that a method parameter should be bound to a URI template * variable. Supported for {@link RequestMapping} annotated handler methods in Servlet @@ -32,6 +34,7 @@ * then the map is populated with all path variable names and values. * * @author Arjen Poutsma + * @author Juergen Hoeller * @since 3.0 * @see RequestMapping * @see org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter @@ -43,8 +46,26 @@ public @interface PathVariable { /** - * The URI template variable to bind to. + * Alias for {@link #name}. */ + @AliasFor("name") String value() default ""; + /** + * The name of the path variable to bind to. + * @since 4.3.3 + */ + @AliasFor("value") + String name() default ""; + + /** + * Whether the path variable is required. + *

Defaults to {@code true}, leading to an exception being thrown if the path + * variable is missing in the incoming request. Switch this to {@code false} if + * you prefer a {@code null} or Java 8 {@code java.util.Optional} in this case. + * e.g. on a {@code ModelAttribute} method which serves for different requests. + * @since 4.3.3 + */ + boolean required() default true; + } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/PathVariableMethodArgumentResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/PathVariableMethodArgumentResolver.java index d7b1cb522ea..73658156b66 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/PathVariableMethodArgumentResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/PathVariableMethodArgumentResolver.java @@ -57,6 +57,7 @@ * * @author Rossen Stoyanchev * @author Arjen Poutsma + * @author Juergen Hoeller * @since 3.1 */ public class PathVariableMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver @@ -65,10 +66,6 @@ public class PathVariableMethodArgumentResolver extends AbstractNamedValueMethod private static final TypeDescriptor STRING_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(String.class); - public PathVariableMethodArgumentResolver() { - } - - @Override public boolean supportsParameter(MethodParameter parameter) { if (!parameter.hasParameterAnnotation(PathVariable.class)) { @@ -96,9 +93,7 @@ protected Object resolveName(String name, MethodParameter parameter, NativeWebRe } @Override - protected void handleMissingValue(String name, MethodParameter parameter) - throws ServletRequestBindingException { - + protected void handleMissingValue(String name, MethodParameter parameter) throws ServletRequestBindingException { throw new MissingPathVariableException(name, parameter); } @@ -121,13 +116,13 @@ protected void handleResolvedValue(Object arg, String name, MethodParameter para public void contributeMethodArgument(MethodParameter parameter, Object value, UriComponentsBuilder builder, Map uriVariables, ConversionService conversionService) { - if (Map.class.isAssignableFrom(parameter.getNestedParameterType())) { + if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) { return; } PathVariable ann = parameter.getParameterAnnotation(PathVariable.class); - String name = (ann == null || StringUtils.isEmpty(ann.value()) ? parameter.getParameterName() : ann.value()); - value = formatUriValue(conversionService, new TypeDescriptor(parameter), value); + String name = (ann != null && !StringUtils.isEmpty(ann.value()) ? ann.value() : parameter.getParameterName()); + value = formatUriValue(conversionService, new TypeDescriptor(parameter.nestedIfOptional()), value); uriVariables.put(name, value); } @@ -150,7 +145,7 @@ else if (cs != null) { private static class PathVariableNamedValueInfo extends NamedValueInfo { public PathVariableNamedValueInfo(PathVariable annotation) { - super(annotation.value(), true, ValueConstants.DEFAULT_NONE); + super(annotation.name(), annotation.required(), ValueConstants.DEFAULT_NONE); } } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/PathVariableMethodArgumentResolverTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/PathVariableMethodArgumentResolverTests.java index 04aefac3cf7..51d53e0f9aa 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/PathVariableMethodArgumentResolverTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/PathVariableMethodArgumentResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,29 +16,37 @@ package org.springframework.web.servlet.mvc.method.annotation; -import static org.junit.Assert.*; - import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import org.junit.Before; import org.junit.Test; import org.springframework.core.MethodParameter; +import org.springframework.core.annotation.SynthesizingMethodParameter; +import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.mock.web.test.MockHttpServletRequest; import org.springframework.mock.web.test.MockHttpServletResponse; +import org.springframework.util.ReflectionUtils; import org.springframework.web.bind.MissingPathVariableException; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.support.ConfigurableWebBindingInitializer; +import org.springframework.web.bind.support.DefaultDataBinderFactory; +import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.servlet.View; +import static org.junit.Assert.*; + /** * Test fixture with {@link PathVariableMethodArgumentResolver}. * * @author Rossen Stoyanchev + * @author Juergen Hoeller */ public class PathVariableMethodArgumentResolverTests { @@ -48,25 +56,33 @@ public class PathVariableMethodArgumentResolverTests { private MethodParameter paramString; + private MethodParameter paramNotRequired; + + private MethodParameter paramOptional; + private ModelAndViewContainer mavContainer; private ServletWebRequest webRequest; private MockHttpServletRequest request; + @Before public void setUp() throws Exception { resolver = new PathVariableMethodArgumentResolver(); - Method method = getClass().getMethod("handle", String.class, String.class); - paramNamedString = new MethodParameter(method, 0); - paramString = new MethodParameter(method, 1); + Method method = ReflectionUtils.findMethod(getClass(), "handle", (Class[]) null); + paramNamedString = new SynthesizingMethodParameter(method, 0); + paramString = new SynthesizingMethodParameter(method, 1); + paramNotRequired = new SynthesizingMethodParameter(method, 2); + paramOptional = new SynthesizingMethodParameter(method, 3); mavContainer = new ModelAndViewContainer(); request = new MockHttpServletRequest(); webRequest = new ServletWebRequest(request, new MockHttpServletResponse()); } + @Test public void supportsParameter() { assertTrue("Parameter with @PathVariable annotation", resolver.supportsParameter(paramNamedString)); @@ -89,21 +105,58 @@ public void resolveArgument() throws Exception { assertEquals("value", pathVars.get("name")); } - @SuppressWarnings("unchecked") + @Test + public void resolveArgumentNotRequired() throws Exception { + Map uriTemplateVars = new HashMap<>(); + uriTemplateVars.put("name", "value"); + request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVars); + + String result = (String) resolver.resolveArgument(paramNotRequired, mavContainer, webRequest, null); + assertEquals("PathVariable not resolved correctly", "value", result); + + @SuppressWarnings("unchecked") + Map pathVars = (Map) request.getAttribute(View.PATH_VARIABLES); + assertNotNull(pathVars); + assertEquals(1, pathVars.size()); + assertEquals("value", pathVars.get("name")); + } + + @Test + public void resolveArgumentWrappedAsOptional() throws Exception { + Map uriTemplateVars = new HashMap<>(); + uriTemplateVars.put("name", "value"); + request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVars); + + ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer(); + initializer.setConversionService(new DefaultConversionService()); + WebDataBinderFactory binderFactory = new DefaultDataBinderFactory(initializer); + + @SuppressWarnings("unchecked") + Optional result = (Optional) + resolver.resolveArgument(paramOptional, mavContainer, webRequest, binderFactory); + assertEquals("PathVariable not resolved correctly", "value", result.get()); + + @SuppressWarnings("unchecked") + Map pathVars = (Map) request.getAttribute(View.PATH_VARIABLES); + assertNotNull(pathVars); + assertEquals(1, pathVars.size()); + assertEquals(Optional.of("value"), pathVars.get("name")); + } + @Test public void resolveArgumentWithExistingPathVars() throws Exception { Map uriTemplateVars = new HashMap(); uriTemplateVars.put("name", "value"); request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVars); - Map pathVars; uriTemplateVars.put("oldName", "oldValue"); request.setAttribute(View.PATH_VARIABLES, uriTemplateVars); String result = (String) resolver.resolveArgument(paramNamedString, mavContainer, webRequest, null); assertEquals("PathVariable not resolved correctly", "value", result); - pathVars = (Map) request.getAttribute(View.PATH_VARIABLES); + @SuppressWarnings("unchecked") + Map pathVars = (Map) request.getAttribute(View.PATH_VARIABLES); assertNotNull(pathVars); assertEquals(2, pathVars.size()); assertEquals("value", pathVars.get("name")); @@ -113,11 +166,28 @@ public void resolveArgumentWithExistingPathVars() throws Exception { @Test(expected = MissingPathVariableException.class) public void handleMissingValue() throws Exception { resolver.resolveArgument(paramNamedString, mavContainer, webRequest, null); - fail("Unresolved path variable should lead to exception."); + fail("Unresolved path variable should lead to exception"); + } + + @Test + public void nullIfNotRequired() throws Exception { + assertNull(resolver.resolveArgument(paramNotRequired, mavContainer, webRequest, null)); } + @Test + public void wrapEmptyWithOptional() throws Exception { + ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer(); + initializer.setConversionService(new DefaultConversionService()); + WebDataBinderFactory binderFactory = new DefaultDataBinderFactory(initializer); + + assertEquals(Optional.empty(), resolver.resolveArgument(paramOptional, mavContainer, webRequest, binderFactory)); + } + + @SuppressWarnings("unused") - public void handle(@PathVariable(value = "name") String param1, String param2) { + public void handle(@PathVariable("name") String param1, String param2, + @PathVariable(name="name", required = false) String param3, + @PathVariable("name") Optional param4) { } -} \ No newline at end of file +} From 086e764845779d391e7d9272475620d6ccf2c159 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 31 Aug 2016 14:45:22 +0200 Subject: [PATCH 0152/1274] ResolvableType.java.forRawClass(Class) supports isAssignableFrom(ResolvableType) as well Issue: SPR-14648 (cherry picked from commit 1a30252) --- .../springframework/core/ResolvableType.java | 11 +++++--- .../core/ResolvableTypeTests.java | 27 +++++++++++++------ 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/ResolvableType.java b/spring-core/src/main/java/org/springframework/core/ResolvableType.java index 60c6c4547ee..c6aafdce821 100644 --- a/spring-core/src/main/java/org/springframework/core/ResolvableType.java +++ b/spring-core/src/main/java/org/springframework/core/ResolvableType.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -276,7 +276,7 @@ private boolean isAssignableFrom(ResolvableType other, Map matchedBe WildcardBounds ourBounds = WildcardBounds.get(this); WildcardBounds typeBounds = WildcardBounds.get(other); - // In the from X is assignable to + // In the form X is assignable to if (typeBounds != null) { return (ourBounds != null && ourBounds.isSameKind(typeBounds) && ourBounds.isAssignableFrom(typeBounds.getBounds())); @@ -937,7 +937,7 @@ public static ResolvableType forClass(Class sourceClass) { * Return a {@link ResolvableType} for the specified {@link Class}, doing * assignability checks against the raw class only (analogous to * {@link Class#isAssignableFrom}, which this serves as a wrapper for. - * For example: {@code ResolvableType.forClass(MyArrayList.class)}. + * For example: {@code ResolvableType.forRawClass(List.class)}. * @param sourceClass the source class ({@code null} is semantically * equivalent to {@code Object.class} for typical use cases here} * @return a {@link ResolvableType} for the specified class @@ -951,6 +951,11 @@ public static ResolvableType forRawClass(Class sourceClass) { public boolean isAssignableFrom(Class other) { return ClassUtils.isAssignable(getRawClass(), other); } + @Override + public boolean isAssignableFrom(ResolvableType other) { + Class otherClass = other.getRawClass(); + return (otherClass != null && ClassUtils.isAssignable(getRawClass(), otherClass)); + } }; } diff --git a/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java b/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java index 5c90f5cba91..84a4ab884fd 100644 --- a/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java +++ b/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,6 +37,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.concurrent.Callable; @@ -1084,7 +1085,6 @@ public void isAssignableFromForArrays() throws Exception { @Test public void isAssignableFromForWildcards() throws Exception { - ResolvableType object = ResolvableType.forClass(Object.class); ResolvableType charSequence = ResolvableType.forClass(CharSequence.class); ResolvableType string = ResolvableType.forClass(String.class); @@ -1287,6 +1287,15 @@ public void testSpr12701() throws Exception { assertThat(((ParameterizedType) type).getActualTypeArguments()[0], is(equalTo(String.class))); } + @Test + public void testSpr14648() throws Exception { + ResolvableType collectionClass = ResolvableType.forRawClass(Collection.class); + ResolvableType setClass = ResolvableType.forRawClass(Set.class); + ResolvableType fromReturnType = ResolvableType.forMethodReturnType(Methods.class.getMethod("wildcardSet")); + assertTrue(collectionClass.isAssignableFrom(fromReturnType)); + assertTrue(setClass.isAssignableFrom(fromReturnType)); + } + private ResolvableType testSerialization(ResolvableType type) throws Exception { ByteArrayOutputStream bos = new ByteArrayOutputStream(); @@ -1385,7 +1394,7 @@ static class TypedFields extends Fields { } - static interface Methods { + interface Methods { List charSequenceReturn(); @@ -1398,6 +1407,8 @@ static interface Methods { void typedParameter(T p); T typedReturn(); + + Set wildcardSet(); } @@ -1453,7 +1464,7 @@ static class Assignment extends AssignmentBase { } - static interface TypedMethods extends Methods { + interface TypedMethods extends Methods { } @@ -1526,19 +1537,19 @@ public class MyCollectionSuperclassType extends MySuperclassType extends List { + interface Wildcard extends List { } - static interface RawExtendsWildcard extends Wildcard { + interface RawExtendsWildcard extends Wildcard { } - static interface VariableNameSwitch extends MultiValueMap { + interface VariableNameSwitch extends MultiValueMap { } - static interface ListOfGenericArray extends List[]> { + interface ListOfGenericArray extends List[]> { } From efb5f17a600b07461b128e9a78e17f505f237896 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 1 Sep 2016 12:41:55 +0200 Subject: [PATCH 0153/1274] Documentation updates around configuration classes (cherry picked from commit aff914c) --- src/asciidoc/core-beans.adoc | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/asciidoc/core-beans.adoc b/src/asciidoc/core-beans.adoc index 3f525dc1338..ea857975db6 100644 --- a/src/asciidoc/core-beans.adoc +++ b/src/asciidoc/core-beans.adoc @@ -2946,7 +2946,6 @@ to perform certain actions upon initialization and destruction of your beans. [TIP] ==== - The JSR-250 `@PostConstruct` and `@PreDestroy` annotations are generally considered best practice for receiving lifecycle callbacks in a modern Spring application. Using these annotations means that your beans are not coupled to Spring specific interfaces. For @@ -6715,13 +6714,17 @@ The behavior could be different according to the scope of your bean. We are talk about singletons here. ==== -[NOTE] +[TIP] ==== There are a few restrictions due to the fact that CGLIB dynamically adds features at -startup-time: +startup-time, in particular that configuration classes must not be final. However, as +of 4.3, any constructors are allowed on configuration classes, including the use of +`@Autowired` or a single non-default constructor declaration for default injection. -* Configuration classes should not be final -* They should have a constructor with no arguments +If you prefer to avoid any CGLIB-imposed limitations, consider declaring your `@Bean` +methods on non-`@Configuration` classes, e.g. on plain `@Component` classes instead. +Cross-method calls between `@Bean` methods won't get intercepted then, so you'll have +to exclusively rely on dependency injection at the constructor or method level there. ==== @@ -6781,6 +6784,14 @@ This approach simplifies container instantiation, as only one class needs to be with, rather than requiring the developer to remember a potentially large number of `@Configuration` classes during construction. +[TIP] +==== +As of Spring Framework 4.2, `@Import` also supports references to regular component +classes, analogous to the `AnnotationConfigApplicationContext.register` method. +This is particularly useful if you'd like to avoid component scanning, using a few +configuration classes as entry points for explicitly defining all your components. +==== + [[beans-java-injecting-imported-beans]] ===== Injecting dependencies on imported @Bean definitions From 49fc4923be48028164e1f0df853b357801ed47bc Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 1 Sep 2016 12:56:25 +0200 Subject: [PATCH 0154/1274] Upgrade to JSR-354 API 1.0.1 and Jackson 2.8 javadocs --- build.gradle | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 1cc79601155..effe1a52e7d 100644 --- a/build.gradle +++ b/build.gradle @@ -202,9 +202,9 @@ configure(allprojects) { project -> "http://ehcache.org/apidocs/${ehcacheVersion}", "http://ehcache.org/apidocs/${ehcache3Version}", "http://quartz-scheduler.org/api/2.2.1/", - "http://fasterxml.github.io/jackson-core/javadoc/2.7/", - "http://fasterxml.github.io/jackson-databind/javadoc/2.7/", - "http://fasterxml.github.io/jackson-dataformat-xml/javadoc/2.7/", + "http://fasterxml.github.io/jackson-core/javadoc/2.8/", + "http://fasterxml.github.io/jackson-databind/javadoc/2.8/", + "http://fasterxml.github.io/jackson-dataformat-xml/javadoc/2.8/", "http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/" ] as String[] } @@ -479,7 +479,7 @@ project("spring-context") { optional("javax.inject:javax.inject:1") optional("javax.ejb:ejb-api:${ejbVersion}") optional("javax.enterprise.concurrent:javax.enterprise.concurrent-api:1.0") - optional("javax.money:money-api:1.0") + optional("javax.money:money-api:1.0.1") optional("org.eclipse.persistence:javax.persistence:2.0.0") optional("javax.validation:validation-api:1.0.0.GA") optional("org.hibernate:hibernate-validator:${hibval4Version}") From dc2cafc88812a5e2ebb3c6480695e91b49125430 Mon Sep 17 00:00:00 2001 From: sylvainlaurent Date: Tue, 30 Aug 2016 20:25:18 +0200 Subject: [PATCH 0155/1274] Fix class literal in instanceof Closes gh-1151 --- src/asciidoc/core-expressions.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/asciidoc/core-expressions.adoc b/src/asciidoc/core-expressions.adoc index 86e34a5061c..477608efdbf 100644 --- a/src/asciidoc/core-expressions.adoc +++ b/src/asciidoc/core-expressions.adoc @@ -785,7 +785,7 @@ expression based `matches` operator. ---- // evaluates to false boolean falseValue = parser.parseExpression( - "'xyz' instanceof T(Integer.class)").getValue(Boolean.class); + "'xyz' instanceof T(Integer)").getValue(Boolean.class); // evaluates to true boolean trueValue = parser.parseExpression( @@ -799,7 +799,7 @@ expression based `matches` operator. [NOTE] ==== Be careful with primitive types as they are immediately boxed up to the wrapper type, -so `1 instanceof T(int)` evaluates to `false` while `1 instanceof T(Integer.class)` +so `1 instanceof T(int)` evaluates to `false` while `1 instanceof T(Integer)` evaluates to `true`, as expected. ==== From f3dae0c9ad76f3c4a7354464a50ba31d30fcdb77 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Fri, 2 Sep 2016 11:35:58 +0200 Subject: [PATCH 0156/1274] Mention AntPathMatcher regexp support This commit documents the regexp support in `AntPathMatcher` when matching for URL patterns. This support is also mentioned in places where developers can register patterns for ViewControllers or resource handlers. Issue: SPR-14652 Cherry-picked from: a8ba065a6e6a --- .../src/main/java/org/springframework/util/AntPathMatcher.java | 3 +++ .../web/servlet/config/annotation/ResourceHandlerRegistry.java | 3 +++ .../web/servlet/config/annotation/ViewControllerRegistry.java | 3 +++ 3 files changed, 9 insertions(+) diff --git a/spring-core/src/main/java/org/springframework/util/AntPathMatcher.java b/spring-core/src/main/java/org/springframework/util/AntPathMatcher.java index 5e91704bbe7..51c479dd26a 100644 --- a/spring-core/src/main/java/org/springframework/util/AntPathMatcher.java +++ b/spring-core/src/main/java/org/springframework/util/AntPathMatcher.java @@ -35,6 +35,7 @@ *

  • {@code ?} matches one character
  • *
  • {@code *} matches zero or more characters
  • *
  • {@code **} matches zero or more directories in a path
  • + *
  • {@code {spring:[a-z]+}} matches the regexp {@code [a-z]+} as a path variable named "spring"
  • * * *

    Examples

    @@ -50,6 +51,8 @@ *
  • org/**/servlet/bla.jsp — matches * {@code org/springframework/servlet/bla.jsp} but also * {@code org/springframework/testing/servlet/bla.jsp} and {@code org/servlet/bla.jsp}
  • + *
  • {@code com/{filename:\\w+}.jsp} will match {@code com/test.jsp} and assign the value {@code test} + * to the {@code filename} variable
  • * * *

    Note: a pattern and a path must both be absolute or must diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistry.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistry.java index 93853ed5680..e4b804045c5 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistry.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistry.java @@ -92,6 +92,9 @@ public ResourceHandlerRegistry(ApplicationContext applicationContext, ServletCon * Add a resource handler for serving static resources based on the specified URL path * patterns. The handler will be invoked for every incoming request that matches to * one of the specified path patterns. + *

    Patterns like {@code "/static/**"} or {@code "/css/{filename:\\w+\\.css}"} + * are allowed. See {@link org.springframework.util.AntPathMatcher} for more details on the + * syntax. * @return A {@link ResourceHandlerRegistration} to use to further configure the * registered resource handler */ diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ViewControllerRegistry.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ViewControllerRegistry.java index 3a2de78a4de..bcc0a10cf0f 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ViewControllerRegistry.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ViewControllerRegistry.java @@ -49,6 +49,9 @@ public class ViewControllerRegistry { /** * Map a view controller to the given URL path (or pattern) in order to render * a response with a pre-configured status code and view. + *

    Patterns like {@code "/admin/**"} or {@code "/articles/{articlename:\\w+}"} + * are allowed. See {@link org.springframework.util.AntPathMatcher} for more details on the + * syntax. */ public ViewControllerRegistration addViewController(String urlPath) { ViewControllerRegistration registration = new ViewControllerRegistration(urlPath); From 6501bc5d32f4150e5197031f3b943add44305aa4 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Tue, 6 Sep 2016 18:17:47 +0200 Subject: [PATCH 0157/1274] Align MVC checkNotModified with reactive support Since SPR-14522, the web reactive framework supports checkNotModified features. This commit aligns the existing MVC infrastructure with web reactive's behavior. Code duplication has been removed from `HttpEntityMethodProcessor` but the Servlet 2.5 baseline is still respected. Issue: SPR-14659 Cherry-picked from: cc5300c4d558e3f86d5 --- .../context/request/ServletWebRequest.java | 278 +++++++++--------- .../ServletWebRequestHttpMethodsTests.java | 10 +- .../annotation/HttpEntityMethodProcessor.java | 81 ++--- .../HttpEntityMethodProcessorMockTests.java | 7 +- 4 files changed, 170 insertions(+), 206 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java b/spring-web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java index 096d216a4fb..73c9474f852 100644 --- a/spring-web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java +++ b/spring-web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java @@ -17,12 +17,18 @@ package org.springframework.web.context.request; import java.security.Principal; -import java.util.Date; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Enumeration; import java.util.Iterator; +import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.TimeZone; import java.util.regex.Matcher; import java.util.regex.Pattern; + import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; @@ -45,25 +51,17 @@ */ public class ServletWebRequest extends ServletRequestAttributes implements NativeWebRequest { - private static final String HEADER_ETAG = "ETag"; - - private static final String HEADER_IF_MODIFIED_SINCE = "If-Modified-Since"; - - private static final String HEADER_IF_UNMODIFIED_SINCE = "If-Unmodified-Since"; - - private static final String HEADER_IF_NONE_MATCH = "If-None-Match"; - - private static final String HEADER_LAST_MODIFIED = "Last-Modified"; + private static final String ETAG = "ETag"; - private static final String METHOD_GET = "GET"; + private static final String IF_MODIFIED_SINCE = "If-Modified-Since"; - private static final String METHOD_HEAD = "HEAD"; + private static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since"; - private static final String METHOD_POST = "POST"; + private static final String IF_NONE_MATCH = "If-None-Match"; - private static final String METHOD_PUT = "PUT"; + private static final String LAST_MODIFIED = "Last-Modified"; - private static final String METHOD_DELETE = "DELETE"; + private static final List SAFE_METHODS = Arrays.asList("GET", "HEAD"); /** * Pattern matching ETag multiple field values in headers such as "If-Match", "If-None-Match" @@ -71,6 +69,17 @@ public class ServletWebRequest extends ServletRequestAttributes implements Nativ */ private static final Pattern ETAG_HEADER_VALUE_PATTERN = Pattern.compile("\\*|\\s*((W\\/)?(\"[^\"]*\"))\\s*,?"); + /** + * Date formats as specified in the HTTP RFC + * @see Section 7.1.1.1 of RFC 7231 + */ + private static final String[] DATE_FORMATS = new String[] { + "EEE, dd MMM yyyy HH:mm:ss zzz", + "EEE, dd-MMM-yy HH:mm:ss zzz", + "EEE MMM dd HH:mm:ss yyyy" + }; + + private static TimeZone GMT = TimeZone.getTimeZone("GMT"); /** Checking for Servlet 3.0+ HttpServletResponse.getHeader(String) */ private static final boolean servlet3Present = @@ -194,103 +203,62 @@ public boolean isSecure() { @Override public boolean checkNotModified(long lastModifiedTimestamp) { - HttpServletResponse response = getResponse(); - if (lastModifiedTimestamp >= 0 && !this.notModified) { - if (isCompatibleWithConditionalRequests(response)) { - this.notModified = isTimestampNotModified(lastModifiedTimestamp); - if (response != null) { - if (supportsNotModifiedStatus()) { - if (this.notModified) { - response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); - } - if (isHeaderAbsent(response, HEADER_LAST_MODIFIED)) { - response.setDateHeader(HEADER_LAST_MODIFIED, lastModifiedTimestamp); - } - } - else if (supportsConditionalUpdate()) { - if (this.notModified) { - response.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED); - } - } - } - } - } - return this.notModified; + return checkNotModified(null, lastModifiedTimestamp); } @Override public boolean checkNotModified(String etag) { - HttpServletResponse response = getResponse(); - if (StringUtils.hasLength(etag) && !this.notModified) { - if (isCompatibleWithConditionalRequests(response)) { - etag = addEtagPadding(etag); - if (hasRequestHeader(HEADER_IF_NONE_MATCH)) { - this.notModified = isEtagNotModified(etag); - } - if (response != null) { - if (this.notModified && supportsNotModifiedStatus()) { - response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); - } - if (isHeaderAbsent(response, HEADER_ETAG)) { - response.setHeader(HEADER_ETAG, etag); - } - } - } - } - return this.notModified; + return checkNotModified(etag, -1); } @Override public boolean checkNotModified(String etag, long lastModifiedTimestamp) { HttpServletResponse response = getResponse(); - if (StringUtils.hasLength(etag) && !this.notModified) { - if (isCompatibleWithConditionalRequests(response)) { - etag = addEtagPadding(etag); - if (hasRequestHeader(HEADER_IF_NONE_MATCH)) { - this.notModified = isEtagNotModified(etag); - } - else if (hasRequestHeader(HEADER_IF_MODIFIED_SINCE)) { - this.notModified = isTimestampNotModified(lastModifiedTimestamp); - } - if (response != null) { - if (supportsNotModifiedStatus()) { - if (this.notModified) { - response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); - } - if (isHeaderAbsent(response, HEADER_ETAG)) { - response.setHeader(HEADER_ETAG, etag); - } - if (isHeaderAbsent(response, HEADER_LAST_MODIFIED)) { - response.setDateHeader(HEADER_LAST_MODIFIED, lastModifiedTimestamp); - } - } - else if (supportsConditionalUpdate()) { - if (this.notModified) { - response.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED); - } - } - } + if (this.notModified || !isStatusOK(response)) { + return this.notModified; + } + + // Evaluate conditions in order of precedence. + // See https://tools.ietf.org/html/rfc7232#section-6 + + if (validateIfUnmodifiedSince(lastModifiedTimestamp)) { + if (this.notModified) { + response.setStatus(HttpStatus.PRECONDITION_FAILED.value()); } + return this.notModified; } - return this.notModified; - } - public boolean isNotModified() { - return this.notModified; - } + boolean validated = validateIfNoneMatch(etag); + if (!validated) { + validateIfModifiedSince(lastModifiedTimestamp); + } - private boolean isCompatibleWithConditionalRequests(HttpServletResponse response) { - try { - if (response == null || !servlet3Present) { - // Can't check response.getStatus() - let's assume we're good - return true; + // Update response + + boolean isHttpGetOrHead = SAFE_METHODS.contains(getRequest().getMethod()); + if (this.notModified) { + response.setStatus(isHttpGetOrHead ? + HttpStatus.NOT_MODIFIED.value() : HttpStatus.PRECONDITION_FAILED.value()); + } + if (isHttpGetOrHead) { + if(lastModifiedTimestamp > 0 && isHeaderAbsent(response, LAST_MODIFIED)) { + response.setDateHeader(LAST_MODIFIED, lastModifiedTimestamp); + } + if (StringUtils.hasLength(etag) && isHeaderAbsent(response, ETAG)) { + response.setHeader(ETAG, padEtagIfNecessary(etag)); } - return HttpStatus.valueOf(response.getStatus()).is2xxSuccessful(); } - catch (IllegalArgumentException ex) { + + return this.notModified; + } + + private boolean isStatusOK(HttpServletResponse response) { + if (response == null || !servlet3Present) { + // Can't check response.getStatus() - let's assume we're good return true; } + return HttpStatus.OK.value() == 200; } private boolean isHeaderAbsent(HttpServletResponse response, String header) { @@ -301,34 +269,78 @@ private boolean isHeaderAbsent(HttpServletResponse response, String header) { return (response.getHeader(header) == null); } - private boolean hasRequestHeader(String headerName) { - return StringUtils.hasLength(getHeader(headerName)); + private boolean validateIfUnmodifiedSince(long lastModifiedTimestamp) { + if (lastModifiedTimestamp < 0) { + return false; + } + long ifUnmodifiedSince = parseDateHeader(IF_UNMODIFIED_SINCE); + if (ifUnmodifiedSince == -1) { + return false; + } + // We will perform this validation... + this.notModified = (ifUnmodifiedSince < (lastModifiedTimestamp / 1000 * 1000)); + return true; } - private boolean supportsNotModifiedStatus() { - String method = getRequest().getMethod(); - return (METHOD_GET.equals(method) || METHOD_HEAD.equals(method)); + private boolean validateIfNoneMatch(String etag) { + if (!StringUtils.hasLength(etag)) { + return false; + } + Enumeration ifNoneMatch; + try { + ifNoneMatch = getRequest().getHeaders(IF_NONE_MATCH); + } + catch (IllegalArgumentException ex) { + return false; + } + if (!ifNoneMatch.hasMoreElements()) { + return false; + } + // We will perform this validation... + etag = padEtagIfNecessary(etag); + while (ifNoneMatch.hasMoreElements()) { + String clientETags = ifNoneMatch.nextElement(); + + Matcher eTagMatcher = ETAG_HEADER_VALUE_PATTERN.matcher(clientETags); + // Compare weak/strong ETags as per https://tools.ietf.org/html/rfc7232#section-2.3 + while (eTagMatcher.find()) { + if (StringUtils.hasLength(eTagMatcher.group()) + && etag.replaceFirst("^W/", "").equals(eTagMatcher.group(3))) { + this.notModified = true; + break; + } + } + } + return true; } - private boolean supportsConditionalUpdate() { - String method = getRequest().getMethod(); - return (METHOD_POST.equals(method) || METHOD_PUT.equals(method) || METHOD_DELETE.equals(method)) - && hasRequestHeader(HEADER_IF_UNMODIFIED_SINCE); + private String padEtagIfNecessary(String etag) { + if (!StringUtils.hasLength(etag)) { + return etag; + } + if ((etag.startsWith("\"") || etag.startsWith("W/\"")) && etag.endsWith("\"")) { + return etag; + } + return "\"" + etag + "\""; } - private boolean isTimestampNotModified(long lastModifiedTimestamp) { - long ifModifiedSince = parseDateHeader(HEADER_IF_MODIFIED_SINCE); - if (ifModifiedSince != -1) { - return (ifModifiedSince >= (lastModifiedTimestamp / 1000 * 1000)); + private boolean validateIfModifiedSince(long lastModifiedTimestamp) { + if (lastModifiedTimestamp < 0) { + return false; } - long ifUnmodifiedSince = parseDateHeader(HEADER_IF_UNMODIFIED_SINCE); - if (ifUnmodifiedSince != -1) { - return (ifUnmodifiedSince < (lastModifiedTimestamp / 1000 * 1000)); + long ifModifiedSince = parseDateHeader(IF_MODIFIED_SINCE); + if (ifModifiedSince == -1) { + return false; } - return false; + // We will perform this validation... + this.notModified = ifModifiedSince >= (lastModifiedTimestamp / 1000 * 1000); + return true; + } + + public boolean isNotModified() { + return this.notModified; } - @SuppressWarnings("deprecation") private long parseDateHeader(String headerName) { long dateValue = -1; try { @@ -340,36 +352,32 @@ private long parseDateHeader(String headerName) { int separatorIndex = headerValue.indexOf(';'); if (separatorIndex != -1) { String datePart = headerValue.substring(0, separatorIndex); - try { - dateValue = Date.parse(datePart); - } - catch (IllegalArgumentException ex2) { - // Giving up - } + dateValue = parseDateValue(datePart); } } return dateValue; } - private boolean isEtagNotModified(String etag) { - String ifNoneMatch = getHeader(HEADER_IF_NONE_MATCH); - // compare weak/strong ETag as per https://tools.ietf.org/html/rfc7232#section-2.3 - String serverETag = etag.replaceFirst("^W/", ""); - Matcher eTagMatcher = ETAG_HEADER_VALUE_PATTERN.matcher(ifNoneMatch); - while (eTagMatcher.find()) { - if ("*".equals(eTagMatcher.group()) - || serverETag.equals(eTagMatcher.group(3))) { - return true; - } + private long parseDateValue(String headerValue) { + if (headerValue == null) { + // No header value sent at all + return -1; } - return false; - } - - private String addEtagPadding(String etag) { - if (!(etag.startsWith("\"") || etag.startsWith("W/\"")) || !etag.endsWith("\"")) { - etag = "\"" + etag + "\""; + if (headerValue.length() >= 3) { + // Short "0" or "-1" like values are never valid HTTP date headers... + // Let's only bother with SimpleDateFormat parsing for long enough values. + for (String dateFormat : DATE_FORMATS) { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat, Locale.US); + simpleDateFormat.setTimeZone(GMT); + try { + return simpleDateFormat.parse(headerValue).getTime(); + } + catch (ParseException ex) { + // ignore + } + } } - return etag; + return -1; } @Override diff --git a/spring-web/src/test/java/org/springframework/web/context/request/ServletWebRequestHttpMethodsTests.java b/spring-web/src/test/java/org/springframework/web/context/request/ServletWebRequestHttpMethodsTests.java index e5ff3c2c858..de9e3eed17a 100644 --- a/spring-web/src/test/java/org/springframework/web/context/request/ServletWebRequestHttpMethodsTests.java +++ b/spring-web/src/test/java/org/springframework/web/context/request/ServletWebRequestHttpMethodsTests.java @@ -94,9 +94,7 @@ public void checkNotModifiedInvalidStatus() { servletRequest.addHeader("If-Modified-Since", epochTime); servletResponse.setStatus(0); - assertTrue(request.checkNotModified(epochTime)); - assertEquals(304, servletResponse.getStatus()); - assertEquals(dateFormat.format(epochTime), servletResponse.getHeader("Last-Modified")); + assertFalse(request.checkNotModified(epochTime)); } @Test // SPR-14559 @@ -202,13 +200,13 @@ public void checkModifiedUnpaddedETag() { } @Test - public void checkNotModifiedWildcardETag() { + public void checkNotModifiedWildcardIsIgnored() { String eTag = "\"Foo\""; servletRequest.addHeader("If-None-Match", "*"); - assertTrue(request.checkNotModified(eTag)); + assertFalse(request.checkNotModified(eTag)); - assertEquals(304, servletResponse.getStatus()); + assertEquals(200, servletResponse.getStatus()); assertEquals(eTag, servletResponse.getHeader("ETag")); } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessor.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessor.java index 2f98f56cdd6..4191adc528e 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessor.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessor.java @@ -28,8 +28,6 @@ import org.springframework.core.ResolvableType; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageConverter; @@ -41,6 +39,7 @@ import org.springframework.web.accept.ContentNegotiationManager; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.method.support.ModelAndViewContainer; /** @@ -182,16 +181,16 @@ public void handleReturnValue(Object returnValue, MethodParameter returnType, } if (responseEntity instanceof ResponseEntity) { - outputMessage.getServletResponse().setStatus(((ResponseEntity) responseEntity).getStatusCodeValue()); - HttpMethod method = inputMessage.getMethod(); - boolean isGetOrHead = (HttpMethod.GET == method || HttpMethod.HEAD == method); - if (isGetOrHead && isResourceNotModified(inputMessage, outputMessage)) { - outputMessage.setStatusCode(HttpStatus.NOT_MODIFIED); - // Ensure headers are flushed, no body should be written. - outputMessage.flush(); - // Skip call to converters, as they may update the body. - return; - } + int responseStatus = ((ResponseEntity) responseEntity).getStatusCodeValue(); + outputMessage.getServletResponse().setStatus(responseStatus); + if(responseStatus == 200) { + if (isResourceNotModified(inputMessage, outputMessage)) { + // Ensure headers are flushed, no body should be written. + outputMessage.flush(); + // Skip call to converters, as they may update the body. + return; + } + } } // Try even with null body. ResponseBodyAdvice could get involved. @@ -223,55 +222,15 @@ private List getVaryRequestHeadersToAdd(HttpHeaders responseHeaders, Htt } private boolean isResourceNotModified(ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) { - boolean notModified = false; - try { - long ifModifiedSince = inputMessage.getHeaders().getIfModifiedSince(); - String eTag = addEtagPadding(outputMessage.getHeaders().getETag()); - long lastModified = outputMessage.getHeaders().getLastModified(); - List ifNoneMatch = inputMessage.getHeaders().getIfNoneMatch(); - if (!ifNoneMatch.isEmpty() && (inputMessage.getHeaders().containsKey(HttpHeaders.IF_UNMODIFIED_SINCE) - || inputMessage.getHeaders().containsKey(HttpHeaders.IF_MATCH))) { - // invalid conditional request, do not process - } - else if (lastModified != -1 && StringUtils.hasLength(eTag)) { - notModified = isETagNotModified(ifNoneMatch, eTag) && isTimeStampNotModified(ifModifiedSince, lastModified); - } - else if (lastModified != -1) { - notModified = isTimeStampNotModified(ifModifiedSince, lastModified); - } - else if (StringUtils.hasLength(eTag)) { - notModified = isETagNotModified(ifNoneMatch, eTag); - } - } - catch (IllegalArgumentException exc) { - // invalid conditional request, do not process - } - return notModified; - } - - private boolean isETagNotModified(List ifNoneMatch, String etag) { - if (StringUtils.hasLength(etag)) { - for (String clientETag : ifNoneMatch) { - // Compare weak/strong ETags as per https://tools.ietf.org/html/rfc7232#section-2.3 - if (StringUtils.hasLength(clientETag) && - (clientETag.replaceFirst("^W/", "").equals(etag.replaceFirst("^W/", "")))) { - return true; - } - } - } - return false; - } - - private boolean isTimeStampNotModified(long ifModifiedSince, long lastModifiedTimestamp) { - return (ifModifiedSince >= (lastModifiedTimestamp / 1000 * 1000)); - } - - private String addEtagPadding(String etag) { - if (StringUtils.hasLength(etag) && - (!(etag.startsWith("\"") || etag.startsWith("W/\"")) || !etag.endsWith("\"")) ) { - etag = "\"" + etag + "\""; - } - return etag; + ServletWebRequest servletWebRequest = + new ServletWebRequest(inputMessage.getServletRequest(), outputMessage.getServletResponse()); + HttpHeaders responseHeaders = outputMessage.getHeaders(); + String etag = responseHeaders.getETag(); + long lastModifiedTimestamp = responseHeaders.getLastModified(); + responseHeaders.remove(HttpHeaders.ETAG); + responseHeaders.remove(HttpHeaders.LAST_MODIFIED); + + return servletWebRequest.checkNotModified(etag, lastModifiedTimestamp); } @Override diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessorMockTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessorMockTests.java index 65787c10ff1..64ad0e6e1de 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessorMockTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessorMockTests.java @@ -476,8 +476,7 @@ public void handleReturnValuePostRequestWithIfNotModified() throws Exception { processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest); assertResponseOkWithBody("body"); - assertEquals(1, servletResponse.getHeaderValues(HttpHeaders.ETAG).size()); - assertEquals(etagValue, servletResponse.getHeader(HttpHeaders.ETAG)); + assertEquals(0, servletResponse.getHeaderValues(HttpHeaders.ETAG).size()); } // SPR-13626 @@ -511,7 +510,7 @@ public void handleReturnValueIfNoneMatchIfMatch() throws Exception { initStringMessageConversion(MediaType.TEXT_PLAIN); processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest); - assertResponseOkWithBody("body"); + assertResponseNotModified(); assertEquals(1, servletResponse.getHeaderValues(HttpHeaders.ETAG).size()); assertEquals(etagValue, servletResponse.getHeader(HttpHeaders.ETAG)); } @@ -529,7 +528,7 @@ public void handleReturnValueIfNoneMatchIfUnmodifiedSince() throws Exception { initStringMessageConversion(MediaType.TEXT_PLAIN); processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest); - assertResponseOkWithBody("body"); + assertResponseNotModified(); assertEquals(1, servletResponse.getHeaderValues(HttpHeaders.ETAG).size()); assertEquals(etagValue, servletResponse.getHeader(HttpHeaders.ETAG)); } From 43c60a02f703ef5d0686452c63299c43e08a03b0 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Tue, 6 Sep 2016 19:13:03 +0200 Subject: [PATCH 0158/1274] Fix response status check in ServletWrbRequest Issue: SPR-14659 --- .../springframework/web/context/request/ServletWebRequest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java b/spring-web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java index 73c9474f852..ca17e8f1d49 100644 --- a/spring-web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java +++ b/spring-web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java @@ -258,7 +258,7 @@ private boolean isStatusOK(HttpServletResponse response) { // Can't check response.getStatus() - let's assume we're good return true; } - return HttpStatus.OK.value() == 200; + return response.getStatus() == 200; } private boolean isHeaderAbsent(HttpServletResponse response, String header) { From 558a10b54f2d12b26508c07e336466e7d0732755 Mon Sep 17 00:00:00 2001 From: nkjackzhang Date: Tue, 6 Sep 2016 17:41:01 +0800 Subject: [PATCH 0159/1274] Fix typo Closes gh-1158 --- src/asciidoc/web-mvc.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/asciidoc/web-mvc.adoc b/src/asciidoc/web-mvc.adoc index 20a6dac8df4..7fd96fc604d 100644 --- a/src/asciidoc/web-mvc.adoc +++ b/src/asciidoc/web-mvc.adoc @@ -171,7 +171,7 @@ Java EE Servlet configuration in a Servlet 3.0+ environment: @Override public void onStartup(ServletContext container) { - ServletRegistration.Dynamic registration = container.addServlet("dispatcher", new DispatcherServlet()); + ServletRegistration.Dynamic registration = container.addServlet("example", new DispatcherServlet()); registration.setLoadOnStartup(1); registration.addMapping("/example/*"); } From fcf3ccba98ec6454eb069598e55a1205e821c5ad Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Wed, 7 Sep 2016 10:47:00 +0200 Subject: [PATCH 0160/1274] Fix default encoding in CONTRIBUTING documentation Sources should be using UTF-8. Issue: SPR-14674 Cherry-picked from: d1f60e3de18545 --- CONTRIBUTING.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cdde5c72954..3258d9118e1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -88,15 +88,14 @@ present in the framework. 1. Preserve existing formatting; i.e. do not reformat code for its own sake 1. Search the codebase using `git grep` and other tools to discover common naming conventions, etc. -1. Latin-1 (ISO-8859-1) encoding for Java sources; use `native2ascii` to convert - if necessary +1. UTF-8 encoding for Java sources ### Add Apache license header to all new classes ```java /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -124,11 +123,11 @@ modified a file in 2015 whose header still reads: * Copyright 2002-2011 the original author or authors. ``` -Then be sure to update it to 2015 accordingly: +Then be sure to update it to 2016 accordingly: ```java /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. ``` ### Use @since tags for newly-added public API types and methods From 819e14f91b0130aae90d86703ce9e97c90c7c78e Mon Sep 17 00:00:00 2001 From: nkjackzhang Date: Fri, 9 Sep 2016 18:25:04 +0800 Subject: [PATCH 0161/1274] Fix typo There is no attribute named `path` in `@RequestParam`, so I change it to `name`. Closes gh-1165 --- src/asciidoc/web-mvc.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/asciidoc/web-mvc.adoc b/src/asciidoc/web-mvc.adoc index 7fd96fc604d..c4f51f4a515 100644 --- a/src/asciidoc/web-mvc.adoc +++ b/src/asciidoc/web-mvc.adoc @@ -1483,7 +1483,7 @@ The following code snippet shows the usage: Parameters using this annotation are required by default, but you can specify that a parameter is optional by setting ``@RequestParam``'s `required` attribute to `false` -(e.g., `@RequestParam(path="id", required=false)`). +(e.g., `@RequestParam(name="id", required=false)`). Type conversion is applied automatically if the target method parameter type is not `String`. See <>. From ae2bbe7f19f97186ed5d50175cff57624878ef26 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sat, 10 Sep 2016 12:37:55 +0200 Subject: [PATCH 0162/1274] MappingJackson2MessageConverter adds message id and destination to type resolution exception Issue: SPR-14672 (cherry picked from commit 8c56606) --- .../support/converter/MappingJackson2MessageConverter.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spring-jms/src/main/java/org/springframework/jms/support/converter/MappingJackson2MessageConverter.java b/spring-jms/src/main/java/org/springframework/jms/support/converter/MappingJackson2MessageConverter.java index 5d9bc44b53f..3919306dd69 100644 --- a/spring-jms/src/main/java/org/springframework/jms/support/converter/MappingJackson2MessageConverter.java +++ b/spring-jms/src/main/java/org/springframework/jms/support/converter/MappingJackson2MessageConverter.java @@ -491,7 +491,9 @@ protected Object convertFromMessage(Message message, JavaType targetJavaType) protected JavaType getJavaTypeForMessage(Message message) throws JMSException { String typeId = message.getStringProperty(this.typeIdPropertyName); if (typeId == null) { - throw new MessageConversionException("Could not find type id property [" + this.typeIdPropertyName + "]"); + throw new MessageConversionException( + "Could not find type id property [" + this.typeIdPropertyName + "] on message [" + + message.getJMSMessageID() + "] from destination [" + message.getJMSDestination() + "]"); } Class mappedClass = this.idClassMappings.get(typeId); if (mappedClass != null) { From 367949e9144ee535346451fc8a59ea75144dd82b Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 13 Sep 2016 21:29:05 +0200 Subject: [PATCH 0163/1274] PropertyValue stores source object in common superclass field Issue: SPR-8337 (cherry picked from commit fa820bc) --- .../springframework/beans/PropertyValue.java | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/PropertyValue.java b/spring-beans/src/main/java/org/springframework/beans/PropertyValue.java index 4f4f37ca792..62f35cde76f 100644 --- a/spring-beans/src/main/java/org/springframework/beans/PropertyValue.java +++ b/spring-beans/src/main/java/org/springframework/beans/PropertyValue.java @@ -45,8 +45,6 @@ public class PropertyValue extends BeanMetadataAttributeAccessor implements Seri private final Object value; - private Object source; - private boolean optional = false; private boolean converted = false; @@ -78,12 +76,12 @@ public PropertyValue(PropertyValue original) { Assert.notNull(original, "Original must not be null"); this.name = original.getName(); this.value = original.getValue(); - this.source = original.getSource(); this.optional = original.isOptional(); this.converted = original.converted; this.convertedValue = original.convertedValue; this.conversionNecessary = original.conversionNecessary; this.resolvedTokens = original.resolvedTokens; + setSource(original.getSource()); copyAttributesFrom(original); } @@ -97,10 +95,10 @@ public PropertyValue(PropertyValue original, Object newValue) { Assert.notNull(original, "Original must not be null"); this.name = original.getName(); this.value = newValue; - this.source = original; this.optional = original.isOptional(); this.conversionNecessary = original.conversionNecessary; this.resolvedTokens = original.resolvedTokens; + setSource(original); copyAttributesFrom(original); } @@ -129,16 +127,28 @@ public Object getValue() { */ public PropertyValue getOriginalPropertyValue() { PropertyValue original = this; - while (original.source instanceof PropertyValue && original.source != original) { - original = (PropertyValue) original.source; + Object source = getSource(); + while (source instanceof PropertyValue && source != original) { + original = (PropertyValue) source; + source = original.getSource(); } return original; } + /** + * Set whether this is an optional value, that is, to be ignored + * when no corresponding property exists on the target class. + * @since 3.0 + */ public void setOptional(boolean optional) { this.optional = optional; } + /** + * Reeurn whether this is an optional value, that is, to be ignored + * when no corresponding property exists on the target class. + * @since 3.0 + */ public boolean isOptional() { return this.optional; } @@ -180,7 +190,7 @@ public boolean equals(Object other) { PropertyValue otherPv = (PropertyValue) other; return (this.name.equals(otherPv.name) && ObjectUtils.nullSafeEquals(this.value, otherPv.value) && - ObjectUtils.nullSafeEquals(this.source, otherPv.source)); + ObjectUtils.nullSafeEquals(getSource(), otherPv.getSource())); } @Override From 4396b211ce06237a7408a2f5be0177e3e81258c0 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 13 Sep 2016 21:34:47 +0200 Subject: [PATCH 0164/1274] Avoid outdated Tibco workaround in shouldCommitAfterNoMessageReceived Issue: SPR-14697 (cherry picked from commit edbc1e9) --- .../AbstractPollingMessageListenerContainer.java | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/spring-jms/src/main/java/org/springframework/jms/listener/AbstractPollingMessageListenerContainer.java b/spring-jms/src/main/java/org/springframework/jms/listener/AbstractPollingMessageListenerContainer.java index 555cd6b8c39..910125f80a6 100644 --- a/spring-jms/src/main/java/org/springframework/jms/listener/AbstractPollingMessageListenerContainer.java +++ b/spring-jms/src/main/java/org/springframework/jms/listener/AbstractPollingMessageListenerContainer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -91,8 +91,6 @@ public abstract class AbstractPollingMessageListenerContainer extends AbstractMe private long receiveTimeout = DEFAULT_RECEIVE_TIMEOUT; - private volatile Boolean commitAfterNoMessageReceived; - @Override public void setSessionTransacted(boolean sessionTransacted) { @@ -347,7 +345,6 @@ protected boolean doReceiveAndExecute( } noMessageReceived(invoker, sessionToUse); // Nevertheless call commit, in order to reset the transaction timeout (if any). - // However, don't do this on Tibco since this may lead to a deadlock there. if (shouldCommitAfterNoMessageReceived(sessionToUse)) { commitIfNecessary(sessionToUse, message); } @@ -381,17 +378,12 @@ protected boolean isSessionLocallyTransacted(Session session) { /** * Determine whether to trigger a commit after no message has been received. - * This is a good idea on any JMS provider other than Tibco, which is what - * this default implementation checks for. + * This is a good idea on any modern-day JMS provider. * @param session the current JMS Session which received no message * @return whether to call {@link #commitIfNecessary} on the given Session */ protected boolean shouldCommitAfterNoMessageReceived(Session session) { - if (this.commitAfterNoMessageReceived == null) { - Session target = ConnectionFactoryUtils.getTargetSession(session); - this.commitAfterNoMessageReceived = !target.getClass().getName().startsWith("com.tibco.tibjms."); - } - return this.commitAfterNoMessageReceived; + return true; } /** From db196ce5d8840a23a16ac5e1ce851fedffb94809 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 13 Sep 2016 21:36:41 +0200 Subject: [PATCH 0165/1274] Correct ISO DateTime example Issue: SPR-14675 (cherry picked from commit d5c9cc6) --- .../org/springframework/format/annotation/DateTimeFormat.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-context/src/main/java/org/springframework/format/annotation/DateTimeFormat.java b/spring-context/src/main/java/org/springframework/format/annotation/DateTimeFormat.java index 3c84ff39287..e52d5d4f8bb 100644 --- a/spring-context/src/main/java/org/springframework/format/annotation/DateTimeFormat.java +++ b/spring-context/src/main/java/org/springframework/format/annotation/DateTimeFormat.java @@ -104,7 +104,7 @@ enum ISO { /** * The most common ISO DateTime Format {@code yyyy-MM-dd'T'HH:mm:ss.SSSZ}, - * e.g. "2000-10-31 01:30:00.000-05:00". + * e.g. "2000-10-31T01:30:00.000-05:00". *

    This is the default if no annotation value is specified. */ DATE_TIME, From bd24b97bd3df75e811c7b2ca07e65c6bfb8c90d6 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 13 Sep 2016 21:44:29 +0200 Subject: [PATCH 0166/1274] IdentityHashMap for scheduled tasks (avoiding hashCode calls on bean instances) Issue: SPR-14666 (cherry picked from commit 480cd2c) --- .../ScheduledAnnotationBeanPostProcessor.java | 44 ++++++++++++------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java index 89d35299a05..7b5cb34f6f0 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java @@ -19,6 +19,7 @@ import java.lang.reflect.Method; import java.util.Collection; import java.util.Collections; +import java.util.IdentityHashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; @@ -113,7 +114,7 @@ public class ScheduledAnnotationBeanPostProcessor implements DestructionAwareBea Collections.newSetFromMap(new ConcurrentHashMap, Boolean>(64)); private final Map> scheduledTasks = - new ConcurrentHashMap>(16); + new IdentityHashMap>(16); @Override @@ -263,8 +264,8 @@ public Object postProcessAfterInitialization(final Object bean, String beanName) new MethodIntrospector.MetadataLookup>() { @Override public Set inspect(Method method) { - Set scheduledMethods = - AnnotatedElementUtils.getMergedRepeatableAnnotations(method, Scheduled.class, Schedules.class); + Set scheduledMethods = AnnotatedElementUtils.getMergedRepeatableAnnotations( + method, Scheduled.class, Schedules.class); return (!scheduledMethods.isEmpty() ? scheduledMethods : null); } }); @@ -302,11 +303,7 @@ protected void processScheduled(Scheduled scheduled, Method method, Object bean) String errorMessage = "Exactly one of the 'cron', 'fixedDelay(String)', or 'fixedRate(String)' attributes is required"; - Set tasks = this.scheduledTasks.get(bean); - if (tasks == null) { - tasks = new LinkedHashSet(4); - this.scheduledTasks.put(bean, tasks); - } + Set tasks = new LinkedHashSet(4); // Determine initial delay long initialDelay = scheduled.initialDelay(); @@ -400,6 +397,16 @@ protected void processScheduled(Scheduled scheduled, Method method, Object bean) // Check whether we had any attribute set Assert.isTrue(processedSchedule, errorMessage); + + // Finally register the scheduled tasks + synchronized (this.scheduledTasks) { + Set registeredTasks = this.scheduledTasks.get(bean); + if (registeredTasks == null) { + registeredTasks = new LinkedHashSet(4); + this.scheduledTasks.put(bean, registeredTasks); + } + registeredTasks.addAll(tasks); + } } catch (IllegalArgumentException ex) { throw new IllegalStateException( @@ -410,7 +417,10 @@ protected void processScheduled(Scheduled scheduled, Method method, Object bean) @Override public void postProcessBeforeDestruction(Object bean, String beanName) { - Set tasks = this.scheduledTasks.remove(bean); + Set tasks; + synchronized (this.scheduledTasks) { + tasks = this.scheduledTasks.remove(bean); + } if (tasks != null) { for (ScheduledTask task : tasks) { task.cancel(); @@ -420,18 +430,22 @@ public void postProcessBeforeDestruction(Object bean, String beanName) { @Override public boolean requiresDestruction(Object bean) { - return this.scheduledTasks.containsKey(bean); + synchronized (this.scheduledTasks) { + return this.scheduledTasks.containsKey(bean); + } } @Override public void destroy() { - Collection> allTasks = this.scheduledTasks.values(); - for (Set tasks : allTasks) { - for (ScheduledTask task : tasks) { - task.cancel(); + synchronized (this.scheduledTasks) { + Collection> allTasks = this.scheduledTasks.values(); + for (Set tasks : allTasks) { + for (ScheduledTask task : tasks) { + task.cancel(); + } } + this.scheduledTasks.clear(); } - this.scheduledTasks.clear(); this.registrar.destroy(); } From 669d5815c93afbc9f641c62843454353e845030c Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 13 Sep 2016 21:47:32 +0200 Subject: [PATCH 0167/1274] Configuration class processing uses MetadataReaderFactory for current ResourceLoader Issue: SPR-14684 (cherry picked from commit 5405c07) --- .../context/annotation/ConfigurationClassPostProcessor.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java index 301256a6a82..d86ac32fc0d 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java @@ -205,6 +205,9 @@ public void setEnvironment(Environment environment) { public void setResourceLoader(ResourceLoader resourceLoader) { Assert.notNull(resourceLoader, "ResourceLoader must not be null"); this.resourceLoader = resourceLoader; + if (!this.setMetadataReaderFactoryCalled) { + this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader); + } } @Override From 09a0615df03446147ceee24d6a48d6c9a11445fa Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 13 Sep 2016 21:49:24 +0200 Subject: [PATCH 0168/1274] Consistent callbacks for TypeFilters, ImportSelectors and ImportBeanDefinitionRegistrars Issue: SPR-14686 (cherry picked from commit 0c2e8a6) --- .../beans/factory/BeanFactory.java | 25 +++---- .../ComponentScanAnnotationParser.java | 43 ++---------- .../annotation/ConfigurationClassParser.java | 32 ++------- .../annotation/ParserStrategyUtils.java | 65 +++++++++++++++++++ 4 files changed, 91 insertions(+), 74 deletions(-) create mode 100644 spring-context/src/main/java/org/springframework/context/annotation/ParserStrategyUtils.java diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java index 49d235a6595..ba0d0b9db93 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java @@ -67,24 +67,27 @@ * 1. BeanNameAware's {@code setBeanName}
    * 2. BeanClassLoaderAware's {@code setBeanClassLoader}
    * 3. BeanFactoryAware's {@code setBeanFactory}
    - * 4. ResourceLoaderAware's {@code setResourceLoader} + * 4. EnvironmentAware's {@code setEnvironment} + * 5. EmbeddedValueResolverAware's {@code setEmbeddedValueResolver} + * 6. ResourceLoaderAware's {@code setResourceLoader} * (only applicable when running in an application context)
    - * 5. ApplicationEventPublisherAware's {@code setApplicationEventPublisher} + * 7. ApplicationEventPublisherAware's {@code setApplicationEventPublisher} * (only applicable when running in an application context)
    - * 6. MessageSourceAware's {@code setMessageSource} + * 8. MessageSourceAware's {@code setMessageSource} * (only applicable when running in an application context)
    - * 7. ApplicationContextAware's {@code setApplicationContext} + * 9. ApplicationContextAware's {@code setApplicationContext} * (only applicable when running in an application context)
    - * 8. ServletContextAware's {@code setServletContext} + * 10. ServletContextAware's {@code setServletContext} * (only applicable when running in a web application context)
    - * 9. {@code postProcessBeforeInitialization} methods of BeanPostProcessors
    - * 10. InitializingBean's {@code afterPropertiesSet}
    - * 11. a custom init-method definition
    - * 12. {@code postProcessAfterInitialization} methods of BeanPostProcessors + * 11. {@code postProcessBeforeInitialization} methods of BeanPostProcessors
    + * 12. InitializingBean's {@code afterPropertiesSet}
    + * 13. a custom init-method definition
    + * 14. {@code postProcessAfterInitialization} methods of BeanPostProcessors * *

    On shutdown of a bean factory, the following lifecycle methods apply:
    - * 1. DisposableBean's {@code destroy}
    - * 2. a custom destroy-method definition + * 1. {@code postProcessBeforeDestruction} methods of DestructionAwareBeanPostProcessors + * 2. DisposableBean's {@code destroy}
    + * 3. a custom destroy-method definition * * @author Rod Johnson * @author Juergen Hoeller diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java b/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java index 247c5838db7..6a8fba7b367 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ComponentScanAnnotationParser.java @@ -25,17 +25,10 @@ import java.util.regex.Pattern; import org.springframework.beans.BeanUtils; -import org.springframework.beans.factory.Aware; -import org.springframework.beans.factory.BeanClassLoaderAware; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.config.BeanDefinitionHolder; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.EnvironmentAware; -import org.springframework.context.ResourceLoaderAware; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.env.Environment; import org.springframework.core.io.ResourceLoader; @@ -61,16 +54,16 @@ */ class ComponentScanAnnotationParser { - private final ResourceLoader resourceLoader; - private final Environment environment; - private final BeanDefinitionRegistry registry; + private final ResourceLoader resourceLoader; private final BeanNameGenerator beanNameGenerator; + private final BeanDefinitionRegistry registry; + - public ComponentScanAnnotationParser(ResourceLoader resourceLoader, Environment environment, + public ComponentScanAnnotationParser(Environment environment, ResourceLoader resourceLoader, BeanNameGenerator beanNameGenerator, BeanDefinitionRegistry registry) { this.resourceLoader = resourceLoader; @@ -90,7 +83,7 @@ public Set parse(AnnotationAttributes componentScan, final scanner.setResourceLoader(this.resourceLoader); Class generatorClass = componentScan.getClass("nameGenerator"); - boolean useInheritedGenerator = BeanNameGenerator.class == generatorClass; + boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass); scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator : BeanUtils.instantiateClass(generatorClass)); @@ -164,7 +157,8 @@ private List typeFiltersFor(AnnotationAttributes filterAttributes) { Assert.isAssignable(TypeFilter.class, filterClass, "An error occurred while processing a @ComponentScan CUSTOM type filter: "); TypeFilter filter = BeanUtils.instantiateClass(filterClass, TypeFilter.class); - invokeAwareMethods(filter); + ParserStrategyUtils.invokeAwareMethods( + filter, this.environment, this.resourceLoader, this.registry); typeFilters.add(filter); break; default: @@ -188,27 +182,4 @@ private List typeFiltersFor(AnnotationAttributes filterAttributes) { return typeFilters; } - /** - * Invoke {@link ResourceLoaderAware}, {@link BeanClassLoaderAware} and - * {@link BeanFactoryAware} contracts if implemented by the given {@code filter}. - */ - private void invokeAwareMethods(TypeFilter filter) { - if (filter instanceof Aware) { - if (filter instanceof EnvironmentAware) { - ((EnvironmentAware) filter).setEnvironment(this.environment); - } - if (filter instanceof ResourceLoaderAware) { - ((ResourceLoaderAware) filter).setResourceLoader(this.resourceLoader); - } - if (filter instanceof BeanClassLoaderAware) { - ClassLoader classLoader = (this.registry instanceof ConfigurableBeanFactory ? - ((ConfigurableBeanFactory) this.registry).getBeanClassLoader() : - this.resourceLoader.getClassLoader()); - ((BeanClassLoaderAware) filter).setBeanClassLoader(classLoader); - } - if (filter instanceof BeanFactoryAware && this.registry instanceof BeanFactory) { - ((BeanFactoryAware) filter).setBeanFactory((BeanFactory) this.registry); - } - } - } } diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java index 46d9863426a..c4c5483288d 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java @@ -158,7 +158,7 @@ public ConfigurationClassParser(MetadataReaderFactory metadataReaderFactory, this.resourceLoader = resourceLoader; this.registry = registry; this.componentScanParser = new ComponentScanAnnotationParser( - resourceLoader, environment, componentScanBeanNameGenerator, registry); + environment, resourceLoader, componentScanBeanNameGenerator, registry); this.conditionEvaluator = new ConditionEvaluator(registry, environment, resourceLoader); } @@ -509,7 +509,8 @@ private void processImports(ConfigurationClass configClass, SourceClass currentS // Candidate class is an ImportSelector -> delegate to it to determine imports Class candidateClass = candidate.loadClass(); ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class); - invokeAwareMethods(selector); + ParserStrategyUtils.invokeAwareMethods( + selector, this.environment, this.resourceLoader, this.registry); if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) { this.deferredImportSelectors.add( new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector)); @@ -526,7 +527,8 @@ else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) { Class candidateClass = candidate.loadClass(); ImportBeanDefinitionRegistrar registrar = BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class); - invokeAwareMethods(registrar); + ParserStrategyUtils.invokeAwareMethods( + registrar, this.environment, this.resourceLoader, this.registry); configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata()); } else { @@ -565,30 +567,6 @@ private boolean isChainedImportOnStack(ConfigurationClass configClass) { return false; } - /** - * Invoke {@link ResourceLoaderAware}, {@link BeanClassLoaderAware} and - * {@link BeanFactoryAware} contracts if implemented by the given {@code bean}. - */ - private void invokeAwareMethods(Object importStrategyBean) { - if (importStrategyBean instanceof Aware) { - if (importStrategyBean instanceof EnvironmentAware) { - ((EnvironmentAware) importStrategyBean).setEnvironment(this.environment); - } - if (importStrategyBean instanceof ResourceLoaderAware) { - ((ResourceLoaderAware) importStrategyBean).setResourceLoader(this.resourceLoader); - } - if (importStrategyBean instanceof BeanClassLoaderAware) { - ClassLoader classLoader = (this.registry instanceof ConfigurableBeanFactory ? - ((ConfigurableBeanFactory) this.registry).getBeanClassLoader() : - this.resourceLoader.getClassLoader()); - ((BeanClassLoaderAware) importStrategyBean).setBeanClassLoader(classLoader); - } - if (importStrategyBean instanceof BeanFactoryAware && this.registry instanceof BeanFactory) { - ((BeanFactoryAware) importStrategyBean).setBeanFactory((BeanFactory) this.registry); - } - } - } - /** * Validate each {@link ConfigurationClass} object. diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ParserStrategyUtils.java b/spring-context/src/main/java/org/springframework/context/annotation/ParserStrategyUtils.java new file mode 100644 index 00000000000..f1ced5158fd --- /dev/null +++ b/spring-context/src/main/java/org/springframework/context/annotation/ParserStrategyUtils.java @@ -0,0 +1,65 @@ +/* + * Copyright 2002-2016 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.context.annotation; + +import org.springframework.beans.factory.Aware; +import org.springframework.beans.factory.BeanClassLoaderAware; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.EnvironmentAware; +import org.springframework.context.ResourceLoaderAware; +import org.springframework.core.env.Environment; +import org.springframework.core.io.ResourceLoader; + +/** + * Common delegate code for the handling of parser strategies, e.g. + * {@code TypeFilter}, {@code ImportSelector}, {@code ImportBeanDefinitionRegistrar} + * + * @author Juergen Hoeller + * @since 4.3.3 + */ +abstract class ParserStrategyUtils { + + /** + * Invoke {@link BeanClassLoaderAware}, {@link BeanFactoryAware}, + * {@link EnvironmentAware}, and {@link ResourceLoaderAware} contracts + * if implemented by the given object. + */ + public static void invokeAwareMethods(Object parserStrategyBean, Environment environment, + ResourceLoader resourceLoader, BeanDefinitionRegistry registry) { + + if (parserStrategyBean instanceof Aware) { + if (parserStrategyBean instanceof BeanClassLoaderAware) { + ClassLoader classLoader = (registry instanceof ConfigurableBeanFactory ? + ((ConfigurableBeanFactory) registry).getBeanClassLoader() : resourceLoader.getClassLoader()); + ((BeanClassLoaderAware) parserStrategyBean).setBeanClassLoader(classLoader); + } + if (parserStrategyBean instanceof BeanFactoryAware && registry instanceof BeanFactory) { + ((BeanFactoryAware) parserStrategyBean).setBeanFactory((BeanFactory) registry); + } + if (parserStrategyBean instanceof EnvironmentAware) { + ((EnvironmentAware) parserStrategyBean).setEnvironment(environment); + } + if (parserStrategyBean instanceof ResourceLoaderAware) { + ((ResourceLoaderAware) parserStrategyBean).setResourceLoader(resourceLoader); + } + } + } + +} From 73bbe0849a6c220d3a895b47b3ae8574478a654a Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 13 Sep 2016 21:55:20 +0200 Subject: [PATCH 0169/1274] Revised IllegalArgumentException handling for Formatter parse calls Issue: SPR-14661 (cherry picked from commit c69e6a3) --- .../beans/AbstractNestablePropertyAccessor.java | 2 +- .../org/springframework/beans/TypeConverterSupport.java | 2 +- .../format/support/FormatterPropertyEditorAdapter.java | 6 ++++-- .../format/support/FormattingConversionService.java | 8 +++++--- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java b/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java index f3e3bd14795..741e303c30d 100644 --- a/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java @@ -590,7 +590,7 @@ private Object convertIfNecessary(String propertyName, Object oldValue, Object n new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, newValue); throw new ConversionNotSupportedException(pce, requiredType, ex); } - catch (Throwable ex) { + catch (IllegalArgumentException ex) { PropertyChangeEvent pce = new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, newValue); throw new TypeMismatchException(pce, requiredType, ex); diff --git a/spring-beans/src/main/java/org/springframework/beans/TypeConverterSupport.java b/spring-beans/src/main/java/org/springframework/beans/TypeConverterSupport.java index ba9fc09cca0..9be206c6943 100644 --- a/spring-beans/src/main/java/org/springframework/beans/TypeConverterSupport.java +++ b/spring-beans/src/main/java/org/springframework/beans/TypeConverterSupport.java @@ -73,7 +73,7 @@ private T doConvert(Object value, Class requiredType, MethodParameter met catch (IllegalStateException ex) { throw new ConversionNotSupportedException(value, requiredType, ex); } - catch (Throwable ex) { + catch (IllegalArgumentException ex) { throw new TypeMismatchException(value, requiredType, ex); } } diff --git a/spring-context/src/main/java/org/springframework/format/support/FormatterPropertyEditorAdapter.java b/spring-context/src/main/java/org/springframework/format/support/FormatterPropertyEditorAdapter.java index 7d490f5d32e..c3812473a11 100644 --- a/spring-context/src/main/java/org/springframework/format/support/FormatterPropertyEditorAdapter.java +++ b/spring-context/src/main/java/org/springframework/format/support/FormatterPropertyEditorAdapter.java @@ -18,7 +18,6 @@ import java.beans.PropertyEditor; import java.beans.PropertyEditorSupport; -import java.text.ParseException; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.format.Formatter; @@ -65,7 +64,10 @@ public void setAsText(String text) throws IllegalArgumentException { try { setValue(this.formatter.parse(text, LocaleContextHolder.getLocale())); } - catch (ParseException ex) { + catch (IllegalArgumentException ex) { + throw ex; + } + catch (Throwable ex) { throw new IllegalArgumentException("Parse attempt failed for value [" + text + "]", ex); } } diff --git a/spring-context/src/main/java/org/springframework/format/support/FormattingConversionService.java b/spring-context/src/main/java/org/springframework/format/support/FormattingConversionService.java index e65e4fbda99..342607ed349 100644 --- a/spring-context/src/main/java/org/springframework/format/support/FormattingConversionService.java +++ b/spring-context/src/main/java/org/springframework/format/support/FormattingConversionService.java @@ -17,7 +17,6 @@ package org.springframework.format.support; import java.lang.annotation.Annotation; -import java.text.ParseException; import java.util.Collections; import java.util.Map; import java.util.Set; @@ -193,11 +192,14 @@ public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor t try { result = this.parser.parse(text, LocaleContextHolder.getLocale()); } - catch (ParseException ex) { + catch (IllegalArgumentException ex) { + throw ex; + } + catch (Throwable ex) { throw new IllegalArgumentException("Parse attempt failed for value [" + text + "]", ex); } if (result == null) { - throw new IllegalStateException("Parsers are not allowed to return null"); + throw new IllegalStateException("Parsers are not allowed to return null: " + this.parser); } TypeDescriptor resultType = TypeDescriptor.valueOf(result.getClass()); if (!resultType.isAssignableTo(targetType)) { From 040d1312849e4545114d6c48e251c16440b9c08c Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 13 Sep 2016 21:58:41 +0200 Subject: [PATCH 0170/1274] Polishing (cherry picked from commit ce42ed4) --- .../format/datetime/DateFormatter.java | 4 ++-- .../expression/spel/support/ReflectionHelper.java | 4 ++-- .../springframework/dao/support/DataAccessUtils.java | 12 ++++++------ ...AndViewResolverMethodReturnValueHandlerTests.java | 5 +++-- ...ervletAnnotationControllerHandlerMethodTests.java | 9 +++------ 5 files changed, 16 insertions(+), 18 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/format/datetime/DateFormatter.java b/spring-context/src/main/java/org/springframework/format/datetime/DateFormatter.java index b11153669b9..6806628bb5f 100644 --- a/spring-context/src/main/java/org/springframework/format/datetime/DateFormatter.java +++ b/spring-context/src/main/java/org/springframework/format/datetime/DateFormatter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 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. @@ -191,7 +191,7 @@ private DateFormat createDateFormat(Locale locale) { if (timeStyle != -1) { return DateFormat.getTimeInstance(timeStyle, locale); } - throw new IllegalStateException("Unsupported style pattern '"+ this.stylePattern+ "'"); + throw new IllegalStateException("Unsupported style pattern '" + this.stylePattern + "'"); } return DateFormat.getDateInstance(this.style, locale); diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java index d080c284f09..248eb45397a 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectionHelper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -194,7 +194,7 @@ else if (typeConverter.canConvert(suppliedArg, expectedArg)) { TypeDescriptor varargsDesc = expectedArgTypes.get(expectedArgTypes.size() - 1); Class varargsParamType = varargsDesc.getElementTypeDescriptor().getType(); - // All remaining parameters must be of this type or convertable to this type + // All remaining parameters must be of this type or convertible to this type for (int i = expectedArgTypes.size() - 1; i < suppliedArgTypes.size(); i++) { TypeDescriptor suppliedArg = suppliedArgTypes.get(i); if (suppliedArg == null) { diff --git a/spring-tx/src/main/java/org/springframework/dao/support/DataAccessUtils.java b/spring-tx/src/main/java/org/springframework/dao/support/DataAccessUtils.java index c3649bad7e6..4d68bd507b3 100644 --- a/spring-tx/src/main/java/org/springframework/dao/support/DataAccessUtils.java +++ b/spring-tx/src/main/java/org/springframework/dao/support/DataAccessUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -122,7 +122,7 @@ public static T requiredUniqueResult(Collection results) throws Incorrect /** * Return a unique result object from the given Collection. * Throws an exception if 0 or more than 1 result objects found, - * of if the unique result object is not convertable to the + * of if the unique result object is not convertible to the * specified required type. * @param results the result Collection (can be {@code null}) * @return the unique result object @@ -162,7 +162,7 @@ else if (Number.class.isAssignableFrom(requiredType) && Number.class.isInstance( /** * Return a unique int result from the given Collection. * Throws an exception if 0 or more than 1 result objects found, - * of if the unique result object is not convertable to an int. + * of if the unique result object is not convertible to an int. * @param results the result Collection (can be {@code null}) * @return the unique int result * @throws IncorrectResultSizeDataAccessException if more than one @@ -170,7 +170,7 @@ else if (Number.class.isAssignableFrom(requiredType) && Number.class.isInstance( * @throws EmptyResultDataAccessException if no result object * at all has been found in the given Collection * @throws TypeMismatchDataAccessException if the unique object - * in the collection is not convertable to an int + * in the collection is not convertible to an int */ public static int intResult(Collection results) throws IncorrectResultSizeDataAccessException, TypeMismatchDataAccessException { @@ -181,7 +181,7 @@ public static int intResult(Collection results) /** * Return a unique long result from the given Collection. * Throws an exception if 0 or more than 1 result objects found, - * of if the unique result object is not convertable to a long. + * of if the unique result object is not convertible to a long. * @param results the result Collection (can be {@code null}) * @return the unique long result * @throws IncorrectResultSizeDataAccessException if more than one @@ -189,7 +189,7 @@ public static int intResult(Collection results) * @throws EmptyResultDataAccessException if no result object * at all has been found in the given Collection * @throws TypeMismatchDataAccessException if the unique object - * in the collection is not convertable to a long + * in the collection is not convertible to a long */ public static long longResult(Collection results) throws IncorrectResultSizeDataAccessException, TypeMismatchDataAccessException { diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewResolverMethodReturnValueHandlerTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewResolverMethodReturnValueHandlerTests.java index b2a6d2aa2b1..a3d65e7b2c9 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewResolverMethodReturnValueHandlerTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewResolverMethodReturnValueHandlerTests.java @@ -126,8 +126,9 @@ public TestModelAndViewResolver(Class returnValueType) { @Override @SuppressWarnings("rawtypes") - public ModelAndView resolveModelAndView(Method method, Class handlerType, Object returnValue, + public ModelAndView resolveModelAndView(Method method, Class handlerType, Object returnValue, ExtendedModelMap model, NativeWebRequest request) { + if (returnValue != null && returnValue.getClass().equals(returnValueType)) { return new ModelAndView("viewName", "modelAttrName", returnValue); } @@ -137,4 +138,4 @@ public ModelAndView resolveModelAndView(Method method, Class handlerType, Object } } -} \ No newline at end of file +} diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java index 48163090a75..fe6a261efe3 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java @@ -2873,18 +2873,15 @@ public static class MyModelAndViewResolver implements ModelAndViewResolver { @Override @SuppressWarnings("rawtypes") - public ModelAndView resolveModelAndView(Method handlerMethod, - Class handlerType, - Object returnValue, - ExtendedModelMap implicitModel, - NativeWebRequest webRequest) { + public ModelAndView resolveModelAndView(Method handlerMethod, Class handlerType, Object returnValue, + ExtendedModelMap implicitModel, NativeWebRequest webRequest) { + if (returnValue instanceof MySpecialArg) { return new ModelAndView(new View() { @Override public String getContentType() { return "text/html"; } - @Override public void render(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception { From 12afc263a46b39886d7ff9918813a3dafd02a983 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 13 Sep 2016 22:32:36 +0200 Subject: [PATCH 0171/1274] HibernateTemplate reflectively calls getNamedQuery (for runtime compatibility with Hibernate 5.0/5.1 vs 5.2) Issue: SPR-14676 --- .../orm/hibernate5/HibernateTemplate.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateTemplate.java b/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateTemplate.java index 83b43d4af36..b671231f7f8 100644 --- a/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateTemplate.java +++ b/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateTemplate.java @@ -87,11 +87,14 @@ public class HibernateTemplate implements HibernateOperations, InitializingBean private static final Method createQueryMethod; + private static final Method getNamedQueryMethod; + static { // Hibernate 5.2's createQuery method declares a new subtype as return type, // so we need to use reflection for binary compatibility with 5.0/5.1 here. try { createQueryMethod = Session.class.getMethod("createQuery", String.class); + getNamedQueryMethod = Session.class.getMethod("getNamedQuery", String.class); } catch (NoSuchMethodException ex) { throw new IllegalStateException("Incompatible Hibernate Session API", ex); @@ -955,7 +958,8 @@ public List findByNamedQuery(final String queryName, final Object... values) @Override @SuppressWarnings({"rawtypes", "deprecation"}) public List doInHibernate(Session session) throws HibernateException { - org.hibernate.Query queryObject = session.getNamedQuery(queryName); + org.hibernate.Query queryObject = (org.hibernate.Query) + ReflectionUtils.invokeMethod(getNamedQueryMethod, session, queryName); prepareQuery(queryObject); if (values != null) { for (int i = 0; i < values.length; i++) { @@ -986,7 +990,8 @@ public List findByNamedQueryAndNamedParam( @Override @SuppressWarnings({"rawtypes", "deprecation"}) public List doInHibernate(Session session) throws HibernateException { - org.hibernate.Query queryObject = session.getNamedQuery(queryName); + org.hibernate.Query queryObject = (org.hibernate.Query) + ReflectionUtils.invokeMethod(getNamedQueryMethod, session, queryName); prepareQuery(queryObject); if (values != null) { for (int i = 0; i < values.length; i++) { @@ -1006,7 +1011,8 @@ public List findByNamedQueryAndValueBean(final String queryName, final Object @Override @SuppressWarnings({"rawtypes", "deprecation"}) public List doInHibernate(Session session) throws HibernateException { - org.hibernate.Query queryObject = session.getNamedQuery(queryName); + org.hibernate.Query queryObject = (org.hibernate.Query) + ReflectionUtils.invokeMethod(getNamedQueryMethod, session, queryName); prepareQuery(queryObject); queryObject.setProperties(valueBean); return queryObject.list(); @@ -1256,7 +1262,7 @@ public CloseSuppressingInvocationHandler(Session target) { } @Override - @SuppressWarnings("deprecation") + @SuppressWarnings({"rawtypes", "deprecation"}) public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // Invocation on Session interface coming in... From 4b445531f56ccb106c935bcbdee27c55cb867b83 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 13 Sep 2016 22:32:57 +0200 Subject: [PATCH 0172/1274] HibernateExceptionTranslator avoids JPA IllegalState/ArgumentException translation Issue: SPR-14681 --- .../orm/hibernate5/HibernateExceptionTranslator.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateExceptionTranslator.java b/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateExceptionTranslator.java index 38070d625a0..4e70b21e4fe 100644 --- a/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateExceptionTranslator.java +++ b/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateExceptionTranslator.java @@ -48,10 +48,13 @@ public DataAccessException translateExceptionIfPossible(RuntimeException ex) { if (ex instanceof HibernateException) { return convertHibernateAccessException((HibernateException) ex); } - if (ex instanceof PersistenceException && ex.getCause() instanceof HibernateException) { - return convertHibernateAccessException((HibernateException) ex.getCause()); + if (ex instanceof PersistenceException) { + if (ex.getCause() instanceof HibernateException) { + return convertHibernateAccessException((HibernateException) ex.getCause()); + } + return EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(ex); } - return EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(ex); + return null; } /** From 55c37d2a5703d1a0c129dc29f25d2e7ade89eee8 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 13 Sep 2016 22:34:19 +0200 Subject: [PATCH 0173/1274] Upgrade to Tomcat 8.5.5 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index effe1a52e7d..bbce5b919b1 100644 --- a/build.gradle +++ b/build.gradle @@ -74,7 +74,7 @@ configure(allprojects) { project -> ext.testngVersion = "6.9.10" ext.tiles2Version = "2.2.2" ext.tiles3Version = "3.0.5" - ext.tomcatVersion = "8.5.4" + ext.tomcatVersion = "8.5.5" ext.tyrusVersion = "1.3.5" // constrained by WebLogic 12.1.3 support ext.undertowVersion = "1.3.24.Final" ext.xmlunitVersion = "1.6" From fbe7ddb6403382554ab75239834ede534d06b6af Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 13 Sep 2016 23:43:52 +0200 Subject: [PATCH 0174/1274] PropertySourcesPropertyResolver does not log retrieved value by default Issue: SPR-14709 --- .../core/env/PropertySourcesPropertyResolver.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/env/PropertySourcesPropertyResolver.java b/spring-core/src/main/java/org/springframework/core/env/PropertySourcesPropertyResolver.java index 3246204328f..1a92673de2a 100644 --- a/spring-core/src/main/java/org/springframework/core/env/PropertySourcesPropertyResolver.java +++ b/spring-core/src/main/java/org/springframework/core/env/PropertySourcesPropertyResolver.java @@ -137,8 +137,10 @@ else if (value instanceof Class) { /** * Log the given key as found in the given {@link PropertySource}, resulting in * the given value. - *

    The default implementation writes a debug log message, including the value. - * Subclasses may override this to change the log level and/or the log message. + *

    The default implementation writes a debug log message with key and source. + * As of 4.3.3, this does not log the value anymore in order to avoid accidental + * logging of sensitive settings. Subclasses may override this method to change + * the log level and/or log message, including the property's value if desired. * @param key the key found * @param propertySource the {@code PropertySource} that the key has been found in * @param value the corresponding value @@ -146,8 +148,8 @@ else if (value instanceof Class) { */ protected void logKeyFound(String key, PropertySource propertySource, Object value) { if (logger.isDebugEnabled()) { - logger.debug(String.format("Found key '%s' in [%s] with type [%s] and value '%s'", - key, propertySource.getName(), value.getClass().getSimpleName(), value)); + logger.debug(String.format("Found key '%s' in [%s] with type [%s]", + key, propertySource.getName(), value.getClass().getSimpleName())); } } From 54db496815b121b4392a876c0eabdd3cc708dc2a Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 13 Sep 2016 23:31:09 +0200 Subject: [PATCH 0175/1274] Polishing (cherry picked from commit 404e7cd) --- .../springframework/beans/PropertyValue.java | 4 +-- .../simp/stomp/Reactor2StompCodec.java | 29 ++++++++++--------- .../simp/stomp/Reactor2TcpStompClient.java | 6 ++-- .../tcp/reactor/Reactor2TcpClient.java | 10 +++---- .../tcp/reactor/Reactor2TcpConnection.java | 9 ++---- 5 files changed, 27 insertions(+), 31 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/PropertyValue.java b/spring-beans/src/main/java/org/springframework/beans/PropertyValue.java index 62f35cde76f..3c2f4e6c5b5 100644 --- a/spring-beans/src/main/java/org/springframework/beans/PropertyValue.java +++ b/spring-beans/src/main/java/org/springframework/beans/PropertyValue.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -145,7 +145,7 @@ public void setOptional(boolean optional) { } /** - * Reeurn whether this is an optional value, that is, to be ignored + * Return whether this is an optional value, that is, to be ignored * when no corresponding property exists on the target class. * @since 3.0 */ diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/Reactor2StompCodec.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/Reactor2StompCodec.java index 4f211d6fc10..91d8c4851b7 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/Reactor2StompCodec.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/Reactor2StompCodec.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -18,14 +18,14 @@ import java.nio.ByteBuffer; - -import org.springframework.messaging.Message; -import org.springframework.util.Assert; import reactor.fn.Consumer; import reactor.fn.Function; import reactor.io.buffer.Buffer; import reactor.io.codec.Codec; +import org.springframework.messaging.Message; +import org.springframework.util.Assert; + /** * A Reactor TCP {@link Codec} for sending and receiving STOMP messages. * @@ -35,25 +35,23 @@ */ public class Reactor2StompCodec extends Codec, Message> { - private final StompDecoder stompDecoder; - - private final StompEncoder stompEncoder; - private final Function, Buffer> encodingFunction; + private final StompDecoder stompDecoder; + public Reactor2StompCodec() { this(new StompEncoder(), new StompDecoder()); } public Reactor2StompCodec(StompEncoder encoder, StompDecoder decoder) { - Assert.notNull(encoder, "'encoder' is required"); - Assert.notNull(decoder, "'decoder' is required"); - this.stompEncoder = encoder; + Assert.notNull(encoder, "StompEncoder is required"); + Assert.notNull(decoder, "StompDecoder is required"); + this.encodingFunction = new EncodingFunction(encoder); this.stompDecoder = decoder; - this.encodingFunction = new EncodingFunction(this.stompEncoder); } + @Override public Function> decoder(final Consumer> messageConsumer) { return new DecodingFunction(this.stompDecoder, messageConsumer); @@ -66,14 +64,15 @@ public Function, Buffer> encoder() { @Override public Buffer apply(Message message) { - return encodingFunction.apply(message); + return this.encodingFunction.apply(message); } + private static class EncodingFunction implements Function, Buffer> { private final StompEncoder encoder; - private EncodingFunction(StompEncoder encoder) { + public EncodingFunction(StompEncoder encoder) { this.encoder = encoder; } @@ -84,6 +83,7 @@ public Buffer apply(Message message) { } } + private static class DecodingFunction implements Function> { private final StompDecoder decoder; @@ -103,4 +103,5 @@ public Message apply(Buffer buffer) { return null; } } + } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/Reactor2TcpStompClient.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/Reactor2TcpStompClient.java index 9fac4c6b658..42a7977474b 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/Reactor2TcpStompClient.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/Reactor2TcpStompClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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.messaging.simp.stomp; -import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Properties; @@ -114,7 +114,7 @@ public ReactorConfiguration read() { String dispatcherName = "StompClient"; DispatcherType dispatcherType = DispatcherType.DISPATCHER_GROUP; DispatcherConfiguration config = new DispatcherConfiguration(dispatcherName, dispatcherType, 128, 0); - List configList = Arrays.asList(config); + List configList = Collections.singletonList(config); return new ReactorConfiguration(configList, dispatcherName, new Properties()); } } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/tcp/reactor/Reactor2TcpClient.java b/spring-messaging/src/main/java/org/springframework/messaging/tcp/reactor/Reactor2TcpClient.java index 71541dcd79b..2cc29a46ba6 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/tcp/reactor/Reactor2TcpClient.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/tcp/reactor/Reactor2TcpClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,15 +151,13 @@ private static NioEventLoopGroup initEventLoopGroup() { try { ioThreadCount = Integer.parseInt(System.getProperty("reactor.tcp.ioThreadCount")); } - catch (Exception ex) { + catch (Throwable ex) { ioThreadCount = -1; } - if (ioThreadCount <= 0l) { + if (ioThreadCount <= 0) { ioThreadCount = Runtime.getRuntime().availableProcessors(); } - - return new NioEventLoopGroup(ioThreadCount, - new NamedDaemonThreadFactory("reactor-tcp-io")); + return new NioEventLoopGroup(ioThreadCount, new NamedDaemonThreadFactory("reactor-tcp-io")); } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/tcp/reactor/Reactor2TcpConnection.java b/spring-messaging/src/main/java/org/springframework/messaging/tcp/reactor/Reactor2TcpConnection.java index 6770aabf97f..98c3a9952c2 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/tcp/reactor/Reactor2TcpConnection.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/tcp/reactor/Reactor2TcpConnection.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,10 +29,9 @@ * An implementation of {@link org.springframework.messaging.tcp.TcpConnection * TcpConnection} based on the TCP client support of the Reactor project. * - * @param

    the payload type of messages read or written to the TCP stream. - * * @author Rossen Stoyanchev * @since 4.2 + * @param

    the payload type of messages read or written to the TCP stream. */ public class Reactor2TcpConnection

    implements TcpConnection

    { @@ -41,9 +40,7 @@ public class Reactor2TcpConnection

    implements TcpConnection

    { private final Promise closePromise; - public Reactor2TcpConnection(ChannelStream, Message

    > channelStream, - Promise closePromise) { - + public Reactor2TcpConnection(ChannelStream, Message

    > channelStream, Promise closePromise) { this.channelStream = channelStream; this.closePromise = closePromise; } From be99603f1b14673d916bfe7e3ba6233c6f63af7e Mon Sep 17 00:00:00 2001 From: kosmaty Date: Wed, 14 Sep 2016 10:24:43 +0200 Subject: [PATCH 0176/1274] Fix doc style Closes gh-1172 --- src/asciidoc/core-beans.adoc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/asciidoc/core-beans.adoc b/src/asciidoc/core-beans.adoc index ea857975db6..2f1cf5c117a 100644 --- a/src/asciidoc/core-beans.adoc +++ b/src/asciidoc/core-beans.adoc @@ -7633,6 +7633,7 @@ but rather completely overridden by a preceding entry. For a common `StandardServletEnvironment`, the full hierarchy looks as follows, with the highest-precedence entries at the top: + * ServletConfig parameters (if applicable, e.g. in case of a `DispatcherServlet` context) * ServletContext parameters (web.xml context-param entries) * JNDI environment variables ("java:comp/env/" entries) From 07d5f8b12300fe8559a034031143a1e598ace059 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Wed, 14 Sep 2016 17:19:49 -0400 Subject: [PATCH 0177/1274] Check both connection and connected flag Issue: SPR-14703 --- .../messaging/simp/stomp/StompBrokerRelayMessageHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompBrokerRelayMessageHandler.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompBrokerRelayMessageHandler.java index f0383acc16b..18185402339 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompBrokerRelayMessageHandler.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompBrokerRelayMessageHandler.java @@ -768,7 +768,7 @@ public void afterConnectionClosed() { public ListenableFuture forward(final Message message, final StompHeaderAccessor accessor) { TcpConnection conn = this.tcpConnection; - if (!this.isStompConnected) { + if (!this.isStompConnected || conn == null) { if (this.isRemoteClientSession) { if (logger.isDebugEnabled()) { logger.debug("TCP connection closed already, ignoring " + From 5dbfe48d248b918992b9e9b405ace678f1503897 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Wed, 14 Sep 2016 21:31:30 -0400 Subject: [PATCH 0178/1274] Improve async request timeout handling Rather than setting the status to 503 directly from the timeout interceptor which no longer seems to work reliably with Servlet containers like Jetty even performing an additional ERROR dispatch back to the original URL, we know rather set the DeferredResult to an AsyncTimeoutException, which results in a dispatch and standard handling within Spring MVC. This should be a more reliable way of dealing with timeouts. Issue: SPR-14669 --- .../async/AsyncRequestTimeoutException.java | 34 +++++++++++++++++++ .../TimeoutCallableProcessingInterceptor.java | 14 ++++---- ...utDeferredResultProcessingInterceptor.java | 18 +++++----- .../async/WebAsyncManagerTimeoutTests.java | 20 +++++------ .../ResponseEntityExceptionHandler.java | 25 +++++++++++++- .../DefaultHandlerExceptionResolver.java | 24 +++++++++++++ .../ResponseEntityExceptionHandlerTests.java | 8 ++++- .../DefaultHandlerExceptionResolverTests.java | 10 ++++++ 8 files changed, 124 insertions(+), 29 deletions(-) create mode 100644 spring-web/src/main/java/org/springframework/web/context/request/async/AsyncRequestTimeoutException.java diff --git a/spring-web/src/main/java/org/springframework/web/context/request/async/AsyncRequestTimeoutException.java b/spring-web/src/main/java/org/springframework/web/context/request/async/AsyncRequestTimeoutException.java new file mode 100644 index 00000000000..f079caf53b0 --- /dev/null +++ b/spring-web/src/main/java/org/springframework/web/context/request/async/AsyncRequestTimeoutException.java @@ -0,0 +1,34 @@ +/* + * Copyright 2002-2016 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.context.request.async; + +/** + * Exception to be thrown when an async request times out. + * Alternatively an applications can register a + * {@link DeferredResultProcessingInterceptor} or a + * {@link CallableProcessingInterceptor} to handle the timeout through + * the MVC Java config or the MVC XML namespace or directly through properties + * of the {@code RequestMappingHandlerAdapter}. + * + *

    By default the exception will be handled as a 503 error. + * + * @author Rossen Stoyanchev + * @since 4.2.8 + */ +@SuppressWarnings("serial") +public class AsyncRequestTimeoutException extends Exception { + +} diff --git a/spring-web/src/main/java/org/springframework/web/context/request/async/TimeoutCallableProcessingInterceptor.java b/spring-web/src/main/java/org/springframework/web/context/request/async/TimeoutCallableProcessingInterceptor.java index a336d64513f..c26e476bb9d 100644 --- a/spring-web/src/main/java/org/springframework/web/context/request/async/TimeoutCallableProcessingInterceptor.java +++ b/spring-web/src/main/java/org/springframework/web/context/request/async/TimeoutCallableProcessingInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,11 @@ /** * Sends a 503 (SERVICE_UNAVAILABLE) in case of a timeout if the response is not - * already committed. Registered at the end, after all other interceptors and + * already committed. As of 4.2.8 this is done indirectly by setting the result + * to an {@link AsyncRequestTimeoutException} which is then handled by + * Spring MVC's default exception handling as a 503 error. + * + *

    Registered at the end, after all other interceptors and * therefore invoked only if no other interceptor handles the timeout. * *

    Note that according to RFC 7231, a 503 without a 'Retry-After' header is @@ -39,11 +43,7 @@ public class TimeoutCallableProcessingInterceptor extends CallableProcessingInte @Override public Object handleTimeout(NativeWebRequest request, Callable task) throws Exception { - HttpServletResponse servletResponse = request.getNativeResponse(HttpServletResponse.class); - if (!servletResponse.isCommitted()) { - servletResponse.sendError(HttpStatus.SERVICE_UNAVAILABLE.value()); - } - return CallableProcessingInterceptor.RESPONSE_HANDLED; + return new AsyncRequestTimeoutException(); } } diff --git a/spring-web/src/main/java/org/springframework/web/context/request/async/TimeoutDeferredResultProcessingInterceptor.java b/spring-web/src/main/java/org/springframework/web/context/request/async/TimeoutDeferredResultProcessingInterceptor.java index 5cb855d9497..80d1172fa51 100644 --- a/spring-web/src/main/java/org/springframework/web/context/request/async/TimeoutDeferredResultProcessingInterceptor.java +++ b/spring-web/src/main/java/org/springframework/web/context/request/async/TimeoutDeferredResultProcessingInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,14 +16,15 @@ package org.springframework.web.context.request.async; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.http.HttpStatus; import org.springframework.web.context.request.NativeWebRequest; /** * Sends a 503 (SERVICE_UNAVAILABLE) in case of a timeout if the response is not - * already committed. Registered at the end, after all other interceptors and + * already committed. As of 4.2.8 this is done indirectly by returning + * {@link AsyncRequestTimeoutException} as the result of processing which is + * then handled by Spring MVC's default exception handling as a 503 error. + * + *

    Registered at the end, after all other interceptors and * therefore invoked only if no other interceptor handles the timeout. * *

    Note that according to RFC 7231, a 503 without a 'Retry-After' header is @@ -37,11 +38,8 @@ public class TimeoutDeferredResultProcessingInterceptor extends DeferredResultProcessingInterceptorAdapter { @Override - public boolean handleTimeout(NativeWebRequest request, DeferredResult deferredResult) throws Exception { - HttpServletResponse servletResponse = request.getNativeResponse(HttpServletResponse.class); - if (!servletResponse.isCommitted()) { - servletResponse.sendError(HttpStatus.SERVICE_UNAVAILABLE.value()); - } + public boolean handleTimeout(NativeWebRequest request, DeferredResult result) throws Exception { + result.setErrorResult(new AsyncRequestTimeoutException()); return false; } diff --git a/spring-web/src/test/java/org/springframework/web/context/request/async/WebAsyncManagerTimeoutTests.java b/spring-web/src/test/java/org/springframework/web/context/request/async/WebAsyncManagerTimeoutTests.java index 766942dc027..01ef6287357 100644 --- a/spring-web/src/test/java/org/springframework/web/context/request/async/WebAsyncManagerTimeoutTests.java +++ b/spring-web/src/test/java/org/springframework/web/context/request/async/WebAsyncManagerTimeoutTests.java @@ -18,7 +18,6 @@ import java.util.concurrent.Callable; import javax.servlet.AsyncEvent; -import javax.servlet.DispatcherType; import org.junit.Before; import org.junit.Test; @@ -29,9 +28,12 @@ import org.springframework.mock.web.test.MockHttpServletResponse; import org.springframework.web.context.request.NativeWebRequest; -import static org.junit.Assert.*; -import static org.mockito.BDDMockito.*; -import static org.springframework.web.context.request.async.CallableProcessingInterceptor.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.mock; +import static org.mockito.BDDMockito.verify; +import static org.springframework.web.context.request.async.CallableProcessingInterceptor.RESULT_NONE; /** * {@link WebAsyncManager} tests where container-triggered timeout/completion @@ -79,9 +81,8 @@ public void startCallableProcessingTimeoutAndComplete() throws Exception { this.asyncWebRequest.onTimeout(ASYNC_EVENT); this.asyncWebRequest.onComplete(ASYNC_EVENT); - assertFalse(this.asyncManager.hasConcurrentResult()); - assertEquals(DispatcherType.REQUEST, this.servletRequest.getDispatcherType()); - assertEquals(503, this.servletResponse.getStatus()); + assertTrue(this.asyncManager.hasConcurrentResult()); + assertEquals(AsyncRequestTimeoutException.class, this.asyncManager.getConcurrentResult().getClass()); verify(interceptor).beforeConcurrentHandling(this.asyncWebRequest, callable); verify(interceptor).afterCompletion(this.asyncWebRequest, callable); @@ -163,9 +164,8 @@ public void startDeferredResultProcessingTimeoutAndComplete() throws Exception { this.asyncWebRequest.onTimeout(ASYNC_EVENT); this.asyncWebRequest.onComplete(ASYNC_EVENT); - assertFalse(this.asyncManager.hasConcurrentResult()); - assertEquals(DispatcherType.REQUEST, this.servletRequest.getDispatcherType()); - assertEquals(503, this.servletResponse.getStatus()); + assertTrue(this.asyncManager.hasConcurrentResult()); + assertEquals(AsyncRequestTimeoutException.class, this.asyncManager.getConcurrentResult().getClass()); verify(interceptor).beforeConcurrentHandling(this.asyncWebRequest, deferredResult); verify(interceptor).preProcess(this.asyncWebRequest, deferredResult); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java index 267f1959973..f36f38718a6 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java @@ -44,6 +44,7 @@ import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.context.request.WebRequest; +import org.springframework.web.context.request.async.AsyncRequestTimeoutException; import org.springframework.web.multipart.support.MissingServletRequestPartException; import org.springframework.web.servlet.NoHandlerFoundException; import org.springframework.web.util.WebUtils; @@ -114,7 +115,8 @@ public abstract class ResponseEntityExceptionHandler { MethodArgumentNotValidException.class, MissingServletRequestPartException.class, BindException.class, - NoHandlerFoundException.class + NoHandlerFoundException.class, + AsyncRequestTimeoutException.class }) public final ResponseEntity handleException(Exception ex, WebRequest request) { HttpHeaders headers = new HttpHeaders(); @@ -178,6 +180,11 @@ else if (ex instanceof NoHandlerFoundException) { HttpStatus status = HttpStatus.NOT_FOUND; return handleNoHandlerFoundException((NoHandlerFoundException) ex, headers, status, request); } + else if (ex instanceof AsyncRequestTimeoutException) { + HttpStatus status = HttpStatus.SERVICE_UNAVAILABLE; + return handleAsyncRequestTimeoutException( + (AsyncRequestTimeoutException) ex, headers, status, request); + } else { logger.warn("Unknown exception type: " + ex.getClass().getName()); HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR; @@ -449,4 +456,20 @@ protected ResponseEntity handleNoHandlerFoundException( return handleExceptionInternal(ex, null, headers, status, request); } + /** + * Customize the response for NoHandlerFoundException. + *

    This method delegates to {@link #handleExceptionInternal}. + * @param ex the exception + * @param headers the headers to be written to the response + * @param status the selected response status + * @param request the current request + * @return a {@code ResponseEntity} instance + * @since 4.2.8 + */ + protected ResponseEntity handleAsyncRequestTimeoutException( + AsyncRequestTimeoutException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { + + return handleExceptionInternal(ex, null, headers, status, request); + } + } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolver.java index 6dea3daaa70..f29e99dde0f 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolver.java @@ -44,6 +44,7 @@ import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.context.request.async.AsyncRequestTimeoutException; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.support.MissingServletRequestPartException; import org.springframework.web.servlet.ModelAndView; @@ -159,6 +160,10 @@ else if (ex instanceof BindException) { else if (ex instanceof NoHandlerFoundException) { return handleNoHandlerFoundException((NoHandlerFoundException) ex, request, response, handler); } + else if (ex instanceof AsyncRequestTimeoutException) { + return handleAsyncRequestTimeoutException( + (AsyncRequestTimeoutException) ex, request, response, handler); + } } catch (Exception handlerException) { if (logger.isWarnEnabled()) { @@ -478,6 +483,25 @@ protected ModelAndView handleNoHandlerFoundException(NoHandlerFoundException ex, return new ModelAndView(); } + /** + * Handle the case where an async request timed out. + *

    The default implementation sends an HTTP 503 error. + * @param ex the {@link AsyncRequestTimeoutException }to be handled + * @param request current HTTP request + * @param response current HTTP response + * @param handler the executed handler, or {@code null} if none chosen + * at the time of the exception (for example, if multipart resolution failed) + * @return an empty ModelAndView indicating the exception was handled + * @throws IOException potentially thrown from response.sendError() + * @since 4.2.8 + */ + protected ModelAndView handleAsyncRequestTimeoutException(AsyncRequestTimeoutException ex, + HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException { + + response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); + return new ModelAndView(); + } + /** * Invoked to send a server error. Sets the status to 500 and also sets the diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandlerTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandlerTests.java index bc25f84feeb..c35b6218f25 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandlerTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -50,6 +50,7 @@ import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.context.request.WebRequest; +import org.springframework.web.context.request.async.AsyncRequestTimeoutException; import org.springframework.web.context.support.StaticWebApplicationContext; import org.springframework.web.multipart.support.MissingServletRequestPartException; import org.springframework.web.servlet.NoHandlerFoundException; @@ -205,6 +206,11 @@ public void noHandlerFoundException() { testException(ex); } + @Test + public void asyncRequestTimeoutException() { + testException(new AsyncRequestTimeoutException()); + } + @Test public void controllerAdvice() throws Exception { StaticWebApplicationContext cxt = new StaticWebApplicationContext(); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolverTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolverTests.java index 40ee1ecb022..df7298f522a 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolverTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolverTests.java @@ -40,6 +40,7 @@ import org.springframework.web.bind.MissingPathVariableException; import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.ServletRequestBindingException; +import org.springframework.web.context.request.async.AsyncRequestTimeoutException; import org.springframework.web.multipart.support.MissingServletRequestPartException; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.NoHandlerFoundException; @@ -216,6 +217,15 @@ public void handleConversionNotSupportedException() throws Exception { assertSame(ex, request.getAttribute("javax.servlet.error.exception")); } + @Test // SPR-14669 + public void handleAsyncRequestTimeoutException() throws Exception { + Exception ex = new AsyncRequestTimeoutException(); + ModelAndView mav = exceptionResolver.resolveException(request, response, null, ex); + assertNotNull("No ModelAndView returned", mav); + assertTrue("No Empty ModelAndView returned", mav.isEmpty()); + assertEquals("Invalid status code", 503, response.getStatus()); + } + @SuppressWarnings("unused") public void handle(String arg) { From e947363a11ded1c84530baf06c79482feb644758 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 15 Sep 2016 08:54:17 +0200 Subject: [PATCH 0179/1274] Timeout exceptions as RuntimeExceptions Issue: SPR-14669 (cherry picked from commit 6dc1898) --- .../messaging/simp/stomp/ConnectionLostException.java | 4 ++-- .../context/request/async/AsyncRequestTimeoutException.java | 3 ++- .../mvc/method/annotation/ResponseEntityExceptionHandler.java | 4 +++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/ConnectionLostException.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/ConnectionLostException.java index 9414ee541b0..5710da24cb7 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/ConnectionLostException.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/ConnectionLostException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,7 @@ * @since 4.2 */ @SuppressWarnings("serial") -public class ConnectionLostException extends Exception { +public class ConnectionLostException extends RuntimeException { public ConnectionLostException(String message) { super(message); diff --git a/spring-web/src/main/java/org/springframework/web/context/request/async/AsyncRequestTimeoutException.java b/spring-web/src/main/java/org/springframework/web/context/request/async/AsyncRequestTimeoutException.java index f079caf53b0..2e5d7488e55 100644 --- a/spring-web/src/main/java/org/springframework/web/context/request/async/AsyncRequestTimeoutException.java +++ b/spring-web/src/main/java/org/springframework/web/context/request/async/AsyncRequestTimeoutException.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.web.context.request.async; /** @@ -29,6 +30,6 @@ * @since 4.2.8 */ @SuppressWarnings("serial") -public class AsyncRequestTimeoutException extends Exception { +public class AsyncRequestTimeoutException extends RuntimeException { } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java index f36f38718a6..50f10e4bb9a 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java @@ -186,7 +186,9 @@ else if (ex instanceof AsyncRequestTimeoutException) { (AsyncRequestTimeoutException) ex, headers, status, request); } else { - logger.warn("Unknown exception type: " + ex.getClass().getName()); + if (logger.isWarnEnabled()) { + logger.warn("Unknown exception type: " + ex.getClass().getName()); + } HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR; return handleExceptionInternal(ex, null, headers, status, request); } From 7ddaf49eb26a90c24be795cece9c41b1216bf5b6 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 15 Sep 2016 14:31:05 +0200 Subject: [PATCH 0180/1274] StringUtils.parseLocaleString accepts Java 7 variants Issue: SPR-14718 (cherry picked from commit f24ce76) --- .../src/main/java/org/springframework/util/StringUtils.java | 2 +- .../test/java/org/springframework/util/StringUtilsTests.java | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/spring-core/src/main/java/org/springframework/util/StringUtils.java b/spring-core/src/main/java/org/springframework/util/StringUtils.java index 121f17ad567..112c14450cf 100644 --- a/spring-core/src/main/java/org/springframework/util/StringUtils.java +++ b/spring-core/src/main/java/org/springframework/util/StringUtils.java @@ -715,7 +715,7 @@ public static Locale parseLocaleString(String localeString) { private static void validateLocalePart(String localePart) { for (int i = 0; i < localePart.length(); i++) { char ch = localePart.charAt(i); - if (ch != '_' && ch != ' ' && !Character.isLetterOrDigit(ch)) { + if (ch != ' ' && ch != '_' && ch != '#' && !Character.isLetterOrDigit(ch)) { throw new IllegalArgumentException( "Locale part \"" + localePart + "\" contains invalid characters"); } diff --git a/spring-core/src/test/java/org/springframework/util/StringUtilsTests.java b/spring-core/src/test/java/org/springframework/util/StringUtilsTests.java index e63ec35575c..21cc7374963 100644 --- a/spring-core/src/test/java/org/springframework/util/StringUtilsTests.java +++ b/spring-core/src/test/java/org/springframework/util/StringUtilsTests.java @@ -692,4 +692,9 @@ public void testParseLocaleWithVariantContainingCountryCode() { assertEquals("Variant containing country code not extracted correctly", variant, locale.getVariant()); } + @Test // SPR-14718 + public void testParseJava7Variant() { + assertEquals("sr_#LATN", StringUtils.parseLocaleString("sr_#LATN").toString()); + } + } From 0dce5701573347a6c9fd880a1035e20d6d2d7c30 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Thu, 15 Sep 2016 16:02:10 +0200 Subject: [PATCH 0181/1274] Add Qualified element on RootBeanDefinition Improve RootBeanDefinition to specify an AnnotatedElement that holds qualifier information. When such element is present, any qualifier that it defines will be used to find a matching candidate. Issue: SPR-14725 (cherry picked from commit 2b0bf9f) --- ...erAnnotationAutowireCandidateResolver.java | 17 +++++++-- .../factory/support/RootBeanDefinition.java | 30 +++++++++++++-- ...wiredAnnotationBeanPostProcessorTests.java | 38 ++++++++++++++++--- .../factory/support/BeanDefinitionTests.java | 4 +- 4 files changed, 76 insertions(+), 13 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java index 0f6d10216ca..f6e770d4078 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -17,6 +17,7 @@ package org.springframework.beans.factory.annotation; import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; import java.util.LinkedHashSet; import java.util.Map; @@ -49,6 +50,7 @@ * * @author Mark Fisher * @author Juergen Hoeller + * @author Stephane Nicoll * @since 2.5 * @see AutowireCandidateQualifier * @see Qualifier @@ -225,8 +227,12 @@ protected boolean checkQualifier( qualifier = bd.getQualifier(ClassUtils.getShortName(type)); } if (qualifier == null) { - // First, check annotation on factory method, if applicable - Annotation targetAnnotation = getFactoryMethodAnnotation(bd, type); + // First, check annotation on qualified element, if any + Annotation targetAnnotation = getQualifiedElementAnnotation(bd, type); + // Then, check annotation on factory method, if applicable + if (targetAnnotation == null) { + targetAnnotation = getFactoryMethodAnnotation(bd, type); + } if (targetAnnotation == null) { RootBeanDefinition dbd = getResolvedDecoratedDefinition(bd); if (dbd != null) { @@ -291,6 +297,11 @@ protected boolean checkQualifier( return true; } + protected Annotation getQualifiedElementAnnotation(RootBeanDefinition bd, Class type) { + AnnotatedElement qualifiedElement = bd.getQualifiedElement(); + return (qualifiedElement != null ? AnnotationUtils.getAnnotation(qualifiedElement, type) : null); + } + protected Annotation getFactoryMethodAnnotation(RootBeanDefinition bd, Class type) { Method resolvedFactoryMethod = bd.getResolvedFactoryMethod(); return (resolvedFactoryMethod != null ? AnnotationUtils.getAnnotation(resolvedFactoryMethod, type) : null); diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java index aa33bcef659..237e672a3ef 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java @@ -16,6 +16,7 @@ package org.springframework.beans.factory.support; +import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.util.HashSet; @@ -51,12 +52,14 @@ public class RootBeanDefinition extends AbstractBeanDefinition { private BeanDefinitionHolder decoratedDefinition; - boolean allowCaching = true; + private AnnotatedElement qualifiedElement; - volatile ResolvableType targetType; + boolean allowCaching = true; boolean isFactoryMethodUnique = false; + volatile ResolvableType targetType; + /** Package-visible field for caching the determined Class of a given bean definition */ volatile Class resolvedTargetType; @@ -178,9 +181,10 @@ public RootBeanDefinition(String beanClassName, ConstructorArgumentValues cargs, public RootBeanDefinition(RootBeanDefinition original) { super(original); this.decoratedDefinition = original.decoratedDefinition; + this.qualifiedElement = original.qualifiedElement; this.allowCaching = original.allowCaching; - this.targetType = original.targetType; this.isFactoryMethodUnique = original.isFactoryMethodUnique; + this.targetType = original.targetType; } /** @@ -219,6 +223,26 @@ public BeanDefinitionHolder getDecoratedDefinition() { return this.decoratedDefinition; } + /** + * Specify the {@link AnnotatedElement} defining qualifiers, + * to be used instead of the target class or factory method. + * @since 4.3.3 + * @see #setTargetType(ResolvableType) + * @see #getResolvedFactoryMethod() + */ + public void setQualifiedElement(AnnotatedElement qualifiedElement) { + this.qualifiedElement = qualifiedElement; + } + + /** + * Return the {@link AnnotatedElement} defining qualifiers, if any. + * Otherwise, the factory method and target class will be checked. + * @since 4.3.3 + */ + public AnnotatedElement getQualifiedElement() { + return this.qualifiedElement; + } + /** * Specify a generics-containing target type of this bean definition, if known in advance. * @since 4.3.3 diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java index 21301e4cb1a..4e33a0bbb6e 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java @@ -63,6 +63,7 @@ import org.springframework.tests.sample.beans.IndexedTestBean; import org.springframework.tests.sample.beans.NestedTestBean; import org.springframework.tests.sample.beans.TestBean; +import org.springframework.util.ReflectionUtils; import org.springframework.util.SerializationTestUtils; import static org.junit.Assert.*; @@ -1026,14 +1027,35 @@ public void testObjectFactoryQualifierInjection() { bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryQualifierInjectionBean.class)); RootBeanDefinition bd = new RootBeanDefinition(TestBean.class); bd.addQualifier(new AutowireCandidateQualifier(Qualifier.class, "testBean")); - bf.registerBeanDefinition("testBean", bd); - bf.registerBeanDefinition("testBean2", new RootBeanDefinition(TestBean.class)); + bf.registerBeanDefinition("dependencyBean", bd); + bf.registerBeanDefinition("dependencyBean2", new RootBeanDefinition(TestBean.class)); ObjectFactoryQualifierInjectionBean bean = (ObjectFactoryQualifierInjectionBean) bf.getBean("annotatedBean"); - assertSame(bf.getBean("testBean"), bean.getTestBean()); + assertSame(bf.getBean("dependencyBean"), bean.getTestBean()); + bf.destroySingletons(); + } + + @Test + public void testObjectFactoryQualifierProviderInjection() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver()); + AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); + bpp.setBeanFactory(bf); + bf.addBeanPostProcessor(bpp); + bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryQualifierInjectionBean.class)); + RootBeanDefinition bd = new RootBeanDefinition(TestBean.class); + bd.setQualifiedElement(ReflectionUtils.findMethod(getClass(), "testBeanQualifierProvider")); + bf.registerBeanDefinition("dependencyBean", bd); + bf.registerBeanDefinition("dependencyBean2", new RootBeanDefinition(TestBean.class)); + + ObjectFactoryQualifierInjectionBean bean = (ObjectFactoryQualifierInjectionBean) bf.getBean("annotatedBean"); + assertSame(bf.getBean("dependencyBean"), bean.getTestBean()); bf.destroySingletons(); } + @Qualifier("testBean") + private void testBeanQualifierProvider() {} + @Test public void testObjectFactorySerialization() throws Exception { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); @@ -1588,11 +1610,12 @@ public void testGenericsBasedFieldInjectionWithMocks() { rbd.setFactoryBeanName("mocksControl"); rbd.setFactoryMethodName("createMock"); rbd.getConstructorArgumentValues().addGenericArgumentValue(Repository.class); - bf.registerBeanDefinition("integerRepo", rbd); + rbd.setQualifiedElement(ReflectionUtils.findField(getClass(), "integerRepositoryQualifierProvider")); + bf.registerBeanDefinition("integerRepository", rbd); // Bean name not matching qualifier RepositoryFieldInjectionBeanWithQualifiers bean = (RepositoryFieldInjectionBeanWithQualifiers) bf.getBean("annotatedBean"); Repository sr = bf.getBean("stringRepo", Repository.class); - Repository ir = bf.getBean("integerRepo", Repository.class); + Repository ir = bf.getBean("integerRepository", Repository.class); assertSame(sr, bean.stringRepository); assertSame(ir, bean.integerRepository); assertSame(1, bean.stringRepositoryArray.length); @@ -1606,9 +1629,12 @@ public void testGenericsBasedFieldInjectionWithMocks() { assertSame(1, bean.stringRepositoryMap.size()); assertSame(1, bean.integerRepositoryMap.size()); assertSame(sr, bean.stringRepositoryMap.get("stringRepo")); - assertSame(ir, bean.integerRepositoryMap.get("integerRepo")); + assertSame(ir, bean.integerRepositoryMap.get("integerRepository")); } + @Qualifier("integerRepo") + private Repository integerRepositoryQualifierProvider; + @Test public void testGenericsBasedFieldInjectionWithSimpleMatch() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanDefinitionTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanDefinitionTests.java index 899bee05526..9e32012468a 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanDefinitionTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanDefinitionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -126,6 +126,7 @@ public void beanDefinitionMerging() { bd.getConstructorArgumentValues().addIndexedArgumentValue(1, new Integer(5)); bd.getPropertyValues().add("name", "myName"); bd.getPropertyValues().add("age", "99"); + bd.setQualifiedElement(getClass()); GenericBeanDefinition childBd = new GenericBeanDefinition(); childBd.setParentName("bd"); @@ -138,6 +139,7 @@ public void beanDefinitionMerging() { mergedBd.getConstructorArgumentValues().getArgumentValue(1, null).setValue(new Integer(9)); assertEquals(new Integer(5), bd.getConstructorArgumentValues().getArgumentValue(1, null).getValue()); + assertEquals(getClass(), bd.getQualifiedElement()); } } From 59cb9a4e60415ce494ca8825ae1d8624ec0026b0 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 16 Sep 2016 11:02:22 +0200 Subject: [PATCH 0182/1274] Latest dependency updates (EhCache 3.1.2, Caffeine 2.3.3, Rome 1.7, Woodstox 5.0.3, Jettison 1.3.8) --- build.gradle | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index bbce5b919b1..b18d894898d 100644 --- a/build.gradle +++ b/build.gradle @@ -32,11 +32,11 @@ configure(allprojects) { project -> version = qualifyVersionIfNecessary(version) ext.aspectjVersion = "1.8.9" - ext.caffeineVersion = "2.3.2" + ext.caffeineVersion = "2.3.3" ext.eclipselinkVersion = "2.4.2" ext.ehcacheVersion = "2.10.2" ext.ehcachejcacheVersion = "1.0.1" - ext.ehcache3Version = "3.1.1" + ext.ehcache3Version = "3.1.2" ext.ejbVersion = "3.0" ext.fileuploadVersion = "1.3.2" ext.freemarkerVersion = "2.3.23" @@ -67,7 +67,7 @@ configure(allprojects) { project -> ext.openjpaVersion = "2.4.1" ext.poiVersion = "3.14" ext.reactorVersion = "2.0.8.RELEASE" - ext.romeVersion = "1.6.0" + ext.romeVersion = "1.7.0" ext.slf4jVersion = "1.7.21" ext.snakeyamlVersion = "1.17" ext.snifferVersion = "1.15" @@ -357,7 +357,7 @@ project("spring-core") { optional("log4j:log4j:${log4jVersion}") testCompile("org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}") testCompile("xmlunit:xmlunit:${xmlunitVersion}") - testCompile("com.fasterxml.woodstox:woodstox-core:5.0.2") { + testCompile("com.fasterxml.woodstox:woodstox-core:5.0.3") { exclude group: "stax", module: "stax-api" } } @@ -576,7 +576,7 @@ project("spring-oxm") { testCompile(project(":spring-context")) testCompile("xmlunit:xmlunit:${xmlunitVersion}") testCompile("xpp3:xpp3:1.1.4c") - testCompile("org.codehaus.jettison:jettison:1.3.7") { + testCompile("org.codehaus.jettison:jettison:1.3.8") { exclude group: 'stax', module: 'stax-api' } testCompile(files(genCastor.classesDir).builtBy(genCastor)) From ace25d498741af1fb1dffd289f014a52680a26f0 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 16 Sep 2016 11:18:50 +0200 Subject: [PATCH 0183/1274] Polishing (cherry picked from commit ed19dc7) --- .../util/StringUtilsTests.java | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/spring-core/src/test/java/org/springframework/util/StringUtilsTests.java b/spring-core/src/test/java/org/springframework/util/StringUtilsTests.java index 21cc7374963..a1abde60b25 100644 --- a/spring-core/src/test/java/org/springframework/util/StringUtilsTests.java +++ b/spring-core/src/test/java/org/springframework/util/StringUtilsTests.java @@ -618,51 +618,50 @@ public void testParseLocaleStringWithEmptyLocaleStringYieldsNullLocale() throws assertNull("When given an empty Locale string, must return null.", locale); } - // SPR-8637 - @Test + @Test // SPR-8637 public void testParseLocaleWithMultiSpecialCharactersInVariant() throws Exception { - final String variant = "proper-northern"; - final String localeString = "en_GB_" + variant; + String variant = "proper-northern"; + String localeString = "en_GB_" + variant; Locale locale = StringUtils.parseLocaleString(localeString); assertEquals("Multi-valued variant portion of the Locale not extracted correctly.", variant, locale.getVariant()); } @Test // SPR-3671 public void testParseLocaleWithMultiValuedVariant() throws Exception { - final String variant = "proper_northern"; - final String localeString = "en_GB_" + variant; + String variant = "proper_northern"; + String localeString = "en_GB_" + variant; Locale locale = StringUtils.parseLocaleString(localeString); assertEquals("Multi-valued variant portion of the Locale not extracted correctly.", variant, locale.getVariant()); } @Test // SPR-3671 public void testParseLocaleWithMultiValuedVariantUsingSpacesAsSeparators() throws Exception { - final String variant = "proper northern"; - final String localeString = "en GB " + variant; + String variant = "proper northern"; + String localeString = "en GB " + variant; Locale locale = StringUtils.parseLocaleString(localeString); assertEquals("Multi-valued variant portion of the Locale not extracted correctly.", variant, locale.getVariant()); } @Test // SPR-3671 public void testParseLocaleWithMultiValuedVariantUsingMixtureOfUnderscoresAndSpacesAsSeparators() throws Exception { - final String variant = "proper northern"; - final String localeString = "en_GB_" + variant; + String variant = "proper northern"; + String localeString = "en_GB_" + variant; Locale locale = StringUtils.parseLocaleString(localeString); assertEquals("Multi-valued variant portion of the Locale not extracted correctly.", variant, locale.getVariant()); } @Test // SPR-3671 public void testParseLocaleWithMultiValuedVariantUsingSpacesAsSeparatorsWithLotsOfLeadingWhitespace() throws Exception { - final String variant = "proper northern"; - final String localeString = "en GB " + variant; // lots of whitespace + String variant = "proper northern"; + String localeString = "en GB " + variant; // lots of whitespace Locale locale = StringUtils.parseLocaleString(localeString); assertEquals("Multi-valued variant portion of the Locale not extracted correctly.", variant, locale.getVariant()); } @Test // SPR-3671 public void testParseLocaleWithMultiValuedVariantUsingUnderscoresAsSeparatorsWithLotsOfLeadingWhitespace() throws Exception { - final String variant = "proper_northern"; - final String localeString = "en_GB_____" + variant; // lots of underscores + String variant = "proper_northern"; + String localeString = "en_GB_____" + variant; // lots of underscores Locale locale = StringUtils.parseLocaleString(localeString); assertEquals("Multi-valued variant portion of the Locale not extracted correctly.", variant, locale.getVariant()); } From 2bbfbb1d507940a51360145ddaf14b5334d9b9b3 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 16 Sep 2016 14:31:53 +0200 Subject: [PATCH 0184/1274] What's New updated for significant refinements in 4.3.3 --- src/asciidoc/whats-new.adoc | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/asciidoc/whats-new.adoc b/src/asciidoc/whats-new.adoc index bb1cb1e52d9..31999a4b813 100644 --- a/src/asciidoc/whats-new.adoc +++ b/src/asciidoc/whats-new.adoc @@ -631,6 +631,7 @@ public @interface MyTestConfig { * Core container exceptions provide richer metadata to evaluate programmatically. * Java 8 default methods get detected as bean property getters/setters. +* Lazy candidate beans are not being created in case of injecting a primary bean. * It is no longer necessary to specify the `@Autowired` annotation if the target bean only defines one constructor. * `@Configuration` classes support constructor injection. @@ -640,6 +641,8 @@ public @interface MyTestConfig { with a single element of the component type of the array. For example, the `String[] path` attribute of `@RequestMapping` can be overridden with `String path` in a composed annotation. +* `@PersistenceContext`/`@PersistenceUnit` selects a primary `EntityManagerFactory` + bean if declared as such. * `@Scheduled` and `@Schedules` may now be used as _meta-annotations_ to create custom _composed annotations_ with attribute overrides. * `@Scheduled` is properly supported on beans of any scope. @@ -688,6 +691,7 @@ Spring 4.3 also improves the caching abstraction as follows: * New `@SessionAttribute` annotation for access to session attributes (see <>). * New `@RequestAttribute` annotation for access to request attributes (see <>). * `@ModelAttribute` allows preventing data binding via `binding=false` attribute (see <>). +* `@PathVariable` may be declared as optional (for use on `@ModelAttribute` methods). * Consistent exposure of Errors and custom Throwables to MVC exception handlers. * Consistent charset handling in HTTP message converters, including a UTF-8 default for multipart text content. * Static resource handling uses the configured `ContentNegotiationManager` for media type determination. @@ -730,11 +734,13 @@ Spring 4.3 also improves the caching abstraction as follows: === Support for new library and server generations * Hibernate ORM 5.2 (still supporting 4.2/4.3 and 5.0/5.1 as well, with 3.6 deprecated now) +* Hibernate Validator 5.3 (minimum remains at 4.3) * Jackson 2.8 (minimum raised to Jackson 2.6+ as of Spring 4.3) * OkHttp 3.x (still supporting OkHttp 2.x side by side) +* Tomcat 8.5 as well as 9.0 milestones * Netty 4.1 * Undertow 1.4 -* Tomcat 8.5.2 as well as 9.0 M6 +* WildFly 10.1 -Furthermore, Spring Framework 4.3 embeds the updated ASM 5.1 and Objenesis 2.4 in -`spring-core.jar`. +Furthermore, Spring Framework 4.3 embeds the updated ASM 5.1, CGLIB 3.2.4, and Objenesis 2.4 +in `spring-core.jar`. From 66b370e103dbd73a6a8c6e322c418de23eebbd9e Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Sat, 17 Sep 2016 22:18:55 +0200 Subject: [PATCH 0185/1274] Check template availability in ScriptTemplateView This commit overrides the `checkResource` implementation in `ScriptTemplateView` in order to check if the template file resource is available and if the resolver can then proceed with rendering the template. Issue: SPR-14729 Cherry-picked from: 97c9b05c15037a75a9 --- .../view/script/ScriptTemplateView.java | 15 ++++ .../view/script/ScriptTemplateViewTests.java | 68 +++++++++---------- 2 files changed, 48 insertions(+), 35 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/script/ScriptTemplateView.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/script/ScriptTemplateView.java index ac8298a8aa3..66a60040437 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/script/ScriptTemplateView.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/script/ScriptTemplateView.java @@ -16,11 +16,13 @@ package org.springframework.web.servlet.view.script; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.Charset; import java.util.Arrays; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import javax.script.Invocable; import javax.script.ScriptEngine; @@ -191,6 +193,19 @@ public void setResourceLoaderPath(String resourceLoaderPath) { } } + @Override + public boolean checkResource(Locale locale) throws Exception { + try { + getTemplate(getUrl()); + return true; + } + catch (IllegalStateException exc) { + if (logger.isDebugEnabled()) { + logger.debug("No ScriptTemplate view found for URL: " + getUrl()); + } + return false; + } + } @Override protected void initApplicationContext(ApplicationContext context) { diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/script/ScriptTemplateViewTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/script/ScriptTemplateViewTests.java index a652d7e9826..d15531260f6 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/script/ScriptTemplateViewTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/script/ScriptTemplateViewTests.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -28,7 +29,9 @@ import javax.script.ScriptEngine; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.springframework.beans.DirectFieldAccessor; import org.springframework.context.ApplicationContextException; @@ -58,6 +61,8 @@ public class ScriptTemplateViewTests { private StaticWebApplicationContext wac; + @Rule + public ExpectedException expectedException = ExpectedException.none(); @Before public void setup() { @@ -68,15 +73,24 @@ public void setup() { } + @Test + public void missingTemplate() throws Exception { + MockServletContext servletContext = new MockServletContext(); + this.wac.setServletContext(servletContext); + this.wac.refresh(); + this.view.setResourceLoaderPath("classpath:org/springframework/web/servlet/view/script/"); + this.view.setUrl("missing.txt"); + this.view.setEngine(mock(InvocableScriptEngine.class)); + this.configurer.setRenderFunction("render"); + this.view.setApplicationContext(this.wac); + assertFalse(this.view.checkResource(Locale.ENGLISH)); + } + @Test public void missingScriptTemplateConfig() throws Exception { - try { - this.view.setApplicationContext(new StaticApplicationContext()); - fail("Should have thrown ApplicationContextException"); - } - catch (ApplicationContextException ex) { - assertTrue(ex.getMessage().contains("ScriptTemplateConfig")); - } + this.expectedException.expect(ApplicationContextException.class); + this.view.setApplicationContext(new StaticApplicationContext()); + this.expectedException.expectMessage(contains("ScriptTemplateConfig")); } @Test @@ -152,25 +166,17 @@ public void nonSharedEngine() throws Exception { @Test public void nonInvocableScriptEngine() throws Exception { - try { - this.view.setEngine(mock(ScriptEngine.class)); - fail("Should have thrown IllegalArgumentException"); - } - catch (IllegalArgumentException ex) { - assertThat(ex.getMessage(), containsString("instance")); - } + this.expectedException.expect(IllegalArgumentException.class); + this.view.setEngine(mock(ScriptEngine.class)); + this.expectedException.expectMessage(contains("instance")); } @Test public void noRenderFunctionDefined() { this.view.setEngine(mock(InvocableScriptEngine.class)); - try { - this.view.setApplicationContext(this.wac); - fail("Should have thrown IllegalArgumentException"); - } - catch (IllegalArgumentException ex) { - assertThat(ex.getMessage(), containsString("renderFunction")); - } + this.expectedException.expect(IllegalArgumentException.class); + this.view.setApplicationContext(this.wac); + this.expectedException.expectMessage(contains("renderFunction")); } @Test @@ -178,13 +184,9 @@ public void engineAndEngineNameBothDefined() { this.view.setEngine(mock(InvocableScriptEngine.class)); this.view.setEngineName("test"); this.view.setRenderFunction("render"); - try { - this.view.setApplicationContext(this.wac); - fail("Should have thrown IllegalArgumentException"); - } - catch (IllegalArgumentException ex) { - assertThat(ex.getMessage(), containsString("'engine' or 'engineName'")); - } + this.expectedException.expect(IllegalArgumentException.class); + this.view.setApplicationContext(this.wac); + this.expectedException.expectMessage(contains("'engine' or 'engineName'")); } @Test @@ -192,13 +194,9 @@ public void engineSetterAndNonSharedEngine() { this.view.setEngine(mock(InvocableScriptEngine.class)); this.view.setRenderFunction("render"); this.view.setSharedEngine(false); - try { - this.view.setApplicationContext(this.wac); - fail("Should have thrown IllegalArgumentException"); - } - catch (IllegalArgumentException ex) { - assertThat(ex.getMessage(), containsString("sharedEngine")); - } + this.expectedException.expect(IllegalArgumentException.class); + this.view.setApplicationContext(this.wac); + this.expectedException.expectMessage(contains("sharedEngine")); } @Test // SPR-14210 From 15d3e8c3e1b8b36d418f25ab4052741072f22518 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Sat, 17 Sep 2016 16:01:08 +0200 Subject: [PATCH 0186/1274] Introduce 'value' alias for @Bean's 'name' attribute In order to simplify configuration for use cases involving @Bean where only a bean name or aliases are supplied as an attribute, this commit introduces a new 'value' attribute that is an @AliasFor 'name' in @Bean. Issue: SPR-14728 (cherry picked from commit 8f62b63) --- .../context/annotation/Bean.java | 37 ++++-- .../ConfigurationClassProcessingTests.java | 112 +++++++++++++----- .../EnableMBeanExportConfigurationTests.java | 4 +- .../WebMvcConfigurationSupportTests.java | 10 +- 4 files changed, 113 insertions(+), 50 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/annotation/Bean.java b/spring-context/src/main/java/org/springframework/context/annotation/Bean.java index 8cbaa0da3b1..b15b7c8ef8e 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/Bean.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/Bean.java @@ -24,6 +24,7 @@ import org.springframework.beans.factory.annotation.Autowire; import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.core.annotation.AliasFor; /** * Indicates that a method produces a bean to be managed by the Spring container. @@ -44,15 +45,15 @@ * *

    Bean Names

    * - *

    While a {@link #name() name} attribute is available, the default strategy for + *

    While a {@link #name} attribute is available, the default strategy for * determining the name of a bean is to use the name of the {@code @Bean} method. * This is convenient and intuitive, but if explicit naming is desired, the - * {@code name} attribute may be used. Also note that {@code name} accepts an array - * of Strings. This is in order to allow for specifying multiple names (i.e., aliases) - * for a single bean. + * {@code name} attribute (or its alias {@code value}) may be used. Also note + * that {@code name} accepts an array of Strings, allowing for multiple names + * (i.e. a primary bean name plus one or more aliases) for a single bean. * *

    - *     @Bean(name={"b1","b2"}) // bean available as 'b1' and 'b2', but not 'myBean'
    + *     @Bean({"b1", "b2"}) // bean available as 'b1' and 'b2', but not 'myBean'
      *     public MyBean myBean() {
      *         // instantiate and configure MyBean obj
      *         return obj;
    @@ -78,9 +79,9 @@
      * 

    {@code @Bean} Methods in {@code @Configuration} Classes

    * *

    Typically, {@code @Bean} methods are declared within {@code @Configuration} - * classes. In this case, bean methods may reference other {@code @Bean} methods - * in the same class by calling them directly. This ensures that references between - * beans are strongly typed and navigable. Such so-called 'inter-bean references' are + * classes. In this case, bean methods may reference other {@code @Bean} methods in the + * same class by calling them directly. This ensures that references between beans + * are strongly typed and navigable. Such so-called 'inter-bean references' are * guaranteed to respect scoping and AOP semantics, just like {@code getBean()} lookups * would. These are the semantics known from the original 'Spring JavaConfig' project * which require CGLIB subclassing of each such configuration class at runtime. As a @@ -190,10 +191,24 @@ public @interface Bean { /** - * The name of this bean, or if plural, aliases for this bean. If left unspecified - * the name of the bean is the name of the annotated method. If specified, the method - * name is ignored. + * Alias for {@link #name}. + *

    Intended to be used when no other attributes are needed, for example: + * {@code @Bean("customBeanName")}. + * @since 4.3.3 + * @see #name */ + @AliasFor("name") + String[] value() default {}; + + /** + * The name of this bean, or if several names, a primary bean name plus aliases. + *

    If left unspecified, the name of the bean is the name of the annotated method. + * If specified, the method name is ignored. + *

    The bean name and aliases may also be configured via the {@link #value} + * attribute if no other attributes are declared. + * @see #value + */ + @AliasFor("value") String[] name() default {}; /** diff --git a/spring-context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationClassProcessingTests.java b/spring-context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationClassProcessingTests.java index bf4c9e6a9eb..88d27eaca75 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationClassProcessingTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/configuration/ConfigurationClassProcessingTests.java @@ -20,9 +20,12 @@ import java.util.Collections; import java.util.List; import java.util.Set; +import java.util.function.Supplier; import javax.inject.Provider; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.BeanFactory; @@ -65,6 +68,7 @@ * * @author Chris Beams * @author Juergen Hoeller + * @author Sam Brannen */ public class ConfigurationClassProcessingTests { @@ -92,40 +96,57 @@ private DefaultListableBeanFactory initBeanFactory(Class... configClasses) { } + @Rule + public final ExpectedException exception = ExpectedException.none(); + + + @Test + public void customBeanNameIsRespectedWhenConfiguredViaNameAttribute() { + customBeanNameIsRespected(ConfigWithBeanWithCustomName.class, + () -> ConfigWithBeanWithCustomName.testBean, "customName"); + } + @Test - public void customBeanNameIsRespected() { + public void customBeanNameIsRespectedWhenConfiguredViaValueAttribute() { + customBeanNameIsRespected(ConfigWithBeanWithCustomNameConfiguredViaValueAttribute.class, + () -> ConfigWithBeanWithCustomNameConfiguredViaValueAttribute.testBean, "enigma"); + } + + private void customBeanNameIsRespected(Class testClass, Supplier testBeanSupplier, String beanName) { GenericApplicationContext ac = new GenericApplicationContext(); AnnotationConfigUtils.registerAnnotationConfigProcessors(ac); - ac.registerBeanDefinition("config", new RootBeanDefinition(ConfigWithBeanWithCustomName.class)); + ac.registerBeanDefinition("config", new RootBeanDefinition(testClass)); ac.refresh(); - assertSame(ac.getBean("customName"), ConfigWithBeanWithCustomName.testBean); + + assertSame(testBeanSupplier.get(), ac.getBean(beanName)); // method name should not be registered - try { - ac.getBean("methodName"); - fail("bean should not have been registered with 'methodName'"); - } - catch (NoSuchBeanDefinitionException ex) { - // expected - } + exception.expect(NoSuchBeanDefinitionException.class); + ac.getBean("methodName"); } @Test - public void aliasesAreRespected() { - BeanFactory factory = initBeanFactory(ConfigWithBeanWithAliases.class); - assertSame(factory.getBean("name1"), ConfigWithBeanWithAliases.testBean); - String[] aliases = factory.getAliases("name1"); - for (String alias : aliases) - assertSame(factory.getBean(alias), ConfigWithBeanWithAliases.testBean); + public void aliasesAreRespectedWhenConfiguredViaNameAttribute() { + aliasesAreRespected(ConfigWithBeanWithAliases.class, + () -> ConfigWithBeanWithAliases.testBean, "name1"); + } + + @Test + public void aliasesAreRespectedWhenConfiguredViaValueAttribute() { + aliasesAreRespected(ConfigWithBeanWithAliasesConfiguredViaValueAttribute.class, + () -> ConfigWithBeanWithAliasesConfiguredViaValueAttribute.testBean, "enigma"); + } + + private void aliasesAreRespected(Class testClass, Supplier testBeanSupplier, String beanName) { + TestBean testBean = testBeanSupplier.get(); + BeanFactory factory = initBeanFactory(testClass); + + assertSame(testBean, factory.getBean(beanName)); + Arrays.stream(factory.getAliases(beanName)).map(factory::getBean).forEach(alias -> assertSame(testBean, alias)); // method name should not be registered - try { - factory.getBean("methodName"); - fail("bean should not have been registered with 'methodName'"); - } - catch (NoSuchBeanDefinitionException ex) { - // expected - } + exception.expect(NoSuchBeanDefinitionException.class); + factory.getBean("methodName"); } @Test // SPR-11830 @@ -146,8 +167,9 @@ public void configWithSetWithProviderImplementation() { assertSame(ac.getBean("customName"), ConfigWithSetWithProviderImplementation.set); } - @Test(expected=BeanDefinitionParsingException.class) + @Test public void testFinalBeanMethod() { + exception.expect(BeanDefinitionParsingException.class); initBeanFactory(ConfigWithFinalBean.class); } @@ -219,6 +241,7 @@ public void configurationWithAdaptivePrototypes() { adaptive = factory.getBean(AdaptiveInjectionPoints.class); assertEquals("adaptiveInjectionPoint1", adaptive.adaptiveInjectionPoint1.getName()); assertEquals("setAdaptiveInjectionPoint2", adaptive.adaptiveInjectionPoint2.getName()); + factory.close(); } @Test @@ -240,15 +263,28 @@ public void configurationWithPostProcessor() { SpousyTestBean listener = factory.getBean("listenerTestBean", SpousyTestBean.class); assertTrue(listener.refreshed); + factory.close(); } @Configuration static class ConfigWithBeanWithCustomName { - static TestBean testBean = new TestBean(); + static TestBean testBean = new TestBean(ConfigWithBeanWithCustomName.class.getSimpleName()); - @Bean(name="customName") + @Bean(name = "customName") + public TestBean methodName() { + return testBean; + } + } + + + @Configuration + static class ConfigWithBeanWithCustomNameConfiguredViaValueAttribute { + + static TestBean testBean = new TestBean(ConfigWithBeanWithCustomNameConfiguredViaValueAttribute.class.getSimpleName()); + + @Bean("enigma") public TestBean methodName() { return testBean; } @@ -258,9 +294,21 @@ public TestBean methodName() { @Configuration static class ConfigWithBeanWithAliases { - static TestBean testBean = new TestBean(); + static TestBean testBean = new TestBean(ConfigWithBeanWithAliases.class.getSimpleName()); + + @Bean(name = { "name1", "alias1", "alias2", "alias3" }) + public TestBean methodName() { + return testBean; + } + } + + + @Configuration + static class ConfigWithBeanWithAliasesConfiguredViaValueAttribute { + + static TestBean testBean = new TestBean(ConfigWithBeanWithAliasesConfiguredViaValueAttribute.class.getSimpleName()); - @Bean(name={"name1", "alias1", "alias2", "alias3"}) + @Bean({ "enigma", "alias1", "alias2", "alias3" }) public TestBean methodName() { return testBean; } @@ -270,9 +318,9 @@ public TestBean methodName() { @Configuration static class ConfigWithBeanWithProviderImplementation implements Provider { - static TestBean testBean = new TestBean(); + static TestBean testBean = new TestBean(ConfigWithBeanWithProviderImplementation.class.getSimpleName()); - @Bean(name="customName") + @Bean(name = "customName") public TestBean get() { return testBean; } @@ -284,7 +332,7 @@ static class ConfigWithSetWithProviderImplementation implements Provider set = Collections.singleton("value"); - @Bean(name="customName") + @Bean(name = "customName") public Set get() { return set; } @@ -406,7 +454,7 @@ public int getOrder() { }; } - //@Bean + // @Bean public BeanFactoryPostProcessor beanFactoryPostProcessor() { return new BeanFactoryPostProcessor() { @Override diff --git a/spring-context/src/test/java/org/springframework/jmx/export/annotation/EnableMBeanExportConfigurationTests.java b/spring-context/src/test/java/org/springframework/jmx/export/annotation/EnableMBeanExportConfigurationTests.java index 90488829522..d9d93306fdd 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/annotation/EnableMBeanExportConfigurationTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/annotation/EnableMBeanExportConfigurationTests.java @@ -228,7 +228,7 @@ public MBeanServerFactoryBean server() throws Exception { return new MBeanServerFactoryBean(); } - @Bean(name="bean:name=testBean4") + @Bean("bean:name=testBean4") @Lazy public AnnotationTestBean testBean4() { AnnotationTestBean bean = new AnnotationTestBean(); @@ -237,7 +237,7 @@ public AnnotationTestBean testBean4() { return bean; } - @Bean(name="bean:name=testBean5") + @Bean("bean:name=testBean5") public AnnotationTestBeanFactory testBean5() throws Exception { return new AnnotationTestBeanFactory(); } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportTests.java index 60de6e8308d..1e36918b513 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportTests.java @@ -333,10 +333,10 @@ private ApplicationContext initContext(Class... configClasses) { @EnableWebMvc - @Configuration @SuppressWarnings("unused") + @Configuration static class WebConfig { - @Bean(name="/testController") + @Bean("/testController") public TestController testController() { return new TestController(); } @@ -350,7 +350,7 @@ public MessageSource messageSource() { } - @Configuration @SuppressWarnings("unused") + @Configuration static class ViewResolverConfig { @Bean @@ -387,7 +387,7 @@ public void addReturnValueHandlers(List handler } - @Controller @SuppressWarnings("unused") + @Controller private static class TestController { @RequestMapping("/") @@ -413,7 +413,7 @@ public void handle() { @Controller - @Scope(value="prototype", proxyMode=ScopedProxyMode.TARGET_CLASS) + @Scope(scopeName = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS) static class ScopedProxyController { @RequestMapping("/scopedProxy") From c26bf871b7f4f9605423d5f9e239fd22bcf92bc4 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Sat, 17 Sep 2016 16:10:45 +0200 Subject: [PATCH 0187/1274] Clean up warnings related to forthcoming removals in Tomcat 9 (cherry picked from commit b521aa8) --- .../web/socket/TomcatWebSocketTestServer.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/TomcatWebSocketTestServer.java b/spring-websocket/src/test/java/org/springframework/web/socket/TomcatWebSocketTestServer.java index 6939b8241f0..abdd3027ff5 100644 --- a/spring-websocket/src/test/java/org/springframework/web/socket/TomcatWebSocketTestServer.java +++ b/spring-websocket/src/test/java/org/springframework/web/socket/TomcatWebSocketTestServer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -95,9 +95,9 @@ public int getPort() { public void deployConfig(WebApplicationContext wac, Filter... filters) { Assert.state(this.port != -1, "setup() was never called."); this.context = this.tomcatServer.addContext("", System.getProperty("java.io.tmpdir")); - this.context.addApplicationListener(WsContextListener.class.getName()); + this.context.addApplicationListener(WsContextListener.class.getName()); Tomcat.addServlet(this.context, "dispatcherServlet", new DispatcherServlet(wac)).setAsyncSupported(true); - this.context.addServletMapping("/", "dispatcherServlet"); + this.context.addServletMappingDecoded("/", "dispatcherServlet"); for (Filter filter : filters) { FilterDef filterDef = new FilterDef(); filterDef.setFilterName(filter.getClass().getName()); From ca17edd5ac32c3936fdb8289ae52433fd37b46e2 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sun, 18 Sep 2016 21:04:37 +0200 Subject: [PATCH 0188/1274] Revised checkResource implementation Issue: SPR-14729 --- .../view/script/ScriptTemplateView.java | 41 +++++++++---------- .../view/script/ScriptTemplateViewTests.java | 2 +- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/script/ScriptTemplateView.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/script/ScriptTemplateView.java index 66a60040437..73f4fbed2b0 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/script/ScriptTemplateView.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/script/ScriptTemplateView.java @@ -16,7 +16,6 @@ package org.springframework.web.servlet.view.script; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.Charset; @@ -193,19 +192,6 @@ public void setResourceLoaderPath(String resourceLoaderPath) { } } - @Override - public boolean checkResource(Locale locale) throws Exception { - try { - getTemplate(getUrl()); - return true; - } - catch (IllegalStateException exc) { - if (logger.isDebugEnabled()) { - logger.debug("No ScriptTemplate view found for URL: " + getUrl()); - } - return false; - } - } @Override protected void initApplicationContext(ApplicationContext context) { @@ -264,7 +250,6 @@ else if (this.engine != null) { Assert.isTrue(this.renderFunction != null, "The 'renderFunction' property must be defined."); } - protected ScriptEngine getEngine() { if (Boolean.FALSE.equals(this.sharedEngine)) { Map engines = enginesHolder.get(); @@ -299,14 +284,17 @@ protected ScriptEngine createEngineFromName() { protected void loadScripts(ScriptEngine engine) { if (!ObjectUtils.isEmpty(this.scripts)) { - try { - for (String script : this.scripts) { - Resource resource = getResource(script); + for (String script : this.scripts) { + Resource resource = getResource(script); + if (resource == null) { + throw new IllegalStateException("Script resource [" + script + "] not found"); + } + try { engine.eval(new InputStreamReader(resource.getInputStream())); } - } - catch (Exception ex) { - throw new IllegalStateException("Failed to load script", ex); + catch (Throwable ex) { + throw new IllegalStateException("Failed to evaluate script [" + script + "]", ex); + } } } } @@ -318,7 +306,7 @@ protected Resource getResource(String location) { return resource; } } - throw new IllegalStateException("Resource [" + location + "] not found"); + return null; } protected ScriptTemplateConfig autodetectViewConfig() throws BeansException { @@ -333,6 +321,12 @@ protected ScriptTemplateConfig autodetectViewConfig() throws BeansException { } } + + @Override + public boolean checkResource(Locale locale) throws Exception { + return (getResource(getUrl()) != null); + } + @Override protected void prepareResponse(HttpServletRequest request, HttpServletResponse response) { super.prepareResponse(request, response); @@ -369,6 +363,9 @@ protected void renderMergedOutputModel(Map model, HttpServletReq protected String getTemplate(String path) throws IOException { Resource resource = getResource(path); + if (resource == null) { + throw new IllegalStateException("Template resource [" + path + "] not found"); + } InputStreamReader reader = new InputStreamReader(resource.getInputStream(), this.charset); return FileCopyUtils.copyToString(reader); } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/script/ScriptTemplateViewTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/script/ScriptTemplateViewTests.java index d15531260f6..5e1e7726266 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/script/ScriptTemplateViewTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/script/ScriptTemplateViewTests.java @@ -44,7 +44,6 @@ import org.springframework.web.context.support.StaticWebApplicationContext; import org.springframework.web.servlet.DispatcherServlet; -import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import static org.mockito.BDDMockito.*; @@ -64,6 +63,7 @@ public class ScriptTemplateViewTests { @Rule public ExpectedException expectedException = ExpectedException.none(); + @Before public void setup() { this.configurer = new ScriptTemplateConfigurer(); From 3df7083f693918563a92287aeaa8eaacdd6cd2a8 Mon Sep 17 00:00:00 2001 From: Krzysztof Kosmatka Date: Mon, 19 Sep 2016 14:24:08 +0200 Subject: [PATCH 0189/1274] Add missing quotation mark Closes gh-1181 --- src/asciidoc/core-validation.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/asciidoc/core-validation.adoc b/src/asciidoc/core-validation.adoc index 0374d8cfd88..76dd177cf6e 100644 --- a/src/asciidoc/core-validation.adoc +++ b/src/asciidoc/core-validation.adoc @@ -136,7 +136,7 @@ and use it like so: } if (!addressValidator.supports(Address.class)) { throw new IllegalArgumentException("The supplied [Validator] must " + - support the validation of [Address] instances."); + "support the validation of [Address] instances."); } this.addressValidator = addressValidator; } From 049861afb4a0e5526caf5d3b2402ccae4a427b07 Mon Sep 17 00:00:00 2001 From: Krzysztof Kosmatka Date: Mon, 19 Sep 2016 09:38:48 +0200 Subject: [PATCH 0190/1274] Documentation formatting fix Removed unnecessary quotation marks that caused improper rendering of in-line code. Closes gh-1180 --- src/asciidoc/core-resources.adoc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/asciidoc/core-resources.adoc b/src/asciidoc/core-resources.adoc index 11cfca24fc5..82b43d0ccb8 100644 --- a/src/asciidoc/core-resources.adoc +++ b/src/asciidoc/core-resources.adoc @@ -494,8 +494,8 @@ When the path location contains an Ant-style pattern, for example: ... the resolver follows a more complex but defined procedure to try to resolve the wildcard. It produces a Resource for the path up to the last non-wildcard segment and -obtains a URL from it. If this URL is not a "jar:" URL or container-specific variant -(e.g. " `zip:`" in WebLogic, " `wsjar`" in WebSphere, etc.), then a `java.io.File` is +obtains a URL from it. If this URL is not a `jar:` URL or container-specific variant +(e.g. `zip:` in WebLogic, `wsjar` in WebSphere, etc.), then a `java.io.File` is obtained from it and used to resolve the wildcard by traversing the filesystem. In the case of a jar URL, the resolver either gets a `java.net.JarURLConnection` from it or manually parses the jar URL and then traverses the contents of the jar file to resolve @@ -555,9 +555,9 @@ inappropriate result is returned, check the application server documentation for settings that might affect the classloader behavior. ==== -The " `classpath*:`" prefix can also be combined with a `PathMatcher` pattern in the -rest of the location path, for example " `classpath*:META-INF/*-beans.xml`". In this -case, the resolution strategy is fairly simple: a ClassLoader.getResources() call is +The `classpath*:` prefix can also be combined with a `PathMatcher` pattern in the +rest of the location path, for example `classpath*:META-INF/*-beans.xml`. In this +case, the resolution strategy is fairly simple: a `ClassLoader.getResources()` call is used on the last non-wildcard path segment to get all the matching resources in the class loader hierarchy, and then off each resource the same PathMatcher resolution strategy described above is used for the wildcard subpath. @@ -567,13 +567,13 @@ strategy described above is used for the wildcard subpath. ==== Other notes relating to wildcards Please note that `classpath*:` when combined with Ant-style patterns will only work reliably with at least one root directory before the pattern starts, unless the actual -target files reside in the file system. This means that a pattern like " -`classpath*:*.xml`" will not retrieve files from the root of jar files but rather only +target files reside in the file system. This means that a pattern like +`classpath*:*.xml` will not retrieve files from the root of jar files but rather only from the root of expanded directories. This originates from a limitation in the JDK's `ClassLoader.getResources()` method which only returns file system locations for a passed-in empty string (indicating potential roots to search). -Ant-style patterns with " `classpath:`" resources are not guaranteed to find matching +Ant-style patterns with `classpath:` resources are not guaranteed to find matching resources if the root package to search is available in multiple class path locations. This is because a resource such as From 78a8245f4a94f61ecf9040be6409ce40d444330f Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Mon, 19 Sep 2016 14:58:00 +0200 Subject: [PATCH 0191/1274] Polish --- src/asciidoc/core-resources.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/asciidoc/core-resources.adoc b/src/asciidoc/core-resources.adoc index 82b43d0ccb8..0646c82fa73 100644 --- a/src/asciidoc/core-resources.adoc +++ b/src/asciidoc/core-resources.adoc @@ -492,7 +492,7 @@ When the path location contains an Ant-style pattern, for example: classpath:com/mycompany/**/applicationContext.xml ---- -... the resolver follows a more complex but defined procedure to try to resolve the +The resolver follows a more complex but defined procedure to try to resolve the wildcard. It produces a Resource for the path up to the last non-wildcard segment and obtains a URL from it. If this URL is not a `jar:` URL or container-specific variant (e.g. `zip:` in WebLogic, `wsjar` in WebSphere, etc.), then a `java.io.File` is From 81f6c22e5103acb02c8a44e431c07ba5a7b5a629 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Mon, 19 Sep 2016 09:01:25 -0400 Subject: [PATCH 0192/1274] Reset connection before delegating to handler Resetting the connection first before invoking a failure callback on the application handler ensures that any checks to isConnected will return false. Issue: SPR-14721 --- .../messaging/simp/stomp/DefaultStompSession.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/DefaultStompSession.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/DefaultStompSession.java index 44cb5f8e7ae..58f80396a67 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/DefaultStompSession.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/DefaultStompSession.java @@ -480,8 +480,8 @@ public void afterConnectionClosed() { logger.debug("Connection closed session id=" + this.sessionId); } if (!this.closing) { - handleFailure(new ConnectionLostException("Connection closed")); resetConnection(); + handleFailure(new ConnectionLostException("Connection closed")); } } @@ -671,8 +671,8 @@ public void run() { if (logger.isDebugEnabled()) { logger.debug(error); } - handleFailure(new IllegalStateException(error)); resetConnection(); + handleFailure(new IllegalStateException(error)); } } From 5934959b622ea176757001260cf686115e4ff965 Mon Sep 17 00:00:00 2001 From: Spring Buildmaster Date: Mon, 19 Sep 2016 15:11:46 +0000 Subject: [PATCH 0193/1274] Next Development Version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 99547c0fb89..061d390dfac 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=4.3.3.BUILD-SNAPSHOT +version=4.3.4.BUILD-SNAPSHOT From 11573b4ea4cc092e807060ba2ac02bbdbf337a55 Mon Sep 17 00:00:00 2001 From: Josh Long Date: Wed, 21 Sep 2016 06:24:06 +0200 Subject: [PATCH 0194/1274] fix spelling of word 'recommendation' this PR fixes the spelling error for the word 'recommendation' (cherry picked from commit 9b87ea0) --- .../org/springframework/http/HttpHeaders.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/HttpHeaders.java b/spring-web/src/main/java/org/springframework/http/HttpHeaders.java index a13b9bb6965..a658f08e9e8 100644 --- a/spring-web/src/main/java/org/springframework/http/HttpHeaders.java +++ b/spring-web/src/main/java/org/springframework/http/HttpHeaders.java @@ -59,6 +59,7 @@ * @author Sebastien Deleuze * @author Brian Clozel * @author Juergen Hoeller + * @author Josh Long * @since 3.0 */ public class HttpHeaders implements MultiValueMap, Serializable { @@ -92,42 +93,42 @@ public class HttpHeaders implements MultiValueMap, Serializable public static final String ACCEPT_RANGES = "Accept-Ranges"; /** * The CORS {@code Access-Control-Allow-Credentials} response header field name. - * @see CORS W3C recommandation + * @see CORS W3C recommendation */ public static final String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials"; /** * The CORS {@code Access-Control-Allow-Headers} response header field name. - * @see CORS W3C recommandation + * @see CORS W3C recommendation */ public static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers"; /** * The CORS {@code Access-Control-Allow-Methods} response header field name. - * @see CORS W3C recommandation + * @see CORS W3C recommendation */ public static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods"; /** * The CORS {@code Access-Control-Allow-Origin} response header field name. - * @see CORS W3C recommandation + * @see CORS W3C recommendation */ public static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin"; /** * The CORS {@code Access-Control-Expose-Headers} response header field name. - * @see CORS W3C recommandation + * @see CORS W3C recommendation */ public static final String ACCESS_CONTROL_EXPOSE_HEADERS = "Access-Control-Expose-Headers"; /** * The CORS {@code Access-Control-Max-Age} response header field name. - * @see CORS W3C recommandation + * @see CORS W3C recommendation */ public static final String ACCESS_CONTROL_MAX_AGE = "Access-Control-Max-Age"; /** * The CORS {@code Access-Control-Request-Headers} request header field name. - * @see CORS W3C recommandation + * @see CORS W3C recommendation */ public static final String ACCESS_CONTROL_REQUEST_HEADERS = "Access-Control-Request-Headers"; /** * The CORS {@code Access-Control-Request-Method} request header field name. - * @see CORS W3C recommandation + * @see CORS W3C recommendation */ public static final String ACCESS_CONTROL_REQUEST_METHOD = "Access-Control-Request-Method"; /** From 0bb2cfe440df51f773b9be56cc8f48e4da70faa4 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 22 Sep 2016 14:25:44 +0200 Subject: [PATCH 0195/1274] Latest dependency updates (Jackson 2.8.3, JavaMail 1.5.6, Jetty 9.3.12, Undertow 1.3.25) --- build.gradle | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index b18d894898d..ec4481934d2 100644 --- a/build.gradle +++ b/build.gradle @@ -52,10 +52,10 @@ configure(allprojects) { project -> ext.hsqldbVersion = "2.3.4" ext.httpasyncVersion = "4.1.2" ext.httpclientVersion = "4.5.2" - ext.jackson2Version = "2.8.2" + ext.jackson2Version = "2.8.3" ext.jasperreportsVersion = "6.2.1" // our tests fail with JR-internal NPEs against 6.2.2 and higher - ext.javamailVersion = "1.5.5" - ext.jettyVersion = "9.3.11.v20160721" + ext.javamailVersion = "1.5.6" + ext.jettyVersion = "9.3.12.v20160915" ext.jodaVersion = "2.9.4" ext.jrubyVersion = "1.7.25" // JRuby 9000 only supported through JSR-223 (StandardScriptFactory) ext.jtaVersion = "1.2" @@ -76,7 +76,7 @@ configure(allprojects) { project -> ext.tiles3Version = "3.0.5" ext.tomcatVersion = "8.5.5" ext.tyrusVersion = "1.3.5" // constrained by WebLogic 12.1.3 support - ext.undertowVersion = "1.3.24.Final" + ext.undertowVersion = "1.3.25.Final" ext.xmlunitVersion = "1.6" ext.xstreamVersion = "1.4.9" From 3346c594e4367f117ede1aa05f9b988c2ddc7ec9 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sun, 25 Sep 2016 21:05:40 +0200 Subject: [PATCH 0196/1274] YamlPropertiesFactoryBean consistently exposes String values Issue: SPR-14737 (cherry picked from commit 74c6188) --- .../factory/config/YamlMapFactoryBean.java | 23 +++++---- .../beans/factory/config/YamlProcessor.java | 18 ++++--- .../config/YamlPropertiesFactoryBean.java | 29 +++++++---- .../config/YamlMapFactoryBeanTests.java | 51 +++++++++++-------- .../factory/config/YamlProcessorTests.java | 42 +++++++-------- .../YamlPropertiesFactoryBeanTests.java | 42 ++++++++------- .../core/CollectionFactory.java | 25 +++++++-- .../springframework/util/CollectionUtils.java | 8 +-- 8 files changed, 146 insertions(+), 92 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlMapFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlMapFactoryBean.java index 7428f997f93..755819a10a0 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlMapFactoryBean.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlMapFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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,12 +25,16 @@ import org.springframework.beans.factory.InitializingBean; /** - * Factory for a Map that reads from a YAML source. YAML is a nice human-readable - * format for configuration, and it has some useful hierarchical properties. It's - * more or less a superset of JSON, so it has a lot of similar features. If - * multiple resources are provided the later ones will override entries in the - * earlier ones hierarchically - that is all entries with the same nested key of - * type Map at any depth are merged. For example: + * Factory for a {@code Map} that reads from a YAML source, preserving the + * YAML-declared value types and their structure. + * + *

    YAML is a nice human-readable format for configuration, and it has some + * useful hierarchical properties. It's more or less a superset of JSON, so it + * has a lot of similar features. + * + *

    If multiple resources are provided the later ones will override entries in + * the earlier ones hierarchically; that is, all entries with the same nested key + * of type {@code Map} at any depth are merged. For example: * *

      * foo:
    @@ -62,6 +66,7 @@
      * with the value in the second, but its nested values are merged.
      *
      * @author Dave Syer
    + * @author Juergen Hoeller
      * @since 4.1
      */
     public class YamlMapFactoryBean extends YamlProcessor implements FactoryBean>, InitializingBean {
    @@ -104,10 +109,10 @@ public Class getObjectType() {
     
     	/**
     	 * Template method that subclasses may override to construct the object
    -	 * returned by this factory. The default implementation returns the
    -	 * merged Map instance.
    +	 * returned by this factory.
     	 * 

    Invoked lazily the first time {@link #getObject()} is invoked in * case of a shared singleton; else, on each {@link #getObject()} call. + *

    The default implementation returns the merged {@code Map} instance. * @return the object returned by this factory * @see #process(java.util.Map, MatchCallback) */ diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlProcessor.java index ac01eb74b7a..4064bf9f53e 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,6 +37,7 @@ import org.yaml.snakeyaml.parser.ParserException; import org.yaml.snakeyaml.reader.UnicodeReader; +import org.springframework.core.CollectionFactory; import org.springframework.core.io.Resource; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -45,6 +46,7 @@ * Base class for YAML factories. * * @author Dave Syer + * @author Juergen Hoeller * @since 4.1 */ public abstract class YamlProcessor { @@ -217,7 +219,7 @@ private Map asMap(Object object) { } private boolean process(Map map, MatchCallback callback) { - Properties properties = new Properties(); + Properties properties = CollectionFactory.createStringAdaptingProperties(); properties.putAll(getFlattenedMap(map)); if (this.documentMatchers.isEmpty()) { @@ -302,21 +304,23 @@ else if (value instanceof Collection) { } } else { - result.put(key, value != null ? value : ""); + result.put(key, (value != null ? value : "")); } } } /** - * Callback interface used to process properties in a resulting map. + * Callback interface used to process the YAML parsing results. */ public interface MatchCallback { /** - * Process the properties. - * @param properties the properties to process - * @param map a mutable result map + * Process the given representation of the parsing results. + * @param properties the properties to process (as a flattened + * representation with indexed keys in case of a collection or map) + * @param map the result map (preserving the original value structure + * in the YAML document) */ void process(Properties properties, Map map); } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlPropertiesFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlPropertiesFactoryBean.java index 0374ddbf550..951ef3183b7 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlPropertiesFactoryBean.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlPropertiesFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,13 +21,23 @@ import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; +import org.springframework.core.CollectionFactory; /** - * Factory for Java Properties that reads from a YAML source. YAML is a nice - * human-readable format for configuration, and it has some useful hierarchical - * properties. It's more or less a superset of JSON, so it has a lot of similar - * features. The Properties created by this factory have nested paths for - * hierarchical objects, so for instance this YAML + * Factory for {@link java.util.Properties} that reads from a YAML source, + * exposing a flat structure of String property values. + * + *

    YAML is a nice human-readable format for configuration, and it has some + * useful hierarchical properties. It's more or less a superset of JSON, so it + * has a lot of similar features. + * + *

    Note: All exposed values are of type {@code String} for access through + * the common {@link Properties#getProperty} method (e.g. in configuration property + * resolution through {@link PropertyResourceConfigurer#setProperties(Properties)}). + * If this is not desirable, use {@link YamlMapFactoryBean} instead. + * + *

    The Properties created by this factory have nested paths for hierarchical + * objects, so for instance this YAML * *

      * environments:
    @@ -39,7 +49,7 @@
      *     name: My Cool App
      * 
    * - * is transformed into these Properties: + * is transformed into these properties: * *
      * environments.dev.url=http://dev.bar.com
    @@ -57,7 +67,7 @@
      * - foo.bar.com
      * 
    * - * becomes Java Properties like this: + * becomes properties like this: * *
      * servers[0]=dev.bar.com
    @@ -66,6 +76,7 @@
      *
      * @author Dave Syer
      * @author Stephane Nicoll
    + * @author Juergen Hoeller
      * @since 4.1
      */
     public class YamlPropertiesFactoryBean extends YamlProcessor implements FactoryBean, InitializingBean {
    @@ -116,7 +127,7 @@ public Class getObjectType() {
     	 * @see #process(MatchCallback) ()
     	 */
     	protected Properties createProperties() {
    -		final Properties result = new Properties();
    +		final Properties result = CollectionFactory.createStringAdaptingProperties();
     		process(new MatchCallback() {
     			@Override
     			public void process(Properties properties, Map map) {
    diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/config/YamlMapFactoryBeanTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/config/YamlMapFactoryBeanTests.java
    index 313b547b457..bd4f968c8f2 100644
    --- a/spring-beans/src/test/java/org/springframework/beans/factory/config/YamlMapFactoryBeanTests.java
    +++ b/spring-beans/src/test/java/org/springframework/beans/factory/config/YamlMapFactoryBeanTests.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright 2002-2014 the original author or authors.
    + * Copyright 2002-2016 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,43 +34,40 @@
      * Tests for {@link YamlMapFactoryBean}.
      *
      * @author Dave Syer
    + * @author Juergen Hoeller
      */
     public class YamlMapFactoryBeanTests {
     
     	private final YamlMapFactoryBean factory = new YamlMapFactoryBean();
     
    +
     	@Test
     	public void testSetIgnoreResourceNotFound() throws Exception {
    -		this.factory
    -				.setResolutionMethod(YamlMapFactoryBean.ResolutionMethod.OVERRIDE_AND_IGNORE);
    -		this.factory.setResources(new FileSystemResource[] {new FileSystemResource(
    -				"non-exsitent-file.yml")});
    +		this.factory.setResolutionMethod(YamlMapFactoryBean.ResolutionMethod.OVERRIDE_AND_IGNORE);
    +		this.factory.setResources(new FileSystemResource("non-exsitent-file.yml"));
     		assertEquals(0, this.factory.getObject().size());
     	}
     
     	@Test(expected = IllegalStateException.class)
     	public void testSetBarfOnResourceNotFound() throws Exception {
    -		this.factory.setResources(new FileSystemResource[] {new FileSystemResource(
    -				"non-exsitent-file.yml")});
    +		this.factory.setResources(new FileSystemResource("non-exsitent-file.yml"));
     		assertEquals(0, this.factory.getObject().size());
     	}
     
     	@Test
     	public void testGetObject() throws Exception {
    -		this.factory.setResources(new ByteArrayResource[] {new ByteArrayResource(
    -				"foo: bar".getBytes())});
    +		this.factory.setResources(new ByteArrayResource("foo: bar".getBytes()));
     		assertEquals(1, this.factory.getObject().size());
     	}
     
     	@SuppressWarnings("unchecked")
     	@Test
    -	public void testOverrideAndremoveDefaults() throws Exception {
    -		this.factory.setResources(new ByteArrayResource[] {
    -				new ByteArrayResource("foo:\n  bar: spam".getBytes()),
    -				new ByteArrayResource("foo:\n  spam: bar".getBytes())});
    +	public void testOverrideAndRemoveDefaults() throws Exception {
    +		this.factory.setResources(new ByteArrayResource("foo:\n  bar: spam".getBytes()),
    +				new ByteArrayResource("foo:\n  spam: bar".getBytes()));
    +
     		assertEquals(1, this.factory.getObject().size());
    -		assertEquals(2,
    -				((Map) this.factory.getObject().get("foo")).size());
    +		assertEquals(2, ((Map) this.factory.getObject().get("foo")).size());
     	}
     
     	@Test
    @@ -81,20 +78,20 @@ public void testFirstFound() throws Exception {
     			public String getDescription() {
     				return "non-existent";
     			}
    -
     			@Override
     			public InputStream getInputStream() throws IOException {
     				throw new IOException("planned");
     			}
     		}, new ByteArrayResource("foo:\n  spam: bar".getBytes()));
    +
     		assertEquals(1, this.factory.getObject().size());
     	}
     
     	@Test
     	public void testMapWithPeriodsInKey() throws Exception {
    -		this.factory.setResources(new ByteArrayResource[] {new ByteArrayResource(
    -				"foo:\n  ? key1.key2\n  : value".getBytes())});
    +		this.factory.setResources(new ByteArrayResource("foo:\n  ? key1.key2\n  : value".getBytes()));
     		Map map = this.factory.getObject();
    +
     		assertEquals(1, map.size());
     		assertTrue(map.containsKey("foo"));
     		Object object = map.get("foo");
    @@ -105,10 +102,24 @@ public void testMapWithPeriodsInKey() throws Exception {
     		assertEquals("value", sub.get("key1.key2"));
     	}
     
    +	@Test
    +	public void testMapWithIntegerValue() throws Exception {
    +		this.factory.setResources(new ByteArrayResource("foo:\n  ? key1.key2\n  : 3".getBytes()));
    +		Map map = this.factory.getObject();
    +
    +		assertEquals(1, map.size());
    +		assertTrue(map.containsKey("foo"));
    +		Object object = map.get("foo");
    +		assertTrue(object instanceof LinkedHashMap);
    +		@SuppressWarnings("unchecked")
    +		Map sub = (Map) object;
    +		assertTrue(sub.containsKey("key1.key2"));
    +		assertEquals(Integer.valueOf(3), sub.get("key1.key2"));
    +	}
    +
     	@Test(expected = ParserException.class)
     	public void testDuplicateKey() throws Exception {
    -		this.factory.setResources(new ByteArrayResource[] {new ByteArrayResource(
    -				"mymap:\n  foo: bar\nmymap:\n  bar: foo".getBytes())});
    +		this.factory.setResources(new ByteArrayResource("mymap:\n  foo: bar\nmymap:\n  bar: foo".getBytes()));
     		this.factory.getObject().get("mymap");
     	}
     
    diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/config/YamlProcessorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/config/YamlProcessorTests.java
    index fd90fbeb666..51740c74184 100644
    --- a/spring-beans/src/test/java/org/springframework/beans/factory/config/YamlProcessorTests.java
    +++ b/spring-beans/src/test/java/org/springframework/beans/factory/config/YamlProcessorTests.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright 2002-2014 the original author or authors.
    + * Copyright 2002-2016 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.
    @@ -13,6 +13,7 @@
      * See the License for the specific language governing permissions and
      * limitations under the License.
      */
    +
     package org.springframework.beans.factory.config;
     
     import java.util.LinkedHashMap;
    @@ -24,6 +25,7 @@
     import org.junit.rules.ExpectedException;
     import org.yaml.snakeyaml.parser.ParserException;
     import org.yaml.snakeyaml.scanner.ScannerException;
    +
     import org.springframework.core.io.ByteArrayResource;
     
     import static org.junit.Assert.*;
    @@ -33,34 +35,38 @@
      * Tests for {@link YamlProcessor}.
      *
      * @author Dave Syer
    + * @author Juergen Hoeller
      */
     public class YamlProcessorTests {
     
    -	private final YamlProcessor processor = new YamlProcessor() {
    -	};
    +	private final YamlProcessor processor = new YamlProcessor() {};
     
     	@Rule
     	public ExpectedException exception = ExpectedException.none();
     
    +
     	@Test
     	public void arrayConvertedToIndexedBeanReference() {
    -		this.processor.setResources(new ByteArrayResource(
    -				"foo: bar\nbar: [1,2,3]".getBytes()));
    +		this.processor.setResources(new ByteArrayResource("foo: bar\nbar: [1,2,3]".getBytes()));
     		this.processor.process(new MatchCallback() {
     			@Override
     			public void process(Properties properties, Map map) {
    +				assertEquals(4, properties.size());
    +				assertEquals("bar", properties.get("foo"));
    +				assertEquals("bar", properties.getProperty("foo"));
     				assertEquals(1, properties.get("bar[0]"));
    +				assertEquals("1", properties.getProperty("bar[0]"));
     				assertEquals(2, properties.get("bar[1]"));
    +				assertEquals("2", properties.getProperty("bar[1]"));
     				assertEquals(3, properties.get("bar[2]"));
    -				assertEquals(4, properties.size());
    +				assertEquals("3", properties.getProperty("bar[2]"));
     			}
     		});
     	}
     
     	@Test
     	public void testStringResource() throws Exception {
    -		this.processor.setResources(new ByteArrayResource(
    -				"foo # a document that is a literal".getBytes()));
    +		this.processor.setResources(new ByteArrayResource("foo # a document that is a literal".getBytes()));
     		this.processor.process(new MatchCallback() {
     			@Override
     			public void process(Properties properties, Map map) {
    @@ -71,8 +77,7 @@ public void process(Properties properties, Map map) {
     
     	@Test
     	public void testBadDocumentStart() throws Exception {
    -		this.processor.setResources(new ByteArrayResource(
    -				"foo # a document\nbar: baz".getBytes()));
    +		this.processor.setResources(new ByteArrayResource("foo # a document\nbar: baz".getBytes()));
     		this.exception.expect(ParserException.class);
     		this.exception.expectMessage("line 2, column 1");
     		this.processor.process(new MatchCallback() {
    @@ -84,8 +89,7 @@ public void process(Properties properties, Map map) {
     
     	@Test
     	public void testBadResource() throws Exception {
    -		this.processor.setResources(new ByteArrayResource(
    -				"foo: bar\ncd\nspam:\n  foo: baz".getBytes()));
    +		this.processor.setResources(new ByteArrayResource("foo: bar\ncd\nspam:\n  foo: baz".getBytes()));
     		this.exception.expect(ScannerException.class);
     		this.exception.expectMessage("line 3, column 1");
     		this.processor.process(new MatchCallback() {
    @@ -97,8 +101,7 @@ public void process(Properties properties, Map map) {
     
     	@Test
     	public void mapConvertedToIndexedBeanReference() {
    -		this.processor.setResources(new ByteArrayResource(
    -				"foo: bar\nbar:\n spam: bucket".getBytes()));
    +		this.processor.setResources(new ByteArrayResource("foo: bar\nbar:\n spam: bucket".getBytes()));
     		this.processor.process(new MatchCallback() {
     			@Override
     			public void process(Properties properties, Map map) {
    @@ -111,8 +114,7 @@ public void process(Properties properties, Map map) {
     
     	@Test
     	public void integerKeyBehaves() {
    -		this.processor.setResources(new ByteArrayResource(
    -				"foo: bar\n1: bar".getBytes()));
    +		this.processor.setResources(new ByteArrayResource("foo: bar\n1: bar".getBytes()));
     		this.processor.process(new MatchCallback() {
     			@Override
     			public void process(Properties properties, Map map) {
    @@ -124,10 +126,8 @@ public void process(Properties properties, Map map) {
     
     	@Test
     	public void integerDeepKeyBehaves() {
    -		this.processor.setResources(new ByteArrayResource(
    -				"foo:\n  1: bar".getBytes()));
    +		this.processor.setResources(new ByteArrayResource("foo:\n  1: bar".getBytes()));
     		this.processor.process(new MatchCallback() {
    -
     			@Override
     			public void process(Properties properties, Map map) {
     				assertEquals("bar", properties.get("foo[1]"));
    @@ -139,8 +139,7 @@ public void process(Properties properties, Map map) {
     	@Test
     	@SuppressWarnings("unchecked")
     	public void flattenedMapIsSameAsPropertiesButOrdered() {
    -		this.processor.setResources(new ByteArrayResource(
    -				"foo: bar\nbar:\n spam: bucket".getBytes()));
    +		this.processor.setResources(new ByteArrayResource("foo: bar\nbar:\n spam: bucket".getBytes()));
     		this.processor.process(new MatchCallback() {
     			@Override
     			public void process(Properties properties, Map map) {
    @@ -155,4 +154,5 @@ public void process(Properties properties, Map map) {
     			}
     		});
     	}
    +
     }
    diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/config/YamlPropertiesFactoryBeanTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/config/YamlPropertiesFactoryBeanTests.java
    index 855e479a641..0bfa5127776 100644
    --- a/spring-beans/src/test/java/org/springframework/beans/factory/config/YamlPropertiesFactoryBeanTests.java
    +++ b/spring-beans/src/test/java/org/springframework/beans/factory/config/YamlPropertiesFactoryBeanTests.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright 2002-2015 the original author or authors.
    + * Copyright 2002-2016 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.
    @@ -38,12 +38,14 @@
      * Tests for {@link YamlPropertiesFactoryBean}.
      *
      * @author Dave Syer
    + * @author Juergen Hoeller
      */
     public class YamlPropertiesFactoryBeanTests {
     
     	@Rule
     	public ExpectedException exception = ExpectedException.none();
     
    +
     	@Test
     	public void testLoadResource() throws Exception {
     		YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
    @@ -113,8 +115,8 @@ public void testLoadResourceWithSelectedDocuments() throws Exception {
     		factory.setDocumentMatchers(new DocumentMatcher() {
     			@Override
     			public MatchStatus matches(Properties properties) {
    -				return "bag".equals(properties.getProperty("foo")) ? MatchStatus.FOUND
    -						: MatchStatus.NOT_FOUND;
    +				return ("bag".equals(properties.getProperty("foo")) ?
    +						MatchStatus.FOUND : MatchStatus.NOT_FOUND);
     			}
     		});
     		Properties properties = factory.getObject();
    @@ -134,8 +136,8 @@ public MatchStatus matches(Properties properties) {
     				if (!properties.containsKey("foo")) {
     					return MatchStatus.ABSTAIN;
     				}
    -				return "bag".equals(properties.getProperty("foo")) ? MatchStatus.FOUND
    -						: MatchStatus.NOT_FOUND;
    +				return ("bag".equals(properties.getProperty("foo")) ?
    +						MatchStatus.FOUND : MatchStatus.NOT_FOUND);
     			}
     		});
     		Properties properties = factory.getObject();
    @@ -156,8 +158,8 @@ public MatchStatus matches(Properties properties) {
     				if (!properties.containsKey("foo")) {
     					return MatchStatus.ABSTAIN;
     				}
    -				return "bag".equals(properties.getProperty("foo")) ? MatchStatus.FOUND
    -						: MatchStatus.NOT_FOUND;
    +				return ("bag".equals(properties.getProperty("foo")) ?
    +						MatchStatus.FOUND : MatchStatus.NOT_FOUND);
     			}
     		});
     		Properties properties = factory.getObject();
    @@ -178,8 +180,8 @@ public MatchStatus matches(Properties properties) {
     				if (!properties.containsKey("foo")) {
     					return MatchStatus.ABSTAIN;
     				}
    -				return "bag".equals(properties.getProperty("foo")) ? MatchStatus.FOUND
    -						: MatchStatus.NOT_FOUND;
    +				return ("bag".equals(properties.getProperty("foo")) ?
    +						MatchStatus.FOUND : MatchStatus.NOT_FOUND);
     			}
     		});
     		Properties properties = factory.getObject();
    @@ -200,8 +202,7 @@ public void testLoadNonExistentResource() throws Exception {
     	@Test
     	public void testLoadNull() throws Exception {
     		YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
    -		factory.setResources(new ByteArrayResource("foo: bar\nspam:"
    -				.getBytes()));
    +		factory.setResources(new ByteArrayResource("foo: bar\nspam:".getBytes()));
     		Properties properties = factory.getObject();
     		assertThat(properties.getProperty("foo"), equalTo("bar"));
     		assertThat(properties.getProperty("spam"), equalTo(""));
    @@ -210,20 +211,28 @@ public void testLoadNull() throws Exception {
     	@Test
     	public void testLoadArrayOfString() throws Exception {
     		YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
    -		factory.setResources(new ByteArrayResource("foo:\n- bar\n- baz"
    -				.getBytes()));
    +		factory.setResources(new ByteArrayResource("foo:\n- bar\n- baz".getBytes()));
     		Properties properties = factory.getObject();
     		assertThat(properties.getProperty("foo[0]"), equalTo("bar"));
     		assertThat(properties.getProperty("foo[1]"), equalTo("baz"));
     		assertThat(properties.get("foo"), is(nullValue()));
     	}
     
    +	@Test
    +	public void testLoadArrayOfInteger() throws Exception {
    +		YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
    +		factory.setResources(new ByteArrayResource("foo:\n- 1\n- 2".getBytes()));
    +		Properties properties = factory.getObject();
    +		assertThat(properties.getProperty("foo[0]"), equalTo("1"));
    +		assertThat(properties.getProperty("foo[1]"), equalTo("2"));
    +		assertThat(properties.get("foo"), is(nullValue()));
    +	}
    +
     	@Test
     	public void testLoadArrayOfObject() throws Exception {
     		YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
     		factory.setResources(new ByteArrayResource(
    -				"foo:\n- bar:\n    spam: crap\n- baz\n- one: two\n  three: four"
    -						.getBytes()
    +				"foo:\n- bar:\n    spam: crap\n- baz\n- one: two\n  three: four".getBytes()
     		));
     		Properties properties = factory.getObject();
     		assertThat(properties.getProperty("foo[0].bar.spam"), equalTo("crap"));
    @@ -239,8 +248,7 @@ public void testYaml() {
     		Yaml yaml = new Yaml();
     		Map map = yaml.loadAs("foo: bar\nspam:\n  foo: baz", Map.class);
     		assertThat(map.get("foo"), equalTo((Object) "bar"));
    -		assertThat(((Map) map.get("spam")).get("foo"),
    -				equalTo((Object) "baz"));
    +		assertThat(((Map) map.get("spam")).get("foo"), equalTo((Object) "baz"));
     	}
     
     }
    diff --git a/spring-core/src/main/java/org/springframework/core/CollectionFactory.java b/spring-core/src/main/java/org/springframework/core/CollectionFactory.java
    index 0a83068ce7b..50c3f0e8ad2 100644
    --- a/spring-core/src/main/java/org/springframework/core/CollectionFactory.java
    +++ b/spring-core/src/main/java/org/springframework/core/CollectionFactory.java
    @@ -29,6 +29,7 @@
     import java.util.Map;
     import java.util.NavigableMap;
     import java.util.NavigableSet;
    +import java.util.Properties;
     import java.util.Set;
     import java.util.SortedMap;
     import java.util.SortedSet;
    @@ -40,12 +41,9 @@
     import org.springframework.util.MultiValueMap;
     
     /**
    - * Factory for collections that is aware of Java 5, Java 6, and Spring
    - * collection types.
    + * Factory for collections that is aware of Java 5, Java 6, and Spring collection types.
    + *
      * 

    Mainly for internal use within the framework. - *

    The goal of this class is to avoid runtime dependencies on a specific - * Java version, while nevertheless using the best collection implementation - * that is available at runtime. * * @author Juergen Hoeller * @author Arjen Poutsma @@ -324,6 +322,23 @@ else if (EnumMap.class == mapType) { } } + /** + * Create a variant of {@code java.util.Properties} that automatically adapts + * non-String values to String representations on {@link Properties#getProperty}. + * @return a new {@code Properties} instance + * @since 4.3.4 + */ + @SuppressWarnings("serial") + public static Properties createStringAdaptingProperties() { + return new Properties() { + @Override + public String getProperty(String key) { + Object value = get(key); + return (value != null ? value.toString() : null); + } + }; + } + /** * Cast the given type to a subtype of {@link Enum}. * @param enumType the enum type, never {@code null} diff --git a/spring-core/src/main/java/org/springframework/util/CollectionUtils.java b/spring-core/src/main/java/org/springframework/util/CollectionUtils.java index ae6d8be36e0..8edb77e9d59 100644 --- a/spring-core/src/main/java/org/springframework/util/CollectionUtils.java +++ b/spring-core/src/main/java/org/springframework/util/CollectionUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -110,10 +110,10 @@ public static void mergePropertiesIntoMap(Properties props, Map map if (props != null) { for (Enumeration en = props.propertyNames(); en.hasMoreElements();) { String key = (String) en.nextElement(); - Object value = props.getProperty(key); + Object value = props.get(key); if (value == null) { - // Potentially a non-String value... - value = props.get(key); + // Allow for defaults fallback or potentially overridden accessor... + value = props.getProperty(key); } map.put((K) key, (V) value); } From acdf139137acce1f6a4c323db0a5aa8919165a50 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 26 Sep 2016 15:15:45 +0200 Subject: [PATCH 0197/1274] DefaultPersistenceUnitManager extracts jar file from default persistence unit root URL Issue: SPR-14749 (cherry picked from commit bb7d207) --- .../jpa/persistenceunit/DefaultPersistenceUnitManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/DefaultPersistenceUnitManager.java b/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/DefaultPersistenceUnitManager.java index a8a11cbb84f..089274d0968 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/DefaultPersistenceUnitManager.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/DefaultPersistenceUnitManager.java @@ -598,8 +598,8 @@ private URL determineDefaultPersistenceUnitRootUrl() { return null; } try { - Resource res = this.resourcePatternResolver.getResource(this.defaultPersistenceUnitRootLocation); - return res.getURL(); + URL url = this.resourcePatternResolver.getResource(this.defaultPersistenceUnitRootLocation).getURL(); + return (ResourceUtils.isJarURL(url) ? ResourceUtils.extractJarFileURL(url) : url); } catch (IOException ex) { throw new PersistenceException("Unable to resolve persistence unit root URL", ex); From 49929f1e56042af6f0ebc6faf1a29f6417cd4ada Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 26 Sep 2016 15:27:01 +0200 Subject: [PATCH 0198/1274] Clarified that getResource never returns null (cherry picked from commit 36f7c7a) --- .../org/springframework/core/io/ResourceLoader.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/io/ResourceLoader.java b/spring-core/src/main/java/org/springframework/core/io/ResourceLoader.java index b753535ab01..1ca7e584c97 100644 --- a/spring-core/src/main/java/org/springframework/core/io/ResourceLoader.java +++ b/spring-core/src/main/java/org/springframework/core/io/ResourceLoader.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -45,8 +45,8 @@ public interface ResourceLoader { /** - * Return a Resource handle for the specified resource. - * The handle should always be a reusable resource descriptor, + * Return a Resource handle for the specified resource location. + *

    The handle should always be a reusable resource descriptor, * allowing for multiple {@link Resource#getInputStream()} calls. *