From d7eadd41d69143a71e0467a5b2e073d8537662d4 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Mon, 4 Jul 2016 12:28:13 +0200 Subject: [PATCH 001/505] 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 3e8083473ed3..b61eae0b3b5f 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 002/505] 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 78f1c77c8979..57d964cffb65 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 003/505] 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 9042bad5cbfa..d3d21347023f 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 004/505] 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 06537de1df3d..601d389ae0af 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 005/505] 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 85b1d2606263..314f67a6f421 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 7fa2b8726910..c08fd7443f20 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 9928530dbd48..908bfeafe660 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 6205fa69cfe3..9cc3c907ea49 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 d2ac971cbc9f..91ad5d8e96cf 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 4291b2deae81..edb951a4ce5e 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 f99b40d756e4..f7f6fa4ec7c6 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 4ea5ccad0aa8..d9f90dc2f148 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 8901ba1945cf..3529438d7844 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 780f8be00b65..4c16bb0e64c0 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 9ab27884430f..10d7c7ac1caf 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 58a9b4027b4f..000000000000 --- 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 5a2b2df765d0..804c26e74ba8 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 dd5f180180d2..ffbbd7ab0d0f 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 17c48614b83d..f10b60d62f09 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 8de0b513cdc7..6251c7aa9683 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 37890be94f2b..90bfa4a7e8dc 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 f42a88f4af49..1a6cd14fbdc5 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 37aac9310ebb..2a36c9e6042d 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 79dd4f07ddb3..fda1f06dba58 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 2d20e195d159..decb2f1cb43b 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 b4134b6351e6..1a435b861d95 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 3af9f4635a98..3d7c25f1d3d7 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 c5d15e78adde..417423507fca 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 0e1dd15ceb50..645a2a9785e3 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 50c614092527..19a9ffc041a7 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 fe448aa2bc91..5beb87333ea8 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 0767c15fb1f0..5d9bc44b53f9 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 c791d289eec6..fbd1d5adc609 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 2d27db7d2b74..84d29f92961c 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 3f2524e81b7d..000000000000 --- 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 f10f2a174eeb..be6d9cf672f4 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 a3bac0ce6123..46f6ae689de6 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 37f532b39bad..67b448a90641 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 9595d8087e84..4a6a46ba79f9 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 c4b2a11ea4d7..47e488cf29dd 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 8e3e9a05c8d2..e0e1bc0e6569 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 df6e10c6f0e0..2fe64f528743 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 beee0a4f0f6d..12cee3308a25 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 08c47e614c86..8746786f1448 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 94b40ae979fd..ea3c0ae844c3 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 b29397f21405..97a227af1128 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 4356d14603d2..305b639642f2 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 6f8e803674f8..e49eeafbfb6b 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 80998d0f6e75..2d0f95402298 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 397c179b0397..44376cbd6aa6 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 602886ba297c..b00d46898a1a 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 7ec8e7c0f929..c3c7b1d01422 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 6361c77ff135..789606dc6373 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 3a3706f06baf..d4a8fe62bc25 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 4380246e8438..fa884f7a496a 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 fe941d830bf1..de17faf1b5dd 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 ca2df2ca23f2..79d50c93a6d7 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 2dbd8157064a..faa32b6911d9 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 bbe55538fcf7..048cd3a03b8c 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 0e6a31df0d00..35886efc7243 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 c16d747a802e..58b294077fee 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 7b7f51d22d72..2d0fac299e90 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 e3cb44882ee0..d8066c2e06d7 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 7067ef25db0d..2309f74f853b 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 1d86998daedb..813b7b5fbf8b 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 006/505] 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 314f67a6f421..52e3b8594e8b 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 007/505] 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 47e488cf29dd..98e9c936533d 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 1324c2190265..b1d7ddfdbb18 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 12cee3308a25..f0ee1c89ee07 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 008/505] 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 33598520741b..289796477e00 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 009/505] 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 faa56c309912..c27f59e1a905 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 dc65b678271d..9489b84775ba 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 010/505] 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 7a76f3f1aaba..b009d5e02bfe 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 011/505] 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 2d050218ec13..5e91704bbe75 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 012/505] 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 b6d783b2d7dd..515c5dc6b679 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 4c15698993e1..12bf96401645 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 013/505] 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 515c5dc6b679..6c5645350467 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 12bf96401645..2711b006ba29 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 014/505] 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 b009d5e02bfe..8af1139edaff 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 015/505] 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 fc0bb983235e..9bd4ac5c5095 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 b6bf3f3479bc..72b9c6d0fad3 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 f200e5a4d8e6..c892d266333a 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 016/505] 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 f112d4462c51..3bc0efd3d565 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 b3183c6e17ff..5631f2ca6e15 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 e1ec8a2732d2..bc626d643101 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 1ba39880035e..b47011643603 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 ac68dacec6f6..9cb3d5392866 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 3369883e7fe1..cc2dc56e8c95 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 90f875b26b16..5ef464d8c9ef 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 5a4f76f9abd9..20cd57e0d5a1 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 017/505] 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 6251c7aa9683..36b1a583c8a4 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 6842bfee1d15..5db3ea51b3a2 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 018/505] 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 f42d6c332ecb..71825c38cffa 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 a57b69eaede2..676fd0c6434d 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 b4b014b735bf..dc9823935aa0 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 000000000000..4767b1985486 --- /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 826760eef04e..894d97709eeb 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 f5ac6d6a487e..7dc173a0d260 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 c5f9755d85af..85ef824d12fe 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 06d2ee5d6886..8cffcc782c7d 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 17f6d9840347..ae6a5c6838aa 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 5073e5d55704..6e90e92a53fd 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 000000000000..440a3e38c737 --- /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 2729922ad9cc..d4e19b8bae86 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 a347cfa86d39..4eb6245dd4c6 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 36738e1b2123..3259ec47d66a 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 019/505] 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 078712179a17..216e5a4aabdc 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 00068ea4b609..c7faf35524e3 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 dc60afb46227..75a383869254 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 bc8a42f71b6f..85faab006b05 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 93a97a8dcf20..adc415d0d576 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 cc5c911c01e4..58b5481461a9 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 5a4d05a7fb0e..48d8eb7ffb69 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 b9531906a4f8..e55ba0d61e94 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 981db621238d..e198d10bc9fe 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 4815b8c3955a..c481fd1b219e 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 020/505] 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 ac4f3059c4af..0a042f4384a6 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 a9cf31bb3eeb..6ecc7687b9b3 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 c547b3e8f629..b4abdc169857 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 471ae154613b..901d0c5eac64 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 9ee0c5a8b70a..f39e02ff22bb 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 021/505] 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 266cdfb78ea2..65a8db26116b 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 022/505] 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 39c2bf78ea3c..968eab46ca2b 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 023/505] 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 690a7e57267e..253e4bb3899e 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 024/505] 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 a088205d438d..c7b356fd1378 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 8c558a224565..07b7537c0e45 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 025/505] 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 4c3141c42b25..38cbe0451ab4 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 a09429fb62bd..7d350e8e7a37 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 026/505] 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 d7d86e96701f..ddbf312a08a4 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 18277312714d..247c5838db73 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 9031872e5b02..522acf33cb9f 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 ffed74571f92..632c40f07b25 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 5bebb953b601..8c96aacd42b9 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 0febd916c241..bc44e15ff5e6 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 86ca194bce88..7fd8dd4263d1 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 395d2dba309d..689e4b31cf88 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 805c769b7830..dfcf28ad1729 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 15ca984e81b9..a2491fe5f41b 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 c8614eb575ed..27e14c43f03a 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 2cc12717b2f1..bd42e059d34b 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 ab001b50ecbd..f35ecd6881a0 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 eea3d8258063..3b3a17dd60e2 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 0d7ba35f2596..93c3e76d5d95 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 00bd9cb1303b..9dfcfd1a82d6 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 3c5bd9a43f68..9c466b777d69 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 0d2176f015eb..ff7b92fba5f8 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 57cdd7ab2eb4..99818400e672 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 5e4083e0fff4..2f377fc0f58f 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 027/505] 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 5e4886f2f8f6..38070d625a09 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 028/505] 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 601d389ae0af..83b43d4af368 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 cafee1046589..746d70c24d28 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 f297f36066f1..7f3d2e28a787 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 b421a4f3740b..1c370a00bd1e 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 09239e5663eb..b806672064b7 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 029/505] 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 305b639642f2..42248276181a 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 030/505] 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 c892d266333a..3735a8d36236 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 031/505] 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 632c40f07b25..673ee5c8a1b1 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 032/505] 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 bc44e15ff5e6..32b8725c2d65 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 689e4b31cf88..97af36d5dee2 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 7ea2c1cc14c9..750b1ca46dd0 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 dfcf28ad1729..0ea2cf0013b1 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 033/505] 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 019952712b82..fe0f79809209 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 2343aef442a9..7b361385ac0c 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 12d9d8d8bb97..965af1588045 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 69bd0c1aca15..3d438200c740 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 82acd2c3bda1..4fc0545b1516 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 81030d3bf2be..8c82eefdd588 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 cb43aa9e042d..621094562691 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 038667f4b674..5e5810e5eac4 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 d449b45d71b4..4091a9b93a13 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 9fae8679613e..df008b774b48 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 034/505] 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 863059a4e53a..4f70016a12dc 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 1a66397b7b4a..f4aa0460480a 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 035/505] 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 52e3b8594e8b..2990386617c3 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 036/505] 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 0543ac917acf..2eb4b3ce48ba 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 037/505] 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 72b9c6d0fad3..65f5bdf77ef8 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 038/505] Upgrade to EJB 3.1 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 2990386617c3..5c7a166def5b 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 039/505] 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 5c7a166def5b..2990386617c3 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 040/505] 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 ee8f505e5166..c922297fd35b 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 041/505] 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 546aca469899..2d3dd80e2f2a 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 c156cbd6bb91..866b37d1ad68 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 042/505] 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 ea188badfa42..83913f02b7ae 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 043/505] 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 866b37d1ad68..2d7d789a83ef 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 044/505] 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 d345263046f0..f74fb8ad7e0e 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 045/505] 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 99818400e672..5908bf3693d2 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 53327096962d..c8d5170cdd21 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 046/505] 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 d2af62814c4b..dbbfd5c1e05b 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 ed32b628ad87..5a90e703910d 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 908653e3fd03..719502060c24 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 047/505] 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 fe378642a788..f77838e37ba3 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 b2979303bb9a..d67744df3a62 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 f68b80640b87..3b2c128c5772 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 048/505] 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 8f1fd9d9eb30..eaae658a4da2 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 049/505] 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 0a042f4384a6..000b63796493 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 c68092971fab..6b2f3e504956 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 6d8062fbdc78..13195a76a794 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 6480c5fa2e38..2227674abc98 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 852f5cd2dc7b..367aed850338 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 050/505] 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 08e4ae39fef8..f3e3bd14795a 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 e297d85fb8eb..4d10dc0c8185 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 051/505] 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 af1cf6039887..a94c53b10640 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 3848510f98f0..258f66bf4d22 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 3533840635a2..82c6883cc189 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 9cc3c907ea49..1ff07eaa6dbd 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 3f04c76db30b..0a92dedb8874 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 052/505] 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 f1275daea40e..5d413bd697c7 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 3956ca935709..50c04c3b2f6b 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 e01fafbfec62..e15885bc8ad4 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 bd6d33fbc414..6f98f4353916 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 41653c8453cd..cbb9d2f15367 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 157bb1cd9e89..7c871d467e82 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 74acbd2fee1b..421805cb909c 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 607a99afcf9f..4c63ac7995b0 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 85865e546ffd..05b5d872dd97 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 96446f02bae1..278e5dff8116 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 52c27a3ce0ac..63497ff91a6a 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 673ee5c8a1b1..5c3b3f61568c 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 bb0a0abd0071..23d0d3a5e873 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 3df5350dfd0e..30f5da7638b3 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 5fb732dc4105..57d18c2f165f 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 b54526248603..f967a3a6c929 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 439f3c1e0e04..fdc2bb839784 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 bff51a79eaff..f8bc2365c391 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 82b20d96630a..a7f24b51bae8 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 9fbf0bbd7de6..29b40fc19650 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 8efc5b5740b4..2d62ee739450 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 a6f5779b8484..e7c034c3a1ab 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 21efe0ba5293..9b8ec774e067 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 3b51e92bc8da..dae9a8a3672d 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 10d7c7ac1caf..c8abcc425a63 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 e9af0617fcd2..0a83068ce7b7 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 5af282f61d89..70a0a87c88d1 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 97af36d5dee2..0cf257d6c908 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 750b1ca46dd0..2fe059a55059 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 a81f6c3818eb..8f61f36b9470 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 f84ccfa9083a..af046049c3c6 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 e3fc2a4e59d5..82a073fb2aae 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 41aa4a03b480..44e6516ed8ec 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 8021208bafbe..908b54d34179 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 6f682939cc7f..a7291f6f7966 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 9cea1c99dfdd..4bea60c83d20 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 93dd3d616e20..ce09285746e0 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 3ea92707e130..9108053f80b9 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 9cb3d5392866..ba669e0b5388 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 86b5415875dd..131e739ebb22 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 ae15a6f123da..32a837eaa9cf 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 af71c8763f9d..fb280e86428b 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 6ed77ef0c131..9544749071c0 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 7a9c139973e7..826b9db68214 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 d4000748c200..eb838dcf8b73 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 9f5cdfd7b3d2..62e0407cb6e5 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 140b35130ee4..23ce60ffd994 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 7c7b6115edd7..d1086a53fba6 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 3f2be1ae4cf8..213bc5d76afe 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 65be88c5ce56..d181a46fe788 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 015a2969cbeb..34a051d92aa8 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 f0ee1c89ee07..046a3b1e9a8e 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 c73e61291395..9f023882dea0 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 e9875107b601..8b4112591ba3 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 a05b782f8ccd..a13890b04535 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 3cc6f52498cd..2aceca89a5ee 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 621094562691..3546efd13056 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 1a7a4fe3221f..a1cd6bf63b56 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 bcaa76b98cf5..54d830748542 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 16fcfc00f224..a8ceeb3bcf87 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 3a887601381b..1376734fdbc5 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 31aa2524bff3..86e8cbab0ce2 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 104/505] 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 f8bd38188309..3c2f4ddbd346 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 65554ce9b57d..cba9e9770ab6 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 91ad5d8e96cf..aa33bcef6596 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 2a4afd0a9d08..96e7e07a0f80 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 105/505] 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 106fa0cafc9c..1f3f8d362860 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 65901004885c..acc87d64950c 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 11591c79bf81..724a9ab829fb 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 106/505] 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 b709f0d8b90f..09d14360a4dc 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 83d72a9d224f..bd4aa5535601 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 1acbcdfc4aa9..6276269eb6ef 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 107/505] 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 95dff09cc044..de49c8af7fce 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 805dabd41ad0..bfa856144a52 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 da243abbc934..f1ee1d4df5db 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 0d55ec7a20c3..afc56975e010 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 dbafa1bbf79b..f5f68df70757 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 4800142dfa00..b90bf3b6dc76 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 5a90e703910d..e7d18462ecba 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 719502060c24..4bb3358773a1 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 3af300d77a91..7f2c6442ae3a 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 baabc7644983..33b9dca29f11 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 108/505] 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 65f5bdf77ef8..20a6dac8df47 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 109/505] 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 303af1cec2ee..99e6076512e9 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 fc60887c40df..19e5389604fc 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 110/505] 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 a0a0a51302a5..0ad57e01e629 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 bae326c44f69..a8a11cbb84f5 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 111/505] 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 99e6076512e9..24a535414c54 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 112/505] 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 31a34ea0e356..db6cce28f0ef 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 113/505] 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 084acd90c6f4..2c181c594b44 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 3a1149721540..6d5ab7996265 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 6ab3eb410bb8..ca00aec989dc 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 1c9e3e0c7e54..3d1c28845e30 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 1b6d6e53cb7b..c2b62ee2d3ca 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 258b67a9a22a..d0c58268453a 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 114/505] 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 c9c9be602011..4e08188c4352 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 6d5ab7996265..10cc5bdac0ea 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 115/505] 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 74d0105e1b1a..44eb0a07e49c 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 10cc5bdac0ea..1510a539f978 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 116/505] 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 8af1139edaff..b6f74bd7bd90 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 aa9e7dc81a44..3efed67525b2 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 117/505] 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 b6f74bd7bd90..3f525dc13382 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 118/505] 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 03b9ba06814b..a388946ffb0e 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 159207c18ab5..792910f7b42e 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 119/505] 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 0403abfc7a9a..666415dc642c 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 120/505] 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 0ba9a02aaf45..53e4881bf5f4 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 016a355be49c..95d1134d7c24 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 30a93e30856d..ba666056ccf2 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 5ef9d4cdeffb..1117cb6bcf6d 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 121/505] 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 b950aef98db2..04e5e39a372f 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 95d1134d7c24..d774012e517e 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 122/505] 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 7f196227ab93..ff8faac6f183 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 123/505] 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 ff8faac6f183..f0c2b847833e 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 124/505] 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 ead37f43f985..a56a4e06281f 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 125/505] 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 76971b265f66..8e38664934d9 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 126/505] 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 aff0017ef915..754ceafe1d4b 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 127/505] 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 724a9ab829fb..fad8a3956d07 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 00e41497aae4..71689a6a8a66 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 96e7e07a0f80..21301e4cb1a0 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 350a7bdf6ff9..5b2cf361fed9 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 128/505] 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 fad8a3956d07..4c5ca7c49623 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 129/505] 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 6d470ae88e8e..d7528106da4d 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 22e979e455a7..11d6e406cf18 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 53e4881bf5f4..b613cd43a9fe 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 77c79da49d2c..ac171d3a999d 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 130/505] 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 24a535414c54..39402a825256 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 d7528106da4d..121f17ad567a 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 11d6e406cf18..e63ec35575c1 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 b613cd43a9fe..9145aecfcd3c 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 ac171d3a999d..c8098be86be8 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 38eda9811f4e..e3007c34a8cc 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 4c557b444a2b..273e13ae5780 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 2d52884bde64..169951e0d69c 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 131/505] 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 5d831dcaf5f1..e82dedee9338 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 ceaa4f0f720f..823887f075ba 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 c922297fd35b..541401b86f98 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 859bf4eb27bc..df6c1203146c 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 af4e8251d528..a337901e5162 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 132/505] 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 a5a8b07ce5af..e5ff3c2c8581 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 6545d4a84046..2f98f56cdd6a 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 6acf1028752d..65787c10ff1d 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 133/505] 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 cbb74dc6e405..f6b44fdaeb50 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 2c3b7a99f94d..f308ddc796de 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 ba44e894870b..5fa61814cce5 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 134/505] 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 c7b754d25ba3..71a6c433fbe3 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 1c7cb2e39e8c..799d8a5def90 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 135/505] 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 edc5b5a70bda..59f18e304cca 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 44baf9dc55b2..fdd7ef946fe0 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 f7e05e60f436..0b4bcbc78beb 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 258a9cc48f47..724df72c6390 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 d5f00fc25486..9d9b654c8207 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 f821dab48c11..a9306cae0ec5 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 97f42ee8e154..731c44034ed7 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 136/505] 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 e82dedee9338..75a1e0f1bb45 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 541401b86f98..6dd110507cdd 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 724df72c6390..f3aac5602c22 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 9d9b654c8207..71f5c4a326f4 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 a337901e5162..a652d7e98266 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 137/505] 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 b77e604634bb..abac5b2711dd 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 138/505] 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 49c38d6dd347..9c518c4eb43e 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 823fec222cb7..4e4cc6953003 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 c78f75739efe..e835aff8c15d 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 fbb222c01b76..85d4e669cfee 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 139/505] 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 7e3d7f12b69b..5bec0672bb1e 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 f58d7e7f9272..eef8f689b206 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 cc969cc5c7c3..86d8b4f0fe76 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 a56a4e06281f..98a3d65aead5 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 22214eafab30..1c388c08d0ed 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 140/505] 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 4e4cc6953003..9c7d45b8df00 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 141/505] 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 f2fbadeef5fd..b1db14696b23 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 8741ef315e45..e09ff565c828 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 142/505] 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 9c7d45b8df00..d73ae890b581 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 66965ed569ab..32e2f64366cf 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 e835aff8c15d..1a5b74f55b7b 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 143/505] 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 5e5b29309072..8f0fd34bb352 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 000000000000..7768421ad1a9 --- /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 144/505] 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 22960e3811cf..f4226d5afeed 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 2644f2c84505..2c95b02e3505 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 145/505] 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 a57eb0c6120b..48de2bffe976 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 23db045521f3..5998522c692d 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 146/505] 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 a0ad5cc0953c..7ca497a5f593 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 8e38664934d9..dc467a9f7b6b 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 c6abad4581c5..4c6e252142dd 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 142fceb14231..7bab3e442f6a 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 9145aecfcd3c..a13b9bb6965a 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 a6ba6dea6514..e3dbb8ca16a1 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 924c1e4cb1de..3fad7788bb4d 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 44eb0a07e49c..b86889a3effb 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 4ec0fe803b3f..817371c16387 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 35886efc7243..9d62b4c573d1 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 147/505] 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 89738660adb0..438c126f8adc 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 9df254c247c4..cbbee976e929 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 25268f848db6..356a7a91437a 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 74664a9cca0d..8d672b5a10cd 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 5e066de54e74..4b2afc5f03d2 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 9ae54f9924c1..0948aec5b73f 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 148/505] 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 39402a825256..1cc796011556 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 149/505] 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 7bab3e442f6a..763be2d527f3 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 5998522c692d..a33b07bc63dd 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 e3dbb8ca16a1..c11a57a008d1 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 3fad7788bb4d..f2ffba68b540 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 817371c16387..e43a1d2a82ab 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 150/505] 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 81f04456e0dd..93853ed5680a 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 32e2f64366cf..83c032fb6cac 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 151/505] @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 2d64574d0ec6..21aa61049f2e 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 d7b1cb522ea2..73658156b66c 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 04aefac3cf7c..51d53e0f9aa6 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 152/505] 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 60c6c4547eea..c6aafdce821d 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 5c90f5cba91b..84a4ab884fd4 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 153/505] 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 3f525dc13382..ea857975db6b 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 154/505] 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 1cc796011556..effe1a52e7da 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 155/505] 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 86e34a5061c5..477608efdbfc 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 156/505] 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 5e91704bbe75..51c479dd26a6 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 93853ed5680a..e4b804045c54 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 3a2de78a4de4..bcc0a10cf0f7 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 157/505] 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 096d216a4fb5..73c9474f852d 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 e5ff3c2c8581..de9e3eed17aa 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 2f98f56cdd6a..4191adc528ec 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 65787c10ff1d..64ad0e6e1deb 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 158/505] 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 73c9474f852d..ca17e8f1d493 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 159/505] 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 20a6dac8df47..7fd96fc604da 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 160/505] 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 cdde5c729547..3258d9118e18 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 161/505] 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 7fd96fc604da..c4f51f4a515c 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 162/505] 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 5d9bc44b53f9..3919306dd696 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 163/505] 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 4f4f37ca792a..62f35cde76fc 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 164/505] 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 555cd6b8c39c..910125f80a69 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 165/505] 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 3c84ff392878..e52d5d4f8bb2 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 166/505] 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 89d35299a057..7b5cb34f6f0f 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 167/505] 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 301256a6a82d..d86ac32fc0db 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 168/505] 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 49d235a65951..ba0d0b9db933 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 247c5838db73..6a8fba7b3671 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 46d9863426a3..c4c5483288db 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 000000000000..f1ced5158fdb --- /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 169/505] 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 f3e3bd14795a..741e303c30d2 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 ba9fc09cca0f..9be206c69432 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 7d490f5d32e2..c3812473a11a 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 e65e4fbda99d..342607ed349f 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 170/505] 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 b11153669b9b..6806628bb5f2 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 d080c284f095..248eb45397ab 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 c3649bad7e61..4d68bd507b30 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 b2a6d2aa2b18..a3d65e7b2c9c 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 48163090a75f..fe6a261efe38 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 171/505] 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 83b43d4af368..b671231f7f8e 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 172/505] 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 38070d625a09..4e70b21e4fee 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 173/505] 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 effe1a52e7da..bbce5b919b19 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 174/505] 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 3246204328f4..1a92673de2ab 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 175/505] 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 62f35cde76fc..3c2f4e6c5b51 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 4f211d6fc10f..91d8c4851b79 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 9fac4c6b6586..42a7977474b5 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 71541dcd79bd..2cc29a46ba6c 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 6770aabf97fc..98c3a9952c23 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 176/505] 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 ea857975db6b..2f1cf5c117a2 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 177/505] 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 f0383acc16bc..181854023393 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 178/505] 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 000000000000..f079caf53b06 --- /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 a336d64513f4..c26e476bb9d5 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 5cb855d9497c..80d1172fa513 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 766942dc027c..01ef62873573 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 267f19599736..f36f38718a67 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 6dea3daaa70c..f29e99dde0fc 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 bc25f84feeb1..c35b6218f25b 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 40ee1ecb022f..df7298f522a3 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 179/505] 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 9414ee541b0d..5710da24cb7d 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 f079caf53b06..2e5d7488e55c 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 f36f38718a67..50f10e4bb9a7 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 180/505] 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 121f17ad567a..112c14450cfa 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 e63ec35575c1..21cc7374963e 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 181/505] 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 0f6d10216ca8..f6e770d4078f 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 aa33bcef6596..237e672a3ef6 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 21301e4cb1a0..4e33a0bbb6e2 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 899bee055260..9e32012468aa 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 182/505] 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 bbce5b919b19..b18d894898dd 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 183/505] 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 21cc7374963e..a1abde60b25c 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 184/505] 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 bb1cb1e52d9d..31999a4b813f 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 185/505] 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 ac8298a8aa39..66a60040437e 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 a652d7e98266..d15531260f68 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 186/505] 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 8cbaa0da3b1f..b15b7c8ef8e7 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 bf4c9e6a9eba..88d27eaca757 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 90488829522f..d9d93306fdde 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 60de6e8308d0..1e36918b5131 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 187/505] 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 6939b8241f07..abdd3027ff5a 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 188/505] 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 66a60040437e..73f4fbed2b06 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 d15531260f68..5e1e7726266d 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 189/505] 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 0374d8cfd88b..76dd177cf6e9 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 190/505] 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 11cfca24fc57..82b43d0ccb81 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 191/505] 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 82b43d0ccb81..0646c82fa731 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 192/505] 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 44cb5f8e7aef..58f80396a67a 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 193/505] Next Development Version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 99547c0fb89c..061d390dfac9 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 194/505] 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 a13b9bb6965a..a658f08e9e81 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 195/505] 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 b18d894898dd..ec4481934d26 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 196/505] 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 7428f997f934..755819a10a07 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 ac01eb74b7ab..4064bf9f53e4 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 0374ddbf5509..951ef3183b79 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 313b547b4571..bd4f968c8f27 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 fd90fbeb6661..51740c741840 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 855e479a6414..0bfa5127776a 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 0a83068ce7b7..50c3f0e8ad2a 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 ae6d8be36e0d..8edb77e9d599 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 197/505] 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 a8a11cbb84f5..089274d09683 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 198/505] 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 b753535ab016..1ca7e584c974 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. *