summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmmanuel Bourg <ebourg@apache.org>2019-01-01 11:22:29 +0100
committerEmmanuel Bourg <ebourg@apache.org>2019-01-01 11:22:29 +0100
commitd2188a36ffbb40643baa12f9a68494774552563f (patch)
treebe7118bd87b5b0960603ccda439d474261fbd69d
parentcb53abe54064010cd788530c7a882ecebb88afcd (diff)
New upstream version 4.3.21
-rw-r--r--build.gradle23
-rw-r--r--gradle.properties2
-rw-r--r--spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfo.java6
-rw-r--r--spring-beans/src/main/java/org/springframework/beans/factory/ListableBeanFactory.java10
-rw-r--r--spring-beans/src/main/java/org/springframework/beans/factory/annotation/AnnotationBeanWiringInfoResolver.java19
-rw-r--r--spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java16
-rw-r--r--spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java4
-rw-r--r--spring-context/src/main/java/org/springframework/context/annotation/DependsOn.java8
-rw-r--r--spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationInterceptor.java19
-rw-r--r--spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java170
-rw-r--r--spring-context/src/test/java/org/springframework/scheduling/annotation/EnableAsyncTests.java5
-rw-r--r--spring-context/src/test/java/org/springframework/tests/mock/jndi/ExpectedLookupTemplate.java27
-rw-r--r--spring-context/src/test/java/org/springframework/tests/mock/jndi/SimpleNamingContextBuilder.java14
-rw-r--r--spring-context/src/test/java/org/springframework/validation/beanvalidation/MethodValidationTests.java24
-rw-r--r--spring-core/src/main/java/org/springframework/asm/package-info.java4
-rw-r--r--spring-core/src/main/java/org/springframework/cglib/package-info.java4
-rw-r--r--spring-core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java17
-rw-r--r--spring-core/src/main/java/org/springframework/core/io/support/SpringFactoriesLoader.java18
-rw-r--r--spring-core/src/main/java/org/springframework/objenesis/package-info.java4
-rw-r--r--spring-core/src/main/java/org/springframework/util/FastByteArrayOutputStream.java8
-rw-r--r--spring-core/src/main/java/org/springframework/util/MultiValueMap.java6
-rw-r--r--spring-core/src/main/java/org/springframework/util/ObjectUtils.java16
-rw-r--r--spring-core/src/main/java/org/springframework/util/ReflectionUtils.java29
-rw-r--r--spring-core/src/test/java/org/springframework/core/io/support/SpringFactoriesLoaderTests.java11
-rw-r--r--spring-core/src/test/java/org/springframework/util/FastByteArrayOutputStreamTests.java54
-rw-r--r--spring-core/src/test/java/org/springframework/util/ObjectUtilsTests.java22
-rw-r--r--spring-core/src/test/resources/META-INF/spring.factories4
-rw-r--r--spring-expression/src/main/java/org/springframework/expression/spel/support/StandardEvaluationContext.java4
-rw-r--r--spring-jdbc/src/main/java/org/springframework/jdbc/core/BatchUpdateUtils.java13
-rw-r--r--spring-jdbc/src/main/java/org/springframework/jdbc/core/JdbcOperations.java3
-rw-r--r--spring-jdbc/src/main/java/org/springframework/jdbc/core/JdbcTemplate.java8
-rw-r--r--spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterBatchUpdateUtils.java11
-rw-r--r--spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterUtils.java16
-rw-r--r--spring-jdbc/src/main/java/org/springframework/jdbc/object/RdbmsOperation.java8
-rw-r--r--spring-jdbc/src/main/java/org/springframework/jdbc/object/SqlCall.java31
-rw-r--r--spring-jdbc/src/test/java/org/springframework/jdbc/core/JdbcTemplateTests.java258
-rw-r--r--spring-jdbc/src/test/java/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplateTests.java206
-rw-r--r--spring-jdbc/src/test/java/org/springframework/jdbc/core/namedparam/NamedParameterUtilsTests.java34
-rw-r--r--spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/InvocableHandlerMethod.java2
-rw-r--r--spring-messaging/src/main/java/org/springframework/messaging/simp/broker/DefaultSubscriptionRegistry.java4
-rw-r--r--spring-orm-hibernate4/src/test/java/org/springframework/validation/hibernatevalidator5/MethodValidationTests.java24
-rw-r--r--spring-test/src/main/java/org/springframework/mock/jndi/ExpectedLookupTemplate.java3
-rw-r--r--spring-test/src/main/java/org/springframework/mock/jndi/SimpleNamingContextBuilder.java12
-rw-r--r--spring-tx/src/main/java/org/springframework/transaction/annotation/Transactional.java7
-rw-r--r--spring-web/src/main/java/org/springframework/http/HttpStatus.java60
-rw-r--r--spring-web/src/main/java/org/springframework/web/bind/annotation/RequestPart.java35
-rw-r--r--spring-web/src/main/java/org/springframework/web/client/DefaultResponseErrorHandler.java37
-rw-r--r--spring-web/src/main/java/org/springframework/web/client/ResponseErrorHandler.java4
-rw-r--r--spring-web/src/main/java/org/springframework/web/method/annotation/MapMethodProcessor.java4
-rw-r--r--spring-web/src/main/java/org/springframework/web/method/annotation/RequestParamMapMethodArgumentResolver.java17
-rw-r--r--spring-web/src/main/java/org/springframework/web/method/annotation/RequestParamMethodArgumentResolver.java18
-rw-r--r--spring-web/src/test/java/org/springframework/web/client/DefaultResponseErrorHandlerTests.java60
-rw-r--r--spring-web/src/test/java/org/springframework/web/method/annotation/RequestHeaderMapMethodArgumentResolverTests.java9
-rw-r--r--spring-web/src/test/java/org/springframework/web/method/annotation/RequestHeaderMethodArgumentResolverTests.java6
-rw-r--r--spring-web/src/test/java/org/springframework/web/method/annotation/RequestParamMapMethodArgumentResolverTests.java19
-rw-r--r--spring-web/src/test/java/org/springframework/web/method/annotation/RequestParamMethodArgumentResolverTests.java20
-rw-r--r--spring-webmvc/src/main/java/org/springframework/web/servlet/HandlerMapping.java9
-rw-r--r--spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerExceptionResolver.java4
-rw-r--r--spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java1
-rw-r--r--spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractUrlHandlerMapping.java1
-rw-r--r--spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/annotation/ResponseStatusExceptionResolver.java3
-rw-r--r--spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestPartMethodArgumentResolver.java10
-rw-r--r--spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolver.java1
-rw-r--r--spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java17
-rw-r--r--spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlEncodingFilter.java103
-rw-r--r--spring-webmvc/src/test/java/org/springframework/web/servlet/handler/HandlerMethodMappingTests.java9
-rw-r--r--spring-webmvc/src/test/java/org/springframework/web/servlet/handler/SimpleUrlHandlerMappingTests.java11
-rw-r--r--spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceUrlEncodingFilterTests.java34
-rw-r--r--spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceUrlProviderJavaConfigTests.java46
-rw-r--r--spring-websocket/src/main/java/org/springframework/web/socket/messaging/StompSubProtocolHandler.java4
-rw-r--r--src/asciidoc/core-beans.adoc30
-rw-r--r--src/asciidoc/data-access.adoc7
-rw-r--r--src/asciidoc/web-mvc.adoc4
73 files changed, 1054 insertions, 717 deletions
diff --git a/build.gradle b/build.gradle
index aa4bc568..bd1f4ec0 100644
--- a/build.gradle
+++ b/build.gradle
@@ -41,7 +41,7 @@ configure(allprojects) { project ->
ext.fileuploadVersion = "1.3.3"
ext.freemarkerVersion = "2.3.23"
ext.groovyVersion = "2.4.15"
- ext.gsonVersion = "2.8.2"
+ ext.gsonVersion = "2.8.5"
ext.guavaVersion = "20.0"
ext.hamcrestVersion = "1.3"
ext.hibernate3Version = "3.6.10.Final"
@@ -50,9 +50,9 @@ configure(allprojects) { project ->
ext.hibval4Version = "4.3.2.Final"
ext.hibval5Version = "5.2.5.Final"
ext.hsqldbVersion = "2.3.4"
- ext.httpasyncVersion = "4.1.3"
- ext.httpclientVersion = "4.5.5"
- ext.jackson2Version = "2.8.11.2"
+ ext.httpasyncVersion = "4.1.4"
+ ext.httpclientVersion = "4.5.6"
+ ext.jackson2Version = "2.8.11.3"
ext.jasperreportsVersion = "6.2.1" // our tests fail with JR-internal NPEs against 6.2.2 and higher
ext.javamailVersion = "1.5.6"
ext.jettyVersion = "9.3.14.v20161028" // as of 9.3.15, Jetty has hard Servlet 3.1 requirement
@@ -62,7 +62,7 @@ configure(allprojects) { project ->
ext.jtaVersion = "1.2"
ext.junitVersion = "4.12"
ext.log4jVersion = "1.2.17"
- ext.nettyVersion = "4.1.29.Final"
+ ext.nettyVersion = "4.1.31.Final"
ext.okhttpVersion = "2.7.5"
ext.okhttp3Version = "3.8.1"
ext.openjpaVersion = "2.4.2"
@@ -71,11 +71,11 @@ configure(allprojects) { project ->
ext.romeVersion = "1.7.4"
ext.slf4jVersion = "1.7.25"
ext.snakeyamlVersion = "1.17"
- ext.snifferVersion = "1.16"
+ ext.snifferVersion = "1.17"
ext.testngVersion = "6.9.10"
ext.tiles2Version = "2.2.2"
- ext.tiles3Version = "3.0.7"
- ext.tomcatVersion = "8.5.33"
+ ext.tiles3Version = "3.0.8"
+ ext.tomcatVersion = "8.5.35"
ext.tyrusVersion = "1.3.5" // constrained by WebLogic 12.1.3 support
ext.undertowVersion = "1.3.33.Final"
ext.xmlunitVersion = "1.6"
@@ -289,10 +289,9 @@ project("spring-build-src") {
project("spring-core") {
description = "Spring Core"
- // As of Spring 4.0.3, spring-core includes asm 5.x and repackages cglib 3.2, inlining
- // both into the spring-core jar. cglib 3.2 itself depends on asm 5.x and is therefore
- // further transformed by the JarJar task to depend on org.springframework.asm; this
- // avoids including two different copies of asm unnecessarily.
+ // spring-core includes asm and repackages cglib, inlining both into the spring-core jar.
+ // cglib itself depends on asm and is therefore further transformed by the JarJar task to
+ // depend on org.springframework.asm; this avoids including two different copies of asm.
def cglibVersion = "3.2.6"
def objenesisVersion = "2.6"
diff --git a/gradle.properties b/gradle.properties
index 4a28008c..08f289ef 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1 +1 @@
-version=4.3.20.RELEASE
+version=4.3.21.RELEASE
diff --git a/spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfo.java b/spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfo.java
index 1411f556..5c0942cf 100644
--- a/spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfo.java
+++ b/spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfo.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -43,8 +43,10 @@ import org.springframework.util.ObjectUtils;
* Decorator for a standard {@link BeanInfo} object, e.g. as created by
* {@link Introspector#getBeanInfo(Class)}, designed to discover and register static
* and/or non-void returning setter methods. For example:
+ *
* <pre class="code">
* public class Bean {
+ *
* private Foo foo;
*
* public Foo getFoo() {
@@ -56,6 +58,7 @@ import org.springframework.util.ObjectUtils;
* return this;
* }
* }</pre>
+ *
* The standard JavaBeans {@code Introspector} will discover the {@code getFoo} read
* method, but will bypass the {@code #setFoo(Foo)} write method, because its non-void
* returning signature does not comply with the JavaBeans specification.
@@ -68,6 +71,7 @@ import org.springframework.util.ObjectUtils;
* indexed properties</a> are fully supported.
*
* @author Chris Beams
+ * @author Juergen Hoeller
* @since 3.1
* @see #ExtendedBeanInfo(BeanInfo)
* @see ExtendedBeanInfoFactory
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/ListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/ListableBeanFactory.java
index e8be3322..1c0c70b4 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/ListableBeanFactory.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/ListableBeanFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -243,7 +243,9 @@ public interface ListableBeanFactory extends BeanFactory {
/**
* Find all names of beans whose {@code Class} has the supplied {@link Annotation}
- * type, without creating any bean instances yet.
+ * type, without creating corresponding bean instances yet.
+ * <p>Note that this method considers objects created by FactoryBeans, which means
+ * that FactoryBeans will get initialized in order to determine their object type.
* @param annotationType the type of annotation to look for
* @return the names of all matching beans
* @since 4.0
@@ -253,6 +255,8 @@ public interface ListableBeanFactory extends BeanFactory {
/**
* Find all beans whose {@code Class} has the supplied {@link Annotation} type,
* returning a Map of bean names with corresponding bean instances.
+ * <p>Note that this method considers objects created by FactoryBeans, which means
+ * that FactoryBeans will get initialized in order to determine their object type.
* @param annotationType the type of annotation to look for
* @return a Map with the matching beans, containing the bean names as
* keys and the corresponding bean instances as values
@@ -267,7 +271,7 @@ public interface ListableBeanFactory extends BeanFactory {
* found on the given class itself.
* @param beanName the name of the bean to look for annotations on
* @param annotationType the annotation class to look for
- * @return the annotation of the given type if found, or {@code null}
+ * @return the annotation of the given type if found, or {@code null} otherwise
* @throws NoSuchBeanDefinitionException if there is no bean with the given name
* @since 3.0
*/
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AnnotationBeanWiringInfoResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AnnotationBeanWiringInfoResolver.java
index 78fe8fe8..e8e20ab0 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AnnotationBeanWiringInfoResolver.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AnnotationBeanWiringInfoResolver.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2018 the original author 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,24 +44,23 @@ public class AnnotationBeanWiringInfoResolver implements BeanWiringInfoResolver
}
/**
- * Build the BeanWiringInfo for the given Configurable annotation.
+ * Build the {@link BeanWiringInfo} for the given {@link Configurable} annotation.
* @param beanInstance the bean instance
* @param annotation the Configurable annotation found on the bean class
* @return the resolved BeanWiringInfo
*/
protected BeanWiringInfo buildWiringInfo(Object beanInstance, Configurable annotation) {
if (!Autowire.NO.equals(annotation.autowire())) {
+ // Autowiring by name or by type
return new BeanWiringInfo(annotation.autowire().value(), annotation.dependencyCheck());
}
+ else if (!"".equals(annotation.value())) {
+ // Explicitly specified bean name for bean definition to take property values from
+ return new BeanWiringInfo(annotation.value(), false);
+ }
else {
- if (!"".equals(annotation.value())) {
- // explicitly specified bean name
- return new BeanWiringInfo(annotation.value(), false);
- }
- else {
- // default bean name
- return new BeanWiringInfo(getDefaultBeanName(beanInstance), true);
- }
+ // Default bean name for bean definition to take property values from
+ return new BeanWiringInfo(getDefaultBeanName(beanInstance), true);
}
}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java
index 734aef5a..1e021cc9 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java
@@ -509,12 +509,21 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
// Generics potentially only match on the target class, not on the proxy...
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
Class<?> targetType = mbd.getTargetType();
- if (targetType != null && targetType != ClassUtils.getUserClass(beanInstance) &&
- typeToMatch.isAssignableFrom(targetType)) {
+ if (targetType != null && targetType != ClassUtils.getUserClass(beanInstance)) {
// Check raw class match as well, making sure it's exposed on the proxy.
Class<?> classToMatch = typeToMatch.resolve();
- return (classToMatch == null || classToMatch.isInstance(beanInstance));
+ if (classToMatch != null && !classToMatch.isInstance(beanInstance)) {
+ return false;
+ }
+ if (typeToMatch.isAssignableFrom(targetType)) {
+ return true;
+ }
}
+ ResolvableType resolvableType = mbd.targetType;
+ if (resolvableType == null) {
+ resolvableType = mbd.factoryMethodReturnType;
+ }
+ return (resolvableType != null && typeToMatch.isAssignableFrom(resolvableType));
}
}
return false;
@@ -1363,6 +1372,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
*/
protected Class<?> resolveBeanClass(final RootBeanDefinition mbd, String beanName, final Class<?>... typesToMatch)
throws CannotLoadBeanClassException {
+
try {
if (mbd.hasBeanClass()) {
return mbd.getBeanClass();
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 c3ea84a0..bb5ff2cf 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
@@ -1574,7 +1574,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
}
}
// Lenient fallback: dummy factory in case of original factory not found...
- return new DefaultListableBeanFactory();
+ DefaultListableBeanFactory dummyFactory = new DefaultListableBeanFactory();
+ dummyFactory.serializationId = this.id;
+ return dummyFactory;
}
}
diff --git a/spring-context/src/main/java/org/springframework/context/annotation/DependsOn.java b/spring-context/src/main/java/org/springframework/context/annotation/DependsOn.java
index 121cec3a..48f3dc05 100644
--- a/spring-context/src/main/java/org/springframework/context/annotation/DependsOn.java
+++ b/spring-context/src/main/java/org/springframework/context/annotation/DependsOn.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2018 the original author 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,6 +28,12 @@ import java.lang.annotation.Target;
* does not explicitly depend on another through properties or constructor arguments,
* but rather depends on the side effects of another bean's initialization.
*
+ * <p>A depends-on declaration can specify both an initialization-time dependency and,
+ * in the case of singleton beans only, a corresponding destruction-time dependency.
+ * Dependent beans that define a depends-on relationship with a given bean are destroyed
+ * first, prior to the given bean itself being destroyed. Thus, a depends-on declaration
+ * can also control shutdown order.
+ *
* <p>May be used on any class directly or indirectly annotated with
* {@link org.springframework.stereotype.Component} or on methods annotated
* with {@link Bean}.
diff --git a/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationInterceptor.java b/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationInterceptor.java
index 42427f4c..473ba922 100644
--- a/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationInterceptor.java
+++ b/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationInterceptor.java
@@ -171,8 +171,23 @@ public class MethodValidationInterceptor implements MethodInterceptor {
private boolean isFactoryBeanMetadataMethod(Method method) {
Class<?> clazz = method.getDeclaringClass();
- return ((clazz == FactoryBean.class || clazz == SmartFactoryBean.class) &&
- !method.getName().equals("getObject"));
+
+ // Call from interface-based proxy handle, allowing for an efficient check?
+ if (clazz.isInterface()) {
+ return ((clazz == FactoryBean.class || clazz == SmartFactoryBean.class) &&
+ !method.getName().equals("getObject"));
+ }
+
+ // Call from CGLIB proxy handle, potentially implementing a FactoryBean method?
+ Class<?> factoryBeanType = null;
+ if (SmartFactoryBean.class.isAssignableFrom(clazz)) {
+ factoryBeanType = SmartFactoryBean.class;
+ }
+ else if (FactoryBean.class.isAssignableFrom(clazz)) {
+ factoryBeanType = FactoryBean.class;
+ }
+ return (factoryBeanType != null && !method.getName().equals("getObject") &&
+ ClassUtils.hasMethod(factoryBeanType, method.getName(), method.getParameterTypes()));
}
/**
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 dcf2f373..dd053d7b 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
@@ -544,6 +544,10 @@ public class ConfigurationClassPostProcessorTests {
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
assertEquals(1, beanNames.length);
assertEquals("stringRepo", beanNames[0]);
+
+ beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
+ assertEquals(1, beanNames.length);
+ assertEquals("stringRepo", beanNames[0]);
}
@Test
@@ -558,6 +562,78 @@ public class ConfigurationClassPostProcessorTests {
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
assertEquals(1, beanNames.length);
assertEquals("stringRepo", beanNames[0]);
+
+ beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
+ assertEquals(1, beanNames.length);
+ assertEquals("stringRepo", beanNames[0]);
+ }
+
+ @Test
+ public void genericsBasedInjectionWithEarlyGenericsMatchingAndRawFactoryMethod() {
+ beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawFactoryMethodRepositoryConfiguration.class));
+ new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
+
+ String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
+ assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo"));
+
+ beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
+ assertEquals(0, beanNames.length);
+
+ beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
+ assertEquals(0, beanNames.length);
+ }
+
+ @Test
+ public void genericsBasedInjectionWithLateGenericsMatchingAndRawFactoryMethod() {
+ beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawFactoryMethodRepositoryConfiguration.class));
+ new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
+ beanFactory.preInstantiateSingletons();
+
+ String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
+ assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo"));
+
+ beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
+ assertEquals(1, beanNames.length);
+ assertEquals("stringRepo", beanNames[0]);
+
+ beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
+ assertEquals(1, beanNames.length);
+ assertEquals("stringRepo", beanNames[0]);
+ }
+
+ @Test
+ public void genericsBasedInjectionWithEarlyGenericsMatchingAndRawInstance() {
+ beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawInstanceRepositoryConfiguration.class));
+ new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
+
+ String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
+ assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo"));
+
+ beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
+ assertEquals(1, beanNames.length);
+ assertEquals("stringRepo", beanNames[0]);
+
+ beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
+ assertEquals(1, beanNames.length);
+ assertEquals("stringRepo", beanNames[0]);
+ }
+
+ @Test
+ public void genericsBasedInjectionWithLateGenericsMatchingAndRawInstance() {
+ beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawInstanceRepositoryConfiguration.class));
+ new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
+ beanFactory.preInstantiateSingletons();
+
+ String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
+ assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo"));
+
+ beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
+ assertEquals(1, beanNames.length);
+ assertEquals("stringRepo", beanNames[0]);
+
+ beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
+ assertEquals(1, beanNames.length);
+ assertEquals("stringRepo", beanNames[0]);
}
@Test
@@ -577,6 +653,10 @@ public class ConfigurationClassPostProcessorTests {
assertEquals(1, beanNames.length);
assertEquals("stringRepo", beanNames[0]);
+ beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
+ assertEquals(1, beanNames.length);
+ assertEquals("stringRepo", beanNames[0]);
+
assertTrue(AopUtils.isCglibProxy(beanFactory.getBean("stringRepo")));
}
@@ -598,12 +678,16 @@ public class ConfigurationClassPostProcessorTests {
assertEquals(1, beanNames.length);
assertEquals("stringRepo", beanNames[0]);
+ beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
+ assertEquals(1, beanNames.length);
+ assertEquals("stringRepo", beanNames[0]);
+
assertTrue(AopUtils.isCglibProxy(beanFactory.getBean("stringRepo")));
}
@Test
public void genericsBasedInjectionWithLateGenericsMatchingOnCglibProxyAndRawFactoryMethod() {
- beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawRepositoryConfiguration.class));
+ beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawFactoryMethodRepositoryConfiguration.class));
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
autoProxyCreator.setProxyTargetClass(true);
@@ -619,6 +703,35 @@ public class ConfigurationClassPostProcessorTests {
assertEquals(1, beanNames.length);
assertEquals("stringRepo", beanNames[0]);
+ beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
+ assertEquals(1, beanNames.length);
+ assertEquals("stringRepo", beanNames[0]);
+
+ assertTrue(AopUtils.isCglibProxy(beanFactory.getBean("stringRepo")));
+ }
+
+ @Test
+ public void genericsBasedInjectionWithLateGenericsMatchingOnCglibProxyAndRawInstance() {
+ beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawInstanceRepositoryConfiguration.class));
+ new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
+ DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
+ autoProxyCreator.setProxyTargetClass(true);
+ autoProxyCreator.setBeanFactory(beanFactory);
+ beanFactory.addBeanPostProcessor(autoProxyCreator);
+ beanFactory.registerSingleton("traceInterceptor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor()));
+ beanFactory.preInstantiateSingletons();
+
+ String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
+ assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo"));
+
+ beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
+ assertEquals(1, beanNames.length);
+ assertEquals("stringRepo", beanNames[0]);
+
+ beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
+ assertEquals(1, beanNames.length);
+ assertEquals("stringRepo", beanNames[0]);
+
assertTrue(AopUtils.isCglibProxy(beanFactory.getBean("stringRepo")));
}
@@ -638,6 +751,10 @@ public class ConfigurationClassPostProcessorTests {
assertEquals(1, beanNames.length);
assertEquals("stringRepo", beanNames[0]);
+ beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(RepositoryInterface.class, String.class));
+ assertEquals(1, beanNames.length);
+ assertEquals("stringRepo", beanNames[0]);
+
assertTrue(AopUtils.isJdkDynamicProxy(beanFactory.getBean("stringRepo")));
}
@@ -658,12 +775,16 @@ public class ConfigurationClassPostProcessorTests {
assertEquals(1, beanNames.length);
assertEquals("stringRepo", beanNames[0]);
+ beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(RepositoryInterface.class, String.class));
+ assertEquals(1, beanNames.length);
+ assertEquals("stringRepo", beanNames[0]);
+
assertTrue(AopUtils.isJdkDynamicProxy(beanFactory.getBean("stringRepo")));
}
@Test
public void genericsBasedInjectionWithLateGenericsMatchingOnJdkProxyAndRawFactoryMethod() {
- beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawRepositoryConfiguration.class));
+ beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawFactoryMethodRepositoryConfiguration.class));
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
autoProxyCreator.setBeanFactory(beanFactory);
@@ -678,6 +799,34 @@ public class ConfigurationClassPostProcessorTests {
assertEquals(1, beanNames.length);
assertEquals("stringRepo", beanNames[0]);
+ beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(RepositoryInterface.class, String.class));
+ assertEquals(1, beanNames.length);
+ assertEquals("stringRepo", beanNames[0]);
+
+ assertTrue(AopUtils.isJdkDynamicProxy(beanFactory.getBean("stringRepo")));
+ }
+
+ @Test
+ public void genericsBasedInjectionWithLateGenericsMatchingOnJdkProxyAndRawInstance() {
+ beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawInstanceRepositoryConfiguration.class));
+ new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
+ DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
+ autoProxyCreator.setBeanFactory(beanFactory);
+ beanFactory.addBeanPostProcessor(autoProxyCreator);
+ beanFactory.registerSingleton("traceInterceptor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor()));
+ beanFactory.preInstantiateSingletons();
+
+ String[] beanNames = beanFactory.getBeanNamesForType(RepositoryInterface.class);
+ assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo"));
+
+ beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(RepositoryInterface.class, String.class));
+ assertEquals(1, beanNames.length);
+ assertEquals("stringRepo", beanNames[0]);
+
+ beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(RepositoryInterface.class, String.class));
+ assertEquals(1, beanNames.length);
+ assertEquals("stringRepo", beanNames[0]);
+
assertTrue(AopUtils.isJdkDynamicProxy(beanFactory.getBean("stringRepo")));
}
@@ -980,7 +1129,7 @@ public class ConfigurationClassPostProcessorTests {
}
@Configuration
- public static class RawRepositoryConfiguration {
+ public static class RawFactoryMethodRepositoryConfiguration {
@Bean
public Repository stringRepo() {
@@ -994,6 +1143,21 @@ public class ConfigurationClassPostProcessorTests {
}
@Configuration
+ public static class RawInstanceRepositoryConfiguration {
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ @Bean
+ public Repository<String> stringRepo() {
+ return new Repository() {
+ @Override
+ public String toString() {
+ return "Repository<String>";
+ }
+ };
+ }
+ }
+
+ @Configuration
public static class ScopedRepositoryConfiguration {
@Bean
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 ed5b11fc..9eb5aefe 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
@@ -161,7 +161,7 @@ public class EnableAsyncTests {
Object bean = ctx.getBean(CustomAsyncBean.class);
assertTrue(AopUtils.isAopProxy(bean));
boolean isAsyncAdvised = false;
- for (Advisor advisor : ((Advised)bean).getAdvisors()) {
+ for (Advisor advisor : ((Advised) bean).getAdvisors()) {
if (advisor instanceof AsyncAnnotationAdvisor) {
isAsyncAdvised = true;
break;
@@ -365,7 +365,8 @@ public class EnableAsyncTests {
@EnableAsync
static class AsyncConfigWithMockito {
- @Bean @Lazy
+ @Bean
+ @Lazy
public AsyncBean asyncBean() {
return Mockito.mock(AsyncBean.class);
}
diff --git a/spring-context/src/test/java/org/springframework/tests/mock/jndi/ExpectedLookupTemplate.java b/spring-context/src/test/java/org/springframework/tests/mock/jndi/ExpectedLookupTemplate.java
index 8cc22aab..00c3d24c 100644
--- a/spring-context/src/test/java/org/springframework/tests/mock/jndi/ExpectedLookupTemplate.java
+++ b/spring-context/src/test/java/org/springframework/tests/mock/jndi/ExpectedLookupTemplate.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2018 the original author 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,28 +23,29 @@ import javax.naming.NamingException;
import org.springframework.jndi.JndiTemplate;
/**
- * Simple extension of the JndiTemplate class that always returns
- * a given object. Very useful for testing. Effectively a mock object.
+ * Simple extension of the JndiTemplate class that always returns a given object.
+ *
+ * <p>Very useful for testing. Effectively a mock object.
*
* @author Rod Johnson
* @author Juergen Hoeller
*/
public class ExpectedLookupTemplate extends JndiTemplate {
- private final Map<String, Object> jndiObjects = new ConcurrentHashMap<String, Object>();
+ private final Map<String, Object> jndiObjects = new ConcurrentHashMap<String, Object>(16);
/**
- * Construct a new JndiTemplate that will always return given objects
- * for given names. To be populated through {@code addObject} calls.
+ * Construct a new JndiTemplate that will always return given objects for
+ * given names. To be populated through {@code addObject} calls.
* @see #addObject(String, Object)
*/
public ExpectedLookupTemplate() {
}
/**
- * Construct a new JndiTemplate that will always return the
- * given object, but honour only requests for the given name.
+ * Construct a new JndiTemplate that will always return the given object,
+ * but honour only requests for the given name.
* @param name the name the client is expected to look up
* @param object the object that will be returned
*/
@@ -54,8 +55,7 @@ public class ExpectedLookupTemplate extends JndiTemplate {
/**
- * Add the given object to the list of JNDI objects that this
- * template will expose.
+ * Add the given object to the list of JNDI objects that this template will expose.
* @param name the name the client is expected to look up
* @param object the object that will be returned
*/
@@ -63,11 +63,10 @@ public class ExpectedLookupTemplate extends JndiTemplate {
this.jndiObjects.put(name, object);
}
-
/**
- * If the name is the expected name specified in the constructor,
- * return the object provided in the constructor. If the name is
- * unexpected, a respective NamingException gets thrown.
+ * If the name is the expected name specified in the constructor, return the
+ * object provided in the constructor. If the name is unexpected, a
+ * respective NamingException gets thrown.
*/
@Override
public Object lookup(String name) throws NamingException {
diff --git a/spring-context/src/test/java/org/springframework/tests/mock/jndi/SimpleNamingContextBuilder.java b/spring-context/src/test/java/org/springframework/tests/mock/jndi/SimpleNamingContextBuilder.java
index 1ae3b78d..b4fe464e 100644
--- a/spring-context/src/test/java/org/springframework/tests/mock/jndi/SimpleNamingContextBuilder.java
+++ b/spring-context/src/test/java/org/springframework/tests/mock/jndi/SimpleNamingContextBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2018 the original author 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 javax.naming.spi.NamingManager;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
@@ -35,13 +36,14 @@ import org.springframework.util.ClassUtils;
* configure JNDI appropriately, so that {@code new InitialContext()}
* will expose the required objects. Also usable for standalone applications,
* e.g. for binding a JDBC DataSource to a well-known JNDI location, to be
- * able to use traditional J2EE data access code outside of a J2EE container.
+ * able to use traditional Java EE data access code outside of a Java EE
+ * container.
*
* <p>There are various choices for DataSource implementations:
* <ul>
* <li>{@code SingleConnectionDataSource} (using the same Connection for all getConnection calls)
* <li>{@code DriverManagerDataSource} (creating a new Connection on each getConnection call)
- * <li>Apache's Jakarta Commons DBCP offers {@code org.apache.commons.dbcp.BasicDataSource} (a real pool)
+ * <li>Apache's Commons DBCP offers {@code org.apache.commons.dbcp.BasicDataSource} (a real pool)
* </ul>
*
* <p>Typical usage in bootstrap code:
@@ -98,7 +100,7 @@ public class SimpleNamingContextBuilder implements InitialContextFactoryBuilder
/**
* If no SimpleNamingContextBuilder is already configuring JNDI,
- * create and activate one. Otherwise take the existing activate
+ * create and activate one. Otherwise take the existing activated
* SimpleNamingContextBuilder, clear it and return it.
* <p>This is mainly intended for test suites that want to
* reinitialize JNDI bindings from scratch repeatedly.
@@ -137,12 +139,10 @@ public class SimpleNamingContextBuilder implements InitialContextFactoryBuilder
logger.info("Activating simple JNDI environment");
synchronized (initializationLock) {
if (!initialized) {
- if (NamingManager.hasInitialContextFactoryBuilder()) {
- throw new IllegalStateException(
+ Assert.state(!NamingManager.hasInitialContextFactoryBuilder(),
"Cannot activate SimpleNamingContextBuilder: there is already a JNDI provider registered. " +
"Note that JNDI is a JVM-wide service, shared at the JVM system class loader level, " +
"with no reset option. As a consequence, a JNDI provider must only be registered once per JVM.");
- }
NamingManager.setInitialContextFactoryBuilder(this);
initialized = true;
}
diff --git a/spring-context/src/test/java/org/springframework/validation/beanvalidation/MethodValidationTests.java b/spring-context/src/test/java/org/springframework/validation/beanvalidation/MethodValidationTests.java
index 63746bbb..dbd8b5cd 100644
--- a/spring-context/src/test/java/org/springframework/validation/beanvalidation/MethodValidationTests.java
+++ b/spring-context/src/test/java/org/springframework/validation/beanvalidation/MethodValidationTests.java
@@ -123,7 +123,16 @@ public class MethodValidationTests {
@Test
public void testLazyValidatorForMethodValidation() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(
- LazyMethodValidationConfig.class, CustomValidatorBean.class, MyValidBean.class, MyValidFactoryBean.class);
+ LazyMethodValidationConfig.class, CustomValidatorBean.class,
+ MyValidBean.class, MyValidFactoryBean.class);
+ ctx.getBeansOfType(MyValidInterface.class).values().forEach(bean -> bean.myValidMethod("value", 5));
+ }
+
+ @Test
+ public void testLazyValidatorForMethodValidationWithProxyTargetClass() {
+ AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(
+ LazyMethodValidationConfigWithProxyTargetClass.class, CustomValidatorBean.class,
+ MyValidBean.class, MyValidFactoryBean.class);
ctx.getBeansOfType(MyValidInterface.class).values().forEach(bean -> bean.myValidMethod("value", 5));
}
@@ -223,4 +232,17 @@ public class MethodValidationTests {
}
}
+
+ @Configuration
+ public static class LazyMethodValidationConfigWithProxyTargetClass {
+
+ @Bean
+ public static MethodValidationPostProcessor methodValidationPostProcessor(@Lazy Validator validator) {
+ MethodValidationPostProcessor postProcessor = new MethodValidationPostProcessor();
+ postProcessor.setValidator(validator);
+ postProcessor.setProxyTargetClass(true);
+ return postProcessor;
+ }
+ }
+
}
diff --git a/spring-core/src/main/java/org/springframework/asm/package-info.java b/spring-core/src/main/java/org/springframework/asm/package-info.java
index dc7bf516..69209083 100644
--- a/spring-core/src/main/java/org/springframework/asm/package-info.java
+++ b/spring-core/src/main/java/org/springframework/asm/package-info.java
@@ -1,7 +1,7 @@
/**
* Spring's repackaging of
- * <a href="http://asm.ow2.org">ASM</a>
- * (for internal use only).
+ * <a href="http://asm.ow2.org">ASM 6.0</a>
+ * (with Spring-specific patches; for internal use only).
*
* <p>This repackaging technique avoids any potential conflicts with
* dependencies on ASM at the application level or from third-party
diff --git a/spring-core/src/main/java/org/springframework/cglib/package-info.java b/spring-core/src/main/java/org/springframework/cglib/package-info.java
index 85d1758e..f82ef60a 100644
--- a/spring-core/src/main/java/org/springframework/cglib/package-info.java
+++ b/spring-core/src/main/java/org/springframework/cglib/package-info.java
@@ -1,7 +1,7 @@
/**
* Spring's repackaging of
- * <a href="http://cglib.sourceforge.net">CGLIB</a>
- * (for internal use only).
+ * <a href="https://github.com/cglib/cglib">CGLIB 3.2</a>
+ * (with Spring naming strategy; for internal use only).
*
* <p>This repackaging technique avoids any potential conflicts with
* dependencies on CGLIB at the application level or from third-party
diff --git a/spring-core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java b/spring-core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java
index 131bb0a1..69de70c4 100644
--- a/spring-core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java
+++ b/spring-core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2017 the original author or authors.
+ * Copyright 2002-2018 the original author 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.core.io.support;
import java.io.File;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
@@ -170,7 +171,7 @@ import org.springframework.util.StringUtils;
* @author Colin Sampaleanu
* @author Marius Bogoevici
* @author Costin Leau
- * @author Phil Webb
+ * @author Phillip Webb
* @since 1.0.2
* @see #CLASSPATH_ALL_URL_PREFIX
* @see org.springframework.util.AntPathMatcher
@@ -716,10 +717,16 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
try {
rootDir = rootDirResource.getFile().getAbsoluteFile();
}
- catch (IOException ex) {
+ catch (FileNotFoundException ex) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Cannot search for matching files underneath " + rootDirResource +
+ " in the file system: " + ex.getMessage());
+ }
+ return Collections.emptySet();
+ }
+ catch (Exception ex) {
if (logger.isWarnEnabled()) {
- logger.warn("Cannot search for matching files underneath " + rootDirResource +
- " because it does not correspond to a directory in the file system", ex);
+ logger.warn("Failed to resolve " + rootDirResource + " in the file system: " + ex);
}
return Collections.emptySet();
}
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 fa378ed0..0f3d891b 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
@@ -57,14 +57,14 @@ import org.springframework.util.StringUtils;
*/
public abstract class SpringFactoriesLoader {
- private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
-
/**
* The location to look for factories.
* <p>Can be present in multiple JAR files.
*/
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
+ private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
+
/**
* Load and instantiate the factory implementations of the given type from
@@ -74,9 +74,9 @@ public abstract class SpringFactoriesLoader {
* to obtain all registered factory names.
* @param factoryClass the interface or abstract class representing the factory
* @param classLoader the ClassLoader to use for loading (can be {@code null} to use the default)
- * @see #loadFactoryNames
* @throws IllegalArgumentException if any factory implementation class cannot
* be loaded or if an error occurs while instantiating any factory
+ * @see #loadFactoryNames
*/
public static <T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader) {
Assert.notNull(factoryClass, "'factoryClass' must not be null");
@@ -103,8 +103,8 @@ public abstract class SpringFactoriesLoader {
* @param factoryClass the interface or abstract class representing the factory
* @param classLoader the ClassLoader to use for loading resources; can be
* {@code null} to use the default
- * @see #loadFactories
* @throws IllegalArgumentException if an error occurs while loading factory names
+ * @see #loadFactories
*/
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
@@ -115,14 +115,16 @@ public abstract class SpringFactoriesLoader {
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
- String factoryClassNames = properties.getProperty(factoryClassName);
- result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
+ String propertyValue = properties.getProperty(factoryClassName);
+ for (String factoryName : StringUtils.commaDelimitedListToStringArray(propertyValue)) {
+ result.add(factoryName.trim());
+ }
}
return result;
}
catch (IOException ex) {
- throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
- "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
+ throw new IllegalArgumentException("Unable to load factories from location [" +
+ FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
diff --git a/spring-core/src/main/java/org/springframework/objenesis/package-info.java b/spring-core/src/main/java/org/springframework/objenesis/package-info.java
index 2c5158a7..71cf5123 100644
--- a/spring-core/src/main/java/org/springframework/objenesis/package-info.java
+++ b/spring-core/src/main/java/org/springframework/objenesis/package-info.java
@@ -1,7 +1,7 @@
/**
* Spring's repackaging of
- * <a href="http://objenesis.org">Objenesis 2.1</a>
- * (for internal use only).
+ * <a href="http://objenesis.org">Objenesis 2.6</a>
+ * (with SpringObjenesis entry point; for internal use only).
*
* <p>This repackaging technique avoids any potential conflicts with
* dependencies on different Objenesis versions at the application
diff --git a/spring-core/src/main/java/org/springframework/util/FastByteArrayOutputStream.java b/spring-core/src/main/java/org/springframework/util/FastByteArrayOutputStream.java
index 886f92c0..81d761ac 100644
--- a/spring-core/src/main/java/org/springframework/util/FastByteArrayOutputStream.java
+++ b/spring-core/src/main/java/org/springframework/util/FastByteArrayOutputStream.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2017 the original author or authors.
+ * Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -367,7 +367,7 @@ public class FastByteArrayOutputStream extends OutputStream {
else {
if (this.nextIndexInCurrentBuffer < this.currentBufferLength) {
this.totalBytesRead++;
- return this.currentBuffer[this.nextIndexInCurrentBuffer++];
+ return this.currentBuffer[this.nextIndexInCurrentBuffer++] & 0xFF;
}
else {
if (this.buffersIterator.hasNext()) {
@@ -487,7 +487,7 @@ public class FastByteArrayOutputStream extends OutputStream {
/**
* Update the message digest with the remaining bytes in this stream.
- * @param messageDigest The message digest to update
+ * @param messageDigest the message digest to update
*/
@Override
public void updateMessageDigest(MessageDigest messageDigest) {
@@ -497,7 +497,7 @@ public class FastByteArrayOutputStream extends OutputStream {
/**
* Update the message digest with the next len bytes in this stream.
* Avoids creating new byte arrays and use internal buffers for performance.
- * @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
*/
@Override
diff --git a/spring-core/src/main/java/org/springframework/util/MultiValueMap.java b/spring-core/src/main/java/org/springframework/util/MultiValueMap.java
index a526af78..408273db 100644
--- a/spring-core/src/main/java/org/springframework/util/MultiValueMap.java
+++ b/spring-core/src/main/java/org/springframework/util/MultiValueMap.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2018 the original author 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,7 @@ public interface MultiValueMap<K, V> extends Map<K, List<V>> {
/**
* Return the first value for the given key.
* @param key the key
- * @return the first value for the specified key, or {@code null}
+ * @return the first value for the specified key, or {@code null} if none
*/
V getFirst(K key);
@@ -55,7 +55,7 @@ public interface MultiValueMap<K, V> extends Map<K, List<V>> {
void setAll(Map<K, V> values);
/**
- * Returns the first values contained in this {@code MultiValueMap}.
+ * Return a {@code Map} with the first values contained in this {@code MultiValueMap}.
* @return a single value representation of this map
*/
Map<K, V> toSingleValueMap();
diff --git a/spring-core/src/main/java/org/springframework/util/ObjectUtils.java b/spring-core/src/main/java/org/springframework/util/ObjectUtils.java
index 10db9f54..05a46229 100644
--- a/spring-core/src/main/java/org/springframework/util/ObjectUtils.java
+++ b/spring-core/src/main/java/org/springframework/util/ObjectUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2017 the original author or authors.
+ * Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -170,7 +170,7 @@ public abstract class ObjectUtils {
/**
* Check whether the given array of enum constants contains a constant with the given name,
* ignoring case when determining a match.
- * @param enumValues the enum values to check, typically the product of a call to MyEnum.values()
+ * @param enumValues the enum values to check, typically obtained via {@code MyEnum.values()}
* @param constant the constant name to find (must not be null or empty string)
* @return whether the constant has been found in the given array
*/
@@ -180,15 +180,14 @@ public abstract class ObjectUtils {
/**
* Check whether the given array of enum constants contains a constant with the given name.
- * @param enumValues the enum values to check, typically the product of a call to MyEnum.values()
+ * @param enumValues the enum values to check, typically obtained via {@code MyEnum.values()}
* @param constant the constant name to find (must not be null or empty string)
* @param caseSensitive whether case is significant in determining a match
* @return whether the constant has been found in the given array
*/
public static boolean containsConstant(Enum<?>[] enumValues, String constant, boolean caseSensitive) {
for (Enum<?> candidate : enumValues) {
- if (caseSensitive ?
- candidate.toString().equals(constant) :
+ if (caseSensitive ? candidate.toString().equals(constant) :
candidate.toString().equalsIgnoreCase(constant)) {
return true;
}
@@ -199,7 +198,7 @@ public abstract class ObjectUtils {
/**
* Case insensitive alternative to {@link Enum#valueOf(Class, String)}.
* @param <E> the concrete Enum type
- * @param enumValues the array of all Enum constants in question, usually per Enum.values()
+ * @param enumValues the array of all Enum constants in question, usually per {@code Enum.values()}
* @param constant the constant to get the enum value of
* @throws IllegalArgumentException if the given constant is not found in the given array
* of enum values. Use {@link #containsConstant(Enum[], String)} as a guard to avoid this exception.
@@ -210,9 +209,8 @@ public abstract class ObjectUtils {
return candidate;
}
}
- throw new IllegalArgumentException(
- String.format("constant [%s] does not exist in enum type %s",
- constant, enumValues.getClass().getComponentType().getName()));
+ throw new IllegalArgumentException("Constant [" + constant + "] does not exist in enum type " +
+ enumValues.getClass().getComponentType().getName());
}
/**
diff --git a/spring-core/src/main/java/org/springframework/util/ReflectionUtils.java b/spring-core/src/main/java/org/springframework/util/ReflectionUtils.java
index a43d016b..4d02886a 100644
--- a/spring-core/src/main/java/org/springframework/util/ReflectionUtils.java
+++ b/spring-core/src/main/java/org/springframework/util/ReflectionUtils.java
@@ -25,7 +25,6 @@ import java.lang.reflect.UndeclaredThrowableException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -635,7 +634,7 @@ public abstract class ReflectionUtils {
for (Method ifcMethod : ifc.getMethods()) {
if (!Modifier.isAbstract(ifcMethod.getModifiers())) {
if (result == null) {
- result = new LinkedList<Method>();
+ result = new ArrayList<Method>();
}
result.add(ifcMethod);
}
@@ -802,19 +801,8 @@ public abstract class ReflectionUtils {
/**
- * Pre-built FieldFilter that matches all non-static, non-final fields.
- */
- public static final FieldFilter COPYABLE_FIELDS = new FieldFilter() {
-
- @Override
- public boolean matches(Field field) {
- return !(Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers()));
- }
- };
-
-
- /**
* Pre-built MethodFilter that matches all non-bridge methods.
+ * @since 3.0
*/
public static final MethodFilter NON_BRIDGED_METHODS = new MethodFilter() {
@@ -828,6 +816,7 @@ public abstract class ReflectionUtils {
/**
* Pre-built MethodFilter that matches all non-bridge methods
* which are not declared on {@code java.lang.Object}.
+ * @since 3.0.5
*/
public static final MethodFilter USER_DECLARED_METHODS = new MethodFilter() {
@@ -837,4 +826,16 @@ public abstract class ReflectionUtils {
}
};
+
+ /**
+ * Pre-built FieldFilter that matches all non-static, non-final fields.
+ */
+ public static final FieldFilter COPYABLE_FIELDS = new FieldFilter() {
+
+ @Override
+ public boolean matches(Field field) {
+ return !(Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers()));
+ }
+ };
+
}
diff --git a/spring-core/src/test/java/org/springframework/core/io/support/SpringFactoriesLoaderTests.java b/spring-core/src/test/java/org/springframework/core/io/support/SpringFactoriesLoaderTests.java
index 4de87d2c..56939b9c 100644
--- a/spring-core/src/test/java/org/springframework/core/io/support/SpringFactoriesLoaderTests.java
+++ b/spring-core/src/test/java/org/springframework/core/io/support/SpringFactoriesLoaderTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2018 the original author 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,8 +33,7 @@ public class SpringFactoriesLoaderTests {
@Test
public void loadFactoriesInCorrectOrder() {
- List<DummyFactory> factories = SpringFactoriesLoader
- .loadFactories(DummyFactory.class, null);
+ List<DummyFactory> factories = SpringFactoriesLoader.loadFactories(DummyFactory.class, null);
assertEquals(2, factories.size());
assertTrue(factories.get(0) instanceof MyDummyFactory1);
assertTrue(factories.get(1) instanceof MyDummyFactory2);
@@ -46,9 +45,9 @@ public class SpringFactoriesLoaderTests {
}
@Test
- public void loadPackagePrivateFactory() throws Exception {
- List<DummyPackagePrivateFactory> factories = SpringFactoriesLoader
- .loadFactories(DummyPackagePrivateFactory.class, null);
+ public void loadPackagePrivateFactory() {
+ List<DummyPackagePrivateFactory> factories =
+ SpringFactoriesLoader.loadFactories(DummyPackagePrivateFactory.class, null);
assertEquals(1, factories.size());
assertTrue((factories.get(0).getClass().getModifiers() & Modifier.PUBLIC) == 0);
}
diff --git a/spring-core/src/test/java/org/springframework/util/FastByteArrayOutputStreamTests.java b/spring-core/src/test/java/org/springframework/util/FastByteArrayOutputStreamTests.java
index 15217c87..69bd0466 100644
--- a/spring-core/src/test/java/org/springframework/util/FastByteArrayOutputStreamTests.java
+++ b/spring-core/src/test/java/org/springframework/util/FastByteArrayOutputStreamTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2018 the original author 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,39 +16,34 @@
package org.springframework.util;
+import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
-import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
/**
- * Test suite for {@link FastByteArrayOutputStream}
+ * Test suite for {@link FastByteArrayOutputStream}.
+ *
* @author Craig Andrews
*/
public class FastByteArrayOutputStreamTests {
private static final int INITIAL_CAPACITY = 256;
- private FastByteArrayOutputStream os;
-
- private byte[] helloBytes;
+ private final FastByteArrayOutputStream os = new FastByteArrayOutputStream(INITIAL_CAPACITY);;
-
- @Before
- public void setUp() throws Exception {
- this.os = new FastByteArrayOutputStream(INITIAL_CAPACITY);
- this.helloBytes = "Hello World".getBytes("UTF-8");
- }
+ private final byte[] helloBytes = "Hello World".getBytes(StandardCharsets.UTF_8);;
@Test
public void size() throws Exception {
this.os.write(this.helloBytes);
- assertEquals(this.os.size(), this.helloBytes.length);
+ assertEquals(this.helloBytes.length, this.os.size());
}
@Test
@@ -124,17 +119,26 @@ public class FastByteArrayOutputStreamTests {
@Test
public void getInputStreamAvailable() throws Exception {
this.os.write(this.helloBytes);
- assertEquals(this.os.getInputStream().available(), this.helloBytes.length);
+ assertEquals(this.helloBytes.length, this.os.getInputStream().available());
}
@Test
public void getInputStreamRead() throws Exception {
this.os.write(this.helloBytes);
InputStream inputStream = this.os.getInputStream();
- assertEquals(inputStream.read(), this.helloBytes[0]);
- assertEquals(inputStream.read(), this.helloBytes[1]);
- assertEquals(inputStream.read(), this.helloBytes[2]);
- assertEquals(inputStream.read(), this.helloBytes[3]);
+ assertEquals(this.helloBytes[0], inputStream.read());
+ assertEquals(this.helloBytes[1], inputStream.read());
+ assertEquals(this.helloBytes[2], inputStream.read());
+ assertEquals(this.helloBytes[3], inputStream.read());
+ }
+
+ @Test
+ public void getInputStreamReadBytePromotion() throws Exception {
+ byte[] bytes = new byte[] { -1 };
+ this.os.write(bytes);
+ InputStream inputStream = this.os.getInputStream();
+ ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
+ assertEquals(bais.read(), inputStream.read());
}
@Test
@@ -166,9 +170,9 @@ public class FastByteArrayOutputStreamTests {
public void getInputStreamSkip() throws Exception {
this.os.write(this.helloBytes);
InputStream inputStream = this.os.getInputStream();
- assertEquals(inputStream.read(), this.helloBytes[0]);
- assertEquals(inputStream.skip(1), 1);
- assertEquals(inputStream.read(), this.helloBytes[2]);
+ assertEquals(this.helloBytes[0], inputStream.read());
+ assertEquals(1, inputStream.skip(1));
+ assertEquals(this.helloBytes[2], inputStream.read());
assertEquals(this.helloBytes.length - 3, inputStream.available());
}
@@ -176,7 +180,7 @@ public class FastByteArrayOutputStreamTests {
public void getInputStreamSkipAll() throws Exception {
this.os.write(this.helloBytes);
InputStream inputStream = this.os.getInputStream();
- assertEquals(inputStream.skip(1000), this.helloBytes.length);
+ assertEquals(this.helloBytes.length, inputStream.skip(1000));
assertEquals(0, inputStream.available());
}
@@ -187,8 +191,7 @@ public class FastByteArrayOutputStreamTests {
InputStream inputStream = this.os.getInputStream();
DigestUtils.appendMd5DigestAsHex(inputStream, builder);
builder.append("\"");
- String actual = builder.toString();
- assertEquals("\"0b10a8db164e0754105b7a99be72e3fe5\"", actual);
+ assertEquals("\"0b10a8db164e0754105b7a99be72e3fe5\"", builder.toString());
}
@Test
@@ -201,8 +204,7 @@ public class FastByteArrayOutputStreamTests {
InputStream inputStream = this.os.getInputStream();
DigestUtils.appendMd5DigestAsHex(inputStream, builder);
builder.append("\"");
- String actual = builder.toString();
- assertEquals("\"06225ca1e4533354c516e74512065331d\"", actual);
+ assertEquals("\"06225ca1e4533354c516e74512065331d\"", builder.toString());
}
diff --git a/spring-core/src/test/java/org/springframework/util/ObjectUtilsTests.java b/spring-core/src/test/java/org/springframework/util/ObjectUtilsTests.java
index d11ea1f7..c783657c 100644
--- a/spring-core/src/test/java/org/springframework/util/ObjectUtilsTests.java
+++ b/spring-core/src/test/java/org/springframework/util/ObjectUtilsTests.java
@@ -45,6 +45,7 @@ public class ObjectUtilsTests {
@Rule
public final ExpectedException exception = ExpectedException.none();
+
@Test
public void isCheckedException() {
assertTrue(ObjectUtils.isCheckedException(new Exception()));
@@ -101,8 +102,8 @@ public class ObjectUtilsTests {
assertTrue(isEmpty(new Object[0]));
assertTrue(isEmpty(new Integer[0]));
- assertFalse(isEmpty(new int[] { 42 }));
- assertFalse(isEmpty(new Integer[] { new Integer(42) }));
+ assertFalse(isEmpty(new int[] {42}));
+ assertFalse(isEmpty(new Integer[] {42}));
}
@Test
@@ -271,7 +272,7 @@ public class ObjectUtilsTests {
@Test
@Deprecated
public void hashCodeWithLong() {
- long lng = 883l;
+ long lng = 883L;
int expected = (new Long(lng)).hashCode();
assertEquals(expected, ObjectUtils.hashCode(lng));
}
@@ -489,12 +490,12 @@ public class ObjectUtilsTests {
@Test
public void nullSafeHashCodeWithLongArray() {
- long lng = 7993l;
+ long lng = 7993L;
int expected = 31 * 7 + (int) (lng ^ (lng >>> 32));
- lng = 84320l;
+ lng = 84320L;
expected = 31 * expected + (int) (lng ^ (lng >>> 32));
- long[] array = {7993l, 84320l};
+ long[] array = {7993L, 84320L};
int actual = ObjectUtils.nullSafeHashCode(array);
assertEquals(expected, actual);
@@ -715,7 +716,7 @@ public class ObjectUtilsTests {
@Test
public void nullSafeToStringWithLongArray() {
- long[] array = {434l, 23423l};
+ long[] array = {434L, 23423L};
assertEquals("{434, 23423}", ObjectUtils.nullSafeToString(array));
}
@@ -737,7 +738,7 @@ public class ObjectUtilsTests {
@Test
public void nullSafeToStringWithObjectArray() {
- Object[] array = {"Han", new Long(43)};
+ Object[] array = {"Han", Long.valueOf(43)};
assertEquals("{Han, 43}", ObjectUtils.nullSafeToString(array));
}
@@ -807,7 +808,8 @@ public class ObjectUtilsTests {
assertThat(ObjectUtils.caseInsensitiveValueOf(Tropes.values(), "BAR"), is(Tropes.BAR));
exception.expect(IllegalArgumentException.class);
- exception.expectMessage(is("constant [bogus] does not exist in enum type org.springframework.util.ObjectUtilsTests$Tropes"));
+ exception.expectMessage(
+ is("Constant [bogus] does not exist in enum type org.springframework.util.ObjectUtilsTests$Tropes"));
ObjectUtils.caseInsensitiveValueOf(Tropes.values(), "bogus");
}
@@ -818,6 +820,6 @@ public class ObjectUtilsTests {
}
- enum Tropes { FOO, BAR, baz }
+ enum Tropes {FOO, BAR, baz}
}
diff --git a/spring-core/src/test/resources/META-INF/spring.factories b/spring-core/src/test/resources/META-INF/spring.factories
index ab64ffe1..3c2c4e9d 100644
--- a/spring-core/src/test/resources/META-INF/spring.factories
+++ b/spring-core/src/test/resources/META-INF/spring.factories
@@ -1,5 +1,5 @@
-org.springframework.core.io.support.DummyFactory=\
-org.springframework.core.io.support.MyDummyFactory2,\
+org.springframework.core.io.support.DummyFactory =\
+org.springframework.core.io.support.MyDummyFactory2, \
org.springframework.core.io.support.MyDummyFactory1
java.lang.String=\
diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardEvaluationContext.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardEvaluationContext.java
index 78a7e91f..8dfa206c 100644
--- a/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardEvaluationContext.java
+++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardEvaluationContext.java
@@ -194,7 +194,7 @@ public class StandardEvaluationContext implements EvaluationContext {
@Override
public TypeConverter getTypeConverter() {
if (this.typeConverter == null) {
- this.typeConverter = new StandardTypeConverter();
+ this.typeConverter = new StandardTypeConverter();
}
return this.typeConverter;
}
@@ -224,7 +224,7 @@ public class StandardEvaluationContext implements EvaluationContext {
this.variables.put(name, value);
}
- public void setVariables(Map<String,Object> variables) {
+ public void setVariables(Map<String, Object> variables) {
this.variables.putAll(variables);
}
diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/BatchUpdateUtils.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/BatchUpdateUtils.java
index d18099a6..d1546a9d 100644
--- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/BatchUpdateUtils.java
+++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/BatchUpdateUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2017 the original author or authors.
+ * Copyright 2002-2018 the original author 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,24 +25,29 @@ import java.util.List;
* Mainly for internal use within the framework.
*
* @author Thomas Risberg
+ * @author Juergen Hoeller
* @since 3.0
*/
public abstract class BatchUpdateUtils {
public static int[] executeBatchUpdate(
- String sql, final List<Object[]> batchValues, final int[] columnTypes, JdbcOperations jdbcOperations) {
+ String sql, final List<Object[]> batchArgs, final int[] columnTypes, JdbcOperations jdbcOperations) {
+
+ if (batchArgs.isEmpty()) {
+ return new int[0];
+ }
return jdbcOperations.batchUpdate(
sql,
new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
- Object[] values = batchValues.get(i);
+ Object[] values = batchArgs.get(i);
setStatementParameters(values, ps, columnTypes);
}
@Override
public int getBatchSize() {
- return batchValues.size();
+ return batchArgs.size();
}
});
}
diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/JdbcOperations.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/JdbcOperations.java
index acd1c034..64aa2a82 100644
--- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/JdbcOperations.java
+++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/JdbcOperations.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2017 the original author or authors.
+ * Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -914,6 +914,7 @@ public interface JdbcOperations {
* @param pss ParameterizedPreparedStatementSetter to use
* @return an array containing for each batch another array containing the numbers of rows affected
* by each update in the batch
+ * @since 3.1
*/
<T> int[][] batchUpdate(String sql, Collection<T> batchArgs, int batchSize,
ParameterizedPreparedStatementSetter<T> pss) throws DataAccessException;
diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/JdbcTemplate.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/JdbcTemplate.java
index 6a2bc943..6f97a196 100644
--- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/JdbcTemplate.java
+++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/JdbcTemplate.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2017 the original author or authors.
+ * Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1018,11 +1018,7 @@ public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
public int[][] doInPreparedStatement(PreparedStatement ps) throws SQLException {
List<int[]> rowsAffected = new ArrayList<int[]>();
try {
- boolean batchSupported = true;
- if (!JdbcUtils.supportsBatchUpdates(ps.getConnection())) {
- batchSupported = false;
- logger.warn("JDBC Driver does not support Batch updates; resorting to single statement execution");
- }
+ boolean batchSupported = JdbcUtils.supportsBatchUpdates(ps.getConnection());
int n = 0;
for (T obj : batchArgs) {
pss.setValues(ps, obj);
diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterBatchUpdateUtils.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterBatchUpdateUtils.java
index cffc7a85..4430e05b 100644
--- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterBatchUpdateUtils.java
+++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterBatchUpdateUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2017 the original author or authors.
+ * Copyright 2002-2018 the original author 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,15 +28,16 @@ import org.springframework.jdbc.core.JdbcOperations;
* Mainly for internal use within the framework.
*
* @author Thomas Risberg
+ * @author Juergen Hoeller
* @since 3.0
*/
public class NamedParameterBatchUpdateUtils extends BatchUpdateUtils {
- public static int[] executeBatchUpdateWithNamedParameters(final ParsedSql parsedSql,
- final SqlParameterSource[] batchArgs, JdbcOperations jdbcOperations) {
+ public static int[] executeBatchUpdateWithNamedParameters(
+ final ParsedSql parsedSql, final SqlParameterSource[] batchArgs, JdbcOperations jdbcOperations) {
- if (batchArgs.length <= 0) {
- return new int[] {0};
+ if (batchArgs.length == 0) {
+ return new int[0];
}
String sqlToUse = NamedParameterUtils.substituteNamedParameters(parsedSql, batchArgs[0]);
diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterUtils.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterUtils.java
index 41a526fb..43d49c87 100644
--- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterUtils.java
+++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterUtils.java
@@ -99,25 +99,25 @@ public abstract class NamedParameterUtils {
char c = statement[i];
if (c == ':' || c == '&') {
int j = i + 1;
- if (j < statement.length && statement[j] == ':' && c == ':') {
+ if (c == ':' && j < statement.length && statement[j] == ':') {
// Postgres-style "::" casting operator should be skipped
i = i + 2;
continue;
}
String parameter = null;
- if (j < statement.length && c == ':' && statement[j] == '{') {
+ if (c == ':' && j < statement.length && statement[j] == '{') {
// :{x} style parameter
- while (j < statement.length && statement[j] != '}') {
+ while (statement[j] != '}') {
j++;
+ if (j >= statement.length) {
+ throw new InvalidDataAccessApiUsageException("Non-terminated named parameter declaration " +
+ "at position " + i + " in statement: " + sql);
+ }
if (statement[j] == ':' || statement[j] == '{') {
throw new InvalidDataAccessApiUsageException("Parameter name contains invalid character '" +
statement[j] + "' at position " + i + " in statement: " + sql);
}
}
- if (j >= statement.length) {
- throw new InvalidDataAccessApiUsageException(
- "Non-terminated named parameter declaration at position " + i + " in statement: " + sql);
- }
if (j - i > 2) {
parameter = sql.substring(i + 2, j);
namedParameterCount = addNewNamedParameter(namedParameters, namedParameterCount, parameter);
@@ -190,7 +190,7 @@ public abstract class NamedParameterUtils {
}
/**
- * Skip over comments and quoted names present in an SQL statement
+ * Skip over comments and quoted names present in an SQL statement.
* @param statement character array containing SQL statement
* @param position current position of statement
* @return next position to process after any comments or quotes are skipped
diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/object/RdbmsOperation.java b/spring-jdbc/src/main/java/org/springframework/jdbc/object/RdbmsOperation.java
index b6ad500c..e285f1b4 100644
--- a/spring-jdbc/src/main/java/org/springframework/jdbc/object/RdbmsOperation.java
+++ b/spring-jdbc/src/main/java/org/springframework/jdbc/object/RdbmsOperation.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2017 the original author or authors.
+ * Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -41,7 +41,7 @@ import org.springframework.util.Assert;
* arguments. Subclasses should be JavaBeans, allowing easy configuration.
*
* <p>This class and subclasses throw runtime exceptions, defined in the
- * <codeorg.springframework.dao package</code> (and as thrown by the
+ * {@code org.springframework.dao} package (and as thrown by the
* {@code org.springframework.jdbc.core} package, which the classes
* in this package use under the hood to perform raw JDBC operations).
*
@@ -71,7 +71,7 @@ public abstract class RdbmsOperation implements InitializingBean {
private boolean returnGeneratedKeys = false;
- private String[] generatedKeysColumnNames = null;
+ private String[] generatedKeysColumnNames;
private String sql;
@@ -282,7 +282,7 @@ public abstract class RdbmsOperation implements InitializingBean {
* Add one or more declared parameters. Used for configuring this operation
* when used in a bean factory. Each parameter will specify SQL type and (optionally)
* the parameter's name.
- * @param parameters Array containing the declared {@link SqlParameter} objects
+ * @param parameters an array containing the declared {@link SqlParameter} objects
* @see #declaredParameters
*/
public void setParameters(SqlParameter... parameters) {
diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/object/SqlCall.java b/spring-jdbc/src/main/java/org/springframework/jdbc/object/SqlCall.java
index ee70d608..eb0d58ff 100644
--- a/spring-jdbc/src/main/java/org/springframework/jdbc/object/SqlCall.java
+++ b/spring-jdbc/src/main/java/org/springframework/jdbc/object/SqlCall.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,31 +39,30 @@ import org.springframework.jdbc.core.SqlParameter;
public abstract class SqlCall extends RdbmsOperation {
/**
- * Object enabling us to create CallableStatementCreators
- * efficiently, based on this class's declared parameters.
- */
- private CallableStatementCreatorFactory callableStatementFactory;
-
- /**
* Flag used to indicate that this call is for a function and to
* use the {? = call get_invoice_count(?)} syntax.
*/
private boolean function = false;
/**
- * Flag used to indicate that the sql for this call should be used exactly as it is
- * defined. No need to add the escape syntax and parameter place holders.
+ * Flag used to indicate that the sql for this call should be used exactly as
+ * it is defined. No need to add the escape syntax and parameter place holders.
*/
private boolean sqlReadyForUse = false;
/**
* Call string as defined in java.sql.CallableStatement.
- * String of form {call add_invoice(?, ?, ?)}
- * or {? = call get_invoice_count(?)} if isFunction is set to true
- * Updated after each parameter is added.
+ * String of form {call add_invoice(?, ?, ?)} or {? = call get_invoice_count(?)}
+ * if isFunction is set to true. Updated after each parameter is added.
*/
private String callString;
+ /**
+ * Object enabling us to create CallableStatementCreators
+ * efficiently, based on this class's declared parameters.
+ */
+ private CallableStatementCreatorFactory callableStatementFactory;
+
/**
* Constructor to allow use as a JavaBean.
@@ -79,8 +78,8 @@ public abstract class SqlCall extends RdbmsOperation {
/**
* Create a new SqlCall object with SQL, but without parameters.
* Must add parameters or settle with none.
- * @param ds DataSource to obtain connections from
- * @param sql SQL to execute
+ * @param ds the DataSource to obtain connections from
+ * @param sql the SQL to execute
*/
public SqlCall(DataSource ds, String sql) {
setDataSource(ds);
@@ -99,7 +98,7 @@ public abstract class SqlCall extends RdbmsOperation {
* Return whether this call is for a function.
*/
public boolean isFunction() {
- return function;
+ return this.function;
}
/**
@@ -113,7 +112,7 @@ public abstract class SqlCall extends RdbmsOperation {
* Return whether the SQL can be used as is.
*/
public boolean isSqlReadyForUse() {
- return sqlReadyForUse;
+ return this.sqlReadyForUse;
}
diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/core/JdbcTemplateTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/core/JdbcTemplateTests.java
index 4e8b2cea..73080c16 100644
--- a/spring-jdbc/src/test/java/org/springframework/jdbc/core/JdbcTemplateTests.java
+++ b/spring-jdbc/src/test/java/org/springframework/jdbc/core/JdbcTemplateTests.java
@@ -28,6 +28,7 @@ import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -152,76 +153,46 @@ public class JdbcTemplateTests {
@Test
public void testStringsWithStaticSql() throws Exception {
- doTestStrings(null, null, null, null, new JdbcTemplateCallback() {
- @Override
- public void doInJdbcTemplate(JdbcTemplate template, String sql, RowCallbackHandler rch) {
- template.query(sql, rch);
- }
- });
+ doTestStrings(null, null, null, null, (template, sql, rch) -> template.query(sql, rch));
}
@Test
public void testStringsWithStaticSqlAndFetchSizeAndMaxRows() throws Exception {
- doTestStrings(10, 20, 30, null, new JdbcTemplateCallback() {
- @Override
- public void doInJdbcTemplate(JdbcTemplate template, String sql, RowCallbackHandler rch) {
- template.query(sql, rch);
- }
- });
+ doTestStrings(10, 20, 30, null, (template, sql, rch) -> template.query(sql, rch));
}
@Test
public void testStringsWithEmptyPreparedStatementSetter() throws Exception {
- doTestStrings(null, null, null, null, new JdbcTemplateCallback() {
- @Override
- public void doInJdbcTemplate(JdbcTemplate template, String sql, RowCallbackHandler rch) {
- template.query(sql, (PreparedStatementSetter) null, rch);
- }
- });
+ doTestStrings(null, null, null, null, (template, sql, rch) ->
+ template.query(sql, (PreparedStatementSetter) null, rch));
}
@Test
public void testStringsWithPreparedStatementSetter() throws Exception {
final Integer argument = 99;
- doTestStrings(null, null, null, argument, new JdbcTemplateCallback() {
- @Override
- public void doInJdbcTemplate(JdbcTemplate template, String sql, RowCallbackHandler rch) {
- template.query(sql, new PreparedStatementSetter() {
- @Override
- public void setValues(PreparedStatement ps) throws SQLException {
- ps.setObject(1, argument);
- }
- }, rch);
- }
- });
+ doTestStrings(null, null, null, argument, (template, sql, rch) -> template.query(sql, ps -> {
+ ps.setObject(1, argument);
+ }, rch));
}
@Test
public void testStringsWithEmptyPreparedStatementArgs() throws Exception {
- doTestStrings(null, null, null, null, new JdbcTemplateCallback() {
- @Override
- public void doInJdbcTemplate(JdbcTemplate template, String sql, RowCallbackHandler rch) {
- template.query(sql, (Object[]) null, rch);
- }
- });
+ doTestStrings(null, null, null, null,
+ (template, sql, rch) -> template.query(sql, (Object[]) null, rch));
}
@Test
public void testStringsWithPreparedStatementArgs() throws Exception {
final Integer argument = 99;
- doTestStrings(null, null, null, argument, new JdbcTemplateCallback() {
- @Override
- public void doInJdbcTemplate(JdbcTemplate template, String sql, RowCallbackHandler rch) {
- template.query(sql, new Object[] { argument }, rch);
- }
- });
+ doTestStrings(null, null, null, argument,
+ (template, sql, rch) -> template.query(sql, new Object[] {argument}, rch));
}
private void doTestStrings(Integer fetchSize, Integer maxRows, Integer queryTimeout,
Object argument, JdbcTemplateCallback jdbcTemplateCallback) throws Exception {
String sql = "SELECT FORENAME FROM CUSTMR";
- String[] results = { "rod", "gary", " portia" };
+ String[] results = {"rod", "gary", " portia"};
class StringHandler implements RowCallbackHandler {
private List<String> list = new LinkedList<>();
@@ -357,11 +328,8 @@ public class JdbcTemplateTests {
this.thrown.expect(sameInstance(runtimeException));
try {
- this.template.query(sql, new RowCallbackHandler() {
- @Override
- public void processRow(ResultSet rs) {
- throw runtimeException;
- }
+ this.template.query(sql, (RowCallbackHandler) rs -> {
+ throw runtimeException;
});
}
finally {
@@ -398,10 +366,10 @@ public class JdbcTemplateTests {
given(this.preparedStatement.executeUpdate()).willReturn(rowsAffected);
int actualRowsAffected = this.template.update(sql,
- new Object[] {4, new SqlParameterValue(Types.NUMERIC, 2, new Float(1.4142))});
+ 4, new SqlParameterValue(Types.NUMERIC, 2, Float.valueOf(1.4142f)));
assertTrue("Actual rows affected is correct", actualRowsAffected == rowsAffected);
verify(this.preparedStatement).setObject(1, 4);
- verify(this.preparedStatement).setObject(2, new Float(1.4142), Types.NUMERIC, 2);
+ verify(this.preparedStatement).setObject(2, Float.valueOf(1.4142f), Types.NUMERIC, 2);
verify(this.preparedStatement).close();
verify(this.connection).close();
}
@@ -463,8 +431,7 @@ public class JdbcTemplateTests {
public void testBatchUpdateWithBatchFailure() throws Exception {
final String[] sql = {"A", "B", "C", "D"};
given(this.statement.executeBatch()).willThrow(
- new BatchUpdateException(new int[] { 1, Statement.EXECUTE_FAILED, 1,
- Statement.EXECUTE_FAILED }));
+ new BatchUpdateException(new int[] {1, Statement.EXECUTE_FAILED, 1, Statement.EXECUTE_FAILED}));
mockDatabaseMetaData(true);
given(this.connection.createStatement()).willReturn(this.statement);
@@ -525,17 +492,15 @@ public class JdbcTemplateTests {
@Test
public void testBatchUpdateWithPreparedStatement() throws Exception {
final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?";
- final int[] ids = new int[] { 100, 200 };
- final int[] rowsAffected = new int[] { 1, 2 };
+ final int[] ids = new int[] {100, 200};
+ final int[] rowsAffected = new int[] {1, 2};
given(this.preparedStatement.executeBatch()).willReturn(rowsAffected);
mockDatabaseMetaData(true);
- BatchPreparedStatementSetter setter =
- new BatchPreparedStatementSetter() {
+ BatchPreparedStatementSetter setter = new BatchPreparedStatementSetter() {
@Override
- public void setValues(PreparedStatement ps, int i)
- throws SQLException {
+ public void setValues(PreparedStatement ps, int i) throws SQLException {
ps.setInt(1, ids[i]);
}
@Override
@@ -561,8 +526,8 @@ public class JdbcTemplateTests {
@Test
public void testInterruptibleBatchUpdate() throws Exception {
final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?";
- final int[] ids = new int[] { 100, 200 };
- final int[] rowsAffected = new int[] { 1, 2 };
+ final int[] ids = new int[] {100, 200};
+ final int[] rowsAffected = new int[] {1, 2};
given(this.preparedStatement.executeBatch()).willReturn(rowsAffected);
mockDatabaseMetaData(true);
@@ -602,8 +567,8 @@ public class JdbcTemplateTests {
@Test
public void testInterruptibleBatchUpdateWithBaseClass() throws Exception {
final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?";
- final int[] ids = new int[] { 100, 200 };
- final int[] rowsAffected = new int[] { 1, 2 };
+ final int[] ids = new int[] {100, 200};
+ final int[] rowsAffected = new int[] {1, 2};
given(this.preparedStatement.executeBatch()).willReturn(rowsAffected);
mockDatabaseMetaData(true);
@@ -639,8 +604,8 @@ public class JdbcTemplateTests {
@Test
public void testInterruptibleBatchUpdateWithBaseClassAndNoBatchSupport() throws Exception {
final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?";
- final int[] ids = new int[] { 100, 200 };
- final int[] rowsAffected = new int[] { 1, 2 };
+ final int[] ids = new int[] {100, 200};
+ final int[] rowsAffected = new int[] {1, 2};
given(this.preparedStatement.executeUpdate()).willReturn(rowsAffected[0], rowsAffected[1]);
mockDatabaseMetaData(false);
@@ -676,8 +641,8 @@ public class JdbcTemplateTests {
@Test
public void testBatchUpdateWithPreparedStatementAndNoBatchSupport() throws Exception {
final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?";
- final int[] ids = new int[] { 100, 200 };
- final int[] rowsAffected = new int[] { 1, 2 };
+ final int[] ids = new int[] {100, 200};
+ final int[] rowsAffected = new int[] {1, 2};
given(this.preparedStatement.executeUpdate()).willReturn(rowsAffected[0], rowsAffected[1]);
@@ -707,7 +672,7 @@ public class JdbcTemplateTests {
@Test
public void testBatchUpdateFails() throws Exception {
final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?";
- final int[] ids = new int[] { 100, 200 };
+ final int[] ids = new int[] {100, 200};
SQLException sqlException = new SQLException();
given(this.preparedStatement.executeBatch()).willThrow(sqlException);
@@ -739,20 +704,27 @@ public class JdbcTemplateTests {
}
@Test
+ public void testBatchUpdateWithEmptyList() throws Exception {
+ final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?";
+ JdbcTemplate template = new JdbcTemplate(this.dataSource, false);
+
+ int[] actualRowsAffected = template.batchUpdate(sql, Collections.emptyList());
+ assertTrue("executed 0 updates", actualRowsAffected.length == 0);
+ }
+
+ @Test
public void testBatchUpdateWithListOfObjectArrays() throws Exception {
final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?";
- final List<Object[]> ids = new ArrayList<>();
+ final List<Object[]> ids = new ArrayList<>(2);
ids.add(new Object[] {100});
ids.add(new Object[] {200});
- final int[] rowsAffected = new int[] { 1, 2 };
+ final int[] rowsAffected = new int[] {1, 2};
given(this.preparedStatement.executeBatch()).willReturn(rowsAffected);
mockDatabaseMetaData(true);
-
JdbcTemplate template = new JdbcTemplate(this.dataSource, false);
int[] actualRowsAffected = template.batchUpdate(sql, ids);
-
assertTrue("executed 2 updates", actualRowsAffected.length == 2);
assertEquals(rowsAffected[0], actualRowsAffected[0]);
assertEquals(rowsAffected[1], actualRowsAffected[1]);
@@ -767,18 +739,17 @@ public class JdbcTemplateTests {
@Test
public void testBatchUpdateWithListOfObjectArraysPlusTypeInfo() throws Exception {
final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?";
- final List<Object[]> ids = new ArrayList<>();
+ final List<Object[]> ids = new ArrayList<>(2);
ids.add(new Object[] {100});
ids.add(new Object[] {200});
final int[] sqlTypes = new int[] {Types.NUMERIC};
- final int[] rowsAffected = new int[] { 1, 2 };
+ final int[] rowsAffected = new int[] {1, 2};
given(this.preparedStatement.executeBatch()).willReturn(rowsAffected);
mockDatabaseMetaData(true);
-
this.template = new JdbcTemplate(this.dataSource, false);
- int[] actualRowsAffected = this.template.batchUpdate(sql, ids, sqlTypes);
+ int[] actualRowsAffected = this.template.batchUpdate(sql, ids, sqlTypes);
assertTrue("executed 2 updates", actualRowsAffected.length == 2);
assertEquals(rowsAffected[0], actualRowsAffected[0]);
assertEquals(rowsAffected[1], actualRowsAffected[1]);
@@ -793,23 +764,17 @@ public class JdbcTemplateTests {
public void testBatchUpdateWithCollectionOfObjects() throws Exception {
final String sql = "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = ?";
final List<Integer> ids = Arrays.asList(100, 200, 300);
- final int[] rowsAffected1 = new int[] { 1, 2 };
- final int[] rowsAffected2 = new int[] { 3 };
+ final int[] rowsAffected1 = new int[] {1, 2};
+ final int[] rowsAffected2 = new int[] {3};
given(this.preparedStatement.executeBatch()).willReturn(rowsAffected1, rowsAffected2);
mockDatabaseMetaData(true);
- ParameterizedPreparedStatementSetter<Integer> setter = new ParameterizedPreparedStatementSetter<Integer>() {
- @Override
- public void setValues(PreparedStatement ps, Integer argument) throws SQLException {
- ps.setInt(1, argument.intValue());
- }
- };
-
+ ParameterizedPreparedStatementSetter<Integer> setter = (ps, argument) -> ps.setInt(1, argument.intValue());
JdbcTemplate template = new JdbcTemplate(this.dataSource, false);
int[][] actualRowsAffected = template.batchUpdate(sql, ids, 2, setter);
- assertTrue("executed 2 updates", actualRowsAffected[0].length == 2);
+ assertEquals("executed 2 updates", 2, actualRowsAffected[0].length);
assertEquals(rowsAffected1[0], actualRowsAffected[0][0]);
assertEquals(rowsAffected1[1], actualRowsAffected[0][1]);
assertEquals(rowsAffected2[0], actualRowsAffected[1][0]);
@@ -823,19 +788,20 @@ public class JdbcTemplateTests {
}
@Test
- public void testCouldntGetConnectionForOperationOrExceptionTranslator() throws SQLException {
+ public void testCouldNotGetConnectionForOperationOrExceptionTranslator() throws SQLException {
SQLException sqlException = new SQLException("foo", "07xxx");
this.dataSource = mock(DataSource.class);
given(this.dataSource.getConnection()).willThrow(sqlException);
JdbcTemplate template = new JdbcTemplate(this.dataSource, false);
RowCountCallbackHandler rcch = new RowCountCallbackHandler();
+
this.thrown.expect(CannotGetJdbcConnectionException.class);
this.thrown.expect(exceptionCause(sameInstance(sqlException)));
template.query("SELECT ID, FORENAME FROM CUSTMR WHERE ID < 3", rcch);
}
@Test
- public void testCouldntGetConnectionForOperationWithLazyExceptionTranslator() throws SQLException {
+ public void testCouldNotGetConnectionForOperationWithLazyExceptionTranslator() throws SQLException {
SQLException sqlException = new SQLException("foo", "07xxx");
this.dataSource = mock(DataSource.class);
given(this.dataSource.getConnection()).willThrow(sqlException);
@@ -843,30 +809,31 @@ public class JdbcTemplateTests {
this.template.setDataSource(this.dataSource);
this.template.afterPropertiesSet();
RowCountCallbackHandler rcch = new RowCountCallbackHandler();
+
this.thrown.expect(CannotGetJdbcConnectionException.class);
this.thrown.expect(exceptionCause(sameInstance(sqlException)));
this.template.query("SELECT ID, FORENAME FROM CUSTMR WHERE ID < 3", rcch);
}
@Test
- public void testCouldntGetConnectionInOperationWithExceptionTranslatorInitializedViaBeanProperty()
+ public void testCouldNotGetConnectionInOperationWithExceptionTranslatorInitializedViaBeanProperty()
throws SQLException {
- doTestCouldntGetConnectionInOperationWithExceptionTranslatorInitialized(true);
+ doTestCouldNotGetConnectionInOperationWithExceptionTranslatorInitialized(true);
}
@Test
- public void testCouldntGetConnectionInOperationWithExceptionTranslatorInitializedInAfterPropertiesSet()
+ public void testCouldNotGetConnectionInOperationWithExceptionTranslatorInitializedInAfterPropertiesSet()
throws SQLException {
- doTestCouldntGetConnectionInOperationWithExceptionTranslatorInitialized(false);
+ doTestCouldNotGetConnectionInOperationWithExceptionTranslatorInitialized(false);
}
/**
* If beanProperty is true, initialize via exception translator bean property;
* if false, use afterPropertiesSet().
*/
- private void doTestCouldntGetConnectionInOperationWithExceptionTranslatorInitialized(boolean beanProperty)
+ private void doTestCouldNotGetConnectionInOperationWithExceptionTranslatorInitialized(boolean beanProperty)
throws SQLException {
SQLException sqlException = new SQLException("foo", "07xxx");
@@ -897,14 +864,9 @@ public class JdbcTemplateTests {
given(this.preparedStatement.executeUpdate()).willReturn(expectedRowsUpdated);
- PreparedStatementSetter pss = new PreparedStatementSetter() {
- @Override
- public void setValues(PreparedStatement ps) throws SQLException {
- ps.setString(1, name);
- }
- };
+ PreparedStatementSetter pss = ps -> ps.setString(1, name);
int actualRowsUpdated = new JdbcTemplate(this.dataSource).update(sql, pss);
- assertTrue("updated correct # of rows", actualRowsUpdated == expectedRowsUpdated);
+ assertEquals("updated correct # of rows", actualRowsUpdated, expectedRowsUpdated);
verify(this.preparedStatement).setString(1, name);
verify(this.preparedStatement).close();
verify(this.connection).close();
@@ -917,12 +879,7 @@ public class JdbcTemplateTests {
SQLException sqlException = new SQLException();
given(this.preparedStatement.executeUpdate()).willThrow(sqlException);
- PreparedStatementSetter pss = new PreparedStatementSetter() {
- @Override
- public void setValues(PreparedStatement ps) throws SQLException {
- ps.setString(1, name);
- }
- };
+ PreparedStatementSetter pss = ps -> ps.setString(1, name);
this.thrown.expect(DataAccessException.class);
this.thrown.expect(exceptionCause(sameInstance(sqlException)));
try {
@@ -936,7 +893,7 @@ public class JdbcTemplateTests {
}
@Test
- public void testCouldntClose() throws Exception {
+ public void testCouldNotClose() throws Exception {
SQLException sqlException = new SQLException("bar");
given(this.connection.createStatement()).willReturn(this.statement);
given(this.resultSet.next()).willReturn(false);
@@ -966,11 +923,8 @@ public class JdbcTemplateTests {
this.thrown.expect(SQLWarningException.class);
this.thrown.expect(exceptionCause(sameInstance(warnings)));
try {
- t.query(sql, new RowCallbackHandler() {
- @Override
- public void processRow(ResultSet rs) throws SQLException {
- rs.getByte(1);
- }
+ t.query(sql, rs -> {
+ rs.getByte(1);
});
}
finally {
@@ -992,11 +946,8 @@ public class JdbcTemplateTests {
// Too long: truncation
this.template.setIgnoreWarnings(true);
- this.template.query(sql, new RowCallbackHandler() {
- @Override
- public void processRow(ResultSet rs) throws java.sql.SQLException {
- rs.getByte(1);
- }
+ this.template.query(sql, rs -> {
+ rs.getByte(1);
});
verify(this.resultSet).close();
@@ -1016,11 +967,8 @@ public class JdbcTemplateTests {
this.thrown.expect(BadSqlGrammarException.class);
this.thrown.expect(exceptionCause(sameInstance(sqlException)));
try {
- this.template.query(sql, new RowCallbackHandler() {
- @Override
- public void processRow(ResultSet rs) throws SQLException {
- throw sqlException;
- }
+ this.template.query(sql, (RowCallbackHandler) rs -> {
+ throw sqlException;
});
fail("Should have thrown BadSqlGrammarException");
}
@@ -1047,11 +995,8 @@ public class JdbcTemplateTests {
this.thrown.expect(BadSqlGrammarException.class);
this.thrown.expect(exceptionCause(sameInstance(sqlException)));
try {
- template.query(sql, new RowCallbackHandler() {
- @Override
- public void processRow(ResultSet rs) throws SQLException {
- throw sqlException;
- }
+ template.query(sql, (RowCallbackHandler) rs -> {
+ throw sqlException;
});
}
finally {
@@ -1084,11 +1029,8 @@ public class JdbcTemplateTests {
this.thrown.expect(BadSqlGrammarException.class);
this.thrown.expect(exceptionCause(sameInstance(sqlException)));
try {
- template.query(sql, new RowCallbackHandler() {
- @Override
- public void processRow(ResultSet rs) throws SQLException {
- throw sqlException;
- }
+ template.query(sql, (RowCallbackHandler) rs -> {
+ throw sqlException;
});
}
finally {
@@ -1204,34 +1146,22 @@ public class JdbcTemplateTests {
given(this.connection.createStatement()).willReturn(this.statement);
try {
- this.template.query("my query", new ResultSetExtractor<Object>() {
- @Override
- public Object extractData(ResultSet rs) {
- throw new InvalidDataAccessApiUsageException("");
- }
+ this.template.query("my query", (ResultSetExtractor<Object>) rs -> {
+ throw new InvalidDataAccessApiUsageException("");
});
fail("Should have thrown InvalidDataAccessApiUsageException");
}
- catch (InvalidDataAccessApiUsageException idaauex) {
+ catch (InvalidDataAccessApiUsageException ex) {
// ok
}
try {
- this.template.query(new PreparedStatementCreator() {
- @Override
- public PreparedStatement createPreparedStatement(Connection con)
- throws SQLException {
- return con.prepareStatement("my query");
- }
- }, new ResultSetExtractor<Object>() {
- @Override
- public Object extractData(ResultSet rs2) {
- throw new InvalidDataAccessApiUsageException("");
- }
+ this.template.query(con -> con.prepareStatement("my query"), (ResultSetExtractor<Object>) rs2 -> {
+ throw new InvalidDataAccessApiUsageException("");
});
fail("Should have thrown InvalidDataAccessApiUsageException");
}
- catch (InvalidDataAccessApiUsageException idaauex) {
+ catch (InvalidDataAccessApiUsageException ex) {
// ok
}
@@ -1247,24 +1177,13 @@ public class JdbcTemplateTests {
given(this.callableStatement.execute()).willReturn(true);
given(this.callableStatement.getUpdateCount()).willReturn(-1);
- List<SqlParameter> params = new ArrayList<>();
- params.add(new SqlReturnResultSet("", new RowCallbackHandler() {
- @Override
- public void processRow(ResultSet rs) {
- throw new InvalidDataAccessApiUsageException("");
- }
-
- }));
+ SqlParameter param = new SqlReturnResultSet("", (RowCallbackHandler) rs -> {
+ throw new InvalidDataAccessApiUsageException("");
+ });
this.thrown.expect(InvalidDataAccessApiUsageException.class);
try {
- this.template.call(new CallableStatementCreator() {
- @Override
- public CallableStatement createCallableStatement(Connection conn)
- throws SQLException {
- return conn.prepareCall("my query");
- }
- }, params);
+ this.template.call(conn -> conn.prepareCall("my query"), Collections.singletonList(param));
}
finally {
verify(this.resultSet).close();
@@ -1275,7 +1194,6 @@ public class JdbcTemplateTests {
@Test
public void testCaseInsensitiveResultsMap() throws Exception {
-
given(this.callableStatement.execute()).willReturn(false);
given(this.callableStatement.getUpdateCount()).willReturn(-1);
given(this.callableStatement.getObject(1)).willReturn("X");
@@ -1287,16 +1205,8 @@ public class JdbcTemplateTests {
assertTrue("now it should have been set to case insensitive",
this.template.isResultsMapCaseInsensitive());
- List<SqlParameter> params = new ArrayList<>();
- params.add(new SqlOutParameter("a", 12));
-
- Map<String, Object> out = this.template.call(new CallableStatementCreator() {
- @Override
- public CallableStatement createCallableStatement(Connection conn)
- throws SQLException {
- return conn.prepareCall("my query");
- }
- }, params);
+ Map<String, Object> out = this.template.call(
+ conn -> conn.prepareCall("my query"), Collections.singletonList(new SqlOutParameter("a", 12)));
assertThat(out, instanceOf(LinkedCaseInsensitiveMap.class));
assertNotNull("we should have gotten the result with upper case", out.get("A"));
diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplateTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplateTests.java
index 56a3bbf4..0b17406c 100644
--- a/spring-jdbc/src/test/java/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplateTests.java
+++ b/spring-jdbc/src/test/java/org/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplateTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2018 the original author 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,14 +34,10 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
-import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.Customer;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCallback;
-import org.springframework.jdbc.core.ResultSetExtractor;
-import org.springframework.jdbc.core.RowCallbackHandler;
-import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.SqlParameterValue;
import static org.junit.Assert.*;
@@ -55,32 +51,40 @@ import static org.mockito.BDDMockito.*;
public class NamedParameterJdbcTemplateTests {
private static final String SELECT_NAMED_PARAMETERS =
- "select id, forename from custmr where id = :id and country = :country";
+ "select id, forename from custmr where id = :id and country = :country";
private static final String SELECT_NAMED_PARAMETERS_PARSED =
- "select id, forename from custmr where id = ? and country = ?";
+ "select id, forename from custmr where id = ? and country = ?";
private static final String SELECT_NO_PARAMETERS =
"select id, forename from custmr";
private static final String UPDATE_NAMED_PARAMETERS =
- "update seat_status set booking_id = null where performance_id = :perfId and price_band_id = :priceId";
+ "update seat_status set booking_id = null where performance_id = :perfId and price_band_id = :priceId";
private static final String UPDATE_NAMED_PARAMETERS_PARSED =
- "update seat_status set booking_id = null where performance_id = ? and price_band_id = ?";
+ "update seat_status set booking_id = null where performance_id = ? and price_band_id = ?";
private static final String[] COLUMN_NAMES = new String[] {"id", "forename"};
+
@Rule
public ExpectedException thrown = ExpectedException.none();
private Connection connection;
+
private DataSource dataSource;
+
private PreparedStatement preparedStatement;
+
private ResultSet resultSet;
+
private DatabaseMetaData databaseMetaData;
- private Map<String, Object> params = new HashMap<String, Object>();
+
+ private Map<String, Object> params = new HashMap<>();
+
private NamedParameterJdbcTemplate namedParameterTemplate;
+
@Before
- public void setUp() throws Exception {
+ public void setup() throws Exception {
connection = mock(Connection.class);
dataSource = mock(DataSource.class);
preparedStatement = mock(PreparedStatement.class);
@@ -95,14 +99,15 @@ public class NamedParameterJdbcTemplateTests {
given(databaseMetaData.supportsBatchUpdates()).willReturn(true);
}
+
@Test
- public void testNullDataSourceProvidedToCtor() throws Exception {
+ public void testNullDataSourceProvidedToCtor() {
thrown.expect(IllegalArgumentException.class);
new NamedParameterJdbcTemplate((DataSource) null);
}
@Test
- public void testNullJdbcTemplateProvidedToCtor() throws Exception {
+ public void testNullJdbcTemplateProvidedToCtor() {
thrown.expect(IllegalArgumentException.class);
new NamedParameterJdbcTemplate((JdbcOperations) null);
}
@@ -114,14 +119,10 @@ public class NamedParameterJdbcTemplateTests {
params.put("perfId", 1);
params.put("priceId", 1);
Object result = namedParameterTemplate.execute(UPDATE_NAMED_PARAMETERS, params,
- new PreparedStatementCallback<Object>() {
- @Override
- public Object doInPreparedStatement(PreparedStatement ps)
- throws SQLException {
- assertEquals(preparedStatement, ps);
- ps.executeUpdate();
- return "result";
- }
+ (PreparedStatementCallback<Object>) ps -> {
+ assertEquals(preparedStatement, ps);
+ ps.executeUpdate();
+ return "result";
});
assertEquals("result", result);
@@ -139,14 +140,10 @@ public class NamedParameterJdbcTemplateTests {
params.put("perfId", new SqlParameterValue(Types.DECIMAL, 1));
params.put("priceId", new SqlParameterValue(Types.INTEGER, 1));
Object result = namedParameterTemplate.execute(UPDATE_NAMED_PARAMETERS, params,
- new PreparedStatementCallback<Object>() {
- @Override
- public Object doInPreparedStatement(PreparedStatement ps)
- throws SQLException {
- assertEquals(preparedStatement, ps);
- ps.executeUpdate();
- return "result";
- }
+ (PreparedStatementCallback<Object>) ps -> {
+ assertEquals(preparedStatement, ps);
+ ps.executeUpdate();
+ return "result";
});
assertEquals("result", result);
@@ -162,14 +159,10 @@ public class NamedParameterJdbcTemplateTests {
given(preparedStatement.executeUpdate()).willReturn(1);
Object result = namedParameterTemplate.execute(SELECT_NO_PARAMETERS,
- new PreparedStatementCallback<Object>() {
- @Override
- public Object doInPreparedStatement(PreparedStatement ps)
- throws SQLException {
- assertEquals(preparedStatement, ps);
- ps.executeQuery();
- return "result";
- }
+ (PreparedStatementCallback<Object>) ps -> {
+ assertEquals(preparedStatement, ps);
+ ps.executeQuery();
+ return "result";
});
assertEquals("result", result);
@@ -187,16 +180,12 @@ public class NamedParameterJdbcTemplateTests {
params.put("id", new SqlParameterValue(Types.DECIMAL, 1));
params.put("country", "UK");
Customer cust = namedParameterTemplate.query(SELECT_NAMED_PARAMETERS, params,
- new ResultSetExtractor<Customer>() {
- @Override
- public Customer extractData(ResultSet rs) throws SQLException,
- DataAccessException {
- rs.next();
- Customer cust = new Customer();
- cust.setId(rs.getInt(COLUMN_NAMES[0]));
- cust.setForename(rs.getString(COLUMN_NAMES[1]));
- return cust;
- }
+ rs -> {
+ rs.next();
+ Customer cust1 = new Customer();
+ cust1.setId(rs.getInt(COLUMN_NAMES[0]));
+ cust1.setForename(rs.getString(COLUMN_NAMES[1]));
+ return cust1;
});
assertTrue("Customer id was assigned correctly", cust.getId() == 1);
@@ -215,16 +204,12 @@ public class NamedParameterJdbcTemplateTests {
given(resultSet.getString("forename")).willReturn("rod");
Customer cust = namedParameterTemplate.query(SELECT_NO_PARAMETERS,
- new ResultSetExtractor<Customer>() {
- @Override
- public Customer extractData(ResultSet rs) throws SQLException,
- DataAccessException {
- rs.next();
- Customer cust = new Customer();
- cust.setId(rs.getInt(COLUMN_NAMES[0]));
- cust.setForename(rs.getString(COLUMN_NAMES[1]));
- return cust;
- }
+ rs -> {
+ rs.next();
+ Customer cust1 = new Customer();
+ cust1.setId(rs.getInt(COLUMN_NAMES[0]));
+ cust1.setForename(rs.getString(COLUMN_NAMES[1]));
+ return cust1;
});
assertTrue("Customer id was assigned correctly", cust.getId() == 1);
@@ -242,15 +227,12 @@ public class NamedParameterJdbcTemplateTests {
params.put("id", new SqlParameterValue(Types.DECIMAL, 1));
params.put("country", "UK");
- final List<Customer> customers = new LinkedList<Customer>();
- namedParameterTemplate.query(SELECT_NAMED_PARAMETERS, params, new RowCallbackHandler() {
- @Override
- public void processRow(ResultSet rs) throws SQLException {
- Customer cust = new Customer();
- cust.setId(rs.getInt(COLUMN_NAMES[0]));
- cust.setForename(rs.getString(COLUMN_NAMES[1]));
- customers.add(cust);
- }
+ final List<Customer> customers = new LinkedList<>();
+ namedParameterTemplate.query(SELECT_NAMED_PARAMETERS, params, rs -> {
+ Customer cust = new Customer();
+ cust.setId(rs.getInt(COLUMN_NAMES[0]));
+ cust.setForename(rs.getString(COLUMN_NAMES[1]));
+ customers.add(cust);
});
assertEquals(1, customers.size());
@@ -269,15 +251,12 @@ public class NamedParameterJdbcTemplateTests {
given(resultSet.getInt("id")).willReturn(1);
given(resultSet.getString("forename")).willReturn("rod");
- final List<Customer> customers = new LinkedList<Customer>();
- namedParameterTemplate.query(SELECT_NO_PARAMETERS, new RowCallbackHandler() {
- @Override
- public void processRow(ResultSet rs) throws SQLException {
- Customer cust = new Customer();
- cust.setId(rs.getInt(COLUMN_NAMES[0]));
- cust.setForename(rs.getString(COLUMN_NAMES[1]));
- customers.add(cust);
- }
+ final List<Customer> customers = new LinkedList<>();
+ namedParameterTemplate.query(SELECT_NO_PARAMETERS, rs -> {
+ Customer cust = new Customer();
+ cust.setId(rs.getInt(COLUMN_NAMES[0]));
+ cust.setForename(rs.getString(COLUMN_NAMES[1]));
+ customers.add(cust);
});
assertEquals(1, customers.size());
@@ -297,14 +276,11 @@ public class NamedParameterJdbcTemplateTests {
params.put("id", new SqlParameterValue(Types.DECIMAL, 1));
params.put("country", "UK");
List<Customer> customers = namedParameterTemplate.query(SELECT_NAMED_PARAMETERS, params,
- new RowMapper<Customer>() {
- @Override
- public Customer mapRow(ResultSet rs, int rownum) throws SQLException {
- Customer cust = new Customer();
- cust.setId(rs.getInt(COLUMN_NAMES[0]));
- cust.setForename(rs.getString(COLUMN_NAMES[1]));
- return cust;
- }
+ (rs, rownum) -> {
+ Customer cust = new Customer();
+ cust.setId(rs.getInt(COLUMN_NAMES[0]));
+ cust.setForename(rs.getString(COLUMN_NAMES[1]));
+ return cust;
});
assertEquals(1, customers.size());
assertTrue("Customer id was assigned correctly", customers.get(0).getId() == 1);
@@ -323,14 +299,11 @@ public class NamedParameterJdbcTemplateTests {
given(resultSet.getString("forename")).willReturn("rod");
List<Customer> customers = namedParameterTemplate.query(SELECT_NO_PARAMETERS,
- new RowMapper<Customer>() {
- @Override
- public Customer mapRow(ResultSet rs, int rownum) throws SQLException {
- Customer cust = new Customer();
- cust.setId(rs.getInt(COLUMN_NAMES[0]));
- cust.setForename(rs.getString(COLUMN_NAMES[1]));
- return cust;
- }
+ (rs, rownum) -> {
+ Customer cust = new Customer();
+ cust.setId(rs.getInt(COLUMN_NAMES[0]));
+ cust.setForename(rs.getString(COLUMN_NAMES[1]));
+ return cust;
});
assertEquals(1, customers.size());
assertTrue("Customer id was assigned correctly", customers.get(0).getId() == 1);
@@ -349,14 +322,11 @@ public class NamedParameterJdbcTemplateTests {
params.put("id", new SqlParameterValue(Types.DECIMAL, 1));
params.put("country", "UK");
Customer cust = namedParameterTemplate.queryForObject(SELECT_NAMED_PARAMETERS, params,
- new RowMapper<Customer>() {
- @Override
- public Customer mapRow(ResultSet rs, int rownum) throws SQLException {
- Customer cust = new Customer();
- cust.setId(rs.getInt(COLUMN_NAMES[0]));
- cust.setForename(rs.getString(COLUMN_NAMES[1]));
- return cust;
- }
+ (rs, rownum) -> {
+ Customer cust1 = new Customer();
+ cust1.setId(rs.getInt(COLUMN_NAMES[0]));
+ cust1.setForename(rs.getString(COLUMN_NAMES[1]));
+ return cust1;
});
assertTrue("Customer id was assigned correctly", cust.getId() == 1);
assertTrue("Customer forename was assigned correctly", cust.getForename().equals("rod"));
@@ -405,15 +375,14 @@ public class NamedParameterJdbcTemplateTests {
final Map<String, Integer>[] ids = new Map[2];
ids[0] = Collections.singletonMap("id", 100);
ids[1] = Collections.singletonMap("id", 200);
- final int[] rowsAffected = new int[] { 1, 2 };
+ final int[] rowsAffected = new int[] {1, 2};
given(preparedStatement.executeBatch()).willReturn(rowsAffected);
given(connection.getMetaData()).willReturn(databaseMetaData);
+ namedParameterTemplate = new NamedParameterJdbcTemplate(new JdbcTemplate(dataSource, false));
- JdbcTemplate template = new JdbcTemplate(dataSource, false);
- namedParameterTemplate = new NamedParameterJdbcTemplate(template);
- int[] actualRowsAffected = namedParameterTemplate.batchUpdate("UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = :id", ids);
-
+ int[] actualRowsAffected = namedParameterTemplate.batchUpdate(
+ "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = :id", ids);
assertTrue("executed 2 updates", actualRowsAffected.length == 2);
assertEquals(rowsAffected[0], actualRowsAffected[0]);
assertEquals(rowsAffected[1], actualRowsAffected[1]);
@@ -426,19 +395,29 @@ public class NamedParameterJdbcTemplateTests {
}
@Test
+ public void testBatchUpdateWithEmptyMap() throws Exception {
+ @SuppressWarnings("unchecked")
+ final Map<String, Integer>[] ids = new Map[0];
+ namedParameterTemplate = new NamedParameterJdbcTemplate(new JdbcTemplate(dataSource, false));
+
+ int[] actualRowsAffected = namedParameterTemplate.batchUpdate(
+ "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = :id", ids);
+ assertTrue("executed 0 updates", actualRowsAffected.length == 0);
+ }
+
+ @Test
public void testBatchUpdateWithSqlParameterSource() throws Exception {
SqlParameterSource[] ids = new SqlParameterSource[2];
ids[0] = new MapSqlParameterSource("id", 100);
ids[1] = new MapSqlParameterSource("id", 200);
- final int[] rowsAffected = new int[] { 1, 2 };
+ final int[] rowsAffected = new int[] {1, 2};
given(preparedStatement.executeBatch()).willReturn(rowsAffected);
given(connection.getMetaData()).willReturn(databaseMetaData);
+ namedParameterTemplate = new NamedParameterJdbcTemplate(new JdbcTemplate(dataSource, false));
- JdbcTemplate template = new JdbcTemplate(dataSource, false);
- namedParameterTemplate = new NamedParameterJdbcTemplate(template);
- int[] actualRowsAffected = namedParameterTemplate.batchUpdate("UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = :id", ids);
-
+ int[] actualRowsAffected = namedParameterTemplate.batchUpdate(
+ "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = :id", ids);
assertTrue("executed 2 updates", actualRowsAffected.length == 2);
assertEquals(rowsAffected[0], actualRowsAffected[0]);
assertEquals(rowsAffected[1], actualRowsAffected[1]);
@@ -455,15 +434,14 @@ public class NamedParameterJdbcTemplateTests {
SqlParameterSource[] ids = new SqlParameterSource[2];
ids[0] = new MapSqlParameterSource().addValue("id", 100, Types.NUMERIC);
ids[1] = new MapSqlParameterSource().addValue("id", 200, Types.NUMERIC);
- final int[] rowsAffected = new int[] { 1, 2 };
+ final int[] rowsAffected = new int[] {1, 2};
given(preparedStatement.executeBatch()).willReturn(rowsAffected);
given(connection.getMetaData()).willReturn(databaseMetaData);
+ namedParameterTemplate = new NamedParameterJdbcTemplate(new JdbcTemplate(dataSource, false));
- JdbcTemplate template = new JdbcTemplate(dataSource, false);
- namedParameterTemplate = new NamedParameterJdbcTemplate(template);
- int[] actualRowsAffected = namedParameterTemplate.batchUpdate("UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = :id", ids);
-
+ int[] actualRowsAffected = namedParameterTemplate.batchUpdate(
+ "UPDATE NOSUCHTABLE SET DATE_DISPATCHED = SYSDATE WHERE ID = :id", ids);
assertTrue("executed 2 updates", actualRowsAffected.length == 2);
assertEquals(rowsAffected[0], actualRowsAffected[0]);
assertEquals(rowsAffected[1], actualRowsAffected[1]);
diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/core/namedparam/NamedParameterUtilsTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/core/namedparam/NamedParameterUtilsTests.java
index 949634f4..3cf4eba6 100644
--- a/spring-jdbc/src/test/java/org/springframework/jdbc/core/namedparam/NamedParameterUtilsTests.java
+++ b/spring-jdbc/src/test/java/org/springframework/jdbc/core/namedparam/NamedParameterUtilsTests.java
@@ -52,11 +52,11 @@ public class NamedParameterUtilsTests {
assertEquals(2, psql2.getTotalParameterCount());
assertEquals(1, psql2.getNamedParameterCount());
- String sql3 = "xxx &a+:b" + '\t' + ":c%10 yyyy ? zzzzz";
+ String sql3 = "xxx &ä+:ö" + '\t' + ":ü%10 yyyy ? zzzzz";
ParsedSql psql3 = NamedParameterUtils.parseSqlStatement(sql3);
- assertEquals("a", psql3.getParameterNames().get(0));
- assertEquals("b", psql3.getParameterNames().get(1));
- assertEquals("c", psql3.getParameterNames().get(2));
+ assertEquals("ä", psql3.getParameterNames().get(0));
+ assertEquals("ö", psql3.getParameterNames().get(1));
+ assertEquals("ü", psql3.getParameterNames().get(2));
}
@Test
@@ -117,13 +117,13 @@ public class NamedParameterUtilsTests {
}
@Test(expected = InvalidDataAccessApiUsageException.class)
- public void buildValueArrayWithMissingParameterValue() throws Exception {
+ public void buildValueArrayWithMissingParameterValue() {
String sql = "select count(0) from foo where id = :id";
NamedParameterUtils.buildValueArray(sql, Collections.<String, Object>emptyMap());
}
@Test
- public void substituteNamedParametersWithStringContainingQuotes() throws Exception {
+ public void substituteNamedParametersWithStringContainingQuotes() {
String expectedSql = "select 'first name' from artists where id = ? and quote = 'exsqueeze me?'";
String sql = "select 'first name' from artists where id = :id and quote = 'exsqueeze me?'";
String newSql = NamedParameterUtils.substituteNamedParameters(sql, new MapSqlParameterSource());
@@ -131,7 +131,7 @@ public class NamedParameterUtilsTests {
}
@Test
- public void testParseSqlStatementWithStringContainingQuotes() throws Exception {
+ public void testParseSqlStatementWithStringContainingQuotes() {
String expectedSql = "select 'first name' from artists where id = ? and quote = 'exsqueeze me?'";
String sql = "select 'first name' from artists where id = :id and quote = 'exsqueeze me?'";
ParsedSql parsedSql = NamedParameterUtils.parseSqlStatement(sql);
@@ -173,7 +173,7 @@ public class NamedParameterUtilsTests {
}
@Test // SPR-4612
- public void parseSqlStatementWithPostgresCasting() throws Exception {
+ public void parseSqlStatementWithPostgresCasting() {
String expectedSql = "select 'first name' from artists where id = ? and birth_date=?::timestamp";
String sql = "select 'first name' from artists where id = :id and birth_date=:birthDate::timestamp";
ParsedSql parsedSql = NamedParameterUtils.parseSqlStatement(sql);
@@ -181,7 +181,7 @@ public class NamedParameterUtilsTests {
}
@Test // SPR-13582
- public void parseSqlStatementWithPostgresContainedOperator() throws Exception {
+ public void parseSqlStatementWithPostgresContainedOperator() {
String expectedSql = "select 'first name' from artists where info->'stat'->'albums' = ?? ? and '[\"1\",\"2\",\"3\"]'::jsonb ?? '4'";
String sql = "select 'first name' from artists where info->'stat'->'albums' = ?? :album and '[\"1\",\"2\",\"3\"]'::jsonb ?? '4'";
ParsedSql parsedSql = NamedParameterUtils.parseSqlStatement(sql);
@@ -190,7 +190,7 @@ public class NamedParameterUtilsTests {
}
@Test // SPR-15382
- public void parseSqlStatementWithPostgresAnyArrayStringsExistsOperator() throws Exception {
+ public void parseSqlStatementWithPostgresAnyArrayStringsExistsOperator() {
String expectedSql = "select '[\"3\", \"11\"]'::jsonb ?| '{1,3,11,12,17}'::text[]";
String sql = "select '[\"3\", \"11\"]'::jsonb ?| '{1,3,11,12,17}'::text[]";
@@ -200,7 +200,7 @@ public class NamedParameterUtilsTests {
}
@Test // SPR-15382
- public void parseSqlStatementWithPostgresAllArrayStringsExistsOperator() throws Exception {
+ public void parseSqlStatementWithPostgresAllArrayStringsExistsOperator() {
String expectedSql = "select '[\"3\", \"11\"]'::jsonb ?& '{1,3,11,12,17}'::text[] AND ? = 'Back in Black'";
String sql = "select '[\"3\", \"11\"]'::jsonb ?& '{1,3,11,12,17}'::text[] AND :album = 'Back in Black'";
@@ -210,7 +210,7 @@ public class NamedParameterUtilsTests {
}
@Test // SPR-7476
- public void parseSqlStatementWithEscapedColon() throws Exception {
+ public void parseSqlStatementWithEscapedColon() {
String expectedSql = "select '0\\:0' as a, foo from bar where baz < DATE(? 23:59:59) and baz = ?";
String sql = "select '0\\:0' as a, foo from bar where baz < DATE(:p1 23\\:59\\:59) and baz = :p2";
@@ -223,7 +223,7 @@ public class NamedParameterUtilsTests {
}
@Test // SPR-7476
- public void parseSqlStatementWithBracketDelimitedParameterNames() throws Exception {
+ public void parseSqlStatementWithBracketDelimitedParameterNames() {
String expectedSql = "select foo from bar where baz = b??z";
String sql = "select foo from bar where baz = b:{p1}:{p2}z";
@@ -236,7 +236,7 @@ public class NamedParameterUtilsTests {
}
@Test // SPR-7476
- public void parseSqlStatementWithEmptyBracketsOrBracketsInQuotes() throws Exception {
+ public void parseSqlStatementWithEmptyBracketsOrBracketsInQuotes() {
String expectedSql = "select foo from bar where baz = b:{}z";
String sql = "select foo from bar where baz = b:{}z";
ParsedSql parsedSql = NamedParameterUtils.parseSqlStatement(sql);
@@ -257,7 +257,7 @@ public class NamedParameterUtilsTests {
public void parseSqlStatementWithSingleLetterInBrackets() {
String expectedSql = "select foo from bar where baz = b?z";
String sql = "select foo from bar where baz = b:{p}z";
-
+
ParsedSql parsedSql = NamedParameterUtils.parseSqlStatement(sql);
assertEquals(1, parsedSql.getParameterNames().size());
assertEquals("p", parsedSql.getParameterNames().get(0));
@@ -273,14 +273,14 @@ public class NamedParameterUtilsTests {
}
@Test // SPR-2544
- public void substituteNamedParametersWithLogicalAnd() throws Exception {
+ public void substituteNamedParametersWithLogicalAnd() {
String expectedSql = "xxx & yyyy";
String newSql = NamedParameterUtils.substituteNamedParameters(expectedSql, new MapSqlParameterSource());
assertEquals(expectedSql, newSql);
}
@Test // SPR-3173
- public void variableAssignmentOperator() throws Exception {
+ public void variableAssignmentOperator() {
String expectedSql = "x := 1";
String newSql = NamedParameterUtils.substituteNamedParameters(expectedSql, new MapSqlParameterSource());
assertEquals(expectedSql, newSql);
diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/InvocableHandlerMethod.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/InvocableHandlerMethod.java
index 0d0a3415..ce00d3ad 100644
--- a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/InvocableHandlerMethod.java
+++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/InvocableHandlerMethod.java
@@ -118,7 +118,7 @@ public class InvocableHandlerMethod extends HandlerMethod {
}
/**
- * Get the method argument values for the current request.
+ * Get the method argument values for the current message.
*/
private Object[] getMethodArgumentValues(Message<?> message, Object... providedArgs) throws Exception {
MethodParameter[] parameters = getMethodParameters();
diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/broker/DefaultSubscriptionRegistry.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/broker/DefaultSubscriptionRegistry.java
index 49673a5b..9691ecea 100644
--- a/spring-messaging/src/main/java/org/springframework/messaging/simp/broker/DefaultSubscriptionRegistry.java
+++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/broker/DefaultSubscriptionRegistry.java
@@ -65,7 +65,7 @@ public class DefaultSubscriptionRegistry extends AbstractSubscriptionRegistry {
public static final int DEFAULT_CACHE_LIMIT = 1024;
/** Static evaluation context to reuse */
- private static EvaluationContext messageEvalContext =
+ private static final EvaluationContext messageEvalContext =
SimpleEvaluationContext.forPropertyAccessors(new SimpMessageHeaderPropertyAccessor()).build();
@@ -128,7 +128,7 @@ public class DefaultSubscriptionRegistry extends AbstractSubscriptionRegistry {
* @since 4.2
*/
public void setSelectorHeaderName(String selectorHeaderName) {
- this.selectorHeaderName = StringUtils.hasText(selectorHeaderName) ? selectorHeaderName : null;
+ this.selectorHeaderName = (StringUtils.hasText(selectorHeaderName) ? selectorHeaderName : null);
}
/**
diff --git a/spring-orm-hibernate4/src/test/java/org/springframework/validation/hibernatevalidator5/MethodValidationTests.java b/spring-orm-hibernate4/src/test/java/org/springframework/validation/hibernatevalidator5/MethodValidationTests.java
index 4a7454b4..0dc53b93 100644
--- a/spring-orm-hibernate4/src/test/java/org/springframework/validation/hibernatevalidator5/MethodValidationTests.java
+++ b/spring-orm-hibernate4/src/test/java/org/springframework/validation/hibernatevalidator5/MethodValidationTests.java
@@ -127,7 +127,16 @@ public class MethodValidationTests {
@Test
public void testLazyValidatorForMethodValidation() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(
- LazyMethodValidationConfig.class, CustomValidatorBean.class, MyValidBean.class, MyValidFactoryBean.class);
+ LazyMethodValidationConfig.class, CustomValidatorBean.class,
+ MyValidBean.class, MyValidFactoryBean.class);
+ ctx.getBeansOfType(MyValidInterface.class).values().forEach(bean -> bean.myValidMethod("value", 5));
+ }
+
+ @Test
+ public void testLazyValidatorForMethodValidationWithProxyTargetClass() {
+ AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(
+ LazyMethodValidationConfigWithProxyTargetClass.class, CustomValidatorBean.class,
+ MyValidBean.class, MyValidFactoryBean.class);
ctx.getBeansOfType(MyValidInterface.class).values().forEach(bean -> bean.myValidMethod("value", 5));
}
@@ -227,4 +236,17 @@ public class MethodValidationTests {
}
}
+
+ @Configuration
+ public static class LazyMethodValidationConfigWithProxyTargetClass {
+
+ @Bean
+ public static MethodValidationPostProcessor methodValidationPostProcessor(@Lazy Validator validator) {
+ MethodValidationPostProcessor postProcessor = new MethodValidationPostProcessor();
+ postProcessor.setValidator(validator);
+ postProcessor.setProxyTargetClass(true);
+ return postProcessor;
+ }
+ }
+
}
diff --git a/spring-test/src/main/java/org/springframework/mock/jndi/ExpectedLookupTemplate.java b/spring-test/src/main/java/org/springframework/mock/jndi/ExpectedLookupTemplate.java
index 7bffe4de..0a6b40fa 100644
--- a/spring-test/src/main/java/org/springframework/mock/jndi/ExpectedLookupTemplate.java
+++ b/spring-test/src/main/java/org/springframework/mock/jndi/ExpectedLookupTemplate.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2018 the original author 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,6 +53,7 @@ public class ExpectedLookupTemplate extends JndiTemplate {
addObject(name, object);
}
+
/**
* Add the given object to the list of JNDI objects that this template will expose.
* @param name the name the client is expected to look up
diff --git a/spring-test/src/main/java/org/springframework/mock/jndi/SimpleNamingContextBuilder.java b/spring-test/src/main/java/org/springframework/mock/jndi/SimpleNamingContextBuilder.java
index d12a968f..fc759e05 100644
--- a/spring-test/src/main/java/org/springframework/mock/jndi/SimpleNamingContextBuilder.java
+++ b/spring-test/src/main/java/org/springframework/mock/jndi/SimpleNamingContextBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2018 the original author 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 javax.naming.spi.NamingManager;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
@@ -35,7 +36,8 @@ import org.springframework.util.ClassUtils;
* configure JNDI appropriately, so that {@code new InitialContext()}
* will expose the required objects. Also usable for standalone applications,
* e.g. for binding a JDBC DataSource to a well-known JNDI location, to be
- * able to use traditional J2EE data access code outside of a J2EE container.
+ * able to use traditional Java EE data access code outside of a Java EE
+ * container.
*
* <p>There are various choices for DataSource implementations:
* <ul>
@@ -98,7 +100,7 @@ public class SimpleNamingContextBuilder implements InitialContextFactoryBuilder
/**
* If no SimpleNamingContextBuilder is already configuring JNDI,
- * create and activate one. Otherwise take the existing activate
+ * create and activate one. Otherwise take the existing activated
* SimpleNamingContextBuilder, clear it and return it.
* <p>This is mainly intended for test suites that want to
* reinitialize JNDI bindings from scratch repeatedly.
@@ -137,12 +139,10 @@ public class SimpleNamingContextBuilder implements InitialContextFactoryBuilder
logger.info("Activating simple JNDI environment");
synchronized (initializationLock) {
if (!initialized) {
- if (NamingManager.hasInitialContextFactoryBuilder()) {
- throw new IllegalStateException(
+ Assert.state(!NamingManager.hasInitialContextFactoryBuilder(),
"Cannot activate SimpleNamingContextBuilder: there is already a JNDI provider registered. " +
"Note that JNDI is a JVM-wide service, shared at the JVM system class loader level, " +
"with no reset option. As a consequence, a JNDI provider must only be registered once per JVM.");
- }
NamingManager.setInitialContextFactoryBuilder(this);
initialized = true;
}
diff --git a/spring-tx/src/main/java/org/springframework/transaction/annotation/Transactional.java b/spring-tx/src/main/java/org/springframework/transaction/annotation/Transactional.java
index eeccff5c..68f7df3f 100644
--- a/spring-tx/src/main/java/org/springframework/transaction/annotation/Transactional.java
+++ b/spring-tx/src/main/java/org/springframework/transaction/annotation/Transactional.java
@@ -27,7 +27,12 @@ import org.springframework.core.annotation.AliasFor;
import org.springframework.transaction.TransactionDefinition;
/**
- * Describes transaction attributes on a method or class.
+ * Describes a transaction attribute on an individual method or on a class.
+ *
+ * <p>At the class level, this annotation applies as a default to all methods of
+ * the declaring class and its subclasses. Note that it does not apply to ancestor
+ * classes up the class hierarchy; methods need to be locally redeclared in order
+ * to participate in a subclass-level annotation.
*
* <p>This annotation type is generally directly comparable to Spring's
* {@link org.springframework.transaction.interceptor.RuleBasedTransactionAttribute}
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 ac65c21c..0d4e2467 100644
--- a/spring-web/src/main/java/org/springframework/http/HttpStatus.java
+++ b/spring-web/src/main/java/org/springframework/http/HttpStatus.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2017 the original author or authors.
+ * Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -420,57 +420,61 @@ public enum HttpStatus {
}
/**
+ * Return the HTTP status series of this status code.
+ * @see HttpStatus.Series
+ */
+ public Series series() {
+ return Series.valueOf(this);
+ }
+
+ /**
* Whether this status code is in the HTTP series
* {@link org.springframework.http.HttpStatus.Series#INFORMATIONAL}.
* This is a shortcut for checking the value of {@link #series()}.
+ * @see #series()
*/
public boolean is1xxInformational() {
- return Series.INFORMATIONAL.equals(series());
+ return (series() == Series.INFORMATIONAL);
}
/**
* Whether this status code is in the HTTP series
* {@link org.springframework.http.HttpStatus.Series#SUCCESSFUL}.
* This is a shortcut for checking the value of {@link #series()}.
+ * @see #series()
*/
public boolean is2xxSuccessful() {
- return Series.SUCCESSFUL.equals(series());
+ return (series() == Series.SUCCESSFUL);
}
/**
* Whether this status code is in the HTTP series
* {@link org.springframework.http.HttpStatus.Series#REDIRECTION}.
* This is a shortcut for checking the value of {@link #series()}.
+ * @see #series()
*/
public boolean is3xxRedirection() {
- return Series.REDIRECTION.equals(series());
+ return (series() == Series.REDIRECTION);
}
-
/**
* Whether this status code is in the HTTP series
* {@link org.springframework.http.HttpStatus.Series#CLIENT_ERROR}.
* This is a shortcut for checking the value of {@link #series()}.
+ * @see #series()
*/
public boolean is4xxClientError() {
- return Series.CLIENT_ERROR.equals(series());
+ return (series() == Series.CLIENT_ERROR);
}
/**
* Whether this status code is in the HTTP series
* {@link org.springframework.http.HttpStatus.Series#SERVER_ERROR}.
* This is a shortcut for checking the value of {@link #series()}.
+ * @see #series()
*/
public boolean is5xxServerError() {
- return Series.SERVER_ERROR.equals(series());
- }
-
- /**
- * Returns the HTTP status series of this status code.
- * @see HttpStatus.Series
- */
- public Series series() {
- return Series.valueOf(this);
+ return (series() == Series.SERVER_ERROR);
}
/**
@@ -523,18 +527,30 @@ public enum HttpStatus {
return this.value;
}
- public static Series valueOf(int status) {
- int seriesCode = status / 100;
+ /**
+ * Return the enum constant of this type with the corresponding series.
+ * @param status a standard HTTP status enum value
+ * @return the enum constant of this type with the corresponding series
+ * @throws IllegalArgumentException if this enum has no corresponding constant
+ */
+ public static Series valueOf(HttpStatus status) {
+ return valueOf(status.value);
+ }
+
+ /**
+ * Return the enum constant of this type with the corresponding series.
+ * @param statusCode the HTTP status code (potentially non-standard)
+ * @return the enum constant of this type with the corresponding series
+ * @throws IllegalArgumentException if this enum has no corresponding constant
+ */
+ public static Series valueOf(int statusCode) {
+ int seriesCode = statusCode / 100;
for (Series series : values()) {
if (series.value == seriesCode) {
return series;
}
}
- throw new IllegalArgumentException("No matching constant for [" + status + "]");
- }
-
- public static Series valueOf(HttpStatus status) {
- return valueOf(status.value);
+ throw new IllegalArgumentException("No matching constant for [" + statusCode + "]");
}
}
diff --git a/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestPart.java b/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestPart.java
index 8f27d5c8..191053cb 100644
--- a/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestPart.java
+++ b/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestPart.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2018 the original author 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,23 +33,24 @@ import org.springframework.web.multipart.MultipartResolver;
* Annotation that can be used to associate the part of a "multipart/form-data" request
* with a method argument.
*
- * <p>Supported method argument types include {@link MultipartFile}
- * in conjunction with Spring's {@link MultipartResolver} abstraction,
- * {@code javax.servlet.http.Part} in conjunction with Servlet 3.0 multipart requests,
- * or otherwise for any other method argument, the content of the part is passed through an
- * {@link HttpMessageConverter} taking into consideration the 'Content-Type' header
- * of the request part. This is analogous to what @{@link RequestBody} does to resolve
- * an argument based on the content of a non-multipart regular request.
+ * <p>Supported method argument types include {@link MultipartFile} in conjunction with
+ * Spring's {@link MultipartResolver} abstraction, {@code javax.servlet.http.Part} in
+ * conjunction with Servlet 3.0 multipart requests, or otherwise for any other method
+ * argument, the content of the part is passed through an {@link HttpMessageConverter}
+ * taking into consideration the 'Content-Type' header of the request part. This is
+ * analogous to what @{@link RequestBody} does to resolve an argument based on the
+ * content of a non-multipart regular request.
*
- * <p>Note that @{@link RequestParam} annotation can also be used to associate the
- * part of a "multipart/form-data" request with a method argument supporting the same
- * method argument types. The main difference is that when the method argument is not a
- * String, @{@link RequestParam} relies on type conversion via a registered
- * {@link Converter} or {@link PropertyEditor} while @{@link RequestPart} relies
- * on {@link HttpMessageConverter}s taking into consideration the 'Content-Type' header
- * of the request part. @{@link RequestParam} is likely to be used with name-value form
- * fields while @{@link RequestPart} is likely to be used with parts containing more
- * complex content (e.g. JSON, XML).
+ * <p>Note that @{@link RequestParam} annotation can also be used to associate the part
+ * of a "multipart/form-data" request with a method argument supporting the same method
+ * argument types. The main difference is that when the method argument is not a String
+ * or raw {@code MultipartFile} / {@code Part}, {@code @RequestParam} relies on type
+ * conversion via a registered {@link Converter} or {@link PropertyEditor} while
+ * {@link RequestPart} relies on {@link HttpMessageConverter HttpMessageConverters}
+ * taking into consideration the 'Content-Type' header of the request part.
+ * {@link RequestParam} is likely to be used with name-value form fields while
+ * {@link RequestPart} is likely to be used with parts containing more complex content
+ * e.g. JSON, XML).
*
* @author Rossen Stoyanchev
* @author Arjen Poutsma
diff --git a/spring-web/src/main/java/org/springframework/web/client/DefaultResponseErrorHandler.java b/spring-web/src/main/java/org/springframework/web/client/DefaultResponseErrorHandler.java
index 467a4767..9c84f776 100644
--- a/spring-web/src/main/java/org/springframework/web/client/DefaultResponseErrorHandler.java
+++ b/spring-web/src/main/java/org/springframework/web/client/DefaultResponseErrorHandler.java
@@ -43,7 +43,11 @@ import org.springframework.util.FileCopyUtils;
public class DefaultResponseErrorHandler implements ResponseErrorHandler {
/**
- * Delegates to {@link #hasError(HttpStatus)} with the response status code.
+ * Delegates to {@link #hasError(HttpStatus)} (for a standard status enum value) or
+ * {@link #hasError(int)} (for an unknown status code) with the response status code.
+ * @see ClientHttpResponse#getRawStatusCode()
+ * @see #hasError(HttpStatus)
+ * @see #hasError(int)
*/
@Override
public boolean hasError(ClientHttpResponse response) throws IOException {
@@ -53,7 +57,7 @@ public class DefaultResponseErrorHandler implements ResponseErrorHandler {
return hasError(statusCode);
}
}
- return false;
+ return hasError(rawStatusCode);
}
/**
@@ -62,13 +66,31 @@ public class DefaultResponseErrorHandler implements ResponseErrorHandler {
* {@link HttpStatus.Series#CLIENT_ERROR CLIENT_ERROR} or
* {@link HttpStatus.Series#SERVER_ERROR SERVER_ERROR}.
* Can be overridden in subclasses.
- * @param statusCode the HTTP status code
- * @return {@code true} if the response has an error; {@code false} otherwise
- * @see #getHttpStatusCode(ClientHttpResponse)
+ * @param statusCode the HTTP status code as enum value
+ * @return {@code true} if the response indicates an error; {@code false} otherwise
+ * @see HttpStatus#is4xxClientError()
+ * @see HttpStatus#is5xxServerError()
*/
protected boolean hasError(HttpStatus statusCode) {
- return (statusCode.series() == HttpStatus.Series.CLIENT_ERROR ||
- statusCode.series() == HttpStatus.Series.SERVER_ERROR);
+ return (statusCode.is4xxClientError() || statusCode.is5xxServerError());
+ }
+
+ /**
+ * Template method called from {@link #hasError(ClientHttpResponse)}.
+ * <p>The default implementation checks if the given status code is
+ * {@code HttpStatus.Series#CLIENT_ERROR CLIENT_ERROR} or
+ * {@code HttpStatus.Series#SERVER_ERROR SERVER_ERROR}.
+ * Can be overridden in subclasses.
+ * @param unknownStatusCode the HTTP status code as raw value
+ * @return {@code true} if the response indicates an error; {@code false} otherwise
+ * @since 4.3.21
+ * @see HttpStatus.Series#CLIENT_ERROR
+ * @see HttpStatus.Series#SERVER_ERROR
+ */
+ protected boolean hasError(int unknownStatusCode) {
+ int seriesCode = unknownStatusCode / 100;
+ return (seriesCode == HttpStatus.Series.CLIENT_ERROR.value() ||
+ seriesCode == HttpStatus.Series.SERVER_ERROR.value());
}
/**
@@ -93,7 +115,6 @@ public class DefaultResponseErrorHandler implements ResponseErrorHandler {
}
}
-
/**
* Determine the HTTP status of the given response.
* <p>Note: Only called from {@link #handleError}, not from {@link #hasError}.
diff --git a/spring-web/src/main/java/org/springframework/web/client/ResponseErrorHandler.java b/spring-web/src/main/java/org/springframework/web/client/ResponseErrorHandler.java
index 2ad6db45..c8056239 100644
--- a/spring-web/src/main/java/org/springframework/web/client/ResponseErrorHandler.java
+++ b/spring-web/src/main/java/org/springframework/web/client/ResponseErrorHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2017 the original author or authors.
+ * Copyright 2002-2018 the original author 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 interface ResponseErrorHandler {
* <p>Implementations will typically inspect the
* {@link ClientHttpResponse#getStatusCode() HttpStatus} of the response.
* @param response the response to inspect
- * @return {@code true} if the response has an error; {@code false} otherwise
+ * @return {@code true} if the response indicates an error; {@code false} otherwise
* @throws IOException in case of I/O errors
*/
boolean hasError(ClientHttpResponse response) throws IOException;
diff --git a/spring-web/src/main/java/org/springframework/web/method/annotation/MapMethodProcessor.java b/spring-web/src/main/java/org/springframework/web/method/annotation/MapMethodProcessor.java
index 07effd2c..30f41dfc 100644
--- a/spring-web/src/main/java/org/springframework/web/method/annotation/MapMethodProcessor.java
+++ b/spring-web/src/main/java/org/springframework/web/method/annotation/MapMethodProcessor.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -56,7 +56,7 @@ public class MapMethodProcessor implements HandlerMethodArgumentResolver, Handle
}
@Override
- @SuppressWarnings({ "unchecked", "rawtypes" })
+ @SuppressWarnings({"unchecked", "rawtypes"})
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
diff --git a/spring-web/src/main/java/org/springframework/web/method/annotation/RequestParamMapMethodArgumentResolver.java b/spring-web/src/main/java/org/springframework/web/method/annotation/RequestParamMapMethodArgumentResolver.java
index 39fce258..2471d034 100644
--- a/spring-web/src/main/java/org/springframework/web/method/annotation/RequestParamMapMethodArgumentResolver.java
+++ b/spring-web/src/main/java/org/springframework/web/method/annotation/RequestParamMapMethodArgumentResolver.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,7 +37,7 @@ import org.springframework.web.method.support.ModelAndViewContainer;
*
* <p>The created {@link Map} contains all request parameter name/value pairs.
* If the method parameter type is {@link MultiValueMap} instead, the created
- * map contains all request parameters and all there values for cases where
+ * map contains all request parameters and all their values for cases where
* request parameters have multiple values.
*
* @author Arjen Poutsma
@@ -50,22 +50,16 @@ public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgum
@Override
public boolean supportsParameter(MethodParameter parameter) {
RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
- if (requestParam != null) {
- if (Map.class.isAssignableFrom(parameter.getParameterType())) {
- return !StringUtils.hasText(requestParam.name());
- }
- }
- return false;
+ return (requestParam != null && Map.class.isAssignableFrom(parameter.getParameterType()) &&
+ !StringUtils.hasText(requestParam.name()));
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
- Class<?> paramType = parameter.getParameterType();
-
Map<String, String[]> parameterMap = webRequest.getParameterMap();
- if (MultiValueMap.class.isAssignableFrom(paramType)) {
+ if (MultiValueMap.class.isAssignableFrom(parameter.getParameterType())) {
MultiValueMap<String, String> result = new LinkedMultiValueMap<String, String>(parameterMap.size());
for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
for (String value : entry.getValue()) {
@@ -84,4 +78,5 @@ public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgum
return result;
}
}
+
}
diff --git a/spring-web/src/main/java/org/springframework/web/method/annotation/RequestParamMethodArgumentResolver.java b/spring-web/src/main/java/org/springframework/web/method/annotation/RequestParamMethodArgumentResolver.java
index 50335548..55c3ba26 100644
--- a/spring-web/src/main/java/org/springframework/web/method/annotation/RequestParamMethodArgumentResolver.java
+++ b/spring-web/src/main/java/org/springframework/web/method/annotation/RequestParamMethodArgumentResolver.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -80,6 +80,7 @@ public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethod
/**
+ * Create a new {@link RequestParamMethodArgumentResolver} instance.
* @param useDefaultResolution in default resolution mode a method argument
* that is a simple type, as defined in {@link BeanUtils#isSimpleProperty},
* is treated as a request parameter even if it isn't annotated, the
@@ -90,6 +91,7 @@ public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethod
}
/**
+ * Create a new {@link RequestParamMethodArgumentResolver} instance.
* @param beanFactory a bean factory used for resolving ${...} placeholder
* and #{...} SpEL expressions in default values, or {@code null} if default
* values are not expected to contain expressions
@@ -108,15 +110,11 @@ public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethod
* Supports the following:
* <ul>
* <li>@RequestParam-annotated method arguments.
- * This excludes {@link Map} params where the annotation doesn't
- * specify a name. See {@link RequestParamMapMethodArgumentResolver}
- * instead for such params.
- * <li>Arguments of type {@link MultipartFile}
- * unless annotated with @{@link RequestPart}.
- * <li>Arguments of type {@code javax.servlet.http.Part}
- * unless annotated with @{@link RequestPart}.
- * <li>In default resolution mode, simple type arguments
- * even if not with @{@link RequestParam}.
+ * This excludes {@link Map} params where the annotation does not specify a name.
+ * See {@link RequestParamMapMethodArgumentResolver} instead for such params.
+ * <li>Arguments of type {@link MultipartFile} unless annotated with @{@link RequestPart}.
+ * <li>Arguments of type {@code Part} unless annotated with @{@link RequestPart}.
+ * <li>In default resolution mode, simple type arguments even if not with @{@link RequestParam}.
* </ul>
*/
@Override
diff --git a/spring-web/src/test/java/org/springframework/web/client/DefaultResponseErrorHandlerTests.java b/spring-web/src/test/java/org/springframework/web/client/DefaultResponseErrorHandlerTests.java
index 1c5e911a..4684a2d7 100644
--- a/spring-web/src/test/java/org/springframework/web/client/DefaultResponseErrorHandlerTests.java
+++ b/spring-web/src/test/java/org/springframework/web/client/DefaultResponseErrorHandlerTests.java
@@ -103,8 +103,20 @@ public class DefaultResponseErrorHandlerTests {
handler.handleError(response);
}
+ @Test // SPR-16108
+ public void hasErrorForUnknownStatusCode() throws Exception {
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.TEXT_PLAIN);
+
+ given(response.getRawStatusCode()).willReturn(999);
+ given(response.getStatusText()).willReturn("Custom status code");
+ given(response.getHeaders()).willReturn(headers);
+
+ assertFalse(handler.hasError(response));
+ }
+
@Test(expected = UnknownHttpStatusCodeException.class) // SPR-9406
- public void unknownStatusCode() throws Exception {
+ public void handleErrorUnknownStatusCode() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.TEXT_PLAIN);
@@ -115,23 +127,59 @@ public class DefaultResponseErrorHandlerTests {
handler.handleError(response);
}
- @Test // SPR-16108
- public void hasErrorForUnknownStatusCode() throws Exception {
+ @Test // SPR-17461
+ public void hasErrorForCustomClientError() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.TEXT_PLAIN);
- given(response.getRawStatusCode()).willReturn(999);
+ given(response.getRawStatusCode()).willReturn(499);
given(response.getStatusText()).willReturn("Custom status code");
given(response.getHeaders()).willReturn(headers);
- assertFalse(handler.hasError(response));
+ assertTrue(handler.hasError(response));
+ }
+
+ @Test(expected = UnknownHttpStatusCodeException.class)
+ public void handleErrorForCustomClientError() throws Exception {
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.TEXT_PLAIN);
+
+ given(response.getStatusCode()).willThrow(new IllegalArgumentException("No matching constant for 499"));
+ given(response.getStatusText()).willReturn("Custom status code");
+ given(response.getHeaders()).willReturn(headers);
+
+ handler.handleError(response);
+ }
+
+ @Test // SPR-17461
+ public void hasErrorForCustomServerError() throws Exception {
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.TEXT_PLAIN);
+
+ given(response.getRawStatusCode()).willReturn(599);
+ given(response.getStatusText()).willReturn("Custom status code");
+ given(response.getHeaders()).willReturn(headers);
+
+ assertTrue(handler.hasError(response));
+ }
+
+ @Test(expected = UnknownHttpStatusCodeException.class)
+ public void handleErrorForCustomServerError() throws Exception {
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.TEXT_PLAIN);
+
+ given(response.getStatusCode()).willThrow(new IllegalArgumentException("No matching constant for 599"));
+ given(response.getStatusText()).willReturn("Custom status code");
+ given(response.getHeaders()).willReturn(headers);
+
+ handler.handleError(response);
}
@Test // SPR-16604
public void bodyAvailableAfterHasErrorForUnknownStatusCode() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.TEXT_PLAIN);
- TestByteArrayInputStream body = new TestByteArrayInputStream("Hello World".getBytes("UTF-8"));
+ TestByteArrayInputStream body = new TestByteArrayInputStream("Hello World".getBytes(UTF8));
given(response.getRawStatusCode()).willReturn(999);
given(response.getStatusText()).willReturn("Custom status code");
diff --git a/spring-web/src/test/java/org/springframework/web/method/annotation/RequestHeaderMapMethodArgumentResolverTests.java b/spring-web/src/test/java/org/springframework/web/method/annotation/RequestHeaderMapMethodArgumentResolverTests.java
index ef2a3f01..8b8bddd3 100644
--- a/spring-web/src/test/java/org/springframework/web/method/annotation/RequestHeaderMapMethodArgumentResolverTests.java
+++ b/spring-web/src/test/java/org/springframework/web/method/annotation/RequestHeaderMapMethodArgumentResolverTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -104,7 +104,7 @@ public class RequestHeaderMapMethodArgumentResolverTests {
request.addHeader(name, value1);
request.addHeader(name, value2);
- MultiValueMap<String, String> expected = new LinkedMultiValueMap<String, String>(1);
+ MultiValueMap<String, String> expected = new LinkedMultiValueMap<>(1);
expected.add(name, value1);
expected.add(name, value2);
@@ -135,9 +135,8 @@ public class RequestHeaderMapMethodArgumentResolverTests {
public void params(@RequestHeader Map<?, ?> param1,
- @RequestHeader MultiValueMap<?, ?> param2,
- @RequestHeader HttpHeaders param3,
- Map<?,?> unsupported) {
+ @RequestHeader MultiValueMap<?, ?> param2, @RequestHeader HttpHeaders param3,
+ Map<?, ?> unsupported) {
}
}
diff --git a/spring-web/src/test/java/org/springframework/web/method/annotation/RequestHeaderMethodArgumentResolverTests.java b/spring-web/src/test/java/org/springframework/web/method/annotation/RequestHeaderMethodArgumentResolverTests.java
index a453abb0..db2e766c 100644
--- a/spring-web/src/test/java/org/springframework/web/method/annotation/RequestHeaderMethodArgumentResolverTests.java
+++ b/spring-web/src/test/java/org/springframework/web/method/annotation/RequestHeaderMethodArgumentResolverTests.java
@@ -44,7 +44,7 @@ import org.springframework.web.context.support.GenericWebApplicationContext;
import static org.junit.Assert.*;
/**
- * Test fixture with {@link org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver}.
+ * Test fixture with {@link RequestHeaderMethodArgumentResolver}.
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
@@ -70,7 +70,7 @@ public class RequestHeaderMethodArgumentResolverTests {
@Before
@SuppressWarnings("resource")
- public void setUp() throws Exception {
+ public void setup() throws Exception {
GenericWebApplicationContext context = new GenericWebApplicationContext();
context.refresh();
resolver = new RequestHeaderMethodArgumentResolver(context.getBeanFactory());
@@ -94,7 +94,7 @@ public class RequestHeaderMethodArgumentResolverTests {
}
@After
- public void teardown() {
+ public void reset() {
RequestContextHolder.resetRequestAttributes();
}
diff --git a/spring-web/src/test/java/org/springframework/web/method/annotation/RequestParamMapMethodArgumentResolverTests.java b/spring-web/src/test/java/org/springframework/web/method/annotation/RequestParamMapMethodArgumentResolverTests.java
index cae73f46..757dbe95 100644
--- a/spring-web/src/test/java/org/springframework/web/method/annotation/RequestParamMapMethodArgumentResolverTests.java
+++ b/spring-web/src/test/java/org/springframework/web/method/annotation/RequestParamMapMethodArgumentResolverTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-2018 the original author 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,10 +59,10 @@ public class RequestParamMapMethodArgumentResolverTests {
@Before
- public void setUp() throws Exception {
+ public void setup() throws Exception {
resolver = new RequestParamMapMethodArgumentResolver();
- Method method = getClass().getMethod("params", Map.class, MultiValueMap.class, Map.class, Map.class);
+ Method method = getClass().getMethod("handle", Map.class, MultiValueMap.class, Map.class, Map.class);
paramMap = new SynthesizingMethodParameter(method, 0);
paramMultiValueMap = new SynthesizingMethodParameter(method, 1);
paramNamedMap = new SynthesizingMethodParameter(method, 2);
@@ -99,9 +99,9 @@ public class RequestParamMapMethodArgumentResolverTests {
String name = "foo";
String value1 = "bar";
String value2 = "baz";
- request.addParameter(name, new String[]{value1, value2});
+ request.addParameter(name, value1, value2);
- MultiValueMap<String, String> expected = new LinkedMultiValueMap<String, String>(1);
+ MultiValueMap<String, String> expected = new LinkedMultiValueMap<>(1);
expected.add(name, value1);
expected.add(name, value2);
@@ -112,10 +112,11 @@ public class RequestParamMapMethodArgumentResolverTests {
}
- public void params(@RequestParam Map<?, ?> param1,
- @RequestParam MultiValueMap<?, ?> param2,
- @RequestParam("name") Map<?, ?> param3,
- Map<?, ?> param4) {
+ public void handle(
+ @RequestParam Map<?, ?> param1,
+ @RequestParam MultiValueMap<?, ?> param2,
+ @RequestParam("name") Map<?, ?> param3,
+ Map<?, ?> param4) {
}
}
diff --git a/spring-web/src/test/java/org/springframework/web/method/annotation/RequestParamMethodArgumentResolverTests.java b/spring-web/src/test/java/org/springframework/web/method/annotation/RequestParamMethodArgumentResolverTests.java
index 881e1d28..9d88a246 100644
--- a/spring-web/src/test/java/org/springframework/web/method/annotation/RequestParamMethodArgumentResolverTests.java
+++ b/spring-web/src/test/java/org/springframework/web/method/annotation/RequestParamMethodArgumentResolverTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -94,7 +94,7 @@ public class RequestParamMethodArgumentResolverTests {
@Before
- public void setUp() throws Exception {
+ public void setup() throws Exception {
resolver = new RequestParamMethodArgumentResolver(null, true);
ParameterNameDiscoverer paramNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
Method method = ReflectionUtils.findMethod(getClass(), "handle", (Class<?>[]) null);
@@ -369,7 +369,7 @@ public class RequestParamMethodArgumentResolverTests {
WebDataBinderFactory binderFactory = mock(WebDataBinderFactory.class);
given(binderFactory.createBinder(webRequest, null, "stringNotAnnot")).willReturn(binder);
- this.request.addParameter("stringNotAnnot", "");
+ request.addParameter("stringNotAnnot", "");
Object arg = resolver.resolveArgument(paramStringNotAnnot, null, webRequest, binderFactory);
assertNull(arg);
@@ -383,7 +383,7 @@ public class RequestParamMethodArgumentResolverTests {
WebDataBinderFactory binderFactory = mock(WebDataBinderFactory.class);
given(binderFactory.createBinder(webRequest, null, "name")).willReturn(binder);
- this.request.addParameter("name", "");
+ request.addParameter("name", "");
Object arg = resolver.resolveArgument(paramNotRequired, null, webRequest, binderFactory);
assertNull(arg);
@@ -406,21 +406,21 @@ public class RequestParamMethodArgumentResolverTests {
@Test // SPR-10180
public void resolveEmptyValueToDefault() throws Exception {
- this.request.addParameter("name", "");
+ request.addParameter("name", "");
Object result = resolver.resolveArgument(paramNamedDefaultValueString, null, webRequest, null);
assertEquals("bar", result);
}
@Test
public void resolveEmptyValueWithoutDefault() throws Exception {
- this.request.addParameter("stringNotAnnot", "");
+ request.addParameter("stringNotAnnot", "");
Object result = resolver.resolveArgument(paramStringNotAnnot, null, webRequest, null);
assertEquals("", result);
}
@Test
public void resolveEmptyValueRequiredWithoutDefault() throws Exception {
- this.request.addParameter("name", "");
+ request.addParameter("name", "");
Object result = resolver.resolveArgument(paramRequired, null, webRequest, null);
assertEquals("", result);
}
@@ -435,7 +435,7 @@ public class RequestParamMethodArgumentResolverTests {
Object result = resolver.resolveArgument(paramOptional, null, webRequest, binderFactory);
assertEquals(Optional.empty(), result);
- this.request.addParameter("name", "123");
+ request.addParameter("name", "123");
result = resolver.resolveArgument(paramOptional, null, webRequest, binderFactory);
assertEquals(Optional.class, result.getClass());
assertEquals(123, ((Optional) result).get());
@@ -466,7 +466,7 @@ public class RequestParamMethodArgumentResolverTests {
Object result = resolver.resolveArgument(paramOptionalArray, null, webRequest, binderFactory);
assertEquals(Optional.empty(), result);
- this.request.addParameter("name", "123", "456");
+ request.addParameter("name", "123", "456");
result = resolver.resolveArgument(paramOptionalArray, null, webRequest, binderFactory);
assertEquals(Optional.class, result.getClass());
assertArrayEquals(new Integer[] {123, 456}, (Integer[]) ((Optional) result).get());
@@ -497,7 +497,7 @@ public class RequestParamMethodArgumentResolverTests {
Object result = resolver.resolveArgument(paramOptionalList, null, webRequest, binderFactory);
assertEquals(Optional.empty(), result);
- this.request.addParameter("name", "123", "456");
+ request.addParameter("name", "123", "456");
result = resolver.resolveArgument(paramOptionalList, null, webRequest, binderFactory);
assertEquals(Optional.class, result.getClass());
assertEquals(Arrays.asList("123", "456"), ((Optional) result).get());
diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/HandlerMapping.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/HandlerMapping.java
index 117bff35..da03f81f 100644
--- a/spring-webmvc/src/main/java/org/springframework/web/servlet/HandlerMapping.java
+++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/HandlerMapping.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2018 the original author 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,13 @@ import javax.servlet.http.HttpServletRequest;
public interface HandlerMapping {
/**
+ * Name of the {@link HttpServletRequest} attribute that contains the mapped
+ * handler for the best matching pattern.
+ * @since 4.3.21
+ */
+ String BEST_MATCHING_HANDLER_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingHandler";
+
+ /**
* Name of the {@link HttpServletRequest} attribute that contains the path
* within the handler mapping, in case of a pattern match, or the full
* relevant URI (typically within the DispatcherServlet's mapping) else.
diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerExceptionResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerExceptionResolver.java
index b401b525..98c33b9d 100644
--- a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerExceptionResolver.java
+++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerExceptionResolver.java
@@ -132,8 +132,8 @@ public abstract class AbstractHandlerExceptionResolver implements HandlerExcepti
ModelAndView result = doResolveException(request, response, handler, ex);
if (result != null) {
// Print warn message when warn logger is not enabled...
- if (logger.isWarnEnabled() && (this.warnLogger == null || !this.warnLogger.isWarnEnabled())) {
- logger.warn("Resolved [" + ex + "]" + (result.isEmpty() ? "" : " to " + result));
+ if (logger.isDebugEnabled() && (this.warnLogger == null || !this.warnLogger.isWarnEnabled())) {
+ logger.debug("Resolved [" + ex + "]" + (result.isEmpty() ? "" : " to " + result));
}
// warnLogger with full stack trace (requires explicit config)
logException(ex, request);
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 f471b36a..3893b2a0 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
@@ -367,6 +367,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
}
}
+ request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractUrlHandlerMapping.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractUrlHandlerMapping.java
index 16a120cb..48571d6b 100644
--- a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractUrlHandlerMapping.java
+++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractUrlHandlerMapping.java
@@ -407,6 +407,7 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping i
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
exposePathWithinMapping(this.bestMatchingPattern, this.pathWithinMapping, request);
+ request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, handler);
request.setAttribute(INTROSPECT_TYPE_LEVEL_MAPPING, supportsTypeLevelMappings());
return true;
}
diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/annotation/ResponseStatusExceptionResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/annotation/ResponseStatusExceptionResolver.java
index 80ee3852..cca63932 100644
--- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/annotation/ResponseStatusExceptionResolver.java
+++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/annotation/ResponseStatusExceptionResolver.java
@@ -72,8 +72,7 @@ public class ResponseStatusExceptionResolver extends AbstractHandlerExceptionRes
}
}
else if (ex.getCause() instanceof Exception) {
- ex = (Exception) ex.getCause();
- return doResolveException(request, response, handler, ex);
+ return doResolveException(request, response, handler, (Exception) ex.getCause());
}
return null;
}
diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestPartMethodArgumentResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestPartMethodArgumentResolver.java
index bddd9190..941e47bd 100644
--- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestPartMethodArgumentResolver.java
+++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestPartMethodArgumentResolver.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2017 the original author or authors.
+ * Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -41,7 +41,7 @@ import org.springframework.web.multipart.support.RequestPartServletServerHttpReq
/**
* Resolves the following method arguments:
* <ul>
- * <li>Annotated with {@code @RequestPart}
+ * <li>Annotated with @{@link RequestPart}
* <li>Of type {@link MultipartFile} in conjunction with Spring's {@link MultipartResolver} abstraction
* <li>Of type {@code javax.servlet.http.Part} in conjunction with Servlet 3.0 multipart requests
* </ul>
@@ -85,11 +85,13 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM
/**
- * Supports the following:
+ * Whether the given {@linkplain MethodParameter method parameter} is a multi-part
+ * supported. Supports the following:
* <ul>
* <li>annotated with {@code @RequestPart}
* <li>of type {@link MultipartFile} unless annotated with {@code @RequestParam}
- * <li>of type {@code javax.servlet.http.Part} unless annotated with {@code @RequestParam}
+ * <li>of type {@code javax.servlet.http.Part} unless annotated with
+ * {@code @RequestParam}
* </ul>
*/
@Override
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 a92c33a6..22a14efa 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
@@ -97,6 +97,7 @@ public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionRes
*/
public DefaultHandlerExceptionResolver() {
setOrder(Ordered.LOWEST_PRECEDENCE);
+ setWarnLogCategory(getClass().getName());
}
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 531167c1..d1dee3c9 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
@@ -641,22 +641,25 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
*/
protected boolean isInvalidPath(String path) {
if (path.contains("WEB-INF") || path.contains("META-INF")) {
- logger.trace("Path contains \"WEB-INF\" or \"META-INF\".");
+ if (logger.isTraceEnabled()) {
+ logger.trace("Path with \"WEB-INF\" or \"META-INF\": [" + path + "]");
+ }
return true;
}
if (path.contains(":/")) {
String relativePath = (path.charAt(0) == '/' ? path.substring(1) : path);
if (ResourceUtils.isUrl(relativePath) || relativePath.startsWith("url:")) {
- logger.trace("Path represents URL or has \"url:\" prefix.");
+ if (logger.isTraceEnabled()) {
+ logger.trace("Path represents URL or has \"url:\" prefix: [" + path + "]");
+ }
return true;
}
}
- if (path.contains("..")) {
- path = StringUtils.cleanPath(path);
- if (path.contains("../")) {
- logger.trace("Path contains \"../\" after call to StringUtils#cleanPath.");
- return true;
+ if (path.contains("..") && StringUtils.cleanPath(path).contains("../")) {
+ if (logger.isTraceEnabled()) {
+ logger.trace("Path contains \"../\" after call to StringUtils#cleanPath: [" + path + "]");
}
+ return true;
}
return false;
}
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 d6ca1c33..64dfed2f 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
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2018 the original author 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 javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
@@ -34,8 +35,7 @@ import org.springframework.web.util.UrlPathHelper;
/**
* A filter that wraps the {@link HttpServletResponse} and overrides its
* {@link HttpServletResponse#encodeURL(String) encodeURL} method in order to
- * translate internal resource request URLs into public URL paths for external
- * use.
+ * translate internal resource request URLs into public URL paths for external use.
*
* @author Jeremy Grelle
* @author Rossen Stoyanchev
@@ -50,67 +50,52 @@ public class ResourceUrlEncodingFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
- throws IOException, ServletException {
+ throws ServletException, IOException {
+
if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
- throw new ServletException("ResourceUrlEncodingFilter just supports HTTP requests");
+ throw new ServletException("ResourceUrlEncodingFilter only supports HTTP requests");
}
- HttpServletRequest httpRequest = (HttpServletRequest) request;
- HttpServletResponse httpResponse = (HttpServletResponse) response;
- filterChain.doFilter(httpRequest, new ResourceUrlEncodingResponseWrapper(httpRequest, httpResponse));
+ ResourceUrlEncodingRequestWrapper wrappedRequest =
+ new ResourceUrlEncodingRequestWrapper((HttpServletRequest) request);
+ ResourceUrlEncodingResponseWrapper wrappedResponse =
+ new ResourceUrlEncodingResponseWrapper(wrappedRequest, (HttpServletResponse) response);
+ filterChain.doFilter(wrappedRequest, wrappedResponse);
}
- private static class ResourceUrlEncodingResponseWrapper extends HttpServletResponseWrapper {
+ private static class ResourceUrlEncodingRequestWrapper extends HttpServletRequestWrapper {
- private final HttpServletRequest request;
+ private ResourceUrlProvider resourceUrlProvider;
- /* Cache the index and prefix of the path within the DispatcherServlet mapping */
private Integer indexLookupPath;
private String prefixLookupPath;
- public ResourceUrlEncodingResponseWrapper(HttpServletRequest request, HttpServletResponse wrapped) {
- super(wrapped);
- this.request = request;
+ ResourceUrlEncodingRequestWrapper(HttpServletRequest request) {
+ super(request);
}
@Override
- public String encodeURL(String url) {
- ResourceUrlProvider resourceUrlProvider = getResourceUrlProvider();
- if (resourceUrlProvider == null) {
- logger.debug("Request attribute exposing ResourceUrlProvider not found");
- return super.encodeURL(url);
- }
-
- initLookupPath(resourceUrlProvider);
- if (url.startsWith(this.prefixLookupPath)) {
- int suffixIndex = getQueryParamsIndex(url);
- String suffix = url.substring(suffixIndex);
- String lookupPath = url.substring(this.indexLookupPath, suffixIndex);
- lookupPath = resourceUrlProvider.getForLookupPath(lookupPath);
- if (lookupPath != null) {
- return super.encodeURL(this.prefixLookupPath + lookupPath + suffix);
+ public void setAttribute(String name, Object value) {
+ super.setAttribute(name, value);
+ if (ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR.equals(name)) {
+ if (value instanceof ResourceUrlProvider) {
+ initLookupPath((ResourceUrlProvider) value);
}
}
- return super.encodeURL(url);
- }
-
- private ResourceUrlProvider getResourceUrlProvider() {
- return (ResourceUrlProvider) this.request.getAttribute(
- ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR);
}
private void initLookupPath(ResourceUrlProvider urlProvider) {
+ this.resourceUrlProvider = urlProvider;
if (this.indexLookupPath == null) {
- UrlPathHelper pathHelper = urlProvider.getUrlPathHelper();
- String requestUri = pathHelper.getRequestUri(this.request);
- String lookupPath = pathHelper.getLookupPathForRequest(this.request);
+ UrlPathHelper pathHelper = this.resourceUrlProvider.getUrlPathHelper();
+ String requestUri = pathHelper.getRequestUri(this);
+ String lookupPath = pathHelper.getLookupPathForRequest(this);
this.indexLookupPath = requestUri.lastIndexOf(lookupPath);
this.prefixLookupPath = requestUri.substring(0, this.indexLookupPath);
-
if ("/".equals(lookupPath) && !"/".equals(requestUri)) {
- String contextPath = pathHelper.getContextPath(this.request);
+ String contextPath = pathHelper.getContextPath(this);
if (requestUri.equals(contextPath)) {
this.indexLookupPath = requestUri.length();
this.prefixLookupPath = requestUri;
@@ -119,10 +104,48 @@ public class ResourceUrlEncodingFilter extends GenericFilterBean {
}
}
+ public String resolveUrlPath(String url) {
+ if (this.resourceUrlProvider == null) {
+ logger.trace("ResourceUrlProvider not available via request attribute " +
+ "ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR");
+ return null;
+ }
+ if (this.indexLookupPath != null && url.startsWith(this.prefixLookupPath)) {
+ int suffixIndex = getQueryParamsIndex(url);
+ String suffix = url.substring(suffixIndex);
+ String lookupPath = url.substring(this.indexLookupPath, suffixIndex);
+ lookupPath = this.resourceUrlProvider.getForLookupPath(lookupPath);
+ if (lookupPath != null) {
+ return this.prefixLookupPath + lookupPath + suffix;
+ }
+ }
+ return null;
+ }
+
private int getQueryParamsIndex(String url) {
int index = url.indexOf('?');
return (index > 0 ? index : url.length());
}
}
+
+ private static class ResourceUrlEncodingResponseWrapper extends HttpServletResponseWrapper {
+
+ private final ResourceUrlEncodingRequestWrapper request;
+
+ ResourceUrlEncodingResponseWrapper(ResourceUrlEncodingRequestWrapper request, HttpServletResponse wrapped) {
+ super(wrapped);
+ this.request = request;
+ }
+
+ @Override
+ public String encodeURL(String url) {
+ String urlPath = this.request.resolveUrlPath(url);
+ if (urlPath != null) {
+ return super.encodeURL(urlPath);
+ }
+ return super.encodeURL(url);
+ }
+ }
+
}
diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/handler/HandlerMethodMappingTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/handler/HandlerMethodMappingTests.java
index 5a181069..6676f2de 100644
--- a/spring-webmvc/src/test/java/org/springframework/web/servlet/handler/HandlerMethodMappingTests.java
+++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/handler/HandlerMethodMappingTests.java
@@ -38,6 +38,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.support.StaticWebApplicationContext;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.util.UrlPathHelper;
@@ -79,8 +80,10 @@ public class HandlerMethodMappingTests {
String key = "foo";
this.mapping.registerMapping(key, this.handler, this.method1);
- HandlerMethod result = this.mapping.getHandlerInternal(new MockHttpServletRequest("GET", key));
+ MockHttpServletRequest request = new MockHttpServletRequest("GET", key);
+ HandlerMethod result = this.mapping.getHandlerInternal(request);
assertEquals(method1, result.getMethod());
+ assertEquals(result, request.getAttribute(HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE));
}
@Test
@@ -88,8 +91,10 @@ public class HandlerMethodMappingTests {
this.mapping.registerMapping("/fo*", this.handler, this.method1);
this.mapping.registerMapping("/f*", this.handler, this.method2);
- HandlerMethod result = this.mapping.getHandlerInternal(new MockHttpServletRequest("GET", "/foo"));
+ MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
+ HandlerMethod result = this.mapping.getHandlerInternal(request);
assertEquals(method1, result.getMethod());
+ assertEquals(result, request.getAttribute(HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE));
}
@Test(expected = IllegalStateException.class)
diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/handler/SimpleUrlHandlerMappingTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/handler/SimpleUrlHandlerMappingTests.java
index dd6b09bd..2550405d 100644
--- a/spring-webmvc/src/test/java/org/springframework/web/servlet/handler/SimpleUrlHandlerMappingTests.java
+++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/handler/SimpleUrlHandlerMappingTests.java
@@ -41,17 +41,18 @@ import static org.junit.Assert.*;
public class SimpleUrlHandlerMappingTests {
@Test
- public void handlerBeanNotFound() throws Exception {
+ @SuppressWarnings("resource")
+ public void handlerBeanNotFound() {
MockServletContext sc = new MockServletContext("");
XmlWebApplicationContext root = new XmlWebApplicationContext();
root.setServletContext(sc);
- root.setConfigLocations(new String[] {"/org/springframework/web/servlet/handler/map1.xml"});
+ root.setConfigLocations("/org/springframework/web/servlet/handler/map1.xml");
root.refresh();
XmlWebApplicationContext wac = new XmlWebApplicationContext();
wac.setParent(root);
wac.setServletContext(sc);
wac.setNamespace("map2err");
- wac.setConfigLocations(new String[] {"/org/springframework/web/servlet/handler/map2err.xml"});
+ wac.setConfigLocations("/org/springframework/web/servlet/handler/map2err.xml");
try {
wac.refresh();
fail("Should have thrown NoSuchBeanDefinitionException");
@@ -93,7 +94,7 @@ public class SimpleUrlHandlerMappingTests {
MockServletContext sc = new MockServletContext("");
XmlWebApplicationContext wac = new XmlWebApplicationContext();
wac.setServletContext(sc);
- wac.setConfigLocations(new String[] {"/org/springframework/web/servlet/handler/map2.xml"});
+ wac.setConfigLocations("/org/springframework/web/servlet/handler/map2.xml");
wac.refresh();
Object bean = wac.getBean("mainController");
Object otherBean = wac.getBean("otherController");
@@ -104,11 +105,13 @@ public class SimpleUrlHandlerMappingTests {
HandlerExecutionChain hec = getHandler(hm, req);
assertTrue("Handler is correct bean", hec != null && hec.getHandler() == bean);
assertEquals("/welcome.html", req.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE));
+ assertEquals(bean, req.getAttribute(HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE));
req = new MockHttpServletRequest("GET", "/welcome.x");
hec = getHandler(hm, req);
assertTrue("Handler is correct bean", hec != null && hec.getHandler() == otherBean);
assertEquals("welcome.x", req.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE));
+ assertEquals(otherBean, req.getAttribute(HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE));
req = new MockHttpServletRequest("GET", "/welcome/");
hec = getHandler(hm, req);
diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceUrlEncodingFilterTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceUrlEncodingFilterTests.java
index d7716c7d..7722f763 100644
--- a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceUrlEncodingFilterTests.java
+++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceUrlEncodingFilterTests.java
@@ -38,7 +38,7 @@ public class ResourceUrlEncodingFilterTests {
private ResourceUrlEncodingFilter filter;
- private ResourceUrlProvider resourceUrlProvider;
+ private ResourceUrlProvider urlProvider;
@Before
public void createFilter() throws Exception {
@@ -49,16 +49,16 @@ public class ResourceUrlEncodingFilterTests {
List<ResourceResolver> resolvers = Arrays.asList(versionResolver, pathResolver);
this.filter = new ResourceUrlEncodingFilter();
- this.resourceUrlProvider = createResourceUrlProvider(resolvers);
+ this.urlProvider = createResourceUrlProvider(resolvers);
}
@Test
public void encodeURL() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/");
- request.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, this.resourceUrlProvider);
MockHttpServletResponse response = new MockHttpServletResponse();
this.filter.doFilter(request, response, (req, res) -> {
+ req.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, this.urlProvider);
String result = ((HttpServletResponse) res).encodeURL("/resources/bar.css");
assertEquals("/resources/bar-11e16cf79faee7ac698c805cf28248d2.css", result);
});
@@ -68,10 +68,26 @@ public class ResourceUrlEncodingFilterTests {
public void encodeURLWithContext() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/context/foo");
request.setContextPath("/context");
- request.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, this.resourceUrlProvider);
MockHttpServletResponse response = new MockHttpServletResponse();
this.filter.doFilter(request, response, (req, res) -> {
+ req.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, this.urlProvider);
+ String result = ((HttpServletResponse) res).encodeURL("/context/resources/bar.css");
+ assertEquals("/context/resources/bar-11e16cf79faee7ac698c805cf28248d2.css", result);
+ });
+ }
+
+
+ @Test
+ public void encodeUrlWithContextAndForwardedRequest() throws Exception {
+ MockHttpServletRequest request = new MockHttpServletRequest("GET", "/context/foo");
+ request.setContextPath("/context");
+ MockHttpServletResponse response = new MockHttpServletResponse();
+
+ this.filter.doFilter(request, response, (req, res) -> {
+ req.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, this.urlProvider);
+ request.setRequestURI("/forwarded");
+ request.setContextPath("/");
String result = ((HttpServletResponse) res).encodeURL("/context/resources/bar.css");
assertEquals("/context/resources/bar-11e16cf79faee7ac698c805cf28248d2.css", result);
});
@@ -82,10 +98,10 @@ public class ResourceUrlEncodingFilterTests {
public void encodeContextPathUrlWithoutSuffix() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/context");
request.setContextPath("/context");
- request.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, this.resourceUrlProvider);
MockHttpServletResponse response = new MockHttpServletResponse();
this.filter.doFilter(request, response, (req, res) -> {
+ req.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, this.urlProvider);
String result = ((HttpServletResponse) res).encodeURL("/context/resources/bar.css");
assertEquals("/context/resources/bar-11e16cf79faee7ac698c805cf28248d2.css", result);
});
@@ -95,10 +111,10 @@ public class ResourceUrlEncodingFilterTests {
public void encodeContextPathUrlWithSuffix() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/context/");
request.setContextPath("/context");
- request.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, this.resourceUrlProvider);
MockHttpServletResponse response = new MockHttpServletResponse();
this.filter.doFilter(request, response, (req, res) -> {
+ req.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, this.urlProvider);
String result = ((HttpServletResponse) res).encodeURL("/context/resources/bar.css");
assertEquals("/context/resources/bar-11e16cf79faee7ac698c805cf28248d2.css", result);
});
@@ -109,10 +125,10 @@ public class ResourceUrlEncodingFilterTests {
public void encodeEmptyURLWithContext() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/context/foo");
request.setContextPath("/context");
- request.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, this.resourceUrlProvider);
MockHttpServletResponse response = new MockHttpServletResponse();
this.filter.doFilter(request, response, (req, res) -> {
+ req.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, this.urlProvider);
String result = ((HttpServletResponse) res).encodeURL("?foo=1");
assertEquals("?foo=1", result);
});
@@ -123,10 +139,10 @@ public class ResourceUrlEncodingFilterTests {
public void encodeURLWithRequestParams() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
request.setContextPath("/");
- request.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, this.resourceUrlProvider);
MockHttpServletResponse response = new MockHttpServletResponse();
this.filter.doFilter(request, response, (req, res) -> {
+ req.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, this.urlProvider);
String result = ((HttpServletResponse) res).encodeURL("/resources/bar.css?foo=bar&url=http://example.org");
assertEquals("/resources/bar-11e16cf79faee7ac698c805cf28248d2.css?foo=bar&url=http://example.org", result);
});
@@ -138,10 +154,10 @@ public class ResourceUrlEncodingFilterTests {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/context-path/index");
request.setContextPath("/context-path");
request.setServletPath("");
- request.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, this.resourceUrlProvider);
MockHttpServletResponse response = new MockHttpServletResponse();
this.filter.doFilter(request, response, (req, res) -> {
+ req.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, this.urlProvider);
String result = ((HttpServletResponse) res).encodeURL("index?key=value");
assertEquals("index?key=value", result);
});
diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceUrlProviderJavaConfigTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceUrlProviderJavaConfigTests.java
index 9ad2f6aa..fbdc0f5c 100644
--- a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceUrlProviderJavaConfigTests.java
+++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceUrlProviderJavaConfigTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2018 the original author 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,14 @@
package org.springframework.web.servlet.resource;
+import java.io.IOException;
+import java.util.Arrays;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -23,6 +31,7 @@ import javax.servlet.http.HttpServletResponse;
import org.junit.Before;
import org.junit.Test;
+import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.mock.web.test.MockFilterChain;
import org.springframework.mock.web.test.MockHttpServletRequest;
@@ -55,8 +64,6 @@ public class ResourceUrlProviderJavaConfigTests {
@Before
@SuppressWarnings("resource")
public void setup() throws Exception {
- this.filterChain = new MockFilterChain(this.servlet, new ResourceUrlEncodingFilter());
-
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.setServletContext(new MockServletContext());
context.register(WebConfig.class);
@@ -66,8 +73,9 @@ public class ResourceUrlProviderJavaConfigTests {
this.request.setContextPath("/myapp");
this.response = new MockHttpServletResponse();
- Object urlProvider = context.getBean(ResourceUrlProvider.class);
- this.request.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, urlProvider);
+ this.filterChain = new MockFilterChain(this.servlet,
+ new ResourceUrlEncodingFilter(),
+ new ResourceUrlProviderExposingFilter(context));
}
@Test
@@ -117,6 +125,34 @@ public class ResourceUrlProviderJavaConfigTests {
}
@SuppressWarnings("serial")
+ private static class ResourceUrlProviderExposingFilter implements Filter {
+
+ private final ApplicationContext context;
+
+ public ResourceUrlProviderExposingFilter(ApplicationContext context) {
+ this.context = context;
+ }
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+
+ }
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+ Object urlProvider = context.getBean(ResourceUrlProvider.class);
+ request.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, urlProvider);
+ chain.doFilter(request, response);
+ }
+
+ @Override
+ public void destroy() {
+
+ }
+ }
+
+ @SuppressWarnings("serial")
private static class TestServlet extends HttpServlet {
private HttpServletResponse wrappedResponse;
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 879ac2d4..73bcf842 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-2017 the original author or authors.
+ * Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -435,7 +435,7 @@ public class StompSubProtocolHandler implements SubProtocolHandler, ApplicationE
else if (StompCommand.CONNECTED.equals(command)) {
this.stats.incrementConnectedCount();
accessor = afterStompSessionConnected(message, accessor, session);
- if (this.eventPublisher != null && StompCommand.CONNECTED.equals(command)) {
+ if (this.eventPublisher != null) {
try {
SimpAttributes simpAttributes = new SimpAttributes(session.getId(), session.getAttributes());
SimpAttributesContextHolder.setAttributes(simpAttributes);
diff --git a/src/asciidoc/core-beans.adoc b/src/asciidoc/core-beans.adoc
index c44b20a5..df41f1a3 100644
--- a/src/asciidoc/core-beans.adoc
+++ b/src/asciidoc/core-beans.adoc
@@ -545,21 +545,21 @@ XML-based configuration metadata, you can use the `<alias/>` element to accompli
<alias name="fromName" alias="toName"/>
----
-In this case, a bean in the same container which is named `fromName`, may also,
+In this case, a bean (in the same container) named `fromName` may also,
after the use of this alias definition, be referred to as `toName`.
-For example, the configuration metadata for subsystem A may refer to a DataSource via
-the name `subsystemA-dataSource`. The configuration metadata for subsystem B may refer to
-a DataSource via the name `subsystemB-dataSource`. When composing the main application
-that uses both these subsystems the main application refers to the DataSource via the
-name `myApp-dataSource`. To have all three names refer to the same object you add to the
-MyApp configuration metadata the following aliases definitions:
+For example, the configuration metadata for subsystem A may refer to a DataSource by the
+name of `subsystemA-dataSource`. The configuration metadata for subsystem B may refer to
+a DataSource by the name of `subsystemB-dataSource`. When composing the main application
+that uses both these subsystems, the main application refers to the DataSource by the
+name of `myApp-dataSource`. To have all three names refer to the same object, you can
+add the following alias definitions to the configuration metadata:
[source,xml,indent=0]
[subs="verbatim,quotes"]
----
- <alias name="subsystemA-dataSource" alias="subsystemB-dataSource"/>
- <alias name="subsystemA-dataSource" alias="myApp-dataSource" />
+ <alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
+ <alias name="myApp-dataSource" alias="subsystemB-dataSource"/>
----
Now each component and the main application can refer to the dataSource through a name
@@ -1911,11 +1911,11 @@ delimiters:
[NOTE]
====
-The `depends-on` attribute in the bean definition can specify both an initialization
-time dependency and, in the case of <<beans-factory-scopes-singleton,singleton>> beans
-only, a corresponding destroy time dependency. Dependent beans that define a
-`depends-on` relationship with a given bean are destroyed first, prior to the given bean
-itself being destroyed. Thus `depends-on` can also control shutdown order.
+The `depends-on` attribute in the bean definition can specify both an initialization-time
+dependency and, in the case of <<beans-factory-scopes-singleton,singleton>> beans only,
+a corresponding destruction-time dependency. Dependent beans that define a `depends-on`
+relationship with a given bean are destroyed first, prior to the given bean itself being
+destroyed. Thus `depends-on` can also control shutdown order.
====
@@ -6762,7 +6762,7 @@ annotation accepts a String array for this purpose.
@Configuration
public class AppConfig {
- @Bean(name = { "dataSource", "subsystemA-dataSource", "subsystemB-dataSource" })
+ @Bean({"dataSource", "subsystemA-dataSource", "subsystemB-dataSource"})
public DataSource dataSource() {
// instantiate, configure and return DataSource bean...
}
diff --git a/src/asciidoc/data-access.adoc b/src/asciidoc/data-access.adoc
index df35df15..95d30375 100644
--- a/src/asciidoc/data-access.adoc
+++ b/src/asciidoc/data-access.adoc
@@ -1090,6 +1090,12 @@ following class definition:
}
----
+Used at the class level as above, the annotation indicates a default for all methods
+of the declaring class (as well as its subclasses). Alternatively, each method can
+get annotated individually. Note that a class-level annotation does not apply to
+ancestor classes up the class hierarchy; in such a scenario, methods need to be
+locally redeclared in order to participate in a subclass-level annotation.
+
When the above POJO is defined as a bean in a Spring IoC container, the bean instance
can be made transactional by adding merely __one__ line of XML configuration:
@@ -1115,6 +1121,7 @@ can be made transactional by adding merely __one__ line of XML configuration:
<!-- enable the configuration of transactional behavior based on annotations -->
__<tx:annotation-driven transaction-manager="txManager"/>__<!-- a PlatformTransactionManager is still required -->
+
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- (this dependency is defined somewhere else) -->
<property name="dataSource" ref="dataSource"/>
diff --git a/src/asciidoc/web-mvc.adoc b/src/asciidoc/web-mvc.adoc
index 96327923..52dac141 100644
--- a/src/asciidoc/web-mvc.adoc
+++ b/src/asciidoc/web-mvc.adoc
@@ -452,8 +452,8 @@ initialization parameters ( `init-param` elements) to the Servlet declaration in
| Parameter| Explanation
| `contextClass`
-| Class that implements `WebApplicationContext`, which instantiates the context used by
- this Servlet. By default, the `XmlWebApplicationContext` is used.
+| Class that implements `ConfigurableWebApplicationContext`, to be instantiated and
+ locally configured by this Servlet. By default, `XmlWebApplicationContext` is used.
| `contextConfigLocation`
| String that is passed to the context instance (specified by `contextClass`) to