From 7b351031359a73fc66c44d8e859d2313f7b81d7e Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Thu, 17 Dec 2015 12:53:44 +0100 Subject: [PATCH 001/344] Switch to 4.2.5.BUILD-SNAPSHOT --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index d5fc1bea40..16c54f6a1c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=4.2.4.BUILD-SNAPSHOT +version=4.2.5.BUILD-SNAPSHOT From f3ac64153ea080f6eb397f3a352c44349c2df082 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 17 Dec 2015 17:11:44 +0100 Subject: [PATCH 002/344] Correct spring-websocket-4.2 versus spring-websocket-4.1 xsd declarations Issue: SPR-13804 (cherry picked from commit 2fd48c9) --- .../socket/config/spring-websocket-4.0.xsd | 19 +------------- .../socket/config/spring-websocket-4.1.xsd | 26 ++++--------------- .../socket/config/spring-websocket-4.2.xsd | 22 +++------------- 3 files changed, 9 insertions(+), 58 deletions(-) diff --git a/spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket-4.0.xsd b/spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket-4.0.xsd index d69f5006a0..36fa8af656 100644 --- a/spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket-4.0.xsd +++ b/spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket-4.0.xsd @@ -1,21 +1,5 @@ - - - + diff --git a/spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket-4.1.xsd b/spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket-4.1.xsd index e020d467e6..a5cabbd659 100644 --- a/spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket-4.1.xsd +++ b/spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket-4.1.xsd @@ -1,21 +1,5 @@ - - - - + + @@ -715,7 +699,7 @@ ]]> - + @@ -747,7 +731,7 @@ ]]> - + @@ -859,7 +843,7 @@ ]]> - + diff --git a/spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket-4.2.xsd b/spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket-4.2.xsd index 569a6d2441..f6a8718a18 100644 --- a/spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket-4.2.xsd +++ b/spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket-4.2.xsd @@ -1,21 +1,5 @@ - - - + @@ -803,7 +787,7 @@ ]]> - + @@ -915,7 +899,7 @@ ]]> - + From 7344dd259cf97229ecae373b35142dcd808b92d3 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 18 Dec 2015 00:02:14 +0100 Subject: [PATCH 003/344] Allow for explicit spring-websocket-4.2.xsd declarations Issue: SPR-13804 --- spring-websocket/src/main/resources/META-INF/spring.schemas | 1 + 1 file changed, 1 insertion(+) diff --git a/spring-websocket/src/main/resources/META-INF/spring.schemas b/spring-websocket/src/main/resources/META-INF/spring.schemas index 4cd992cdf6..338e12b0de 100644 --- a/spring-websocket/src/main/resources/META-INF/spring.schemas +++ b/spring-websocket/src/main/resources/META-INF/spring.schemas @@ -1,3 +1,4 @@ http\://www.springframework.org/schema/websocket/spring-websocket-4.0.xsd=org/springframework/web/socket/config/spring-websocket-4.0.xsd http\://www.springframework.org/schema/websocket/spring-websocket-4.1.xsd=org/springframework/web/socket/config/spring-websocket-4.1.xsd +http\://www.springframework.org/schema/websocket/spring-websocket-4.2.xsd=org/springframework/web/socket/config/spring-websocket-4.2.xsd http\://www.springframework.org/schema/websocket/spring-websocket.xsd=org/springframework/web/socket/config/spring-websocket-4.2.xsd From d59a97c1018496d381ac6ae4bd9185304874e643 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 18 Dec 2015 00:02:48 +0100 Subject: [PATCH 004/344] Polishing --- .../MappingJackson2HttpMessageConverter.java | 12 +++---- ...appingJackson2XmlHttpMessageConverter.java | 7 ++-- .../view/json/AbstractJackson2View.java | 35 +++++++++---------- 3 files changed, 26 insertions(+), 28 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/converter/json/MappingJackson2HttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/json/MappingJackson2HttpMessageConverter.java index 80f9b430a3..43f2eba97f 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/json/MappingJackson2HttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/json/MappingJackson2HttpMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,14 +24,14 @@ import org.springframework.http.MediaType; /** - * Implementation of {@link org.springframework.http.converter.HttpMessageConverter HttpMessageConverter} that - * can read and write JSON using Jackson 2.x's {@link ObjectMapper}. + * Implementation of {@link org.springframework.http.converter.HttpMessageConverter} that can read and + * write JSON using Jackson 2.x's {@link ObjectMapper}. * - *

This converter can be used to bind to typed beans, or untyped {@link java.util.HashMap HashMap} instances. + *

This converter can be used to bind to typed beans, or untyped {@code HashMap} instances. * *

By default, this converter supports {@code application/json} and {@code application/*+json} - * with {@code UTF-8} character set. - * This can be overridden by setting the {@link #setSupportedMediaTypes supportedMediaTypes} property. + * with {@code UTF-8} character set. This can be overridden by setting the + * {@link #setSupportedMediaTypes supportedMediaTypes} property. * *

The default constructor uses the default configuration provided by {@link Jackson2ObjectMapperBuilder}. * diff --git a/spring-web/src/main/java/org/springframework/http/converter/xml/MappingJackson2XmlHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/xml/MappingJackson2XmlHttpMessageConverter.java index f6099585cc..2f5aaffc10 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/xml/MappingJackson2XmlHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/xml/MappingJackson2XmlHttpMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,8 +30,8 @@ * Jackson 2.x extension component for reading and writing XML encoded data. * *

By default, this converter supports {@code application/xml}, {@code text/xml}, and - * {@code application/*+xml} with {@code UTF-8} character set. - * This can be overridden by setting the {@link #setSupportedMediaTypes(java.util.List) supportedMediaTypes} property. + * {@code application/*+xml} with {@code UTF-8} character set. This can be overridden by + * setting the {@link #setSupportedMediaTypes supportedMediaTypes} property. * *

The default constructor uses the default configuration provided by {@link Jackson2ObjectMapperBuilder}. * @@ -72,4 +72,5 @@ public void setObjectMapper(ObjectMapper objectMapper) { Assert.isAssignable(XmlMapper.class, objectMapper.getClass()); super.setObjectMapper(objectMapper); } + } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/json/AbstractJackson2View.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/json/AbstractJackson2View.java index 2069bbcd60..34b75fa2fd 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/json/AbstractJackson2View.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/json/AbstractJackson2View.java @@ -122,12 +122,6 @@ private void configurePrettyPrint() { } } - /** - * Set the attribute in the model that should be rendered by this view. - * When set, all other model attributes will be ignored. - */ - public abstract void setModelKey(String modelKey); - /** * Disables caching of the generated JSON. *

Default is {@code true}, which will prevent the client from caching the generated JSON. @@ -187,23 +181,13 @@ protected Object filterAndWrapModel(Map model, HttpServletReques return value; } - /** - * Filter out undesired attributes from the given model. - * The return value can be either another {@link Map} or a single value object. - * @param model the model, as passed on to {@link #renderMergedOutputModel} - * @return the value to be rendered - */ - protected abstract Object filterModel(Map model); - /** * Write the actual JSON content to the stream. * @param stream the output stream to use * @param object the value to be rendered, as returned from {@link #filterModel} * @throws IOException if writing failed */ - protected void writeContent(OutputStream stream, Object object) - throws IOException { - + protected void writeContent(OutputStream stream, Object object) throws IOException { JsonGenerator generator = this.objectMapper.getFactory().createGenerator(stream, this.encoding); writePrefix(generator, object); @@ -230,13 +214,27 @@ else if (filters != null) { generator.flush(); } + + /** + * Set the attribute in the model that should be rendered by this view. + * When set, all other model attributes will be ignored. + */ + public abstract void setModelKey(String modelKey); + + /** + * Filter out undesired attributes from the given model. + * The return value can be either another {@link Map} or a single value object. + * @param model the model, as passed on to {@link #renderMergedOutputModel} + * @return the value to be rendered + */ + protected abstract Object filterModel(Map model); + /** * Write a prefix before the main content. * @param generator the generator to use for writing content. * @param object the object to write to the output message. */ protected void writePrefix(JsonGenerator generator, Object object) throws IOException { - } /** @@ -245,7 +243,6 @@ protected void writePrefix(JsonGenerator generator, Object object) throws IOExce * @param object the object to write to the output message. */ protected void writeSuffix(JsonGenerator generator, Object object) throws IOException { - } } From 6f2f3595e9e566b36e8e17d7d1a45c257ef61562 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 18 Dec 2015 00:18:09 +0100 Subject: [PATCH 005/344] Backported import and declaration cleanup --- .../aspectj/AbstractAsyncExecutionAspect.aj | 2 -- .../factory/support/MethodOverrides.java | 1 - .../datetime/standard/PeriodFormatter.java | 1 - .../support/ObjectToObjectConverter.java | 2 +- .../MappingJackson2MessageConverter.java | 2 +- .../AsyncHandlerMethodReturnValueHandler.java | 1 - .../springframework/web/util/UriUtils.java | 31 ++----------------- .../CommonsPortletMultipartResolver.java | 1 - .../ServletWrappingPortletContext.java | 3 -- .../CallableMethodReturnValueHandler.java | 1 - ...eferredResultMethodReturnValueHandler.java | 1 - .../UndertowRequestUpgradeStrategy.java | 2 -- .../AbstractHttpSendingTransportHandler.java | 1 - 13 files changed, 4 insertions(+), 45 deletions(-) diff --git a/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AbstractAsyncExecutionAspect.aj b/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AbstractAsyncExecutionAspect.aj index 684eaf9e1c..f11400f05e 100644 --- a/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AbstractAsyncExecutionAspect.aj +++ b/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AbstractAsyncExecutionAspect.aj @@ -24,9 +24,7 @@ import org.aspectj.lang.annotation.SuppressAjWarnings; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.aop.interceptor.AsyncExecutionAspectSupport; -import org.springframework.core.task.AsyncListenableTaskExecutor; import org.springframework.core.task.AsyncTaskExecutor; -import org.springframework.util.concurrent.ListenableFuture; /** * Abstract aspect that routes selected methods asynchronously. diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/MethodOverrides.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/MethodOverrides.java index 6df31aff78..05159bd78d 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/MethodOverrides.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/MethodOverrides.java @@ -17,7 +17,6 @@ package org.springframework.beans.factory.support; import java.lang.reflect.Method; -import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Set; diff --git a/spring-context/src/main/java/org/springframework/format/datetime/standard/PeriodFormatter.java b/spring-context/src/main/java/org/springframework/format/datetime/standard/PeriodFormatter.java index 5ac4fbff32..8b13574af5 100644 --- a/spring-context/src/main/java/org/springframework/format/datetime/standard/PeriodFormatter.java +++ b/spring-context/src/main/java/org/springframework/format/datetime/standard/PeriodFormatter.java @@ -17,7 +17,6 @@ package org.springframework.format.datetime.standard; import java.text.ParseException; -import java.time.Instant; import java.time.Period; import java.util.Locale; diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/ObjectToObjectConverter.java b/spring-core/src/main/java/org/springframework/core/convert/support/ObjectToObjectConverter.java index b46512419d..fed1e45dec 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/support/ObjectToObjectConverter.java +++ b/spring-core/src/main/java/org/springframework/core/convert/support/ObjectToObjectConverter.java @@ -101,7 +101,7 @@ public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor t } } else if (member instanceof Constructor) { - Constructor ctor = (Constructor) member; + Constructor ctor = (Constructor) member; return ctor.newInstance(source); } } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/converter/MappingJackson2MessageConverter.java b/spring-messaging/src/main/java/org/springframework/messaging/converter/MappingJackson2MessageConverter.java index 4283889bea..cf2b676084 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/converter/MappingJackson2MessageConverter.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/converter/MappingJackson2MessageConverter.java @@ -277,7 +277,7 @@ else if (conversionHint instanceof JsonView) { return extractViewClass((JsonView) conversionHint, conversionHint); } else if (conversionHint instanceof Class) { - return (Class) conversionHint; + return (Class) conversionHint; } // No JSON view specified... diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/AsyncHandlerMethodReturnValueHandler.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/AsyncHandlerMethodReturnValueHandler.java index 9dda4d014d..5da73ba3f6 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/AsyncHandlerMethodReturnValueHandler.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/AsyncHandlerMethodReturnValueHandler.java @@ -17,7 +17,6 @@ package org.springframework.messaging.handler.invocation; import org.springframework.core.MethodParameter; -import org.springframework.messaging.Message; import org.springframework.util.concurrent.ListenableFuture; /** diff --git a/spring-web/src/main/java/org/springframework/web/util/UriUtils.java b/spring-web/src/main/java/org/springframework/web/util/UriUtils.java index 050cc2ee95..c68092971f 100644 --- a/spring-web/src/main/java/org/springframework/web/util/UriUtils.java +++ b/spring-web/src/main/java/org/springframework/web/util/UriUtils.java @@ -18,7 +18,6 @@ import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; -import java.util.regex.Pattern; import org.springframework.util.Assert; @@ -26,11 +25,11 @@ * Utility class for URI encoding and decoding based on RFC 3986. * Offers encoding methods for the various URI components. * - *

All {@code encode*(String, String} methods in this class operate in a similar way: + *

All {@code encode*(String, String)} methods in this class operate in a similar way: *

    *
  • Valid characters for the specific URI component as defined in RFC 3986 stay the same.
  • *
  • All other characters are converted into one or more bytes in the given encoding scheme. - * Each of the resulting bytes is written as a hexadecimal string in the "{@code %xy}" + * Each of the resulting bytes is written as a hexadecimal string in the "%xy" * format.
  • *
* @@ -40,32 +39,6 @@ */ public abstract class UriUtils { - private static final String SCHEME_PATTERN = "([^:/?#]+):"; - - private static final String HTTP_PATTERN = "(http|https):"; - - private static final String USERINFO_PATTERN = "([^@/]*)"; - - private static final String HOST_PATTERN = "([^/?#:]*)"; - - private static final String PORT_PATTERN = "(\\d*)"; - - private static final String PATH_PATTERN = "([^?#]*)"; - - private static final String QUERY_PATTERN = "([^#]*)"; - - private static final String LAST_PATTERN = "(.*)"; - - // Regex patterns that matches URIs. See RFC 3986, appendix B - private static final Pattern URI_PATTERN = Pattern.compile( - "^(" + SCHEME_PATTERN + ")?" + "(//(" + USERINFO_PATTERN + "@)?" + HOST_PATTERN + "(:" + PORT_PATTERN + - ")?" + ")?" + PATH_PATTERN + "(\\?" + QUERY_PATTERN + ")?" + "(#" + LAST_PATTERN + ")?"); - - private static final Pattern HTTP_URL_PATTERN = Pattern.compile( - "^" + HTTP_PATTERN + "(//(" + USERINFO_PATTERN + "@)?" + HOST_PATTERN + "(:" + PORT_PATTERN + ")?" + ")?" + - PATH_PATTERN + "(\\?" + LAST_PATTERN + ")?"); - - /** * Encodes the given URI scheme with the given encoding. * @param scheme the scheme to be encoded diff --git a/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/multipart/CommonsPortletMultipartResolver.java b/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/multipart/CommonsPortletMultipartResolver.java index aa17131413..2644f2c845 100644 --- a/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/multipart/CommonsPortletMultipartResolver.java +++ b/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/multipart/CommonsPortletMultipartResolver.java @@ -146,7 +146,6 @@ protected void initializeMultipart() { * @return the parsing result * @throws MultipartException if multipart resolution failed. */ - @SuppressWarnings("unchecked") protected MultipartParsingResult parseRequest(ActionRequest request) throws MultipartException { String encoding = determineEncoding(request); FileUpload fileUpload = prepareFileUpload(encoding); diff --git a/spring-webmvc-portlet/src/test/java/org/springframework/mock/web/portlet/ServletWrappingPortletContext.java b/spring-webmvc-portlet/src/test/java/org/springframework/mock/web/portlet/ServletWrappingPortletContext.java index 8a7150757a..545f159e28 100644 --- a/spring-webmvc-portlet/src/test/java/org/springframework/mock/web/portlet/ServletWrappingPortletContext.java +++ b/spring-webmvc-portlet/src/test/java/org/springframework/mock/web/portlet/ServletWrappingPortletContext.java @@ -100,7 +100,6 @@ public String getRealPath(String path) { } @Override - @SuppressWarnings("unchecked") public Set getResourcePaths(String path) { return this.servletContext.getResourcePaths(path); } @@ -116,7 +115,6 @@ public Object getAttribute(String name) { } @Override - @SuppressWarnings("unchecked") public Enumeration getAttributeNames() { return this.servletContext.getAttributeNames(); } @@ -127,7 +125,6 @@ public String getInitParameter(String name) { } @Override - @SuppressWarnings("unchecked") public Enumeration getInitParameterNames() { return this.servletContext.getInitParameterNames(); } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/CallableMethodReturnValueHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/CallableMethodReturnValueHandler.java index dbb46cc799..cfd5dade84 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/CallableMethodReturnValueHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/CallableMethodReturnValueHandler.java @@ -22,7 +22,6 @@ import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.async.WebAsyncUtils; import org.springframework.web.method.support.AsyncHandlerMethodReturnValueHandler; -import org.springframework.web.method.support.HandlerMethodReturnValueHandler; import org.springframework.web.method.support.ModelAndViewContainer; /** diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/DeferredResultMethodReturnValueHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/DeferredResultMethodReturnValueHandler.java index 620b9654e2..147e6afff8 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/DeferredResultMethodReturnValueHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/DeferredResultMethodReturnValueHandler.java @@ -21,7 +21,6 @@ import org.springframework.web.context.request.async.DeferredResult; import org.springframework.web.context.request.async.WebAsyncUtils; import org.springframework.web.method.support.AsyncHandlerMethodReturnValueHandler; -import org.springframework.web.method.support.HandlerMethodReturnValueHandler; import org.springframework.web.method.support.ModelAndViewContainer; /** diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/UndertowRequestUpgradeStrategy.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/UndertowRequestUpgradeStrategy.java index ec2bc3f4a3..6d5edef28d 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/UndertowRequestUpgradeStrategy.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/UndertowRequestUpgradeStrategy.java @@ -17,9 +17,7 @@ package org.springframework.web.socket.server.standard; import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; -import java.lang.reflect.Proxy; import java.util.Arrays; import java.util.Collections; import java.util.List; diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/handler/AbstractHttpSendingTransportHandler.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/handler/AbstractHttpSendingTransportHandler.java index e4df40bf66..87659d7c1f 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/handler/AbstractHttpSendingTransportHandler.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/handler/AbstractHttpSendingTransportHandler.java @@ -23,7 +23,6 @@ import org.springframework.http.MediaType; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; -import org.springframework.util.Assert; import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; import org.springframework.web.socket.WebSocketHandler; From 9021f8fd7128443a8f2168daebe7d7a1f3cccb20 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 18 Dec 2015 17:49:15 +0100 Subject: [PATCH 006/344] Upgrade to JavaMail 1.5.5 and Hibernate ORM 5.0.6 (cherry picked from commit 06056e6) --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 7962a9d2d5..482f0510cf 100644 --- a/build.gradle +++ b/build.gradle @@ -41,7 +41,7 @@ configure(allprojects) { project -> ext.hamcrestVersion = "1.3" ext.hibernate3Version = "3.6.10.Final" ext.hibernate4Version = "4.3.11.Final" - ext.hibernate5Version = "5.0.5.Final" + ext.hibernate5Version = "5.0.6.Final" ext.hibval4Version = "4.3.2.Final" ext.hibval5Version = "5.2.2.Final" ext.hsqldbVersion = "2.3.3" @@ -50,7 +50,7 @@ configure(allprojects) { project -> ext.httpclientVersion = "4.5.1" ext.jackson2Version = "2.6.4" ext.jasperreportsVersion = "6.2.0" - ext.javamailVersion = "1.5.4" + ext.javamailVersion = "1.5.5" ext.jettyVersion = "9.3.6.v20151106" ext.jodaVersion = "2.9.1" ext.jrubyVersion = "1.7.23" // JRuby 9000 only supported through JSR-223 (StandardScriptFactory) From 935d714333ec8ee4841d8e0c1621aca7ea7bbd54 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 21 Dec 2015 20:37:44 +0100 Subject: [PATCH 007/344] SimpleNamespaceContext implements all subtleties of the NamespaceContext contract Issue: SPR-13713 (cherry picked from commit f8860e2) --- .../util/xml/SimpleNamespaceContext.java | 140 ++++++------ .../util/xml/SimpleNamespaceContextTests.java | 203 ++++++++++++------ 2 files changed, 217 insertions(+), 126 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/util/xml/SimpleNamespaceContext.java b/spring-core/src/main/java/org/springframework/util/xml/SimpleNamespaceContext.java index f865c9566d..05f3de1394 100644 --- a/spring-core/src/main/java/org/springframework/util/xml/SimpleNamespaceContext.java +++ b/spring-core/src/main/java/org/springframework/util/xml/SimpleNamespaceContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2015 the original author 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,36 +16,38 @@ package org.springframework.util.xml; -import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; -import java.util.List; +import java.util.LinkedHashSet; import java.util.Map; +import java.util.Set; import javax.xml.XMLConstants; import javax.xml.namespace.NamespaceContext; import org.springframework.util.Assert; /** - * Simple {@code javax.xml.namespace.NamespaceContext} implementation. Follows the standard - * {@code NamespaceContext} contract, and is loadable via a {@code java.util.Map} or - * {@code java.util.Properties} object + * Simple {@code javax.xml.namespace.NamespaceContext} implementation. + * Follows the standard {@code NamespaceContext} contract, and is loadable + * via a {@code java.util.Map} or {@code java.util.Properties} object * * @author Arjen Poutsma + * @author Juergen Hoeller * @since 3.0 */ public class SimpleNamespaceContext implements NamespaceContext { - private Map prefixToNamespaceUri = new HashMap(); + private final Map prefixToNamespaceUri = new HashMap(); - private Map> namespaceUriToPrefixes = new HashMap>(); + private final Map> namespaceUriToPrefixes = new HashMap>(); private String defaultNamespaceUri = ""; + @Override public String getNamespaceURI(String prefix) { - Assert.notNull(prefix, "prefix is null"); + Assert.notNull(prefix, "No prefix given"); if (XMLConstants.XML_NS_PREFIX.equals(prefix)) { return XMLConstants.XML_NS_URI; } @@ -53,29 +55,46 @@ else if (XMLConstants.XMLNS_ATTRIBUTE.equals(prefix)) { return XMLConstants.XMLNS_ATTRIBUTE_NS_URI; } else if (XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)) { - return defaultNamespaceUri; + return this.defaultNamespaceUri; } - else if (prefixToNamespaceUri.containsKey(prefix)) { - return prefixToNamespaceUri.get(prefix); + else if (this.prefixToNamespaceUri.containsKey(prefix)) { + return this.prefixToNamespaceUri.get(prefix); } return ""; } @Override public String getPrefix(String namespaceUri) { - List prefixes = getPrefixesInternal(namespaceUri); - return prefixes.isEmpty() ? null : (String) prefixes.get(0); + Set prefixes = getPrefixesSet(namespaceUri); + return (!prefixes.isEmpty() ? prefixes.iterator().next() : null); } @Override public Iterator getPrefixes(String namespaceUri) { - return getPrefixesInternal(namespaceUri).iterator(); + return getPrefixesSet(namespaceUri).iterator(); } + private Set getPrefixesSet(String namespaceUri) { + Assert.notNull(namespaceUri, "No namespaceUri given"); + if (this.defaultNamespaceUri.equals(namespaceUri)) { + return Collections.singleton(XMLConstants.DEFAULT_NS_PREFIX); + } + else if (XMLConstants.XML_NS_URI.equals(namespaceUri)) { + return Collections.singleton(XMLConstants.XML_NS_PREFIX); + } + else if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(namespaceUri)) { + return Collections.singleton(XMLConstants.XMLNS_ATTRIBUTE); + } + else { + Set prefixes = this.namespaceUriToPrefixes.get(namespaceUri); + return (prefixes != null ? Collections.unmodifiableSet(prefixes) : Collections.emptySet()); + } + } + + /** - * Sets the bindings for this namespace context. The supplied map must consist of string key value pairs. - * - * @param bindings the bindings + * Set the bindings for this namespace context. + * The supplied map must consist of string key value pairs. */ public void setBindings(Map bindings) { for (Map.Entry entry : bindings.entrySet()) { @@ -84,8 +103,7 @@ public void setBindings(Map bindings) { } /** - * Binds the given namespace as default namespace. - * + * Bind the given namespace as default namespace. * @param namespaceUri the namespace uri */ public void bindDefaultNamespaceUri(String namespaceUri) { @@ -93,70 +111,62 @@ public void bindDefaultNamespaceUri(String namespaceUri) { } /** - * Binds the given prefix to the given namespace. - * - * @param prefix the namespace prefix + * Bind the given prefix to the given namespace. + * @param prefix the namespace prefix * @param namespaceUri the namespace uri */ public void bindNamespaceUri(String prefix, String namespaceUri) { Assert.notNull(prefix, "No prefix given"); Assert.notNull(namespaceUri, "No namespaceUri given"); if (XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)) { - defaultNamespaceUri = namespaceUri; + this.defaultNamespaceUri = namespaceUri; } else { - prefixToNamespaceUri.put(prefix, namespaceUri); - getPrefixesInternal(namespaceUri).add(prefix); + this.prefixToNamespaceUri.put(prefix, namespaceUri); + Set prefixes = this.namespaceUriToPrefixes.get(namespaceUri); + if (prefixes == null) { + prefixes = new LinkedHashSet(); + this.namespaceUriToPrefixes.put(namespaceUri, prefixes); + } + prefixes.add(prefix); } } - /** Removes all declared prefixes. */ - public void clear() { - prefixToNamespaceUri.clear(); - } - /** - * Returns all declared prefixes. - * - * @return the declared prefixes + * Remove the given prefix from this context. + * @param prefix the prefix to be removed */ - public Iterator getBoundPrefixes() { - return prefixToNamespaceUri.keySet().iterator(); - } - - private List getPrefixesInternal(String namespaceUri) { - if (defaultNamespaceUri.equals(namespaceUri)) { - return Collections.singletonList(XMLConstants.DEFAULT_NS_PREFIX); - } - else if (XMLConstants.XML_NS_URI.equals(namespaceUri)) { - return Collections.singletonList(XMLConstants.XML_NS_PREFIX); - } - else if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(namespaceUri)) { - return Collections.singletonList(XMLConstants.XMLNS_ATTRIBUTE); + public void removeBinding(String prefix) { + if (XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)) { + this.defaultNamespaceUri = ""; } - else { - List list = namespaceUriToPrefixes.get(namespaceUri); - if (list == null) { - list = new ArrayList(); - namespaceUriToPrefixes.put(namespaceUri, list); + else if (prefix != null) { + String namespaceUri = this.prefixToNamespaceUri.remove(prefix); + if (namespaceUri != null) { + Set prefixes = this.namespaceUriToPrefixes.get(namespaceUri); + if (prefixes != null) { + prefixes.remove(prefix); + if (prefixes.isEmpty()) { + this.namespaceUriToPrefixes.remove(namespaceUri); + } + } } - return list; } } /** - * Removes the given prefix from this context. - * - * @param prefix the prefix to be removed + * Remove all declared prefixes. */ - public void removeBinding(String prefix) { - if (XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)) { - defaultNamespaceUri = ""; - } - else { - String namespaceUri = prefixToNamespaceUri.remove(prefix); - List prefixes = getPrefixesInternal(namespaceUri); - prefixes.remove(prefix); - } + public void clear() { + this.prefixToNamespaceUri.clear(); + this.namespaceUriToPrefixes.clear(); + } + + /** + * Return all declared prefixes. + */ + public Iterator getBoundPrefixes() { + return this.prefixToNamespaceUri.keySet().iterator(); } + } diff --git a/spring-core/src/test/java/org/springframework/util/xml/SimpleNamespaceContextTests.java b/spring-core/src/test/java/org/springframework/util/xml/SimpleNamespaceContextTests.java index 80759270d2..4d191f676a 100644 --- a/spring-core/src/test/java/org/springframework/util/xml/SimpleNamespaceContextTests.java +++ b/spring-core/src/test/java/org/springframework/util/xml/SimpleNamespaceContextTests.java @@ -16,101 +16,182 @@ package org.springframework.util.xml; -import java.util.Collections; +import java.util.HashSet; import java.util.Iterator; +import java.util.Set; import javax.xml.XMLConstants; import org.junit.Test; +import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; +/** + * @author Arjen Poutsma + * @author Leo Arnold + */ public class SimpleNamespaceContextTests { - private final SimpleNamespaceContext context = new SimpleNamespaceContext() {{ - bindNamespaceUri("prefix", "namespaceURI"); - }}; + private final String unboundPrefix = "unbound"; + private final String prefix = "prefix"; + private final String namespaceUri = "http://Namespace-name-URI"; + private final String additionalNamespaceUri = "http://Additional-namespace-name-URI"; + private final String unboundNamespaceUri = "http://Unbound-namespace-name-URI"; + private final String defaultNamespaceUri = "http://Default-namespace-name-URI"; + + private final SimpleNamespaceContext context = new SimpleNamespaceContext(); + + @Test(expected = IllegalArgumentException.class) + public void getNamespaceURI_withNull() throws Exception { + context.getNamespaceURI(null); + } @Test public void getNamespaceURI() { - assertEquals("Invalid namespaceURI for default namespace", "", - context.getNamespaceURI(XMLConstants.DEFAULT_NS_PREFIX)); - String defaultNamespaceUri = "defaultNamespace"; - context.bindNamespaceUri(XMLConstants.DEFAULT_NS_PREFIX, defaultNamespaceUri); - assertEquals("Invalid namespaceURI for default namespace", defaultNamespaceUri, - context.getNamespaceURI(XMLConstants.DEFAULT_NS_PREFIX)); - assertEquals("Invalid namespaceURI for bound prefix", "namespaceURI", context.getNamespaceURI("prefix")); - assertEquals("Invalid namespaceURI for unbound prefix", "", context.getNamespaceURI("unbound")); - assertEquals("Invalid namespaceURI for namespace prefix", XMLConstants.XML_NS_URI, - context.getNamespaceURI(XMLConstants.XML_NS_PREFIX)); - assertEquals("Invalid namespaceURI for attribute prefix", XMLConstants.XMLNS_ATTRIBUTE_NS_URI, - context.getNamespaceURI(XMLConstants.XMLNS_ATTRIBUTE)); + context.bindNamespaceUri(XMLConstants.XMLNS_ATTRIBUTE, additionalNamespaceUri); + assertThat("Always returns \"http://www.w3.org/2000/xmlns/\" for \"xmlns\"", + context.getNamespaceURI(XMLConstants.XMLNS_ATTRIBUTE), is(XMLConstants.XMLNS_ATTRIBUTE_NS_URI)); + context.bindNamespaceUri(XMLConstants.XML_NS_PREFIX, additionalNamespaceUri); + assertThat("Always returns \"http://www.w3.org/XML/1998/namespace\" for \"xml\"", + context.getNamespaceURI(XMLConstants.XML_NS_PREFIX), is(XMLConstants.XML_NS_URI)); + + assertThat("Returns \"\" for an unbound prefix", context.getNamespaceURI(unboundPrefix), + is(XMLConstants.NULL_NS_URI)); + context.bindNamespaceUri(prefix, namespaceUri); + assertThat("Returns the bound namespace URI for a bound prefix", context.getNamespaceURI(prefix), + is(namespaceUri)); + + assertThat("By default returns URI \"\" for the default namespace prefix", + context.getNamespaceURI(XMLConstants.DEFAULT_NS_PREFIX), is(XMLConstants.NULL_NS_URI)); + context.bindDefaultNamespaceUri(defaultNamespaceUri); + assertThat("Returns the set URI for the default namespace prefix", + context.getNamespaceURI(XMLConstants.DEFAULT_NS_PREFIX), is(defaultNamespaceUri)); + } + + @Test(expected = IllegalArgumentException.class) + public void getPrefix_withNull() throws Exception { + context.getPrefix(null); } @Test public void getPrefix() { - assertEquals("Invalid prefix for default namespace", XMLConstants.DEFAULT_NS_PREFIX, context.getPrefix("")); - assertEquals("Invalid prefix for bound namespace", "prefix", context.getPrefix("namespaceURI")); - assertNull("Invalid prefix for unbound namespace", context.getPrefix("unbound")); - assertEquals("Invalid prefix for namespace", XMLConstants.XML_NS_PREFIX, - context.getPrefix(XMLConstants.XML_NS_URI)); - assertEquals("Invalid prefix for attribute namespace", XMLConstants.XMLNS_ATTRIBUTE, - context.getPrefix(XMLConstants.XMLNS_ATTRIBUTE_NS_URI)); + assertThat("Always returns \"xmlns\" for \"http://www.w3.org/2000/xmlns/\"", + context.getPrefix(XMLConstants.XMLNS_ATTRIBUTE_NS_URI), is(XMLConstants.XMLNS_ATTRIBUTE)); + assertThat("Always returns \"xml\" for \"http://www.w3.org/XML/1998/namespace\"", + context.getPrefix(XMLConstants.XML_NS_URI), is(XMLConstants.XML_NS_PREFIX)); + + assertThat("Returns null for an unbound namespace URI", context.getPrefix(unboundNamespaceUri), + is(nullValue())); + context.bindNamespaceUri("prefix1", namespaceUri); + context.bindNamespaceUri("prefix2", namespaceUri); + assertThat("Returns a prefix for a bound namespace URI", context.getPrefix(namespaceUri), + anyOf(is("prefix1"), is("prefix2"))); + } + + @Test(expected = IllegalArgumentException.class) + public void getPrefixes_withNull() throws Exception { + context.getPrefixes(null); + } + + @Test(expected = UnsupportedOperationException.class) + public void getPrefixes_IteratorIsNotModifiable() throws Exception { + context.bindNamespaceUri(prefix, namespaceUri); + Iterator iterator = context.getPrefixes(namespaceUri); + iterator.remove(); } @Test public void getPrefixes() { - assertPrefixes("", XMLConstants.DEFAULT_NS_PREFIX); - assertPrefixes("namespaceURI", "prefix"); - assertFalse("Invalid prefix for unbound namespace", context.getPrefixes("unbound").hasNext()); - assertPrefixes(XMLConstants.XML_NS_URI, XMLConstants.XML_NS_PREFIX); - assertPrefixes(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, XMLConstants.XMLNS_ATTRIBUTE); + assertThat("Returns only \"xmlns\" for \"http://www.w3.org/2000/xmlns/\"", + getItemSet(context.getPrefixes(XMLConstants.XMLNS_ATTRIBUTE_NS_URI)), + is(makeSet(XMLConstants.XMLNS_ATTRIBUTE))); + assertThat("Returns only \"xml\" for \"http://www.w3.org/XML/1998/namespace\"", + getItemSet(context.getPrefixes(XMLConstants.XML_NS_URI)), is(makeSet(XMLConstants.XML_NS_PREFIX))); + + assertThat("Returns empty iterator for unbound prefix", context.getPrefixes("unbound Namespace URI").hasNext(), + is(false)); + context.bindNamespaceUri("prefix1", namespaceUri); + context.bindNamespaceUri("prefix2", namespaceUri); + assertThat("Returns all prefixes (and only those) bound to the namespace URI", + getItemSet(context.getPrefixes(namespaceUri)), is(makeSet("prefix1", "prefix2"))); } - @Test - public void multiplePrefixes() { - context.bindNamespaceUri("prefix1", "namespace"); - context.bindNamespaceUri("prefix2", "namespace"); - Iterator iterator = context.getPrefixes("namespace"); - assertNotNull("getPrefixes returns null", iterator); - assertTrue("iterator is empty", iterator.hasNext()); - String result = iterator.next(); - assertTrue("Invalid prefix", result.equals("prefix1") || result.equals("prefix2")); - assertTrue("iterator is empty", iterator.hasNext()); - result = iterator.next(); - assertTrue("Invalid prefix", result.equals("prefix1") || result.equals("prefix2")); - assertFalse("iterator contains more than two values", iterator.hasNext()); + @Test(expected = IllegalArgumentException.class) + public void bindNamespaceUri_withNullNamespaceUri() { + context.bindNamespaceUri("prefix", null); } - private void assertPrefixes(String namespaceUri, String prefix) { - Iterator iterator = context.getPrefixes(namespaceUri); - assertNotNull("getPrefixes returns null", iterator); - assertTrue("iterator is empty", iterator.hasNext()); - String result = iterator.next(); - assertEquals("Invalid prefix", prefix, result); - assertFalse("iterator contains multiple values", iterator.hasNext()); + @Test(expected = IllegalArgumentException.class) + public void bindNamespaceUri_withNullPrefix() { + context.bindNamespaceUri(null, namespaceUri); + } + + @Test + public void bindNamespaceUri() { + context.bindNamespaceUri(prefix, namespaceUri); + assertThat("The Namespace URI was bound to the prefix", context.getNamespaceURI(prefix), is(namespaceUri)); + assertThat("The prefix was bound to the namespace URI", getItemSet(context.getPrefixes(namespaceUri)), + hasItem(prefix)); } @Test - public void getBoundPrefixes() throws Exception { - Iterator iterator = context.getBoundPrefixes(); - assertNotNull("getPrefixes returns null", iterator); - assertTrue("iterator is empty", iterator.hasNext()); - String result = iterator.next(); - assertEquals("Invalid prefix", "prefix", result); - assertFalse("iterator contains multiple values", iterator.hasNext()); + public void getBoundPrefixes() { + context.bindNamespaceUri("prefix1", namespaceUri); + context.bindNamespaceUri("prefix2", namespaceUri); + context.bindNamespaceUri("prefix3", additionalNamespaceUri); + assertThat("Returns all bound prefixes", getItemSet(context.getBoundPrefixes()), + is(makeSet("prefix1", "prefix2", "prefix3"))); } @Test - public void setBindings() throws Exception { - context.setBindings(Collections.singletonMap("prefix", "namespace")); - assertEquals("Invalid namespace uri", "namespace", context.getNamespaceURI("prefix")); + public void clear() { + context.bindNamespaceUri("prefix1", namespaceUri); + context.bindNamespaceUri("prefix2", namespaceUri); + context.bindNamespaceUri("prefix3", additionalNamespaceUri); + context.clear(); + assertThat("All bound prefixes were removed", context.getBoundPrefixes().hasNext(), is(false)); + assertThat("All bound namespace URIs were removed", context.getPrefixes(namespaceUri).hasNext(), is(false)); } @Test - public void removeBinding() throws Exception { - context.removeBinding("prefix"); - assertNull("Invalid prefix for unbound namespace", context.getPrefix("prefix")); + public void removeBinding() { + context.removeBinding(unboundPrefix); + + context.bindNamespaceUri(prefix, namespaceUri); + context.removeBinding(prefix); + assertThat("Returns default namespace URI for removed prefix", context.getNamespaceURI(prefix), + is(XMLConstants.NULL_NS_URI)); + assertThat("#getPrefix returns null when all prefixes for a namespace URI were removed", + context.getPrefix(namespaceUri), is(nullValue())); + assertThat("#getPrefixes returns an empty iterator when all prefixes for a namespace URI were removed", + context.getPrefixes(namespaceUri).hasNext(), is(false)); + + context.bindNamespaceUri("prefix1", additionalNamespaceUri); + context.bindNamespaceUri("prefix2", additionalNamespaceUri); + context.removeBinding("prefix1"); + assertThat("Prefix was unbound", context.getNamespaceURI("prefix1"), is(XMLConstants.NULL_NS_URI)); + assertThat("#getPrefix returns a bound prefix after removal of another prefix for the same namespace URI", + context.getPrefix(additionalNamespaceUri), is("prefix2")); + assertThat("Prefix was removed from namespace URI", getItemSet(context.getPrefixes(additionalNamespaceUri)), + is(makeSet("prefix2"))); + } + + + private Set getItemSet(Iterator iterator) { + Set itemSet = new HashSet(); + while (iterator.hasNext()) { + itemSet.add(iterator.next()); + } + return itemSet; + } + + private Set makeSet(String... items) { + Set itemSet = new HashSet(); + for (String item : items) { + itemSet.add(item); + } + return itemSet; } } From 36940e003d8b979756bb8d0f08390614f77a5737 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 21 Dec 2015 22:38:25 +0100 Subject: [PATCH 008/344] Polishing --- .../propertyeditors/ResourceBundleEditor.java | 44 +++++++++---------- .../ResourceBundleEditorTests.java | 28 +++++++----- .../servlet/i18n/CookieLocaleResolver.java | 8 ++-- 3 files changed, 43 insertions(+), 37 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/ResourceBundleEditor.java b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/ResourceBundleEditor.java index 7a827c9481..7eabc102b7 100644 --- a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/ResourceBundleEditor.java +++ b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/ResourceBundleEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,14 +24,13 @@ import org.springframework.util.StringUtils; /** - * {@link java.beans.PropertyEditor} implementation for + * {@link java.beans.PropertyEditor} implementation for standard JDK * {@link java.util.ResourceBundle ResourceBundles}. * - *

Only supports conversion from a String, but not - * to a String. + *

Only supports conversion from a String, but not to a String. * - * Find below some examples of using this class in a - * (properly configured) Spring container using XML-based metadata: + * Find below some examples of using this class in a (properly configured) + * Spring container using XML-based metadata: * *

 <bean id="errorDialog" class="...">
  *    <!--
@@ -62,19 +61,20 @@
  *    </property>
  * </bean>
* - *

Please note that this {@link java.beans.PropertyEditor} is - * not registered by default with any of the Spring infrastructure. + *

Please note that this {@link java.beans.PropertyEditor} is not + * registered by default with any of the Spring infrastructure. * *

Thanks to David Leal Valmana for the suggestion and initial prototype. * * @author Rick Evans + * @author Juergen Hoeller * @since 2.0 */ public class ResourceBundleEditor extends PropertyEditorSupport { /** - * The separator used to distinguish between the base name and the - * locale (if any) when {@link #setAsText(String) converting from a String}. + * The separator used to distinguish between the base name and the locale + * (if any) when {@link #setAsText(String) converting from a String}. */ public static final String BASE_NAME_SEPARATOR = "_"; @@ -82,25 +82,23 @@ public class ResourceBundleEditor extends PropertyEditorSupport { @Override public void setAsText(String text) throws IllegalArgumentException { Assert.hasText(text, "'text' must not be empty"); - ResourceBundle bundle; - String rawBaseName = text.trim(); - int indexOfBaseNameSeparator = rawBaseName.indexOf(BASE_NAME_SEPARATOR); - if (indexOfBaseNameSeparator == -1) { - bundle = ResourceBundle.getBundle(rawBaseName); + String name = text.trim(); + + int separator = name.indexOf(BASE_NAME_SEPARATOR); + if (separator == -1) { + setValue(ResourceBundle.getBundle(name)); } else { - // it potentially has locale information - String baseName = rawBaseName.substring(0, indexOfBaseNameSeparator); + // The name potentially contains locale information + String baseName = name.substring(0, separator); if (!StringUtils.hasText(baseName)) { - throw new IllegalArgumentException("Bad ResourceBundle name : received '" + text + "' as argument to 'setAsText(String value)'."); + throw new IllegalArgumentException("Invalid ResourceBundle name: '" + text + "'"); } - String localeString = rawBaseName.substring(indexOfBaseNameSeparator + 1); + String localeString = name.substring(separator + 1); Locale locale = StringUtils.parseLocaleString(localeString); - bundle = (StringUtils.hasText(localeString)) - ? ResourceBundle.getBundle(baseName, locale) - : ResourceBundle.getBundle(baseName); + setValue((StringUtils.hasText(localeString)) ? ResourceBundle.getBundle(baseName, locale) : + ResourceBundle.getBundle(baseName)); } - setValue(bundle); } } diff --git a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/ResourceBundleEditorTests.java b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/ResourceBundleEditorTests.java index c6659d113e..613959dc3c 100644 --- a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/ResourceBundleEditorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/ResourceBundleEditorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2006 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,9 +28,10 @@ * @author Rick Evans * @author Chris Beams */ -public final class ResourceBundleEditorTests { +public class ResourceBundleEditorTests { private static final String BASE_NAME = ResourceBundleEditorTests.class.getName(); + private static final String MESSAGE_KEY = "punk"; @@ -40,7 +41,8 @@ public void testSetAsTextWithJustBaseName() throws Exception { editor.setAsText(BASE_NAME); Object value = editor.getValue(); assertNotNull("Returned ResourceBundle was null (must not be for valid setAsText(..) call).", value); - assertTrue("Returned object was not a ResourceBundle (must be for valid setAsText(..) call).", value instanceof ResourceBundle); + assertTrue("Returned object was not a ResourceBundle (must be for valid setAsText(..) call).", + value instanceof ResourceBundle); ResourceBundle bundle = (ResourceBundle) value; String string = bundle.getString(MESSAGE_KEY); assertEquals(MESSAGE_KEY, string); @@ -52,7 +54,8 @@ public void testSetAsTextWithBaseNameThatEndsInDefaultSeparator() throws Excepti editor.setAsText(BASE_NAME + "_"); Object value = editor.getValue(); assertNotNull("Returned ResourceBundle was null (must not be for valid setAsText(..) call).", value); - assertTrue("Returned object was not a ResourceBundle (must be for valid setAsText(..) call).", value instanceof ResourceBundle); + assertTrue("Returned object was not a ResourceBundle (must be for valid setAsText(..) call).", + value instanceof ResourceBundle); ResourceBundle bundle = (ResourceBundle) value; String string = bundle.getString(MESSAGE_KEY); assertEquals(MESSAGE_KEY, string); @@ -64,7 +67,8 @@ public void testSetAsTextWithBaseNameAndLanguageCode() throws Exception { editor.setAsText(BASE_NAME + "Lang" + "_en"); Object value = editor.getValue(); assertNotNull("Returned ResourceBundle was null (must not be for valid setAsText(..) call).", value); - assertTrue("Returned object was not a ResourceBundle (must be for valid setAsText(..) call).", value instanceof ResourceBundle); + assertTrue("Returned object was not a ResourceBundle (must be for valid setAsText(..) call).", + value instanceof ResourceBundle); ResourceBundle bundle = (ResourceBundle) value; String string = bundle.getString(MESSAGE_KEY); assertEquals("yob", string); @@ -76,7 +80,8 @@ public void testSetAsTextWithBaseNameLanguageAndCountryCode() throws Exception { editor.setAsText(BASE_NAME + "LangCountry" + "_en_GB"); Object value = editor.getValue(); assertNotNull("Returned ResourceBundle was null (must not be for valid setAsText(..) call).", value); - assertTrue("Returned object was not a ResourceBundle (must be for valid setAsText(..) call).", value instanceof ResourceBundle); + assertTrue("Returned object was not a ResourceBundle (must be for valid setAsText(..) call).", + value instanceof ResourceBundle); ResourceBundle bundle = (ResourceBundle) value; String string = bundle.getString(MESSAGE_KEY); assertEquals("chav", string); @@ -88,31 +93,32 @@ public void testSetAsTextWithTheKitchenSink() throws Exception { editor.setAsText(BASE_NAME + "LangCountryDialect" + "_en_GB_GLASGOW"); Object value = editor.getValue(); assertNotNull("Returned ResourceBundle was null (must not be for valid setAsText(..) call).", value); - assertTrue("Returned object was not a ResourceBundle (must be for valid setAsText(..) call).", value instanceof ResourceBundle); + assertTrue("Returned object was not a ResourceBundle (must be for valid setAsText(..) call).", + value instanceof ResourceBundle); ResourceBundle bundle = (ResourceBundle) value; String string = bundle.getString(MESSAGE_KEY); assertEquals("ned", string); } - @Test(expected=IllegalArgumentException.class) + @Test(expected = IllegalArgumentException.class) public void testSetAsTextWithNull() throws Exception { ResourceBundleEditor editor = new ResourceBundleEditor(); editor.setAsText(null); } - @Test(expected=IllegalArgumentException.class) + @Test(expected = IllegalArgumentException.class) public void testSetAsTextWithEmptyString() throws Exception { ResourceBundleEditor editor = new ResourceBundleEditor(); editor.setAsText(""); } - @Test(expected=IllegalArgumentException.class) + @Test(expected = IllegalArgumentException.class) public void testSetAsTextWithWhiteSpaceString() throws Exception { ResourceBundleEditor editor = new ResourceBundleEditor(); editor.setAsText(" "); } - @Test(expected=IllegalArgumentException.class) + @Test(expected = IllegalArgumentException.class) public void testSetAsTextWithJustSeparatorString() throws Exception { ResourceBundleEditor editor = new ResourceBundleEditor(); editor.setAsText("_"); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/i18n/CookieLocaleResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/i18n/CookieLocaleResolver.java index 4bf43e2845..547a91fc35 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/i18n/CookieLocaleResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/i18n/CookieLocaleResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -111,6 +111,7 @@ protected Locale getDefaultLocale() { /** * Set a fixed TimeZone that this resolver will return if no cookie found. + * @since 4.0 */ public void setDefaultTimeZone(TimeZone defaultTimeZone) { this.defaultTimeZone = defaultTimeZone; @@ -119,6 +120,7 @@ public void setDefaultTimeZone(TimeZone defaultTimeZone) { /** * Return the fixed TimeZone that this resolver will return if no cookie found, * if any. + * @since 4.0 */ protected TimeZone getDefaultTimeZone() { return this.defaultTimeZone; @@ -171,7 +173,7 @@ private void parseLocaleCookieIfNecessary(HttpServletRequest request) { } } request.setAttribute(LOCALE_REQUEST_ATTRIBUTE_NAME, - (locale != null ? locale: determineDefaultLocale(request))); + (locale != null ? locale : determineDefaultLocale(request))); request.setAttribute(TIME_ZONE_REQUEST_ATTRIBUTE_NAME, (timeZone != null ? timeZone : determineDefaultTimeZone(request))); } @@ -197,7 +199,7 @@ public void setLocaleContext(HttpServletRequest request, HttpServletResponse res removeCookie(response); } request.setAttribute(LOCALE_REQUEST_ATTRIBUTE_NAME, - (locale != null ? locale: determineDefaultLocale(request))); + (locale != null ? locale : determineDefaultLocale(request))); request.setAttribute(TIME_ZONE_REQUEST_ATTRIBUTE_NAME, (timeZone != null ? timeZone : determineDefaultTimeZone(request))); } From deae8729126eb6493dd9e7c92d96193a2566c278 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 22 Dec 2015 23:50:05 +0100 Subject: [PATCH 009/344] Consistent use of varargs plus related polishing --- .../springframework/aop/MethodMatcher.java | 4 +- .../aop/TrueMethodMatcher.java | 17 ++- .../aop/aspectj/AbstractAspectJAdvice.java | 35 +++-- .../aop/aspectj/AspectJAfterAdvice.java | 3 +- .../aspectj/AspectJAfterReturningAdvice.java | 2 + .../aspectj/AspectJAfterThrowingAdvice.java | 15 +- .../aop/aspectj/AspectJAroundAdvice.java | 4 +- .../aspectj/AspectJExpressionPointcut.java | 2 +- .../aspectj/AspectJMethodBeforeAdvice.java | 3 +- .../AbstractAspectJAdvisorFactory.java | 60 ++++---- .../annotation/AspectJAdvisorFactory.java | 26 ++-- .../aspectj/annotation/AspectMetadata.java | 18 +-- ...ntiationModelAwarePointcutAdvisorImpl.java | 56 ++++---- .../PrototypeAspectInstanceFactory.java | 6 +- .../ReflectiveAspectJAdvisorFactory.java | 61 ++++---- .../aop/framework/AdvisedSupport.java | 4 +- .../aop/support/ControlFlowPointcut.java | 11 +- .../aop/support/MethodMatchers.java | 6 +- .../aop/support/Pointcuts.java | 14 +- .../aop/support/StaticMethodMatcher.java | 4 +- .../AspectJExpressionPointcutTests.java | 62 ++++---- .../TigerAspectJExpressionPointcutTests.java | 61 ++++---- .../AbstractAspectJAdvisorFactoryTests.java | 5 +- .../AspectJPointcutAdvisorTests.java | 34 +++-- .../aop/framework/MethodInvocationTests.java | 6 +- .../adapter/ThrowsAdviceInterceptorTests.java | 7 +- .../AbstractRegexpMethodPointcutTests.java | 28 ++-- .../aop/support/ComposablePointcutTests.java | 34 ++--- .../aop/support/MethodMatchersTests.java | 22 +-- .../aop/support/PointcutsTests.java | 136 +++++++++--------- .../groovy/GroovyBeanDefinitionReader.java | 12 +- .../aop/framework/AbstractAopProxyTests.java | 78 +++++----- .../aop/framework/CglibProxyTests.java | 20 +-- .../aop/framework/JdkDynamicProxyTests.java | 8 +- .../aop/framework/ProxyFactoryBeanTests.java | 27 ++-- 35 files changed, 460 insertions(+), 431 deletions(-) diff --git a/spring-aop/src/main/java/org/springframework/aop/MethodMatcher.java b/spring-aop/src/main/java/org/springframework/aop/MethodMatcher.java index 15526c1fc8..7cba7648cf 100644 --- a/spring-aop/src/main/java/org/springframework/aop/MethodMatcher.java +++ b/spring-aop/src/main/java/org/springframework/aop/MethodMatcher.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -86,7 +86,7 @@ public interface MethodMatcher { * @return whether there's a runtime match * @see MethodMatcher#matches(Method, Class) */ - boolean matches(Method method, Class targetClass, Object[] args); + boolean matches(Method method, Class targetClass, Object... args); /** diff --git a/spring-aop/src/main/java/org/springframework/aop/TrueMethodMatcher.java b/spring-aop/src/main/java/org/springframework/aop/TrueMethodMatcher.java index 0b82427e8b..cf21e97c91 100644 --- a/spring-aop/src/main/java/org/springframework/aop/TrueMethodMatcher.java +++ b/spring-aop/src/main/java/org/springframework/aop/TrueMethodMatcher.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,12 +29,14 @@ class TrueMethodMatcher implements MethodMatcher, Serializable { public static final TrueMethodMatcher INSTANCE = new TrueMethodMatcher(); + /** * Enforce Singleton pattern. */ private TrueMethodMatcher() { } + @Override public boolean isRuntime() { return false; @@ -46,11 +48,17 @@ public boolean matches(Method method, Class targetClass) { } @Override - public boolean matches(Method method, Class targetClass, Object[] args) { + public boolean matches(Method method, Class targetClass, Object... args) { // Should never be invoked as isRuntime returns false. throw new UnsupportedOperationException(); } + + @Override + public String toString() { + return "MethodMatcher.TRUE"; + } + /** * Required to support serialization. Replaces with canonical * instance on deserialization, protecting Singleton pattern. @@ -60,9 +68,4 @@ private Object readResolve() { return INSTANCE; } - @Override - public String toString() { - return "MethodMatcher.TRUE"; - } - } diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/AbstractAspectJAdvice.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/AbstractAspectJAdvice.java index 4d0392c247..71bd31d934 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/AbstractAspectJAdvice.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/AbstractAspectJAdvice.java @@ -135,7 +135,7 @@ public static JoinPoint currentJoinPoint() { */ private int joinPointStaticPartArgumentIndex = -1; - private Map argumentBindings = null; + private Map argumentBindings; private boolean argumentsIntrospected = false; @@ -250,17 +250,17 @@ public void setArgumentNamesFromStringArray(String... args) { this.argumentNames[i] + "' that is not a valid Java identifier"); } } - if (argumentNames != null) { - if (aspectJAdviceMethod.getParameterTypes().length == argumentNames.length + 1) { + if (this.argumentNames != null) { + if (this.aspectJAdviceMethod.getParameterTypes().length == this.argumentNames.length + 1) { // May need to add implicit join point arg name... - Class firstArgType = aspectJAdviceMethod.getParameterTypes()[0]; + Class firstArgType = this.aspectJAdviceMethod.getParameterTypes()[0]; if (firstArgType == JoinPoint.class || firstArgType == ProceedingJoinPoint.class || firstArgType == JoinPoint.StaticPart.class) { - String[] oldNames = argumentNames; - argumentNames = new String[oldNames.length + 1]; - argumentNames[0] = "THIS_JOIN_POINT"; - System.arraycopy(oldNames, 0, argumentNames, 1, oldNames.length); + String[] oldNames = this.argumentNames; + this.argumentNames = new String[oldNames.length + 1]; + this.argumentNames[0] = "THIS_JOIN_POINT"; + System.arraycopy(oldNames, 0, this.argumentNames, 1, oldNames.length); } } } @@ -456,8 +456,8 @@ private void bindExplicitArguments(int numArgumentsLeftToBind) { int numExpectedArgumentNames = this.aspectJAdviceMethod.getParameterTypes().length; if (this.argumentNames.length != numExpectedArgumentNames) { - throw new IllegalStateException("Expecting to find " + numExpectedArgumentNames - + " arguments to bind by name in advice, but actually found " + + throw new IllegalStateException("Expecting to find " + numExpectedArgumentNames + + " arguments to bind by name in advice, but actually found " + this.argumentNames.length + " arguments."); } @@ -471,8 +471,8 @@ private void bindExplicitArguments(int numArgumentsLeftToBind) { // specified, and find the discovered argument types. if (this.returningName != null) { if (!this.argumentBindings.containsKey(this.returningName)) { - throw new IllegalStateException("Returning argument name '" - + this.returningName + "' was not bound in advice arguments"); + throw new IllegalStateException("Returning argument name '" + this.returningName + + "' was not bound in advice arguments"); } else { Integer index = this.argumentBindings.get(this.returningName); @@ -482,8 +482,8 @@ private void bindExplicitArguments(int numArgumentsLeftToBind) { } if (this.throwingName != null) { if (!this.argumentBindings.containsKey(this.throwingName)) { - throw new IllegalStateException("Throwing argument name '" - + this.throwingName + "' was not bound in advice arguments"); + throw new IllegalStateException("Throwing argument name '" + this.throwingName + + "' was not bound in advice arguments"); } else { Integer index = this.argumentBindings.get(this.throwingName); @@ -581,10 +581,9 @@ else if (this.joinPointStaticPartArgumentIndex != -1) { } if (numBound != this.adviceInvocationArgumentCount) { - throw new IllegalStateException("Required to bind " + this.adviceInvocationArgumentCount - + " arguments, but only bound " + numBound + " (JoinPointMatch " + - (jpMatch == null ? "was NOT" : "WAS") + - " bound in invocation)"); + throw new IllegalStateException("Required to bind " + this.adviceInvocationArgumentCount + + " arguments, but only bound " + numBound + " (JoinPointMatch " + + (jpMatch == null ? "was NOT" : "WAS") + " bound in invocation)"); } return adviceInvocationArgs; diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterAdvice.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterAdvice.java index c6669af251..a1900b8998 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterAdvice.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterAdvice.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,6 +37,7 @@ public AspectJAfterAdvice( super(aspectJBeforeAdviceMethod, pointcut, aif); } + @Override public Object invoke(MethodInvocation mi) throws Throwable { try { diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterReturningAdvice.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterReturningAdvice.java index 59d5ae6e09..5e0b489c63 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterReturningAdvice.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterReturningAdvice.java @@ -40,6 +40,7 @@ public AspectJAfterReturningAdvice( super(aspectJBeforeAdviceMethod, pointcut, aif); } + @Override public boolean isBeforeAdvice() { return false; @@ -62,6 +63,7 @@ public void afterReturning(Object returnValue, Method method, Object[] args, Obj } } + /** * Following AspectJ semantics, if a returning clause was specified, then the * advice is only invoked if the returned value is an instance of the given diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterThrowingAdvice.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterThrowingAdvice.java index 486b261275..be74c7e402 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterThrowingAdvice.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterThrowingAdvice.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,6 +37,7 @@ public AspectJAfterThrowingAdvice( super(aspectJBeforeAdviceMethod, pointcut, aif); } + @Override public boolean isBeforeAdvice() { return false; @@ -57,11 +58,11 @@ public Object invoke(MethodInvocation mi) throws Throwable { try { return mi.proceed(); } - catch (Throwable t) { - if (shouldInvokeOnThrowing(t)) { - invokeAdviceMethod(getJoinPointMatch(), null, t); + catch (Throwable ex) { + if (shouldInvokeOnThrowing(ex)) { + invokeAdviceMethod(getJoinPointMatch(), null, ex); } - throw t; + throw ex; } } @@ -69,8 +70,8 @@ public Object invoke(MethodInvocation mi) throws Throwable { * In AspectJ semantics, after throwing advice that specifies a throwing clause * is only invoked if the thrown exception is a subtype of the given throwing type. */ - private boolean shouldInvokeOnThrowing(Throwable t) { - return getDiscoveredThrowingType().isAssignableFrom(t.getClass()); + private boolean shouldInvokeOnThrowing(Throwable ex) { + return getDiscoveredThrowingType().isAssignableFrom(ex.getClass()); } } diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAroundAdvice.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAroundAdvice.java index b9c546342b..3efefd276c 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAroundAdvice.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAroundAdvice.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2015 the original author 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,6 +41,7 @@ public AspectJAroundAdvice( super(aspectJAroundAdviceMethod, pointcut, aif); } + @Override public boolean isBeforeAdvice() { return false; @@ -56,7 +57,6 @@ protected boolean supportsProceedingJoinPoint() { return true; } - @Override public Object invoke(MethodInvocation mi) throws Throwable { if (!(mi instanceof ProxyMethodInvocation)) { diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJExpressionPointcut.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJExpressionPointcut.java index 60427610a6..3cb2613689 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJExpressionPointcut.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJExpressionPointcut.java @@ -305,7 +305,7 @@ public boolean isRuntime() { } @Override - public boolean matches(Method method, Class targetClass, Object[] args) { + public boolean matches(Method method, Class targetClass, Object... args) { checkReadyToMatch(); ShadowMatch shadowMatch = getShadowMatch(AopUtils.getMostSpecificMethod(method, targetClass), method); ShadowMatch originalShadowMatch = getShadowMatch(method, method); diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJMethodBeforeAdvice.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJMethodBeforeAdvice.java index 6f7ce26b39..65a5dce1be 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJMethodBeforeAdvice.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJMethodBeforeAdvice.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,6 +35,7 @@ public AspectJMethodBeforeAdvice( super(aspectJBeforeAdviceMethod, pointcut, aif); } + @Override public void before(Method method, Object[] args, Object target) throws Throwable { invokeAdviceMethod(getJoinPointMatch(), null, null); diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactory.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactory.java index 3fad461004..98c74f7ad0 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactory.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -61,34 +61,6 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac private static final String AJC_MAGIC = "ajc$"; - /** - * Find and return the first AspectJ annotation on the given method - * (there should only be one anyway...) - */ - @SuppressWarnings("unchecked") - protected static AspectJAnnotation findAspectJAnnotationOnMethod(Method method) { - Class[] classesToLookFor = new Class[] { - Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class}; - for (Class c : classesToLookFor) { - AspectJAnnotation foundAnnotation = findAnnotation(method, (Class) c); - if (foundAnnotation != null) { - return foundAnnotation; - } - } - return null; - } - - private static AspectJAnnotation findAnnotation(Method method, Class toLookFor) { - A result = AnnotationUtils.findAnnotation(method, toLookFor); - if (result != null) { - return new AspectJAnnotation(result); - } - else { - return null; - } - } - - /** Logger available to subclasses */ protected final Log logger = LogFactory.getLog(getClass()); @@ -181,6 +153,7 @@ private Class[] extractPointcutParameterTypes(String[] argNames, Method advic throw new IllegalStateException("Expecting at least " + argNames.length + " arguments in the advice declaration, but only found " + paramTypes.length); } + // Make the simplifying assumption for now that all of the JoinPoint based arguments // come first in the advice declaration. int typeOffset = paramTypes.length - argNames.length; @@ -191,7 +164,36 @@ private Class[] extractPointcutParameterTypes(String[] argNames, Method advic } + /** + * Find and return the first AspectJ annotation on the given method + * (there should only be one anyway...) + */ + @SuppressWarnings("unchecked") + protected static AspectJAnnotation findAspectJAnnotationOnMethod(Method method) { + Class[] classesToLookFor = new Class[] { + Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class}; + for (Class c : classesToLookFor) { + AspectJAnnotation foundAnnotation = findAnnotation(method, (Class) c); + if (foundAnnotation != null) { + return foundAnnotation; + } + } + return null; + } + + private static AspectJAnnotation findAnnotation(Method method, Class toLookFor) { + A result = AnnotationUtils.findAnnotation(method, toLookFor); + if (result != null) { + return new AspectJAnnotation(result); + } + else { + return null; + } + } + + protected enum AspectJAnnotationType { + AtPointcut, AtBefore, AtAfter, diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AspectJAdvisorFactory.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AspectJAdvisorFactory.java index 36bf115105..7eba0606d5 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AspectJAdvisorFactory.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AspectJAdvisorFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -63,31 +63,31 @@ public interface AspectJAdvisorFactory { /** * Build Spring AOP Advisors for all annotated At-AspectJ methods * on the specified aspect instance. - * @param aif the aspect instance factory (not the aspect instance itself - * in order to avoid eager instantiation) + * @param aspectInstanceFactory the aspect instance factory + * (not the aspect instance itself in order to avoid eager instantiation) * @return a list of advisors for this class */ - List getAdvisors(MetadataAwareAspectInstanceFactory aif); + List getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory); /** * Build a Spring AOP Advisor for the given AspectJ advice method. * @param candidateAdviceMethod the candidate advice method - * @param aif the aspect instance factory - * @param declarationOrderInAspect the declaration order within the aspect + * @param aspectInstanceFactory the aspect instance factory + * @param declarationOrder the declaration order within the aspect * @param aspectName the name of the aspect * @return {@code null} if the method is not an AspectJ advice method * or if it is a pointcut that will be used by other advice but will not * create a Spring advice in its own right */ - Advisor getAdvisor(Method candidateAdviceMethod, - MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName); + Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory, + int declarationOrder, String aspectName); /** * Build a Spring AOP Advice for the given AspectJ advice method. * @param candidateAdviceMethod the candidate advice method - * @param pointcut the corresponding AspectJ expression pointcut - * @param aif the aspect instance factory - * @param declarationOrderInAspect the declaration order within the aspect + * @param expressionPointcut the AspectJ expression pointcut + * @param aspectInstanceFactory the aspect instance factory + * @param declarationOrder the declaration order within the aspect * @param aspectName the name of the aspect * @return {@code null} if the method is not an AspectJ advice method * or if it is a pointcut that will be used by other advice but will not @@ -98,7 +98,7 @@ Advisor getAdvisor(Method candidateAdviceMethod, * @see org.springframework.aop.aspectj.AspectJAfterReturningAdvice * @see org.springframework.aop.aspectj.AspectJAfterThrowingAdvice */ - Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut pointcut, - MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName); + Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut, + MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName); } diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AspectMetadata.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AspectMetadata.java index a70010e1f9..d20e5222a0 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AspectMetadata.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AspectMetadata.java @@ -42,6 +42,13 @@ */ public class AspectMetadata { + /** + * The name of this aspect as defined to Spring (the bean name) - + * allows us to determine if two pieces of advice come from the + * same aspect and hence their relative precedence. + */ + private final String aspectName; + /** * AspectJ reflection information (AspectJ 5 / Java 5 specific). */ @@ -54,13 +61,6 @@ public class AspectMetadata { */ private final Pointcut perClausePointcut; - /** - * The name of this aspect as defined to Spring (the bean name) - - * allows us to determine if two pieces of advice come from the - * same aspect and hence their relative precedence. - */ - private String aspectName; - /** * Create a new AspectMetadata instance for the given aspect class. @@ -83,10 +83,10 @@ public AspectMetadata(Class aspectClass, String aspectName) { if (ajType == null) { throw new IllegalArgumentException("Class '" + aspectClass.getName() + "' is not an @AspectJ aspect"); } - this.ajType = ajType; - if (this.ajType.getDeclarePrecedence().length > 0) { + if (ajType.getDeclarePrecedence().length > 0) { throw new IllegalArgumentException("DeclarePrecendence not presently supported in Spring AOP"); } + this.ajType = ajType; switch (this.ajType.getPerClause().getKind()) { case SINGLETON : diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/InstantiationModelAwarePointcutAdvisorImpl.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/InstantiationModelAwarePointcutAdvisorImpl.java index 200936f140..750a1eb265 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/InstantiationModelAwarePointcutAdvisorImpl.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/InstantiationModelAwarePointcutAdvisorImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,53 +42,55 @@ class InstantiationModelAwarePointcutAdvisorImpl private final AspectJExpressionPointcut declaredPointcut; - private Pointcut pointcut; + private final Method aspectJAdviceMethod; - private final MetadataAwareAspectInstanceFactory aspectInstanceFactory; + private final AspectJAdvisorFactory aspectJAdvisorFactory; - private final Method method; + private final MetadataAwareAspectInstanceFactory aspectInstanceFactory; - private final boolean lazy; + private final int declarationOrder; - private final AspectJAdvisorFactory atAspectJAdvisorFactory; + private final String aspectName; - private Advice instantiatedAdvice; + private final Pointcut pointcut; - private int declarationOrder; + private final boolean lazy; - private String aspectName; + private Advice instantiatedAdvice; private Boolean isBeforeAdvice; private Boolean isAfterAdvice; - public InstantiationModelAwarePointcutAdvisorImpl(AspectJAdvisorFactory af, AspectJExpressionPointcut ajexp, - MetadataAwareAspectInstanceFactory aif, Method method, int declarationOrderInAspect, String aspectName) { + public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut, + Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory, + MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) { - this.declaredPointcut = ajexp; - this.method = method; - this.atAspectJAdvisorFactory = af; - this.aspectInstanceFactory = aif; - this.declarationOrder = declarationOrderInAspect; + this.declaredPointcut = declaredPointcut; + this.aspectJAdviceMethod = aspectJAdviceMethod; + this.aspectJAdvisorFactory = aspectJAdvisorFactory; + this.aspectInstanceFactory = aspectInstanceFactory; + this.declarationOrder = declarationOrder; this.aspectName = aspectName; - if (aif.getAspectMetadata().isLazilyInstantiated()) { + if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) { // Static part of the pointcut is a lazy type. - Pointcut preInstantiationPointcut = - Pointcuts.union(aif.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut); + Pointcut preInstantiationPointcut = Pointcuts.union( + aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut); // Make it dynamic: must mutate from pre-instantiation to post-instantiation state. // If it's not a dynamic pointcut, it may be optimized out // by the Spring AOP infrastructure after the first evaluation. - this.pointcut = new PerTargetInstantiationModelPointcut(this.declaredPointcut, preInstantiationPointcut, aif); + this.pointcut = new PerTargetInstantiationModelPointcut( + this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory); this.lazy = true; } else { // A singleton aspect. - this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut); - this.pointcut = declaredPointcut; + this.pointcut = this.declaredPointcut; this.lazy = false; + this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut); } } @@ -142,8 +144,8 @@ public synchronized boolean isAdviceInstantiated() { private Advice instantiateAdvice(AspectJExpressionPointcut pcut) { - return this.atAspectJAdvisorFactory.getAdvice( - this.method, pcut, this.aspectInstanceFactory, this.declarationOrder, this.aspectName); + return this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pcut, + this.aspectInstanceFactory, this.declarationOrder, this.aspectName); } public MetadataAwareAspectInstanceFactory getAspectInstanceFactory() { @@ -191,7 +193,7 @@ public boolean isAfterAdvice() { */ private void determineAdviceType() { AspectJAnnotation aspectJAnnotation = - AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(this.method); + AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(this.aspectJAdviceMethod); if (aspectJAnnotation == null) { this.isBeforeAdvice = false; this.isAfterAdvice = false; @@ -220,7 +222,7 @@ private void determineAdviceType() { @Override public String toString() { return "InstantiationModelAwarePointcutAdvisor: expression [" + getDeclaredPointcut().getExpression() + - "]; advice method [" + this.method + "]; perClauseKind=" + + "]; advice method [" + this.aspectJAdviceMethod + "]; perClauseKind=" + this.aspectInstanceFactory.getAspectMetadata().getAjType().getPerClause().getKind(); } @@ -256,7 +258,7 @@ public boolean matches(Method method, Class targetClass) { } @Override - public boolean matches(Method method, Class targetClass, Object[] args) { + public boolean matches(Method method, Class targetClass, Object... args) { // This can match only on declared pointcut. return (isAspectMaterialized() && this.declaredPointcut.matches(method, targetClass)); } diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/PrototypeAspectInstanceFactory.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/PrototypeAspectInstanceFactory.java index 5d6fc9079b..fc2978a9a8 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/PrototypeAspectInstanceFactory.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/PrototypeAspectInstanceFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,8 +19,8 @@ import org.springframework.beans.factory.BeanFactory; /** - * AspectInstanceFactory backed by a BeanFactory-provided prototype, - * enforcing prototype semantics. + * {@link org.springframework.aop.aspectj.AspectInstanceFactory} backed by a + * {@link BeanFactory}-provided prototype, enforcing prototype semantics. * *

Note that this may instantiate multiple times, which probably won't give the * semantics you expect. Use a {@link LazySingletonAspectInstanceFactoryDecorator} diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/ReflectiveAspectJAdvisorFactory.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/ReflectiveAspectJAdvisorFactory.java index bf8cab2b08..1a35493001 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/ReflectiveAspectJAdvisorFactory.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/ReflectiveAspectJAdvisorFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -77,8 +77,9 @@ public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFacto new Converter() { @Override public Annotation convert(Method method) { - AspectJAnnotation annotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method); - return annotation == null ? null : annotation.getAnnotation(); + AspectJAnnotation annotation = + AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method); + return (annotation != null ? annotation.getAnnotation() : null); } })); comparator.addComparator(new ConvertingComparator( @@ -93,17 +94,17 @@ public String convert(Method method) { @Override - public List getAdvisors(MetadataAwareAspectInstanceFactory maaif) { - final Class aspectClass = maaif.getAspectMetadata().getAspectClass(); - final String aspectName = maaif.getAspectMetadata().getAspectName(); + public List getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) { + Class aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); + String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName(); validate(aspectClass); // We need to wrap the MetadataAwareAspectInstanceFactory with a decorator // so that it will only instantiate once. - final MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory = - new LazySingletonAspectInstanceFactoryDecorator(maaif); + MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory = + new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory); - final List advisors = new LinkedList(); + List advisors = new LinkedList(); for (Method method : getAdvisorMethods(aspectClass)) { Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName); if (advisor != null) { @@ -169,18 +170,19 @@ private Advisor getDeclareParentsAdvisor(Field introductionField) { @Override - public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aif, + public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrderInAspect, String aspectName) { - validate(aif.getAspectMetadata().getAspectClass()); + validate(aspectInstanceFactory.getAspectMetadata().getAspectClass()); - AspectJExpressionPointcut ajexp = - getPointcut(candidateAdviceMethod, aif.getAspectMetadata().getAspectClass()); - if (ajexp == null) { + AspectJExpressionPointcut expressionPointcut = getPointcut( + candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass()); + if (expressionPointcut == null) { return null; } - return new InstantiationModelAwarePointcutAdvisorImpl( - this, ajexp, aif, candidateAdviceMethod, declarationOrderInAspect, aspectName); + + return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod, + this, aspectInstanceFactory, declarationOrderInAspect, aspectName); } private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class candidateAspectClass) { @@ -189,6 +191,7 @@ private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Clas if (aspectJAnnotation == null) { return null; } + AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class[0]); ajexp.setExpression(aspectJAnnotation.getPointcutExpression()); @@ -197,10 +200,10 @@ private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Clas @Override - public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut ajexp, - MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName) { + public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut, + MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) { - Class candidateAspectClass = aif.getAspectMetadata().getAspectClass(); + Class candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); validate(candidateAspectClass); AspectJAnnotation aspectJAnnotation = @@ -225,27 +228,32 @@ public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut switch (aspectJAnnotation.getAnnotationType()) { case AtBefore: - springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, ajexp, aif); + springAdvice = new AspectJMethodBeforeAdvice( + candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; case AtAfter: - springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, ajexp, aif); + springAdvice = new AspectJAfterAdvice( + candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; case AtAfterReturning: - springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, ajexp, aif); + springAdvice = new AspectJAfterReturningAdvice( + candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation(); if (StringUtils.hasText(afterReturningAnnotation.returning())) { springAdvice.setReturningName(afterReturningAnnotation.returning()); } break; case AtAfterThrowing: - springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, ajexp, aif); + springAdvice = new AspectJAfterThrowingAdvice( + candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation(); if (StringUtils.hasText(afterThrowingAnnotation.throwing())) { springAdvice.setThrowingName(afterThrowingAnnotation.throwing()); } break; case AtAround: - springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, ajexp, aif); + springAdvice = new AspectJAroundAdvice( + candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; case AtPointcut: if (logger.isDebugEnabled()) { @@ -254,12 +262,12 @@ public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut return null; default: throw new UnsupportedOperationException( - "Unsupported advice type on method " + candidateAdviceMethod); + "Unsupported advice type on method: " + candidateAdviceMethod); } // Now to configure the advice... springAdvice.setAspectName(aspectName); - springAdvice.setDeclarationOrder(declarationOrderInAspect); + springAdvice.setDeclarationOrder(declarationOrder); String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod); if (argNames != null) { springAdvice.setArgumentNamesFromStringArray(argNames); @@ -268,6 +276,7 @@ public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut return springAdvice; } + /** * Synthetic advisor that instantiates the aspect. * Triggered by per-clause pointcut on non-singleton aspect. diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/AdvisedSupport.java b/spring-aop/src/main/java/org/springframework/aop/framework/AdvisedSupport.java index ed95bd96de..9b3b307467 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/AdvisedSupport.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/AdvisedSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -113,7 +113,7 @@ public AdvisedSupport() { * Create a AdvisedSupport instance with the given parameters. * @param interfaces the proxied interfaces */ - public AdvisedSupport(Class[] interfaces) { + public AdvisedSupport(Class... interfaces) { this(); setInterfaces(interfaces); } diff --git a/spring-aop/src/main/java/org/springframework/aop/support/ControlFlowPointcut.java b/spring-aop/src/main/java/org/springframework/aop/support/ControlFlowPointcut.java index d41a36c250..1989a6dae4 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/ControlFlowPointcut.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/ControlFlowPointcut.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -91,17 +91,17 @@ public boolean isRuntime() { } @Override - public boolean matches(Method method, Class targetClass, Object[] args) { - ++this.evaluations; + public boolean matches(Method method, Class targetClass, Object... args) { + this.evaluations++; ControlFlow cflow = ControlFlowFactory.createControlFlow(); - return (this.methodName != null) ? cflow.under(this.clazz, this.methodName) : cflow.under(this.clazz); + return (this.methodName != null ? cflow.under(this.clazz, this.methodName) : cflow.under(this.clazz)); } /** * It's useful to know how many times we've fired, for optimization. */ public int getEvaluations() { - return evaluations; + return this.evaluations; } @@ -115,6 +115,7 @@ public MethodMatcher getMethodMatcher() { return this; } + @Override public boolean equals(Object other) { if (this == other) { diff --git a/spring-aop/src/main/java/org/springframework/aop/support/MethodMatchers.java b/spring-aop/src/main/java/org/springframework/aop/support/MethodMatchers.java index b71e2c2399..9a64d07431 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/MethodMatchers.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/MethodMatchers.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -138,7 +138,7 @@ public boolean isRuntime() { } @Override - public boolean matches(Method method, Class targetClass, Object[] args) { + public boolean matches(Method method, Class targetClass, Object... args) { return this.mm1.matches(method, targetClass, args) || this.mm2.matches(method, targetClass, args); } @@ -245,7 +245,7 @@ public boolean isRuntime() { } @Override - public boolean matches(Method method, Class targetClass, Object[] args) { + public boolean matches(Method method, Class targetClass, Object... args) { // Because a dynamic intersection may be composed of a static and dynamic part, // we must avoid calling the 3-arg matches method on a dynamic matcher, as // it will probably be an unsupported operation. diff --git a/spring-aop/src/main/java/org/springframework/aop/support/Pointcuts.java b/spring-aop/src/main/java/org/springframework/aop/support/Pointcuts.java index df6e57bfa4..b17977ba48 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/Pointcuts.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/Pointcuts.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -71,7 +71,7 @@ public static Pointcut intersection(Pointcut pc1, Pointcut pc2) { * @param args arguments to the method * @return whether there's a runtime match */ - public static boolean matches(Pointcut pointcut, Method method, Class targetClass, Object[] args) { + public static boolean matches(Pointcut pointcut, Method method, Class targetClass, Object... args) { Assert.notNull(pointcut, "Pointcut must not be null"); if (pointcut == Pointcut.TRUE) { return true; @@ -98,9 +98,9 @@ private static class SetterPointcut extends StaticMethodMatcherPointcut implemen @Override public boolean matches(Method method, Class targetClass) { - return method.getName().startsWith("set") && - method.getParameterTypes().length == 1 && - method.getReturnType() == Void.TYPE; + return (method.getName().startsWith("set") && + method.getParameterTypes().length == 1 && + method.getReturnType() == Void.TYPE); } private Object readResolve() { @@ -119,8 +119,8 @@ private static class GetterPointcut extends StaticMethodMatcherPointcut implemen @Override public boolean matches(Method method, Class targetClass) { - return method.getName().startsWith("get") && - method.getParameterTypes().length == 0; + return (method.getName().startsWith("get") && + method.getParameterTypes().length == 0); } private Object readResolve() { diff --git a/spring-aop/src/main/java/org/springframework/aop/support/StaticMethodMatcher.java b/spring-aop/src/main/java/org/springframework/aop/support/StaticMethodMatcher.java index 8bb1d74d73..0627248ee9 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/StaticMethodMatcher.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/StaticMethodMatcher.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,7 +32,7 @@ public final boolean isRuntime() { } @Override - public final boolean matches(Method method, Class targetClass, Object[] args) { + public final boolean matches(Method method, Class targetClass, Object... args) { // should never be invoked because isRuntime() returns false throw new UnsupportedOperationException("Illegal MethodMatcher usage"); } diff --git a/spring-aop/src/test/java/org/springframework/aop/aspectj/AspectJExpressionPointcutTests.java b/spring-aop/src/test/java/org/springframework/aop/aspectj/AspectJExpressionPointcutTests.java index 6419fbe774..28622ba8d2 100644 --- a/spring-aop/src/test/java/org/springframework/aop/aspectj/AspectJExpressionPointcutTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/aspectj/AspectJExpressionPointcutTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -53,17 +53,15 @@ public final class AspectJExpressionPointcutTests { private Method setSomeNumber; - private Method isPostProcessed; - @Before public void setUp() throws NoSuchMethodException { - getAge = TestBean.class.getMethod("getAge", (Class[])null); - setAge = TestBean.class.getMethod("setAge", new Class[]{int.class}); - setSomeNumber = TestBean.class.getMethod("setSomeNumber", new Class[]{Number.class}); - isPostProcessed = TestBean.class.getMethod("isPostProcessed", (Class[]) null); + getAge = TestBean.class.getMethod("getAge"); + setAge = TestBean.class.getMethod("setAge", int.class); + setSomeNumber = TestBean.class.getMethod("setSomeNumber", Number.class); } + @Test public void testMatchExplicit() { String expression = "execution(int org.springframework.tests.sample.beans.TestBean.getAge())"; @@ -111,21 +109,9 @@ public void testTarget() throws SecurityException, NoSuchMethodException { testThisOrTarget("target"); } - public static class OtherIOther implements IOther { - - @Override - public void absquatulate() { - // Empty - } - - } - /** * This and target are equivalent. Really instanceof pointcuts. * @param which this or target - * @throws Exception - * @throws NoSuchMethodException - * @throws SecurityException */ private void testThisOrTarget(String which) throws SecurityException, NoSuchMethodException { String matchesTestBean = which + "(org.springframework.tests.sample.beans.TestBean)"; @@ -138,11 +124,8 @@ private void testThisOrTarget(String which) throws SecurityException, NoSuchMeth assertTrue(testBeanPc.matches(TestBean.class)); assertTrue(testBeanPc.matches(getAge, TestBean.class)); - assertTrue(iOtherPc.matches(OtherIOther.class.getMethod("absquatulate", (Class[])null), - OtherIOther.class)); - - assertFalse(testBeanPc.matches(OtherIOther.class.getMethod("absquatulate", (Class[])null), - OtherIOther.class)); + assertTrue(iOtherPc.matches(OtherIOther.class.getMethod("absquatulate"), OtherIOther.class)); + assertFalse(testBeanPc.matches(OtherIOther.class.getMethod("absquatulate"), OtherIOther.class)); } @Test @@ -171,8 +154,7 @@ private void testWithinPackage(boolean matchSubpackages) throws SecurityExceptio assertEquals(matchSubpackages, withinBeansPc.matches( DeepBean.class.getMethod("aMethod", String.class), DeepBean.class)); assertFalse(withinBeansPc.matches(String.class)); - assertFalse(withinBeansPc.matches(OtherIOther.class.getMethod("absquatulate", (Class[])null), - OtherIOther.class)); + assertFalse(withinBeansPc.matches(OtherIOther.class.getMethod("absquatulate"), OtherIOther.class)); } @Test @@ -183,7 +165,7 @@ public void testFriendlyErrorOnNoLocationClassMatching() { fail(); } catch (IllegalStateException ex) { - assertTrue(ex.getMessage().indexOf("expression") != -1); + assertTrue(ex.getMessage().contains("expression")); } } @@ -195,7 +177,7 @@ public void testFriendlyErrorOnNoLocation2ArgMatching() { fail(); } catch (IllegalStateException ex) { - assertTrue(ex.getMessage().indexOf("expression") != -1); + assertTrue(ex.getMessage().contains("expression")); } } @@ -207,7 +189,7 @@ public void testFriendlyErrorOnNoLocation3ArgMatching() { fail(); } catch (IllegalStateException ex) { - assertTrue(ex.getMessage().indexOf("expression") != -1); + assertTrue(ex.getMessage().contains("expression")); } } @@ -226,10 +208,10 @@ public void testMatchWithArgs() throws Exception { //assertDoesNotMatchStringClass(classFilter); assertTrue("Should match with setSomeNumber with Double input", - methodMatcher.matches(setSomeNumber, TestBean.class, new Object[]{new Double(12)})); + methodMatcher.matches(setSomeNumber, TestBean.class, new Double(12))); assertFalse("Should not match setSomeNumber with Integer input", - methodMatcher.matches(setSomeNumber, TestBean.class, new Object[]{new Integer(11)})); - assertFalse("Should not match getAge", methodMatcher.matches(getAge, TestBean.class, null)); + methodMatcher.matches(setSomeNumber, TestBean.class, new Integer(11))); + assertFalse("Should not match getAge", methodMatcher.matches(getAge, TestBean.class)); assertTrue("Should be a runtime match", methodMatcher.isRuntime()); } @@ -281,7 +263,6 @@ public void testInvalidExpression() { } catch (IllegalArgumentException ex) { assertTrue(true); - System.out.println(ex.getMessage()); } } @@ -326,16 +307,14 @@ public void testWithUnsupportedPointcutPrimitive() throws Exception { @Test public void testAndSubstitution() { Pointcut pc = getPointcut("execution(* *(..)) and args(String)"); - PointcutExpression expr = - ((AspectJExpressionPointcut) pc).getPointcutExpression(); + PointcutExpression expr = ((AspectJExpressionPointcut) pc).getPointcutExpression(); assertEquals("execution(* *(..)) && args(String)",expr.getPointcutExpression()); } @Test public void testMultipleAndSubstitutions() { Pointcut pc = getPointcut("execution(* *(..)) and args(String) and this(Object)"); - PointcutExpression expr = - ((AspectJExpressionPointcut) pc).getPointcutExpression(); + PointcutExpression expr = ((AspectJExpressionPointcut) pc).getPointcutExpression(); assertEquals("execution(* *(..)) && args(String) && this(Object)",expr.getPointcutExpression()); } @@ -344,6 +323,15 @@ private Pointcut getPointcut(String expression) { pointcut.setExpression(expression); return pointcut; } + + + public static class OtherIOther implements IOther { + + @Override + public void absquatulate() { + // Empty + } + } } diff --git a/spring-aop/src/test/java/org/springframework/aop/aspectj/TigerAspectJExpressionPointcutTests.java b/spring-aop/src/test/java/org/springframework/aop/aspectj/TigerAspectJExpressionPointcutTests.java index f14bee5dfc..ddb7c1afe2 100644 --- a/spring-aop/src/test/java/org/springframework/aop/aspectj/TigerAspectJExpressionPointcutTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/aspectj/TigerAspectJExpressionPointcutTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,7 +36,7 @@ * @author Rod Johnson * @author Chris Beams */ -public final class TigerAspectJExpressionPointcutTests { +public class TigerAspectJExpressionPointcutTests { // TODO factor into static in AspectJExpressionPointcut private Method getAge; @@ -46,7 +46,7 @@ public final class TigerAspectJExpressionPointcutTests { @Before public void setUp() throws NoSuchMethodException { - getAge = TestBean.class.getMethod("getAge", (Class[]) null); + getAge = TestBean.class.getMethod("getAge"); // Assumes no overloading for (Method m : HasGeneric.class.getMethods()) { methodsOnHasGeneric.put(m.getName(), m); @@ -54,18 +54,6 @@ public void setUp() throws NoSuchMethodException { } - public static class HasGeneric { - - public void setFriends(List friends) { - } - public void setEnemies(List enemies) { - } - public void setPartners(List partners) { - } - public void setPhoneNumbers(List numbers) { - } - } - @Test public void testMatchGenericArgument() { String expression = "execution(* set*(java.util.List) )"; @@ -132,15 +120,12 @@ public void testMatchAnnotationOnClassWithoutBinding() throws SecurityException, public void testMatchAnnotationOnClassWithSubpackageWildcard() throws SecurityException, NoSuchMethodException { String expression = "within(@(test.annotation..*) *)"; AspectJExpressionPointcut springAnnotatedPc = testMatchAnnotationOnClass(expression); - assertFalse(springAnnotatedPc.matches(TestBean.class.getMethod("setName", String.class), - TestBean.class)); - assertTrue(springAnnotatedPc.matches(SpringAnnotated.class.getMethod("foo", (Class[]) null), - SpringAnnotated.class)); + assertFalse(springAnnotatedPc.matches(TestBean.class.getMethod("setName", String.class), TestBean.class)); + assertTrue(springAnnotatedPc.matches(SpringAnnotated.class.getMethod("foo"), SpringAnnotated.class)); expression = "within(@(test.annotation.transaction..*) *)"; AspectJExpressionPointcut springTxAnnotatedPc = testMatchAnnotationOnClass(expression); - assertFalse(springTxAnnotatedPc.matches(SpringAnnotated.class.getMethod("foo", (Class[]) null), - SpringAnnotated.class)); + assertFalse(springTxAnnotatedPc.matches(SpringAnnotated.class.getMethod("foo"), SpringAnnotated.class)); } @Test @@ -154,7 +139,7 @@ private AspectJExpressionPointcut testMatchAnnotationOnClass(String expression) ajexp.setExpression(expression); assertFalse(ajexp.matches(getAge, TestBean.class)); - assertTrue(ajexp.matches(HasTransactionalAnnotation.class.getMethod("foo", (Class[]) null), HasTransactionalAnnotation.class)); + assertTrue(ajexp.matches(HasTransactionalAnnotation.class.getMethod("foo"), HasTransactionalAnnotation.class)); assertTrue(ajexp.matches(HasTransactionalAnnotation.class.getMethod("bar", String.class), HasTransactionalAnnotation.class)); assertTrue(ajexp.matches(BeanB.class.getMethod("setName", String.class), BeanB.class)); assertFalse(ajexp.matches(BeanA.class.getMethod("setName", String.class), BeanA.class)); @@ -168,10 +153,10 @@ public void testAnnotationOnMethodWithFQN() throws SecurityException, NoSuchMeth ajexp.setExpression(expression); assertFalse(ajexp.matches(getAge, TestBean.class)); - assertFalse(ajexp.matches(HasTransactionalAnnotation.class.getMethod("foo", (Class[]) null), HasTransactionalAnnotation.class)); + assertFalse(ajexp.matches(HasTransactionalAnnotation.class.getMethod("foo"), HasTransactionalAnnotation.class)); assertFalse(ajexp.matches(HasTransactionalAnnotation.class.getMethod("bar", String.class), HasTransactionalAnnotation.class)); assertFalse(ajexp.matches(BeanA.class.getMethod("setName", String.class), BeanA.class)); - assertTrue(ajexp.matches(BeanA.class.getMethod("getAge", (Class[]) null), BeanA.class)); + assertTrue(ajexp.matches(BeanA.class.getMethod("getAge"), BeanA.class)); assertFalse(ajexp.matches(BeanA.class.getMethod("setName", String.class), BeanA.class)); } @@ -182,10 +167,10 @@ public void testAnnotationOnMethodWithWildcard() throws SecurityException, NoSuc anySpringMethodAnnotation.setExpression(expression); assertFalse(anySpringMethodAnnotation.matches(getAge, TestBean.class)); - assertFalse(anySpringMethodAnnotation.matches(HasTransactionalAnnotation.class.getMethod("foo", (Class[]) null), HasTransactionalAnnotation.class)); + assertFalse(anySpringMethodAnnotation.matches(HasTransactionalAnnotation.class.getMethod("foo"), HasTransactionalAnnotation.class)); assertFalse(anySpringMethodAnnotation.matches(HasTransactionalAnnotation.class.getMethod("bar", String.class), HasTransactionalAnnotation.class)); assertFalse(anySpringMethodAnnotation.matches(BeanA.class.getMethod("setName", String.class), BeanA.class)); - assertTrue(anySpringMethodAnnotation.matches(BeanA.class.getMethod("getAge", (Class[]) null), BeanA.class)); + assertTrue(anySpringMethodAnnotation.matches(BeanA.class.getMethod("getAge"), BeanA.class)); assertFalse(anySpringMethodAnnotation.matches(BeanA.class.getMethod("setName", String.class), BeanA.class)); } @@ -196,10 +181,10 @@ public void testAnnotationOnMethodArgumentsWithFQN() throws SecurityException, N takesSpringAnnotatedArgument2.setExpression(expression); assertFalse(takesSpringAnnotatedArgument2.matches(getAge, TestBean.class)); - assertFalse(takesSpringAnnotatedArgument2.matches(HasTransactionalAnnotation.class.getMethod("foo", (Class[]) null), HasTransactionalAnnotation.class)); + assertFalse(takesSpringAnnotatedArgument2.matches(HasTransactionalAnnotation.class.getMethod("foo"), HasTransactionalAnnotation.class)); assertFalse(takesSpringAnnotatedArgument2.matches(HasTransactionalAnnotation.class.getMethod("bar", String.class), HasTransactionalAnnotation.class)); assertFalse(takesSpringAnnotatedArgument2.matches(BeanA.class.getMethod("setName", String.class), BeanA.class)); - assertFalse(takesSpringAnnotatedArgument2.matches(BeanA.class.getMethod("getAge", (Class[]) null), BeanA.class)); + assertFalse(takesSpringAnnotatedArgument2.matches(BeanA.class.getMethod("getAge"), BeanA.class)); assertFalse(takesSpringAnnotatedArgument2.matches(BeanA.class.getMethod("setName", String.class), BeanA.class)); assertTrue(takesSpringAnnotatedArgument2.matches( @@ -213,8 +198,7 @@ public void testAnnotationOnMethodArgumentsWithFQN() throws SecurityException, N assertFalse(takesSpringAnnotatedArgument2.matches( ProcessesSpringAnnotatedParameters.class.getMethod("takesNoAnnotatedParameters", TestBean.class, BeanA.class), - ProcessesSpringAnnotatedParameters.class, - new Object[] { new TestBean(), new BeanA()}) + ProcessesSpringAnnotatedParameters.class, new TestBean(), new BeanA()) ); } @@ -225,10 +209,10 @@ public void testAnnotationOnMethodArgumentsWithWildcards() throws SecurityExcept takesSpringAnnotatedArgument2.setExpression(expression); assertFalse(takesSpringAnnotatedArgument2.matches(getAge, TestBean.class)); - assertFalse(takesSpringAnnotatedArgument2.matches(HasTransactionalAnnotation.class.getMethod("foo", (Class[]) null), HasTransactionalAnnotation.class)); + assertFalse(takesSpringAnnotatedArgument2.matches(HasTransactionalAnnotation.class.getMethod("foo"), HasTransactionalAnnotation.class)); assertFalse(takesSpringAnnotatedArgument2.matches(HasTransactionalAnnotation.class.getMethod("bar", String.class), HasTransactionalAnnotation.class)); assertFalse(takesSpringAnnotatedArgument2.matches(BeanA.class.getMethod("setName", String.class), BeanA.class)); - assertFalse(takesSpringAnnotatedArgument2.matches(BeanA.class.getMethod("getAge", (Class[]) null), BeanA.class)); + assertFalse(takesSpringAnnotatedArgument2.matches(BeanA.class.getMethod("getAge"), BeanA.class)); assertFalse(takesSpringAnnotatedArgument2.matches(BeanA.class.getMethod("setName", String.class), BeanA.class)); assertTrue(takesSpringAnnotatedArgument2.matches( @@ -240,6 +224,19 @@ public void testAnnotationOnMethodArgumentsWithWildcards() throws SecurityExcept } + public static class HasGeneric { + + public void setFriends(List friends) { + } + public void setEnemies(List enemies) { + } + public void setPartners(List partners) { + } + public void setPhoneNumbers(List numbers) { + } + } + + public static class ProcessesSpringAnnotatedParameters { public void takesAnnotatedParameters(TestBean tb, SpringAnnotated sa) { diff --git a/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactoryTests.java b/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactoryTests.java index 6d925271ce..3f34ff592e 100644 --- a/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactoryTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.aop.aspectj.annotation; import java.io.FileNotFoundException; @@ -961,7 +962,7 @@ public void recordModificationIfSetterArgumentDiffersFromOldValue(JoinPoint jp, private Method getGetterFromSetter(Method setter) { String getterName = setter.getName().replaceFirst("set", "get"); try { - return setter.getDeclaringClass().getMethod(getterName, (Class[]) null); + return setter.getDeclaringClass().getMethod(getterName); } catch (NoSuchMethodException ex) { // must be write only diff --git a/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AspectJPointcutAdvisorTests.java b/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AspectJPointcutAdvisorTests.java index b524e0c74b..2f84af9c41 100644 --- a/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AspectJPointcutAdvisorTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/aspectj/annotation/AspectJPointcutAdvisorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,18 +31,21 @@ * @author Rod Johnson * @author Chris Beams */ -public final class AspectJPointcutAdvisorTests { +public class AspectJPointcutAdvisorTests { + + private final AspectJAdvisorFactory af = new ReflectiveAspectJAdvisorFactory(); - private AspectJAdvisorFactory af = new ReflectiveAspectJAdvisorFactory(); @Test public void testSingleton() throws SecurityException, NoSuchMethodException { AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut(); ajexp.setExpression(AspectJExpressionPointcutTests.MATCH_ALL_METHODS); - InstantiationModelAwarePointcutAdvisorImpl ajpa = new InstantiationModelAwarePointcutAdvisorImpl(af, ajexp, - new SingletonMetadataAwareAspectInstanceFactory(new AbstractAspectJAdvisorFactoryTests.ExceptionAspect(null),"someBean"), - TestBean.class.getMethod("getAge", (Class[]) null),1,"someBean"); + InstantiationModelAwarePointcutAdvisorImpl ajpa = new InstantiationModelAwarePointcutAdvisorImpl( + ajexp, TestBean.class.getMethod("getAge"), af, + new SingletonMetadataAwareAspectInstanceFactory(new AbstractAspectJAdvisorFactoryTests.ExceptionAspect(null), "someBean"), + 1, "someBean"); + assertSame(Pointcut.TRUE, ajpa.getAspectMetadata().getPerClausePointcut()); assertFalse(ajpa.isPerInstance()); } @@ -52,34 +55,35 @@ public void testPerTarget() throws SecurityException, NoSuchMethodException { AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut(); ajexp.setExpression(AspectJExpressionPointcutTests.MATCH_ALL_METHODS); - InstantiationModelAwarePointcutAdvisorImpl ajpa = new InstantiationModelAwarePointcutAdvisorImpl(af, ajexp, - new SingletonMetadataAwareAspectInstanceFactory(new PerTargetAspect(),"someBean"), null, 1, "someBean"); + InstantiationModelAwarePointcutAdvisorImpl ajpa = new InstantiationModelAwarePointcutAdvisorImpl( + ajexp, TestBean.class.getMethod("getAge"), af, + new SingletonMetadataAwareAspectInstanceFactory(new PerTargetAspect(), "someBean"), + 1, "someBean"); + assertNotSame(Pointcut.TRUE, ajpa.getAspectMetadata().getPerClausePointcut()); assertTrue(ajpa.getAspectMetadata().getPerClausePointcut() instanceof AspectJExpressionPointcut); assertTrue(ajpa.isPerInstance()); assertTrue(ajpa.getAspectMetadata().getPerClausePointcut().getClassFilter().matches(TestBean.class)); assertFalse(ajpa.getAspectMetadata().getPerClausePointcut().getMethodMatcher().matches( - TestBean.class.getMethod("getAge", (Class[]) null), - TestBean.class)); + TestBean.class.getMethod("getAge"), TestBean.class)); assertTrue(ajpa.getAspectMetadata().getPerClausePointcut().getMethodMatcher().matches( - TestBean.class.getMethod("getSpouse", (Class[]) null), - TestBean.class)); + TestBean.class.getMethod("getSpouse"), TestBean.class)); } - @Test(expected=AopConfigException.class) + @Test(expected = AopConfigException.class) public void testPerCflowTarget() { testIllegalInstantiationModel(AbstractAspectJAdvisorFactoryTests.PerCflowAspect.class); } - @Test(expected=AopConfigException.class) + @Test(expected = AopConfigException.class) public void testPerCflowBelowTarget() { testIllegalInstantiationModel(AbstractAspectJAdvisorFactoryTests.PerCflowBelowAspect.class); } private void testIllegalInstantiationModel(Class c) throws AopConfigException { - new AspectMetadata(c,"someBean"); + new AspectMetadata(c, "someBean"); } } diff --git a/spring-aop/src/test/java/org/springframework/aop/framework/MethodInvocationTests.java b/spring-aop/src/test/java/org/springframework/aop/framework/MethodInvocationTests.java index 7083320483..2a7f726fb7 100644 --- a/spring-aop/src/test/java/org/springframework/aop/framework/MethodInvocationTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/framework/MethodInvocationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 the original author 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 @@ public final class MethodInvocationTests { @Test public void testValidInvocation() throws Throwable { - Method m = Object.class.getMethod("hashCode", (Class[]) null); + Method m = Object.class.getMethod("hashCode"); Object proxy = new Object(); final Object returnValue = new Object(); List is = new LinkedList(); @@ -67,7 +67,7 @@ public String toString() { }; List is = new LinkedList(); - Method m = Object.class.getMethod("hashCode", (Class[]) null); + Method m = Object.class.getMethod("hashCode"); Object proxy = new Object(); ReflectiveMethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, m, null, null, is); diff --git a/spring-aop/src/test/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptorTests.java b/spring-aop/src/test/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptorTests.java index 25e35f5783..6cb0c47cf5 100644 --- a/spring-aop/src/test/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptorTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/framework/adapter/ThrowsAdviceInterceptorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -78,7 +78,7 @@ public void testCorrectHandlerUsed() throws Throwable { ThrowsAdviceInterceptor ti = new ThrowsAdviceInterceptor(th); FileNotFoundException ex = new FileNotFoundException(); MethodInvocation mi = mock(MethodInvocation.class); - given(mi.getMethod()).willReturn(Object.class.getMethod("hashCode", (Class[]) null)); + given(mi.getMethod()).willReturn(Object.class.getMethod("hashCode")); given(mi.getThis()).willReturn(new Object()); given(mi.proceed()).willThrow(ex); try { @@ -140,12 +140,15 @@ public void afterThrowing(RemoteException ex) throws Throwable { assertEquals(1, th.getCalls("remoteException")); } + @SuppressWarnings("serial") static class MyThrowsHandler extends MethodCounter implements ThrowsAdvice { + // Full method signature public void afterThrowing(Method m, Object[] args, Object target, IOException ex) { count("ioException"); } + public void afterThrowing(RemoteException ex) throws Throwable { count("remoteException"); } diff --git a/spring-aop/src/test/java/org/springframework/aop/support/AbstractRegexpMethodPointcutTests.java b/spring-aop/src/test/java/org/springframework/aop/support/AbstractRegexpMethodPointcutTests.java index 64ab845a04..39cbed336e 100644 --- a/spring-aop/src/test/java/org/springframework/aop/support/AbstractRegexpMethodPointcutTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/support/AbstractRegexpMethodPointcutTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 the original author 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,8 +54,8 @@ public void testSerializationWithNoPatternSupplied() throws Exception { } protected void noPatternSuppliedTests(AbstractRegexpMethodPointcut rpc) throws Exception { - assertFalse(rpc.matches(Object.class.getMethod("hashCode", (Class[]) null), String.class)); - assertFalse(rpc.matches(Object.class.getMethod("wait", (Class[]) null), Object.class)); + assertFalse(rpc.matches(Object.class.getMethod("hashCode"), String.class)); + assertFalse(rpc.matches(Object.class.getMethod("wait"), Object.class)); assertEquals(0, rpc.getPatterns().length); } @@ -69,38 +69,38 @@ public void testExactMatch() throws Exception { protected void exactMatchTests(AbstractRegexpMethodPointcut rpc) throws Exception { // assumes rpc.setPattern("java.lang.Object.hashCode"); - assertTrue(rpc.matches(Object.class.getMethod("hashCode", (Class[]) null), String.class)); - assertTrue(rpc.matches(Object.class.getMethod("hashCode", (Class[]) null), Object.class)); - assertFalse(rpc.matches(Object.class.getMethod("wait", (Class[]) null), Object.class)); + assertTrue(rpc.matches(Object.class.getMethod("hashCode"), String.class)); + assertTrue(rpc.matches(Object.class.getMethod("hashCode"), Object.class)); + assertFalse(rpc.matches(Object.class.getMethod("wait"), Object.class)); } @Test public void testSpecificMatch() throws Exception { rpc.setPattern("java.lang.String.hashCode"); - assertTrue(rpc.matches(Object.class.getMethod("hashCode", (Class[]) null), String.class)); - assertFalse(rpc.matches(Object.class.getMethod("hashCode", (Class[]) null), Object.class)); + assertTrue(rpc.matches(Object.class.getMethod("hashCode"), String.class)); + assertFalse(rpc.matches(Object.class.getMethod("hashCode"), Object.class)); } @Test public void testWildcard() throws Exception { rpc.setPattern(".*Object.hashCode"); - assertTrue(rpc.matches(Object.class.getMethod("hashCode", (Class[]) null), Object.class)); - assertFalse(rpc.matches(Object.class.getMethod("wait", (Class[]) null), Object.class)); + assertTrue(rpc.matches(Object.class.getMethod("hashCode"), Object.class)); + assertFalse(rpc.matches(Object.class.getMethod("wait"), Object.class)); } @Test public void testWildcardForOneClass() throws Exception { rpc.setPattern("java.lang.Object.*"); - assertTrue(rpc.matches(Object.class.getMethod("hashCode", (Class[]) null), String.class)); - assertTrue(rpc.matches(Object.class.getMethod("wait", (Class[]) null), String.class)); + assertTrue(rpc.matches(Object.class.getMethod("hashCode"), String.class)); + assertTrue(rpc.matches(Object.class.getMethod("wait"), String.class)); } @Test public void testMatchesObjectClass() throws Exception { rpc.setPattern("java.lang.Object.*"); - assertTrue(rpc.matches(Exception.class.getMethod("hashCode", (Class[]) null), IOException.class)); + assertTrue(rpc.matches(Exception.class.getMethod("hashCode"), IOException.class)); // Doesn't match a method from Throwable - assertFalse(rpc.matches(Exception.class.getMethod("getMessage", (Class[]) null), Exception.class)); + assertFalse(rpc.matches(Exception.class.getMethod("getMessage"), Exception.class)); } @Test diff --git a/spring-aop/src/test/java/org/springframework/aop/support/ComposablePointcutTests.java b/spring-aop/src/test/java/org/springframework/aop/support/ComposablePointcutTests.java index b229c3ee59..b34438ee3d 100644 --- a/spring-aop/src/test/java/org/springframework/aop/support/ComposablePointcutTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/support/ComposablePointcutTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,7 +32,7 @@ * @author Rod Johnson * @author Chris Beams */ -public final class ComposablePointcutTests { +public class ComposablePointcutTests { public static MethodMatcher GETTER_METHOD_MATCHER = new StaticMethodMatcher() { @Override @@ -62,11 +62,12 @@ public boolean matches(Method m, Class targetClass) { } }; + @Test public void testMatchAll() throws NoSuchMethodException { Pointcut pc = new ComposablePointcut(); assertTrue(pc.getClassFilter().matches(Object.class)); - assertTrue(pc.getMethodMatcher().matches(Object.class.getMethod("hashCode", (Class[]) null), Exception.class)); + assertTrue(pc.getMethodMatcher().matches(Object.class.getMethod("hashCode"), Exception.class)); } @Test @@ -93,23 +94,23 @@ public void testFilterByClass() throws NoSuchMethodException { public void testUnionMethodMatcher() { // Matches the getAge() method in any class ComposablePointcut pc = new ComposablePointcut(ClassFilter.TRUE, GET_AGE_METHOD_MATCHER); - assertFalse(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_ABSQUATULATE, TestBean.class, null)); - assertTrue(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_GET_AGE, TestBean.class, null)); - assertFalse(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_GET_NAME, TestBean.class, null)); + assertFalse(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_ABSQUATULATE, TestBean.class)); + assertTrue(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_GET_AGE, TestBean.class)); + assertFalse(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_GET_NAME, TestBean.class)); pc.union(GETTER_METHOD_MATCHER); // Should now match all getter methods - assertFalse(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_ABSQUATULATE, TestBean.class, null)); - assertTrue(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_GET_AGE, TestBean.class, null)); - assertTrue(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_GET_NAME, TestBean.class, null)); + assertFalse(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_ABSQUATULATE, TestBean.class)); + assertTrue(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_GET_AGE, TestBean.class)); + assertTrue(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_GET_NAME, TestBean.class)); pc.union(ABSQUATULATE_METHOD_MATCHER); // Should now match absquatulate() as well - assertTrue(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_ABSQUATULATE, TestBean.class, null)); - assertTrue(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_GET_AGE, TestBean.class, null)); - assertTrue(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_GET_NAME, TestBean.class, null)); + assertTrue(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_ABSQUATULATE, TestBean.class)); + assertTrue(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_GET_AGE, TestBean.class)); + assertTrue(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_GET_NAME, TestBean.class)); // But it doesn't match everything - assertFalse(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_SET_AGE, TestBean.class, null)); + assertFalse(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_SET_AGE, TestBean.class)); } @Test @@ -124,9 +125,9 @@ public void testIntersectionMethodMatcher() { assertTrue(pc.getMethodMatcher().matches(PointcutsTests.TEST_BEAN_GET_NAME, TestBean.class)); pc.intersection(GET_AGE_METHOD_MATCHER); // Use the Pointcuts matches method - assertFalse(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_ABSQUATULATE, TestBean.class, null)); - assertTrue(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_GET_AGE, TestBean.class, null)); - assertFalse(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_GET_NAME, TestBean.class, null)); + assertFalse(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_ABSQUATULATE, TestBean.class)); + assertTrue(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_GET_AGE, TestBean.class)); + assertFalse(Pointcuts.matches(pc, PointcutsTests.TEST_BEAN_GET_NAME, TestBean.class)); } @Test @@ -153,4 +154,5 @@ public void testEqualsAndHashCode() throws Exception { assertEquals(pc1, pc2); assertEquals(pc1.hashCode(), pc2.hashCode()); } + } diff --git a/spring-aop/src/test/java/org/springframework/aop/support/MethodMatchersTests.java b/spring-aop/src/test/java/org/springframework/aop/support/MethodMatchersTests.java index afa1726b01..5b12fa19e9 100644 --- a/spring-aop/src/test/java/org/springframework/aop/support/MethodMatchersTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/support/MethodMatchersTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,7 +32,7 @@ * @author Juergen Hoeller * @author Chris Beams */ -public final class MethodMatchersTests { +public class MethodMatchersTests { private final Method EXCEPTION_GETMESSAGE; @@ -44,10 +44,10 @@ public final class MethodMatchersTests { public MethodMatchersTests() throws Exception { - EXCEPTION_GETMESSAGE = Exception.class.getMethod("getMessage", (Class[]) null); - ITESTBEAN_GETAGE = ITestBean.class.getMethod("getAge", (Class[]) null); - ITESTBEAN_SETAGE = ITestBean.class.getMethod("setAge", new Class[] { int.class }); - IOTHER_ABSQUATULATE = IOther.class.getMethod("absquatulate", (Class[]) null); + EXCEPTION_GETMESSAGE = Exception.class.getMethod("getMessage"); + ITESTBEAN_GETAGE = ITestBean.class.getMethod("getAge"); + ITESTBEAN_SETAGE = ITestBean.class.getMethod("setAge", int.class); + IOTHER_ABSQUATULATE = IOther.class.getMethod("absquatulate"); } @@ -82,12 +82,12 @@ public void testDynamicAndStaticMethodMatcherIntersection() throws Exception { MethodMatcher intersection = MethodMatchers.intersection(mm1, mm2); assertTrue("Intersection is a dynamic matcher", intersection.isRuntime()); assertTrue("2Matched setAge method", intersection.matches(ITESTBEAN_SETAGE, TestBean.class)); - assertTrue("3Matched setAge method", intersection.matches(ITESTBEAN_SETAGE, TestBean.class, new Object[] { new Integer(5) })); + assertTrue("3Matched setAge method", intersection.matches(ITESTBEAN_SETAGE, TestBean.class, new Integer(5))); // Knock out dynamic part intersection = MethodMatchers.intersection(intersection, new TestDynamicMethodMatcherWhichDoesNotMatch()); assertTrue("Intersection is a dynamic matcher", intersection.isRuntime()); assertTrue("2Matched setAge method", intersection.matches(ITESTBEAN_SETAGE, TestBean.class)); - assertFalse("3 - not Matched setAge method", intersection.matches(ITESTBEAN_SETAGE, TestBean.class, new Object[] { new Integer(5) })); + assertFalse("3 - not Matched setAge method", intersection.matches(ITESTBEAN_SETAGE, TestBean.class, new Integer(5))); } @Test @@ -125,18 +125,20 @@ public boolean matches(Method m, Class targetClass) { } } + private static class TestDynamicMethodMatcherWhichMatches extends DynamicMethodMatcher { @Override - public boolean matches(Method m, Class targetClass, Object[] args) { + public boolean matches(Method m, Class targetClass, Object... args) { return true; } } + private static class TestDynamicMethodMatcherWhichDoesNotMatch extends DynamicMethodMatcher { @Override - public boolean matches(Method m, Class targetClass, Object[] args) { + public boolean matches(Method m, Class targetClass, Object... args) { return false; } } diff --git a/spring-aop/src/test/java/org/springframework/aop/support/PointcutsTests.java b/spring-aop/src/test/java/org/springframework/aop/support/PointcutsTests.java index 6404c8d760..5e5569a949 100644 --- a/spring-aop/src/test/java/org/springframework/aop/support/PointcutsTests.java +++ b/spring-aop/src/test/java/org/springframework/aop/support/PointcutsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 the original author 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 @@ * @author Rod Johnson * @author Chris Beams */ -public final class PointcutsTests { +public class PointcutsTests { public static Method TEST_BEAN_SET_AGE; public static Method TEST_BEAN_GET_AGE; @@ -39,10 +39,10 @@ public final class PointcutsTests { static { try { - TEST_BEAN_SET_AGE = TestBean.class.getMethod("setAge", new Class[] { int.class }); - TEST_BEAN_GET_AGE = TestBean.class.getMethod("getAge", (Class[]) null); - TEST_BEAN_GET_NAME = TestBean.class.getMethod("getName", (Class[]) null); - TEST_BEAN_ABSQUATULATE = TestBean.class.getMethod("absquatulate", (Class[]) null); + TEST_BEAN_SET_AGE = TestBean.class.getMethod("setAge", int.class); + TEST_BEAN_GET_AGE = TestBean.class.getMethod("getAge"); + TEST_BEAN_GET_NAME = TestBean.class.getMethod("getName"); + TEST_BEAN_ABSQUATULATE = TestBean.class.getMethod("absquatulate"); } catch (Exception ex) { throw new RuntimeException("Shouldn't happen: error in test suite"); @@ -125,22 +125,22 @@ public boolean matches(Method m, Class targetClass) { @Test public void testTrue() { - assertTrue(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_SET_AGE, TestBean.class, new Object[] { new Integer(6)})); - assertTrue(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_GET_AGE, TestBean.class, null)); - assertTrue(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_ABSQUATULATE, TestBean.class, null)); - assertTrue(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_SET_AGE, TestBean.class, new Object[] { new Integer(6)})); - assertTrue(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_GET_AGE, TestBean.class, null)); - assertTrue(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_ABSQUATULATE, TestBean.class, null)); + assertTrue(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_SET_AGE, TestBean.class, new Integer(6))); + assertTrue(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_GET_AGE, TestBean.class)); + assertTrue(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_ABSQUATULATE, TestBean.class)); + assertTrue(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_SET_AGE, TestBean.class, new Integer(6))); + assertTrue(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_GET_AGE, TestBean.class)); + assertTrue(Pointcuts.matches(Pointcut.TRUE, TEST_BEAN_ABSQUATULATE, TestBean.class)); } @Test public void testMatches() { - assertTrue(Pointcuts.matches(allClassSetterPointcut, TEST_BEAN_SET_AGE, TestBean.class, new Object[] { new Integer(6)})); - assertFalse(Pointcuts.matches(allClassSetterPointcut, TEST_BEAN_GET_AGE, TestBean.class, null)); - assertFalse(Pointcuts.matches(allClassSetterPointcut, TEST_BEAN_ABSQUATULATE, TestBean.class, null)); - assertFalse(Pointcuts.matches(allClassGetterPointcut, TEST_BEAN_SET_AGE, TestBean.class, new Object[] { new Integer(6)})); - assertTrue(Pointcuts.matches(allClassGetterPointcut, TEST_BEAN_GET_AGE, TestBean.class, null)); - assertFalse(Pointcuts.matches(allClassGetterPointcut, TEST_BEAN_ABSQUATULATE, TestBean.class, null)); + assertTrue(Pointcuts.matches(allClassSetterPointcut, TEST_BEAN_SET_AGE, TestBean.class, new Integer(6))); + assertFalse(Pointcuts.matches(allClassSetterPointcut, TEST_BEAN_GET_AGE, TestBean.class)); + assertFalse(Pointcuts.matches(allClassSetterPointcut, TEST_BEAN_ABSQUATULATE, TestBean.class)); + assertFalse(Pointcuts.matches(allClassGetterPointcut, TEST_BEAN_SET_AGE, TestBean.class, new Integer(6))); + assertTrue(Pointcuts.matches(allClassGetterPointcut, TEST_BEAN_GET_AGE, TestBean.class)); + assertFalse(Pointcuts.matches(allClassGetterPointcut, TEST_BEAN_ABSQUATULATE, TestBean.class)); } /** @@ -149,29 +149,29 @@ public void testMatches() { @Test public void testUnionOfSettersAndGetters() { Pointcut union = Pointcuts.union(allClassGetterPointcut, allClassSetterPointcut); - assertTrue(Pointcuts.matches(union, TEST_BEAN_SET_AGE, TestBean.class, new Object[] { new Integer(6)})); - assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_AGE, TestBean.class, null)); - assertFalse(Pointcuts.matches(union, TEST_BEAN_ABSQUATULATE, TestBean.class, null)); + assertTrue(Pointcuts.matches(union, TEST_BEAN_SET_AGE, TestBean.class, new Integer(6))); + assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_AGE, TestBean.class)); + assertFalse(Pointcuts.matches(union, TEST_BEAN_ABSQUATULATE, TestBean.class)); } @Test public void testUnionOfSpecificGetters() { Pointcut union = Pointcuts.union(allClassGetAgePointcut, allClassGetNamePointcut); - assertFalse(Pointcuts.matches(union, TEST_BEAN_SET_AGE, TestBean.class, new Object[] { new Integer(6)})); - assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_AGE, TestBean.class, null)); - assertFalse(Pointcuts.matches(allClassGetAgePointcut, TEST_BEAN_GET_NAME, TestBean.class, null)); - assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_NAME, TestBean.class, null)); - assertFalse(Pointcuts.matches(union, TEST_BEAN_ABSQUATULATE, TestBean.class, null)); + assertFalse(Pointcuts.matches(union, TEST_BEAN_SET_AGE, TestBean.class, new Integer(6))); + assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_AGE, TestBean.class)); + assertFalse(Pointcuts.matches(allClassGetAgePointcut, TEST_BEAN_GET_NAME, TestBean.class)); + assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_NAME, TestBean.class)); + assertFalse(Pointcuts.matches(union, TEST_BEAN_ABSQUATULATE, TestBean.class)); // Union with all setters union = Pointcuts.union(union, allClassSetterPointcut); - assertTrue(Pointcuts.matches(union, TEST_BEAN_SET_AGE, TestBean.class, new Object[] { new Integer(6)})); - assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_AGE, TestBean.class, null)); - assertFalse(Pointcuts.matches(allClassGetAgePointcut, TEST_BEAN_GET_NAME, TestBean.class, null)); - assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_NAME, TestBean.class, null)); - assertFalse(Pointcuts.matches(union, TEST_BEAN_ABSQUATULATE, TestBean.class, null)); + assertTrue(Pointcuts.matches(union, TEST_BEAN_SET_AGE, TestBean.class, new Integer(6))); + assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_AGE, TestBean.class)); + assertFalse(Pointcuts.matches(allClassGetAgePointcut, TEST_BEAN_GET_NAME, TestBean.class)); + assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_NAME, TestBean.class)); + assertFalse(Pointcuts.matches(union, TEST_BEAN_ABSQUATULATE, TestBean.class)); - assertTrue(Pointcuts.matches(union, TEST_BEAN_SET_AGE, TestBean.class, new Object[] { new Integer(6)})); + assertTrue(Pointcuts.matches(union, TEST_BEAN_SET_AGE, TestBean.class, new Integer(6))); } /** @@ -180,16 +180,16 @@ public void testUnionOfSpecificGetters() { */ @Test public void testUnionOfAllSettersAndSubclassSetters() { - assertFalse(Pointcuts.matches(myTestBeanSetterPointcut, TEST_BEAN_SET_AGE, TestBean.class, new Object[] { new Integer(6)})); - assertTrue(Pointcuts.matches(myTestBeanSetterPointcut, TEST_BEAN_SET_AGE, MyTestBean.class, new Object[] { new Integer(6)})); - assertFalse(Pointcuts.matches(myTestBeanSetterPointcut, TEST_BEAN_GET_AGE, TestBean.class, null)); + assertFalse(Pointcuts.matches(myTestBeanSetterPointcut, TEST_BEAN_SET_AGE, TestBean.class, new Integer(6))); + assertTrue(Pointcuts.matches(myTestBeanSetterPointcut, TEST_BEAN_SET_AGE, MyTestBean.class, new Integer(6))); + assertFalse(Pointcuts.matches(myTestBeanSetterPointcut, TEST_BEAN_GET_AGE, TestBean.class)); Pointcut union = Pointcuts.union(myTestBeanSetterPointcut, allClassGetterPointcut); - assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_AGE, TestBean.class, null)); - assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_AGE, MyTestBean.class, null)); + assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_AGE, TestBean.class)); + assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_AGE, MyTestBean.class)); // Still doesn't match superclass setter - assertTrue(Pointcuts.matches(union, TEST_BEAN_SET_AGE, MyTestBean.class, new Object[] { new Integer(6)})); - assertFalse(Pointcuts.matches(union, TEST_BEAN_SET_AGE, TestBean.class, new Object[] { new Integer(6)})); + assertTrue(Pointcuts.matches(union, TEST_BEAN_SET_AGE, MyTestBean.class, new Integer(6))); + assertFalse(Pointcuts.matches(union, TEST_BEAN_SET_AGE, TestBean.class, new Integer(6))); } /** @@ -198,44 +198,44 @@ public void testUnionOfAllSettersAndSubclassSetters() { */ @Test public void testIntersectionOfSpecificGettersAndSubclassGetters() { - assertTrue(Pointcuts.matches(allClassGetAgePointcut, TEST_BEAN_GET_AGE, TestBean.class, null)); - assertTrue(Pointcuts.matches(allClassGetAgePointcut, TEST_BEAN_GET_AGE, MyTestBean.class, null)); - assertFalse(Pointcuts.matches(myTestBeanGetterPointcut, TEST_BEAN_GET_NAME, TestBean.class, null)); - assertFalse(Pointcuts.matches(myTestBeanGetterPointcut, TEST_BEAN_GET_AGE, TestBean.class, null)); - assertTrue(Pointcuts.matches(myTestBeanGetterPointcut, TEST_BEAN_GET_NAME, MyTestBean.class, null)); - assertTrue(Pointcuts.matches(myTestBeanGetterPointcut, TEST_BEAN_GET_AGE, MyTestBean.class, null)); + assertTrue(Pointcuts.matches(allClassGetAgePointcut, TEST_BEAN_GET_AGE, TestBean.class)); + assertTrue(Pointcuts.matches(allClassGetAgePointcut, TEST_BEAN_GET_AGE, MyTestBean.class)); + assertFalse(Pointcuts.matches(myTestBeanGetterPointcut, TEST_BEAN_GET_NAME, TestBean.class)); + assertFalse(Pointcuts.matches(myTestBeanGetterPointcut, TEST_BEAN_GET_AGE, TestBean.class)); + assertTrue(Pointcuts.matches(myTestBeanGetterPointcut, TEST_BEAN_GET_NAME, MyTestBean.class)); + assertTrue(Pointcuts.matches(myTestBeanGetterPointcut, TEST_BEAN_GET_AGE, MyTestBean.class)); Pointcut intersection = Pointcuts.intersection(allClassGetAgePointcut, myTestBeanGetterPointcut); - assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_NAME, TestBean.class, null)); - assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_AGE, TestBean.class, null)); - assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_NAME, MyTestBean.class, null)); - assertTrue(Pointcuts.matches(intersection, TEST_BEAN_GET_AGE, MyTestBean.class, null)); + assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_NAME, TestBean.class)); + assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_AGE, TestBean.class)); + assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_NAME, MyTestBean.class)); + assertTrue(Pointcuts.matches(intersection, TEST_BEAN_GET_AGE, MyTestBean.class)); // Matches subclass of MyTestBean - assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_NAME, MyTestBeanSubclass.class, null)); - assertTrue(Pointcuts.matches(intersection, TEST_BEAN_GET_AGE, MyTestBeanSubclass.class, null)); + assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_NAME, MyTestBeanSubclass.class)); + assertTrue(Pointcuts.matches(intersection, TEST_BEAN_GET_AGE, MyTestBeanSubclass.class)); // Now intersection with MyTestBeanSubclass getters should eliminate MyTestBean target intersection = Pointcuts.intersection(intersection, myTestBeanSubclassGetterPointcut); - assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_NAME, TestBean.class, null)); - assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_AGE, TestBean.class, null)); - assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_NAME, MyTestBean.class, null)); - assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_AGE, MyTestBean.class, null)); + assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_NAME, TestBean.class)); + assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_AGE, TestBean.class)); + assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_NAME, MyTestBean.class)); + assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_AGE, MyTestBean.class)); // Still matches subclass of MyTestBean - assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_NAME, MyTestBeanSubclass.class, null)); - assertTrue(Pointcuts.matches(intersection, TEST_BEAN_GET_AGE, MyTestBeanSubclass.class, null)); + assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_NAME, MyTestBeanSubclass.class)); + assertTrue(Pointcuts.matches(intersection, TEST_BEAN_GET_AGE, MyTestBeanSubclass.class)); // Now union with all TestBean methods Pointcut union = Pointcuts.union(intersection, allTestBeanMethodsPointcut); - assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_NAME, TestBean.class, null)); - assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_AGE, TestBean.class, null)); - assertFalse(Pointcuts.matches(union, TEST_BEAN_GET_NAME, MyTestBean.class, null)); - assertFalse(Pointcuts.matches(union, TEST_BEAN_GET_AGE, MyTestBean.class, null)); + assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_NAME, TestBean.class)); + assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_AGE, TestBean.class)); + assertFalse(Pointcuts.matches(union, TEST_BEAN_GET_NAME, MyTestBean.class)); + assertFalse(Pointcuts.matches(union, TEST_BEAN_GET_AGE, MyTestBean.class)); // Still matches subclass of MyTestBean - assertFalse(Pointcuts.matches(union, TEST_BEAN_GET_NAME, MyTestBeanSubclass.class, null)); - assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_AGE, MyTestBeanSubclass.class, null)); + assertFalse(Pointcuts.matches(union, TEST_BEAN_GET_NAME, MyTestBeanSubclass.class)); + assertTrue(Pointcuts.matches(union, TEST_BEAN_GET_AGE, MyTestBeanSubclass.class)); - assertTrue(Pointcuts.matches(union, TEST_BEAN_ABSQUATULATE, TestBean.class, null)); - assertFalse(Pointcuts.matches(union, TEST_BEAN_ABSQUATULATE, MyTestBean.class, null)); + assertTrue(Pointcuts.matches(union, TEST_BEAN_ABSQUATULATE, TestBean.class)); + assertFalse(Pointcuts.matches(union, TEST_BEAN_ABSQUATULATE, MyTestBean.class)); } @@ -245,9 +245,9 @@ public void testIntersectionOfSpecificGettersAndSubclassGetters() { @Test public void testSimpleIntersection() { Pointcut intersection = Pointcuts.intersection(allClassGetterPointcut, allClassSetterPointcut); - assertFalse(Pointcuts.matches(intersection, TEST_BEAN_SET_AGE, TestBean.class, new Object[] { new Integer(6)})); - assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_AGE, TestBean.class, null)); - assertFalse(Pointcuts.matches(intersection, TEST_BEAN_ABSQUATULATE, TestBean.class, null)); + assertFalse(Pointcuts.matches(intersection, TEST_BEAN_SET_AGE, TestBean.class, new Integer(6))); + assertFalse(Pointcuts.matches(intersection, TEST_BEAN_GET_AGE, TestBean.class)); + assertFalse(Pointcuts.matches(intersection, TEST_BEAN_ABSQUATULATE, TestBean.class)); } } diff --git a/spring-beans-groovy/src/main/java/org/springframework/beans/factory/groovy/GroovyBeanDefinitionReader.java b/spring-beans-groovy/src/main/java/org/springframework/beans/factory/groovy/GroovyBeanDefinitionReader.java index bcbe1985d1..43d6823331 100644 --- a/spring-beans-groovy/src/main/java/org/springframework/beans/factory/groovy/GroovyBeanDefinitionReader.java +++ b/spring-beans-groovy/src/main/java/org/springframework/beans/factory/groovy/GroovyBeanDefinitionReader.java @@ -31,7 +31,6 @@ import groovy.lang.GroovyShell; import groovy.lang.GroovySystem; import groovy.lang.MetaClass; - import org.codehaus.groovy.runtime.DefaultGroovyMethods; import org.codehaus.groovy.runtime.InvokerHelper; @@ -337,8 +336,8 @@ public void xmlns(Map definition) { if (uri == null) { throw new IllegalArgumentException("Namespace definition must supply a non-null URI"); } - NamespaceHandler namespaceHandler = this.groovyDslXmlBeanDefinitionReader.getNamespaceHandlerResolver().resolve( - uri); + NamespaceHandler namespaceHandler = + this.groovyDslXmlBeanDefinitionReader.getNamespaceHandlerResolver().resolve(uri); if (namespaceHandler == null) { throw new BeanDefinitionParsingException(new Problem("No namespace handler found for URI: " + uri, new Location(new DescriptiveResource(("Groovy"))))); @@ -375,7 +374,7 @@ else if ("ref".equals(name)) { throw new IllegalArgumentException("Argument to ref() is not a valid bean or was not found"); if (args[0] instanceof RuntimeBeanReference) { - refName = ((RuntimeBeanReference)args[0]).getBeanName(); + refName = ((RuntimeBeanReference) args[0]).getBeanName(); } else { refName = args[0].toString(); @@ -388,7 +387,7 @@ else if ("ref".equals(name)) { } return new RuntimeBeanReference(refName, parentRef); } - else if (this.namespaces.containsKey(name) && args.length > 0 && (args[0] instanceof Closure)) { + else if (this.namespaces.containsKey(name) && args.length > 0 && args[0] instanceof Closure) { GroovyDynamicElementReader reader = createDynamicElementReader(name); reader.invokeMethod("doCall", args); } @@ -396,7 +395,8 @@ else if (args.length > 0 && args[0] instanceof Closure) { // abstract bean definition return invokeBeanDefiningMethod(name, args); } - else if (args.length > 0 && (args[0] instanceof Class || args[0] instanceof RuntimeBeanReference || args[0] instanceof Map)) { + else if (args.length > 0 && + (args[0] instanceof Class || args[0] instanceof RuntimeBeanReference || args[0] instanceof Map)) { return invokeBeanDefiningMethod(name, args); } else if (args.length > 1 && args[args.length -1] instanceof Closure) { diff --git a/spring-context/src/test/java/org/springframework/aop/framework/AbstractAopProxyTests.java b/spring-context/src/test/java/org/springframework/aop/framework/AbstractAopProxyTests.java index 2e554c192b..3114bf62a8 100644 --- a/spring-context/src/test/java/org/springframework/aop/framework/AbstractAopProxyTests.java +++ b/spring-context/src/test/java/org/springframework/aop/framework/AbstractAopProxyTests.java @@ -31,10 +31,13 @@ import org.aopalliance.aop.Advice; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; - import org.junit.After; import org.junit.Before; import org.junit.Test; +import test.mixin.LockMixin; +import test.mixin.LockMixinAdvisor; +import test.mixin.Lockable; +import test.mixin.LockedException; import org.springframework.aop.Advisor; import org.springframework.aop.AfterReturningAdvice; @@ -72,11 +75,6 @@ import org.springframework.util.SerializationTestUtils; import org.springframework.util.StopWatch; -import test.mixin.LockMixin; -import test.mixin.LockMixinAdvisor; -import test.mixin.Lockable; -import test.mixin.LockedException; - import static org.junit.Assert.*; /** @@ -123,7 +121,7 @@ protected boolean requiresTarget() { @Test(expected = AopConfigException.class) public void testNoInterceptorsAndNoTarget() { - AdvisedSupport pc = new AdvisedSupport(new Class[] { ITestBean.class }); + AdvisedSupport pc = new AdvisedSupport(ITestBean.class); // Add no interceptors AopProxy aop = createAopProxy(pc); aop.getProxy(); @@ -408,7 +406,7 @@ public Object invoke(MethodInvocation invocation) throws Throwable { return s; } }; - AdvisedSupport pc = new AdvisedSupport(new Class[] {ITestBean.class}); + AdvisedSupport pc = new AdvisedSupport(ITestBean.class); if (context) { pc.addAdvice(ExposeInvocationInterceptor.INSTANCE); } @@ -452,7 +450,7 @@ public Object invoke(MethodInvocation invocation) throws Throwable { throw expectedException; } }; - AdvisedSupport pc = new AdvisedSupport(new Class[] {ITestBean.class}); + AdvisedSupport pc = new AdvisedSupport(ITestBean.class); pc.addAdvice(ExposeInvocationInterceptor.INSTANCE); pc.addAdvice(mi); @@ -487,7 +485,7 @@ public Object invoke(MethodInvocation invocation) throws Throwable { throw unexpectedException; } }; - AdvisedSupport pc = new AdvisedSupport(new Class[] {ITestBean.class}); + AdvisedSupport pc = new AdvisedSupport(ITestBean.class); pc.addAdvice(ExposeInvocationInterceptor.INSTANCE); pc.addAdvice(mi); @@ -520,7 +518,7 @@ public Object invoke(MethodInvocation invocation) throws Throwable { throw unexpectedException; } }; - AdvisedSupport pc = new AdvisedSupport(new Class[] {ITestBean.class}); + AdvisedSupport pc = new AdvisedSupport(ITestBean.class); pc.addAdvice(ExposeInvocationInterceptor.INSTANCE); pc.addAdvice(mi); @@ -548,7 +546,7 @@ public Object invoke(MethodInvocation invocation) throws Throwable { @Test public void testTargetCanGetInvocationEvenIfNoAdviceChain() throws Throwable { NeedsToSeeProxy target = new NeedsToSeeProxy(); - AdvisedSupport pc = new AdvisedSupport(new Class[] {INeedsToSeeProxy.class}); + AdvisedSupport pc = new AdvisedSupport(INeedsToSeeProxy.class); pc.setTarget(target); pc.setExposeProxy(true); @@ -563,7 +561,7 @@ public void testTargetCanGetInvocationEvenIfNoAdviceChain() throws Throwable { public void testTargetCanGetInvocation() throws Throwable { final InvocationCheckExposedInvocationTestBean expectedTarget = new InvocationCheckExposedInvocationTestBean(); - AdvisedSupport pc = new AdvisedSupport(new Class[] {ITestBean.class, IOther.class}); + AdvisedSupport pc = new AdvisedSupport(ITestBean.class, IOther.class); pc.addAdvice(ExposeInvocationInterceptor.INSTANCE); TrapTargetInterceptor tii = new TrapTargetInterceptor() { @Override @@ -600,7 +598,8 @@ private void assertNoInvocationContext() { @Test public void testMixinWithIntroductionAdvisor() throws Throwable { TestBean tb = new TestBean(); - ProxyFactory pc = new ProxyFactory(new Class[] {ITestBean.class}); + ProxyFactory pc = new ProxyFactory(); + pc.addInterface(ITestBean.class); pc.addAdvisor(new LockMixinAdvisor()); pc.setTarget(tb); @@ -610,7 +609,8 @@ public void testMixinWithIntroductionAdvisor() throws Throwable { @Test public void testMixinWithIntroductionInfo() throws Throwable { TestBean tb = new TestBean(); - ProxyFactory pc = new ProxyFactory(new Class[] {ITestBean.class}); + ProxyFactory pc = new ProxyFactory(); + pc.addInterface(ITestBean.class); // We don't use an IntroductionAdvisor, we can just add an advice that implements IntroductionInfo pc.addAdvice(new LockMixin()); pc.setTarget(tb); @@ -648,7 +648,8 @@ private void testTestBeanIntroduction(ProxyFactory pc) { @Test public void testReplaceArgument() throws Throwable { TestBean tb = new TestBean(); - ProxyFactory pc = new ProxyFactory(new Class[] {ITestBean.class}); + ProxyFactory pc = new ProxyFactory(); + pc.addInterface(ITestBean.class); pc.setTarget(tb); pc.addAdvisor(new StringSetterNullReplacementAdvice()); @@ -825,7 +826,7 @@ public void testCannotAddIntroductionAdviceToIntroduceClass() throws Throwable { fail("Shouldn't be able to add introduction advice that introduces a class, rather than an interface"); } catch (IllegalArgumentException ex) { - assertTrue(ex.getMessage().indexOf("interface") > -1); + assertTrue(ex.getMessage().contains("interface")); } // Check it still works: proxy factory state shouldn't have been corrupted ITestBean proxied = (ITestBean) createProxy(pc); @@ -873,7 +874,7 @@ public void testCannotAddAdvisorWhenFrozenUsingCast() throws Throwable { fail("Shouldn't be able to add Advisor when frozen"); } catch (AopConfigException ex) { - assertTrue(ex.getMessage().indexOf("frozen") > -1); + assertTrue(ex.getMessage().contains("frozen")); } // Check it still works: proxy factory state shouldn't have been corrupted assertEquals(target.getAge(), proxied.getAge()); @@ -897,7 +898,7 @@ public void testCannotRemoveAdvisorWhenFrozen() throws Throwable { fail("Shouldn't be able to remove Advisor when frozen"); } catch (AopConfigException ex) { - assertTrue(ex.getMessage().indexOf("frozen") > -1); + assertTrue(ex.getMessage().contains("frozen")); } // Didn't get removed assertEquals(1, advised.getAdvisors().length); @@ -938,7 +939,7 @@ public void testUseAsHashKey() { public void testProxyConfigString() { TestBean target = new TestBean(); ProxyFactory pc = new ProxyFactory(target); - pc.setInterfaces(new Class[] {ITestBean.class}); + pc.setInterfaces(ITestBean.class); pc.addAdvice(new NopInterceptor()); MethodBeforeAdvice mba = new CountingBeforeAdvice(); Advisor advisor = new DefaultPointcutAdvisor(new NameMatchMethodPointcut(), mba); @@ -946,15 +947,15 @@ public void testProxyConfigString() { ITestBean proxied = (ITestBean) createProxy(pc); String proxyConfigString = ((Advised) proxied).toProxyConfigString(); - assertTrue(proxyConfigString.indexOf(advisor.toString()) != -1); - assertTrue(proxyConfigString.indexOf("1 interface") != -1); + assertTrue(proxyConfigString.contains(advisor.toString())); + assertTrue(proxyConfigString.contains("1 interface")); } @Test public void testCanPreventCastToAdvisedUsingOpaque() { TestBean target = new TestBean(); ProxyFactory pc = new ProxyFactory(target); - pc.setInterfaces(new Class[] {ITestBean.class}); + pc.setInterfaces(ITestBean.class); pc.addAdvice(new NopInterceptor()); CountingBeforeAdvice mba = new CountingBeforeAdvice(); Advisor advisor = new DefaultPointcutAdvisor(new NameMatchMethodPointcut().addMethodName("setAge"), mba); @@ -1055,7 +1056,8 @@ public void testExistingProxyChangesTarget() throws Throwable { @Test public void testDynamicMethodPointcutThatAlwaysAppliesStatically() throws Throwable { TestBean tb = new TestBean(); - ProxyFactory pc = new ProxyFactory(new Class[] {ITestBean.class}); + ProxyFactory pc = new ProxyFactory(); + pc.addInterface(ITestBean.class); TestDynamicPointcutAdvice dp = new TestDynamicPointcutAdvice(new NopInterceptor(), "getAge"); pc.addAdvisor(dp); pc.setTarget(tb); @@ -1071,7 +1073,8 @@ public void testDynamicMethodPointcutThatAlwaysAppliesStatically() throws Throwa @Test public void testDynamicMethodPointcutThatAppliesStaticallyOnlyToSetters() throws Throwable { TestBean tb = new TestBean(); - ProxyFactory pc = new ProxyFactory(new Class[] {ITestBean.class}); + ProxyFactory pc = new ProxyFactory(); + pc.addInterface(ITestBean.class); // Could apply dynamically to getAge/setAge but not to getName TestDynamicPointcutForSettersOnly dp = new TestDynamicPointcutForSettersOnly(new NopInterceptor(), "Age"); pc.addAdvisor(dp); @@ -1093,7 +1096,8 @@ public void testDynamicMethodPointcutThatAppliesStaticallyOnlyToSetters() throws @Test public void testStaticMethodPointcut() throws Throwable { TestBean tb = new TestBean(); - ProxyFactory pc = new ProxyFactory(new Class[] {ITestBean.class}); + ProxyFactory pc = new ProxyFactory(); + pc.addInterface(ITestBean.class); NopInterceptor di = new NopInterceptor(); TestStaticPointcutAdvice sp = new TestStaticPointcutAdvice(di, "getAge"); pc.addAdvisor(sp); @@ -1207,6 +1211,7 @@ public Object invoke(MethodInvocation mi) throws Throwable { public void testOverloadedMethodsWithDifferentAdvice() throws Throwable { Overloads target = new Overloads(); ProxyFactory pc = new ProxyFactory(target); + NopInterceptor overLoadVoids = new NopInterceptor(); pc.addAdvisor(new StaticMethodMatcherPointcutAdvisor(overLoadVoids) { @Override @@ -1214,12 +1219,13 @@ public boolean matches(Method m, Class targetClass) { return m.getName().equals("overload") && m.getParameterTypes().length == 0; } }); + NopInterceptor overLoadInts = new NopInterceptor(); pc.addAdvisor(new StaticMethodMatcherPointcutAdvisor(overLoadInts) { @Override public boolean matches(Method m, Class targetClass) { return m.getName().equals("overload") && m.getParameterTypes().length == 1 && - m.getParameterTypes()[0].equals(int.class); + m.getParameterTypes()[0].equals(int.class); } }); @@ -1245,24 +1251,22 @@ public void testProxyIsBoundBeforeTargetSourceInvoked() { pf.setExposeProxy(true); final ITestBean proxy = (ITestBean) createProxy(pf); Advised config = (Advised) proxy; + // This class just checks proxy is bound before getTarget() call config.setTargetSource(new TargetSource() { @Override public Class getTargetClass() { return TestBean.class; } - @Override public boolean isStatic() { return false; } - @Override public Object getTarget() throws Exception { assertEquals(proxy, AopContext.currentProxy()); return target; } - @Override public void releaseTarget(Object target) throws Exception { } @@ -1351,7 +1355,7 @@ public Object invoke(MethodInvocation invocation) throws Throwable { return invocation.proceed(); } }; - AdvisedSupport pc = new AdvisedSupport(new Class[] {ITestBean.class}); + AdvisedSupport pc = new AdvisedSupport(ITestBean.class); MapAwareMethodInterceptor mami1 = new MapAwareMethodInterceptor(new HashMap(), new HashMap()); Map firstValuesToAdd = new HashMap(); firstValuesToAdd.put("test", ""); @@ -1683,7 +1687,7 @@ public StringSetterNullReplacementAdvice() { super(cleaner); setPointcut(new DynamicMethodMatcherPointcut() { @Override - public boolean matches(Method m, Class targetClass, Object[] args) { + public boolean matches(Method m, Class targetClass, Object... args) { return args[0] == null; } @Override @@ -1706,8 +1710,8 @@ public TestDynamicPointcutAdvice(MethodInterceptor mi, final String pattern) { super(mi); setPointcut(new DynamicMethodMatcherPointcut() { @Override - public boolean matches(Method m, Class targetClass, Object[] args) { - boolean run = m.getName().indexOf(pattern) != -1; + public boolean matches(Method m, Class targetClass, Object... args) { + boolean run = m.getName().contains(pattern); if (run) ++count; return run; } @@ -1725,8 +1729,8 @@ public TestDynamicPointcutForSettersOnly(MethodInterceptor mi, final String patt super(mi); setPointcut(new DynamicMethodMatcherPointcut() { @Override - public boolean matches(Method m, Class targetClass, Object[] args) { - boolean run = m.getName().indexOf(pattern) != -1; + public boolean matches(Method m, Class targetClass, Object... args) { + boolean run = m.getName().contains(pattern); if (run) ++count; return run; } @@ -1750,7 +1754,7 @@ public TestStaticPointcutAdvice(MethodInterceptor mi, String pattern) { } @Override public boolean matches(Method m, Class targetClass) { - return m.getName().indexOf(pattern) != -1; + return m.getName().contains(pattern); } } diff --git a/spring-context/src/test/java/org/springframework/aop/framework/CglibProxyTests.java b/spring-context/src/test/java/org/springframework/aop/framework/CglibProxyTests.java index 1052a2599c..af8b1be5ea 100644 --- a/spring-context/src/test/java/org/springframework/aop/framework/CglibProxyTests.java +++ b/spring-context/src/test/java/org/springframework/aop/framework/CglibProxyTests.java @@ -81,7 +81,7 @@ public void testNullConfig() { @Test(expected = AopConfigException.class) public void testNoTarget() { - AdvisedSupport pc = new AdvisedSupport(new Class[] { ITestBean.class }); + AdvisedSupport pc = new AdvisedSupport(ITestBean.class); pc.addAdvice(new NopInterceptor()); AopProxy aop = createAopProxy(pc); aop.getProxy(); @@ -93,7 +93,7 @@ public void testProtectedMethodInvocation() { bean.value = "foo"; mockTargetSource.setTarget(bean); - AdvisedSupport as = new AdvisedSupport(new Class[]{}); + AdvisedSupport as = new AdvisedSupport(); as.setTargetSource(mockTargetSource); as.addAdvice(new NopInterceptor()); AopProxy aop = new CglibAopProxy(as); @@ -110,7 +110,7 @@ public void testPackageMethodInvocation() { bean.value = "foo"; mockTargetSource.setTarget(bean); - AdvisedSupport as = new AdvisedSupport(new Class[]{}); + AdvisedSupport as = new AdvisedSupport(); as.setTargetSource(mockTargetSource); as.addAdvice(new NopInterceptor()); AopProxy aop = new CglibAopProxy(as); @@ -130,7 +130,7 @@ public void testPackageMethodInvocationWithDifferentClassLoader() { bean.value = "foo"; mockTargetSource.setTarget(bean); - AdvisedSupport as = new AdvisedSupport(new Class[]{}); + AdvisedSupport as = new AdvisedSupport(); as.setTargetSource(mockTargetSource); as.addAdvice(new NopInterceptor()); AopProxy aop = new CglibAopProxy(as); @@ -164,7 +164,7 @@ public void testMethodInvocationDuringConstructor() { CglibTestBean bean = new CglibTestBean(); bean.setName("Rob Harrop"); - AdvisedSupport as = new AdvisedSupport(new Class[]{}); + AdvisedSupport as = new AdvisedSupport(); as.setTarget(bean); as.addAdvice(new NopInterceptor()); AopProxy aop = new CglibAopProxy(as); @@ -178,7 +178,7 @@ public void testUnadvisedProxyCreationWithCallDuringConstructor() throws Excepti CglibTestBean target = new CglibTestBean(); target.setName("Rob Harrop"); - AdvisedSupport pc = new AdvisedSupport(new Class[]{}); + AdvisedSupport pc = new AdvisedSupport(); pc.setFrozen(true); pc.setTarget(target); @@ -264,7 +264,7 @@ public void testWithNoArgConstructor() { target.reset(); mockTargetSource.setTarget(target); - AdvisedSupport pc = new AdvisedSupport(new Class[]{}); + AdvisedSupport pc = new AdvisedSupport(); pc.setTargetSource(mockTargetSource); CglibAopProxy aop = new CglibAopProxy(pc); aop.setConstructorArguments(new Object[] {"Rob Harrop", 22}, new Class[] {String.class, int.class}); @@ -280,7 +280,7 @@ public void testProxyAProxy() { ITestBean target = new TestBean(); mockTargetSource.setTarget(target); - AdvisedSupport as = new AdvisedSupport(new Class[]{}); + AdvisedSupport as = new AdvisedSupport(); as.setTargetSource(mockTargetSource); as.addAdvice(new NopInterceptor()); CglibAopProxy cglib = new CglibAopProxy(as); @@ -301,7 +301,7 @@ public void testProxyAProxyWithAdditionalInterface() { ITestBean target = new TestBean(); mockTargetSource.setTarget(target); - AdvisedSupport as = new AdvisedSupport(new Class[]{}); + AdvisedSupport as = new AdvisedSupport(); as.setTargetSource(mockTargetSource); as.addAdvice(new NopInterceptor()); as.addInterface(Serializable.class); @@ -324,7 +324,7 @@ public void testExceptionHandling() { ExceptionThrower bean = new ExceptionThrower(); mockTargetSource.setTarget(bean); - AdvisedSupport as = new AdvisedSupport(new Class[]{}); + AdvisedSupport as = new AdvisedSupport(); as.setTargetSource(mockTargetSource); as.addAdvice(new NopInterceptor()); AopProxy aop = new CglibAopProxy(as); diff --git a/spring-context/src/test/java/org/springframework/aop/framework/JdkDynamicProxyTests.java b/spring-context/src/test/java/org/springframework/aop/framework/JdkDynamicProxyTests.java index cb5b07e908..dd8d1f3a45 100644 --- a/spring-context/src/test/java/org/springframework/aop/framework/JdkDynamicProxyTests.java +++ b/spring-context/src/test/java/org/springframework/aop/framework/JdkDynamicProxyTests.java @@ -63,7 +63,7 @@ public void testNullConfig() { public void testProxyIsJustInterface() throws Throwable { TestBean raw = new TestBean(); raw.setAge(32); - AdvisedSupport pc = new AdvisedSupport(new Class[] {ITestBean.class}); + AdvisedSupport pc = new AdvisedSupport(ITestBean.class); pc.setTarget(raw); JdkDynamicAopProxy aop = new JdkDynamicAopProxy(pc); @@ -78,7 +78,7 @@ public void testInterceptorIsInvokedWithNoTarget() throws Throwable { final int age = 25; MethodInterceptor mi = (invocation -> age); - AdvisedSupport pc = new AdvisedSupport(new Class[] {ITestBean.class}); + AdvisedSupport pc = new AdvisedSupport(ITestBean.class); pc.addAdvice(mi); AopProxy aop = createAopProxy(pc); @@ -97,7 +97,7 @@ protected void assertions(MethodInvocation invocation) { } }; - AdvisedSupport pc = new AdvisedSupport(new Class[] {ITestBean.class, IOther.class}); + AdvisedSupport pc = new AdvisedSupport(ITestBean.class, IOther.class); pc.addAdvice(ExposeInvocationInterceptor.INSTANCE); TrapTargetInterceptor tii = new TrapTargetInterceptor() { @Override @@ -129,7 +129,7 @@ public void testProxyNotWrappedIfIncompatible() { @Test public void testEqualsAndHashCodeDefined() throws Exception { - AdvisedSupport as = new AdvisedSupport(new Class[]{Named.class}); + AdvisedSupport as = new AdvisedSupport(Named.class); as.setTarget(new Person()); JdkDynamicAopProxy aopProxy = new JdkDynamicAopProxy(as); Named proxy = (Named) aopProxy.getProxy(); diff --git a/spring-context/src/test/java/org/springframework/aop/framework/ProxyFactoryBeanTests.java b/spring-context/src/test/java/org/springframework/aop/framework/ProxyFactoryBeanTests.java index 8eaaed36a1..78b8e5f25b 100644 --- a/spring-context/src/test/java/org/springframework/aop/framework/ProxyFactoryBeanTests.java +++ b/spring-context/src/test/java/org/springframework/aop/framework/ProxyFactoryBeanTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -88,6 +88,7 @@ public final class ProxyFactoryBeanTests { private BeanFactory factory; + @Before public void setUp() throws Exception { DefaultListableBeanFactory parent = new DefaultListableBeanFactory(); @@ -97,6 +98,7 @@ public void setUp() throws Exception { new ClassPathResource(CONTEXT, getClass())); } + @Test public void testIsDynamicProxyWhenInterfaceSpecified() { ITestBean test1 = (ITestBean) factory.getBean("test1"); @@ -157,7 +159,7 @@ public void testTargetSourceNotAtEndOfInterceptorNamesIsRejected() { catch (BeanCreationException ex) { // Root cause of the problem must be an AOP exception AopConfigException aex = (AopConfigException) ex.getCause(); - assertTrue(aex.getMessage().indexOf("interceptorNames") != -1); + assertTrue(aex.getMessage().contains("interceptorNames")); } } @@ -332,13 +334,6 @@ public Object invoke(MethodInvocation invocation) throws Throwable { } } - public static class DependsOnITestBean { - public final ITestBean tb; - public DependsOnITestBean(ITestBean tb) { - this.tb = tb; - } - } - /** * Test that inner bean for target means that we can use * autowire without ambiguity from target and proxy @@ -711,6 +706,8 @@ public void testDetectsInterfaces() throws Exception { ITestBean proxy = (ITestBean) fb.getObject(); assertTrue(AopUtils.isJdkDynamicProxy(proxy)); } + + /** * Fires only on void methods. Saves list of methods intercepted. */ @@ -733,7 +730,7 @@ public Object invoke(MethodInvocation invocation) throws Throwable { }); setPointcut(new DynamicMethodMatcherPointcut() { @Override - public boolean matches(Method m, Class targetClass, Object[] args) { + public boolean matches(Method m, Class targetClass, Object... args) { return m.getReturnType() == Void.TYPE; } }); @@ -741,10 +738,20 @@ public boolean matches(Method m, Class targetClass, Object[] args) { } + public static class DependsOnITestBean { + + public final ITestBean tb; + + public DependsOnITestBean(ITestBean tb) { + this.tb = tb; + } + } + /** * Aspect interface */ public interface AddedGlobalInterface { + int globalsAdded(); } From e1bdf5577e94616786e07d6bcf7d424399dd2704 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 29 Dec 2015 11:19:21 +0100 Subject: [PATCH 010/344] StatementCreatorUtils always tries getParameterType on Oracle 12c driver Issue: SPR-13825 (cherry picked from commit e48ec4f) --- .../springframework/jdbc/core/StatementCreatorUtils.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/StatementCreatorUtils.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/StatementCreatorUtils.java index 9d1fdd7e8a..4aed272b13 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/StatementCreatorUtils.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/StatementCreatorUtils.java @@ -277,7 +277,11 @@ private static void setNull(PreparedStatement ps, int paramIndex, int sqlType, S if (jdbcDriverName == null) { jdbcDriverName = dbmd.getDriverName(); } - if (checkGetParameterType) { + if (checkGetParameterType && + !(jdbcDriverName.startsWith("Oracle") && dbmd.getDriverMajorVersion() >= 12)) { + // Register JDBC driver with no support for getParameterType, except for the + // Oracle 12c driver where getParameterType fails for specific statements only + // (so an exception thrown above does not indicate general lack of support). driversWithNoSupportForGetParameterType.add(jdbcDriverName); } String databaseProductName = dbmd.getDatabaseProductName(); From 35b8696523d01faa9689eefc2904f9f9bace1143 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 29 Dec 2015 11:24:56 +0100 Subject: [PATCH 011/344] Polishing --- .../src/main/java/org/springframework/util/ClassUtils.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/util/ClassUtils.java b/spring-core/src/main/java/org/springframework/util/ClassUtils.java index c4ee177cab..93dd3d616e 100644 --- a/spring-core/src/main/java/org/springframework/util/ClassUtils.java +++ b/spring-core/src/main/java/org/springframework/util/ClassUtils.java @@ -428,7 +428,7 @@ public static String getShortName(Class clazz) { * @see java.beans.Introspector#decapitalize(String) */ public static String getShortNameAsProperty(Class clazz) { - String shortName = ClassUtils.getShortName(clazz); + String shortName = getShortName(clazz); int dotIndex = shortName.lastIndexOf(PACKAGE_SEPARATOR); shortName = (dotIndex != -1 ? shortName.substring(dotIndex + 1) : shortName); return Introspector.decapitalize(shortName); @@ -498,7 +498,7 @@ private static String getQualifiedNameForArray(Class clazz) { StringBuilder result = new StringBuilder(); while (clazz.isArray()) { clazz = clazz.getComponentType(); - result.append(ClassUtils.ARRAY_SUFFIX); + result.append(ARRAY_SUFFIX); } result.insert(0, clazz.getName()); return result.toString(); @@ -1222,7 +1222,7 @@ public static boolean isVisible(Class clazz, ClassLoader classLoader) { * @see org.springframework.aop.support.AopUtils#isCglibProxy(Object) */ public static boolean isCglibProxy(Object object) { - return ClassUtils.isCglibProxyClass(object.getClass()); + return isCglibProxyClass(object.getClass()); } /** From 4a863c906678ecefc302e8193cc3653adf32703e Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 23 Dec 2015 01:18:55 +0100 Subject: [PATCH 012/344] Upgrade to Undertow 1.3.11 (cherry picked from commit 873b173) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 482f0510cf..c2e847ef6a 100644 --- a/build.gradle +++ b/build.gradle @@ -74,7 +74,7 @@ configure(allprojects) { project -> ext.tiles3Version = "3.0.5" ext.tomcatVersion = "8.0.30" ext.tyrusVersion = "1.3.5" // constrained by WebLogic 12.1.3 support - ext.undertowVersion = "1.3.10.Final" + ext.undertowVersion = "1.3.11.Final" ext.woodstoxVersion = "5.0.1" ext.xmlunitVersion = "1.6" ext.xstreamVersion = "1.4.8" From acecda7153efccaf1f99d1a7002d7f3c8945b698 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 29 Dec 2015 21:39:43 +0100 Subject: [PATCH 013/344] Avoid sensitive Proxy.getInvocationHandler call in synthesizeAnnotation Issue: SPR-13829 (cherry picked from commit aecb8b6) --- .../core/annotation/AnnotationUtils.java | 48 +++++++++---------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java index 9265dbe580..ea634b5cac 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java @@ -495,8 +495,8 @@ public static A findAnnotation(AnnotatedElement annotated // Do NOT store result in the findAnnotationCache since doing so could break // findAnnotation(Class, Class) and findAnnotation(Method, Class). - return synthesizeAnnotation( - findAnnotation(annotatedElement, annotationType, new HashSet()), annotatedElement); + A ann = findAnnotation(annotatedElement, annotationType, new HashSet()); + return synthesizeAnnotation(ann, annotatedElement); } /** @@ -1360,8 +1360,7 @@ public static A synthesizeAnnotation(A annotation, Annota if (annotation == null) { return null; } - if (annotation instanceof SynthesizedAnnotation || (Proxy.isProxyClass(annotation.getClass()) && - Proxy.getInvocationHandler(annotation) instanceof SynthesizedAnnotationInvocationHandler)) { + if (annotation instanceof SynthesizedAnnotation) { return annotation; } @@ -1373,8 +1372,10 @@ public static A synthesizeAnnotation(A annotation, Annota DefaultAnnotationAttributeExtractor attributeExtractor = new DefaultAnnotationAttributeExtractor(annotation, annotatedElement); InvocationHandler handler = new SynthesizedAnnotationInvocationHandler(attributeExtractor); - Class[] exposedInterfaces = (canExposeSynthesizedMarker(annotationType) ? - new Class[] {annotationType, SynthesizedAnnotation.class} : new Class[] {annotationType}); + + // Can always expose Spring's SynthesizedAnnotation marker since we explicitly check for a + // synthesizable annotation before (which needs to declare @AliasFor from the same package) + Class[] exposedInterfaces = new Class[] {annotationType, SynthesizedAnnotation.class}; return (A) Proxy.newProxyInstance(annotation.getClass().getClassLoader(), exposedInterfaces, handler); } @@ -1945,11 +1946,11 @@ private AliasDescriptor(Method sourceAttribute, AliasFor aliasFor) { this.sourceAttribute = sourceAttribute; this.sourceAnnotationType = (Class) declaringClass; - this.sourceAttributeName = this.sourceAttribute.getName(); + this.sourceAttributeName = sourceAttribute.getName(); this.aliasedAnnotationType = (Annotation.class == aliasFor.annotation() ? this.sourceAnnotationType : aliasFor.annotation()); - this.aliasedAttributeName = getAliasedAttributeName(aliasFor, this.sourceAttribute); + this.aliasedAttributeName = getAliasedAttributeName(aliasFor, sourceAttribute); try { this.aliasedAttribute = this.aliasedAnnotationType.getDeclaredMethod(this.aliasedAttributeName); } @@ -1977,16 +1978,14 @@ private void validate() { if (this.isAliasPair) { AliasFor mirrorAliasFor = this.aliasedAttribute.getAnnotation(AliasFor.class); if (mirrorAliasFor == null) { - String msg = String.format( - "Attribute [%s] in annotation [%s] must be declared as an @AliasFor [%s].", + String msg = String.format("Attribute [%s] in annotation [%s] must be declared as an @AliasFor [%s].", this.aliasedAttributeName, this.sourceAnnotationType.getName(), this.sourceAttributeName); throw new AnnotationConfigurationException(msg); } String mirrorAliasedAttributeName = getAliasedAttributeName(mirrorAliasFor, this.aliasedAttribute); if (!this.sourceAttributeName.equals(mirrorAliasedAttributeName)) { - String msg = String.format( - "Attribute [%s] in annotation [%s] must be declared as an @AliasFor [%s], not [%s].", + String msg = String.format("Attribute [%s] in annotation [%s] must be declared as an @AliasFor [%s], not [%s].", this.aliasedAttributeName, this.sourceAnnotationType.getName(), this.sourceAttributeName, mirrorAliasedAttributeName); throw new AnnotationConfigurationException(msg); @@ -2124,13 +2123,6 @@ private AliasDescriptor getAttributeOverrideDescriptor() { return AliasDescriptor.from(this.aliasedAttribute); } - @Override - public String toString() { - return String.format("%s: @%s(%s) is an alias for @%s(%s)", getClass().getSimpleName(), - this.sourceAnnotationType.getSimpleName(), this.sourceAttributeName, - this.aliasedAnnotationType.getSimpleName(), this.aliasedAttributeName); - } - /** * Get the name of the aliased attribute configured via the supplied * {@link AliasFor @AliasFor} annotation on the supplied {@code attribute}. @@ -2145,9 +2137,8 @@ public String toString() { * @return the name of the aliased attribute (never {@code null} or empty) * @throws AnnotationConfigurationException if invalid configuration of * {@code @AliasFor} is detected - * @since 4.2 */ - private static String getAliasedAttributeName(AliasFor aliasFor, Method attribute) { + private String getAliasedAttributeName(AliasFor aliasFor, Method attribute) { String attributeName = aliasFor.attribute(); String value = aliasFor.value(); boolean attributeDeclared = StringUtils.hasText(attributeName); @@ -2155,10 +2146,10 @@ private static String getAliasedAttributeName(AliasFor aliasFor, Method attribut // Ensure user did not declare both 'value' and 'attribute' in @AliasFor if (attributeDeclared && valueDeclared) { - throw new AnnotationConfigurationException(String.format( - "In @AliasFor declared on attribute [%s] in annotation [%s], attribute 'attribute' and its " + - "alias 'value' are present with values of [%s] and [%s], but only one is permitted.", - attribute.getName(), attribute.getDeclaringClass().getName(), attributeName, value)); + String msg = String.format("In @AliasFor declared on attribute [%s] in annotation [%s], attribute 'attribute' " + + "and its alias 'value' are present with values of [%s] and [%s], but only one is permitted.", + attribute.getName(), attribute.getDeclaringClass().getName(), attributeName, value); + throw new AnnotationConfigurationException(msg); } attributeName = (attributeDeclared ? attributeName : value); @@ -2173,6 +2164,13 @@ private static String getAliasedAttributeName(AliasFor aliasFor, Method attribut return attributeName.trim(); } + + @Override + public String toString() { + return String.format("%s: @%s(%s) is an alias for @%s(%s)", getClass().getSimpleName(), + this.sourceAnnotationType.getSimpleName(), this.sourceAttributeName, + this.aliasedAnnotationType.getSimpleName(), this.aliasedAttributeName); + } } From 342d760f70fbcb3007bbb4790145bf0fef0e7ef2 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 29 Dec 2015 21:40:20 +0100 Subject: [PATCH 014/344] Polishing (cherry picked from commit 27c2e8c) --- .../SpringCacheAnnotationParser.java | 4 +- .../annotation/BeanAnnotationHelper.java | 11 +-- ...mponentScanAnnotationIntegrationTests.java | 69 +++++++++++-------- .../ComponentScanAnnotationTests.java | 6 +- ...ScanParserBeanDefinitionDefaultsTests.java | 7 +- .../ComponentScanParserScopedProxyTests.java | 6 ++ .../annotation/ComponentScanParserTests.java | 6 +- ...nParserWithUserDefinedStrategiesTests.java | 8 +-- ...ynthesizedAnnotationInvocationHandler.java | 12 ++-- 9 files changed, 72 insertions(+), 57 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/cache/annotation/SpringCacheAnnotationParser.java b/spring-context/src/main/java/org/springframework/cache/annotation/SpringCacheAnnotationParser.java index 7a1011405f..3fd43c091d 100644 --- a/spring-context/src/main/java/org/springframework/cache/annotation/SpringCacheAnnotationParser.java +++ b/spring-context/src/main/java/org/springframework/cache/annotation/SpringCacheAnnotationParser.java @@ -195,7 +195,7 @@ DefaultCacheConfig getDefaultCacheConfig(Class target) { } private Collection getAnnotations(AnnotatedElement ae, Class annotationType) { - Collection anns = new ArrayList(2); + Collection anns = new ArrayList(1); // look at raw annotation A ann = ae.getAnnotation(annotationType); @@ -211,7 +211,7 @@ private Collection getAnnotations(AnnotatedElement ae, } } - return (anns.isEmpty() ? null : anns); + return (!anns.isEmpty() ? anns : null); } /** diff --git a/spring-context/src/main/java/org/springframework/context/annotation/BeanAnnotationHelper.java b/spring-context/src/main/java/org/springframework/context/annotation/BeanAnnotationHelper.java index f44d7b5051..626a328f41 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/BeanAnnotationHelper.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/BeanAnnotationHelper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,17 +29,18 @@ class BeanAnnotationHelper { /** - * Return whether the given method is annotated directly or indirectly with @Bean. + * Return whether the given method is directly or indirectly annotated with + * the {@link Bean} annotation. */ public static boolean isBeanAnnotated(Method method) { - return AnnotationUtils.findAnnotation(method, Bean.class) != null; + return (AnnotationUtils.findAnnotation(method, Bean.class) != null); } public static String determineBeanNameFor(Method beanMethod) { - // by default the bean name is the name of the @Bean-annotated method + // By default, the bean name is the name of the @Bean-annotated method String beanName = beanMethod.getName(); - // check to see if the user has explicitly set the bean name + // Check to see if the user has explicitly set a custom bean name... Bean bean = AnnotationUtils.findAnnotation(beanMethod, Bean.class); if (bean != null && bean.name().length > 0) { beanName = bean.name()[0]; diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationIntegrationTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationIntegrationTests.java index b6be49d751..f579aa3277 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationIntegrationTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationIntegrationTests.java @@ -147,7 +147,7 @@ public void viaBeanRegistration() { @Test public void withCustomBeanNameGenerator() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); - ctx.register(ComponentScanWithBeanNameGenenerator.class); + ctx.register(ComponentScanWithBeanNameGenerator.class); ctx.refresh(); assertThat(ctx.containsBean("custom_fooServiceImpl"), is(true)); assertThat(ctx.containsBean("fooServiceImpl"), is(false)); @@ -241,7 +241,8 @@ public void withBasePackagesAndValueAlias() { @ComponentScan @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) - public static @interface ComposedConfiguration { + public @interface ComposedConfiguration { + String[] basePackages() default {}; } @@ -253,8 +254,9 @@ public static class ComposedAnnotationConfig { @Configuration -@ComponentScan(basePackageClasses=example.scannable._package.class) +@ComponentScan(basePackageClasses = example.scannable._package.class) class ComponentScanAnnotatedConfig { + @Bean public TestBean testBean() { return new TestBean(); @@ -264,6 +266,7 @@ public TestBean testBean() { @Configuration @ComponentScan("example.scannable") class ComponentScanAnnotatedConfig_WithValueAttribute { + @Bean public TestBean testBean() { return new TestBean(); @@ -272,13 +275,16 @@ public TestBean testBean() { @Configuration @ComponentScan -class ComponentScanWithNoPackagesConfig {} +class ComponentScanWithNoPackagesConfig { +} @Configuration -@ComponentScan(basePackages="example.scannable", nameGenerator=MyBeanNameGenerator.class) -class ComponentScanWithBeanNameGenenerator {} +@ComponentScan(basePackages = "example.scannable", nameGenerator = MyBeanNameGenerator.class) +class ComponentScanWithBeanNameGenerator { +} class MyBeanNameGenerator extends AnnotationBeanNameGenerator { + @Override public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) { return "custom_" + super.generateBeanName(definition, registry); @@ -286,10 +292,12 @@ public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry } @Configuration -@ComponentScan(basePackages="example.scannable_scoped", scopeResolver=MyScopeMetadataResolver.class) -class ComponentScanWithScopeResolver {} +@ComponentScan(basePackages = "example.scannable_scoped", scopeResolver = MyScopeMetadataResolver.class) +class ComponentScanWithScopeResolver { +} class MyScopeMetadataResolver extends AnnotationScopeMetadataResolver { + MyScopeMetadataResolver() { this.scopeAnnotationType = MyScope.class; } @@ -297,13 +305,14 @@ class MyScopeMetadataResolver extends AnnotationScopeMetadataResolver { @Configuration @ComponentScan( - basePackages="org.springframework.context.annotation", - useDefaultFilters=false, + basePackages = "org.springframework.context.annotation", + useDefaultFilters = false, includeFilters = @Filter(type = FilterType.CUSTOM, classes = ComponentScanParserTests.CustomTypeFilter.class), // exclude this class from scanning since it's in the scanned package excludeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = ComponentScanWithCustomTypeFilter.class), lazyInit = true) class ComponentScanWithCustomTypeFilter { + @Bean @SuppressWarnings({ "rawtypes", "serial", "unchecked" }) public static CustomAutowireConfigurer customAutowireConfigurer() { @@ -318,30 +327,30 @@ public ComponentScanParserTests.KustomAnnotationAutowiredBean testBean() { } @Configuration -@ComponentScan(basePackages="example.scannable", - scopedProxy=ScopedProxyMode.INTERFACES, - useDefaultFilters=false, +@ComponentScan(basePackages = "example.scannable", + scopedProxy = ScopedProxyMode.INTERFACES, + useDefaultFilters = false, includeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = ScopedProxyTestBean.class)) class ComponentScanWithScopedProxy {} @Configuration -@ComponentScan(basePackages="example.scannable", - scopedProxy=ScopedProxyMode.INTERFACES, - useDefaultFilters=false, - includeFilters=@Filter(type=FilterType.REGEX, pattern ="((?:[a-z.]+))ScopedProxyTestBean")) +@ComponentScan(basePackages = "example.scannable", + scopedProxy = ScopedProxyMode.INTERFACES, + useDefaultFilters = false, + includeFilters = @Filter(type=FilterType.REGEX, pattern = "((?:[a-z.]+))ScopedProxyTestBean")) class ComponentScanWithScopedProxyThroughRegex {} @Configuration -@ComponentScan(basePackages="example.scannable", - scopedProxy=ScopedProxyMode.INTERFACES, - useDefaultFilters=false, - includeFilters=@Filter(type=FilterType.ASPECTJ, pattern ="*..ScopedProxyTestBean")) +@ComponentScan(basePackages = "example.scannable", + scopedProxy = ScopedProxyMode.INTERFACES, + useDefaultFilters = false, + includeFilters = @Filter(type=FilterType.ASPECTJ, pattern = "*..ScopedProxyTestBean")) class ComponentScanWithScopedProxyThroughAspectJPattern {} @Configuration -@ComponentScan(basePackages="example.scannable", - useDefaultFilters=false, - includeFilters={ +@ComponentScan(basePackages = "example.scannable", + useDefaultFilters = false, + includeFilters = { @Filter(CustomStereotype.class), @Filter(CustomComponent.class) } @@ -349,15 +358,15 @@ class ComponentScanWithScopedProxyThroughAspectJPattern {} class ComponentScanWithMultipleAnnotationIncludeFilters1 {} @Configuration -@ComponentScan(basePackages="example.scannable", - useDefaultFilters=false, - includeFilters=@Filter({CustomStereotype.class, CustomComponent.class}) +@ComponentScan(basePackages = "example.scannable", + useDefaultFilters = false, + includeFilters = @Filter({CustomStereotype.class, CustomComponent.class}) ) class ComponentScanWithMultipleAnnotationIncludeFilters2 {} @Configuration @ComponentScan( - value="example.scannable", - basePackages="example.scannable", - basePackageClasses=example.scannable._package.class) + value = "example.scannable", + basePackages = "example.scannable", + basePackageClasses = example.scannable._package.class) class ComponentScanWithBasePackagesAndValueAlias {} diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationTests.java index ec17007cdc..2325b36f66 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanAnnotationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 the original author 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,7 +44,7 @@ public void noop() { @Configuration @ComponentScan( - basePackageClasses={TestBean.class}, + basePackageClasses = TestBean.class, nameGenerator = DefaultBeanNameGenerator.class, scopedProxy = ScopedProxyMode.NO, scopeResolver = AnnotationScopeMetadataResolver.class, @@ -61,6 +61,6 @@ public void noop() { class MyConfig { } -@ComponentScan(basePackageClasses=example.scannable.NamedComponent.class) +@ComponentScan(basePackageClasses = example.scannable.NamedComponent.class) class SimpleConfig { } diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanParserBeanDefinitionDefaultsTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanParserBeanDefinitionDefaultsTests.java index daad584f1c..bef499ff74 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanParserBeanDefinitionDefaultsTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanParserBeanDefinitionDefaultsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -122,7 +122,7 @@ public void testAutowireByType() { context.refresh(); fail("expected exception due to multiple matches for byType autowiring"); } - catch (UnsatisfiedDependencyException e) { + catch (UnsatisfiedDependencyException ex) { // expected } } @@ -161,7 +161,7 @@ public void testDependencyCheckAll() { context.refresh(); fail("expected exception due to dependency check"); } - catch (UnsatisfiedDependencyException e) { + catch (UnsatisfiedDependencyException ex) { // expected } } @@ -230,7 +230,6 @@ private static class DefaultsTestBean { private boolean destroyed; - public DefaultsTestBean() { INIT_COUNT++; } diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanParserScopedProxyTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanParserScopedProxyTests.java index 5d93bbbc99..f8a50d76d8 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanParserScopedProxyTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanParserScopedProxyTests.java @@ -42,11 +42,13 @@ public class ComponentScanParserScopedProxyTests { @Rule public final ExpectedException exception = ExpectedException.none(); + @Test public void testDefaultScopedProxy() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "org/springframework/context/annotation/scopedProxyDefaultTests.xml"); context.getBeanFactory().registerScope("myScope", new SimpleMapScope()); + ScopedProxyTestBean bean = (ScopedProxyTestBean) context.getBean("scopedProxyTestBean"); // should not be a proxy assertFalse(AopUtils.isAopProxy(bean)); @@ -58,6 +60,7 @@ public void testNoScopedProxy() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "org/springframework/context/annotation/scopedProxyNoTests.xml"); context.getBeanFactory().registerScope("myScope", new SimpleMapScope()); + ScopedProxyTestBean bean = (ScopedProxyTestBean) context.getBean("scopedProxyTestBean"); // should not be a proxy assertFalse(AopUtils.isAopProxy(bean)); @@ -69,6 +72,7 @@ public void testInterfacesScopedProxy() throws Exception { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "org/springframework/context/annotation/scopedProxyInterfacesTests.xml"); context.getBeanFactory().registerScope("myScope", new SimpleMapScope()); + // should cast to the interface FooService bean = (FooService) context.getBean("scopedProxyTestBean"); // should be dynamic proxy @@ -86,6 +90,7 @@ public void testTargetClassScopedProxy() throws Exception { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "org/springframework/context/annotation/scopedProxyTargetClassTests.xml"); context.getBeanFactory().registerScope("myScope", new SimpleMapScope()); + ScopedProxyTestBean bean = (ScopedProxyTestBean) context.getBean("scopedProxyTestBean"); // should be a class-based proxy assertTrue(AopUtils.isCglibProxy(bean)); @@ -103,6 +108,7 @@ public void testInvalidConfigScopedProxy() throws Exception { exception.expect(BeanDefinitionParsingException.class); exception.expectMessage(containsString("Cannot define both 'scope-resolver' and 'scoped-proxy' on tag")); exception.expectMessage(containsString("Offending resource: class path resource [org/springframework/context/annotation/scopedProxyInvalidConfigTests.xml]")); + new ClassPathXmlApplicationContext("org/springframework/context/annotation/scopedProxyInvalidConfigTests.xml"); } diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanParserTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanParserTests.java index 9298ad6caf..20d62e14a9 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanParserTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -145,9 +145,9 @@ public void componentScanRespectsProfileAnnotation() { } - @Target({ ElementType.TYPE, ElementType.FIELD }) + @Target({ElementType.TYPE, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) - public static @interface CustomAnnotation { + public @interface CustomAnnotation { } diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanParserWithUserDefinedStrategiesTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanParserWithUserDefinedStrategiesTests.java index 6c2984cdad..ad7fbc39c6 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanParserWithUserDefinedStrategiesTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/ComponentScanParserWithUserDefinedStrategiesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2015 the original author 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,7 +53,7 @@ public void testInvalidConstructorBeanNameGenerator() { "org/springframework/context/annotation/invalidConstructorNameGeneratorTests.xml"); fail("should have failed: no-arg constructor is required"); } - catch (BeansException e) { + catch (BeansException ex) { // expected } } @@ -65,9 +65,9 @@ public void testInvalidClassNameScopeMetadataResolver() { "org/springframework/context/annotation/invalidClassNameScopeResolverTests.xml"); fail("should have failed: no such class"); } - catch (BeansException e) { + catch (BeansException ex) { // expected } } -} \ No newline at end of file +} diff --git a/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedAnnotationInvocationHandler.java b/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedAnnotationInvocationHandler.java index b933823f54..c8614eb575 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedAnnotationInvocationHandler.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedAnnotationInvocationHandler.java @@ -60,6 +60,7 @@ class SynthesizedAnnotationInvocationHandler implements InvocationHandler { this.attributeExtractor = attributeExtractor; } + @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (isEqualsMethod(method)) { @@ -75,8 +76,8 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl return annotationType(); } if (!isAttributeMethod(method)) { - String msg = String.format("Method [%s] is unsupported for synthesized annotation type [%s]", method, - annotationType()); + String msg = String.format("Method [%s] is unsupported for synthesized annotation type [%s]", + method, annotationType()); throw new AnnotationConfigurationException(msg); } return getAttributeValue(method); @@ -92,9 +93,9 @@ private Object getAttributeValue(Method attributeMethod) { if (value == null) { value = this.attributeExtractor.getAttributeValue(attributeMethod); if (value == null) { - throw new IllegalStateException(String.format( - "%s returned null for attribute name [%s] from attribute source [%s]", - this.attributeExtractor.getClass().getName(), attributeName, this.attributeExtractor.getSource())); + String msg = String.format("%s returned null for attribute name [%s] from attribute source [%s]", + this.attributeExtractor.getClass().getName(), attributeName, this.attributeExtractor.getSource()); + throw new IllegalStateException(msg); } // Synthesize nested annotations before returning them. @@ -200,7 +201,6 @@ private int annotationHashCode() { * in Spring's {@link ObjectUtils} because those hash code generation * algorithms do not comply with the requirements specified in * {@link Annotation#hashCode()}. - * * @param array the array to compute the hash code for */ private int hashCodeForArray(Object array) { From 15da48eba52f134e8470e7df205fb13985557915 Mon Sep 17 00:00:00 2001 From: holub Date: Mon, 28 Dec 2015 14:48:24 -0500 Subject: [PATCH 015/344] Update SockJS client code snippet --- src/asciidoc/web-websocket.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/asciidoc/web-websocket.adoc b/src/asciidoc/web-websocket.adoc index 735b48be6e..9a2b8087dc 100644 --- a/src/asciidoc/web-websocket.adoc +++ b/src/asciidoc/web-websocket.adoc @@ -932,7 +932,7 @@ The example below shows how to create a SockJS client and connect to a SockJS en [subs="verbatim,quotes"] ---- List transports = new ArrayList<>(2); - transports.add(new WebSocketTransport(StandardWebSocketClient())); + transports.add(new WebSocketTransport(new StandardWebSocketClient())); transports.add(new RestTemplateXhrTransport()); SockJsClient sockJsClient = new SockJsClient(transports); From 24e58c1ac3d4a9d9198065f7a617b9a995a1aec6 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 31 Dec 2015 11:44:12 +0100 Subject: [PATCH 016/344] Refined RequestMappingHandlerMapping bean definition Issue: SPR-13832 (cherry picked from commit 9bffb9e) --- src/asciidoc/web-mvc.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/asciidoc/web-mvc.adoc b/src/asciidoc/web-mvc.adoc index 8382a35b92..e185334707 100644 --- a/src/asciidoc/web-mvc.adoc +++ b/src/asciidoc/web-mvc.adoc @@ -2466,7 +2466,7 @@ The following example shows how to configure an interceptor: [subs="verbatim,quotes"] ---- - + From 84e5234eb5f6c8001bf9bbb7c83dd672bfad9c68 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 31 Dec 2015 11:47:46 +0100 Subject: [PATCH 017/344] Avoid getTypeForFactoryBean warn logging for lazy-init beans Issue: SPR-13833 (cherry picked from commit 04f3181) --- .../beans/factory/support/AbstractBeanFactory.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 cddecfc3b1..e33f085ec7 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 @@ -1475,9 +1475,14 @@ protected Class getTypeForFactoryBean(String beanName, RootBeanDefinition mbd logger.debug("Bean currently in creation on FactoryBean type check: " + ex); } } + else if (mbd.isLazyInit()) { + if (logger.isDebugEnabled()) { + logger.debug("Bean creation exception on lazy FactoryBean type check: " + ex); + } + } else { if (logger.isWarnEnabled()) { - logger.warn("Bean creation exception on FactoryBean type check: " + ex); + logger.warn("Bean creation exception on non-lazy FactoryBean type check: " + ex); } } onSuppressedException(ex); From 158159709271927c0b9c691d05b88d5a1dd5fc3f Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Tue, 5 Jan 2016 14:55:14 +0100 Subject: [PATCH 018/344] Preserve ETag HTTP header for versioned resources Prior to this change, a resource handler chain configured with a `VersionResourceResolver` would add the resource version to the request attributes when serving that resource. This approach would not work when a `CachingResourceResolver` is configured and the resource is already cached. Indeed, that code path is not executed when the resource is resolved from the cache. This commit adds a new `VersionedResource` interface that's used by the `VersionResourceResolver`, adding a `getVersion()` method that returns the version string for that resource. This way, the version information is cached with the resource itself and the request attributes are no longer used for this. Issue: SPR-13817 (cherry picked from commit 473cf9c) --- .../resource/ResourceHttpRequestHandler.java | 24 +---- .../resource/VersionResourceResolver.java | 98 +++++++++++++++++-- .../servlet/resource/VersionedResource.java | 32 ++++++ .../ResourceHttpRequestHandlerTests.java | 17 +++- .../VersionResourceResolverTests.java | 8 +- 5 files changed, 143 insertions(+), 36 deletions(-) create mode 100644 spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionedResource.java 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 0b12ef71b6..8d322fed4e 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -266,7 +266,6 @@ public void handleRequest(HttpServletRequest request, HttpServletResponse respon } if (request.getHeader(HttpHeaders.RANGE) == null) { - setETagHeader(request, response); setHeaders(response, resource, mediaType); writeContent(response, resource); } @@ -406,21 +405,6 @@ protected MediaType getMediaType(Resource resource) { return mediaType; } - /** - * Set the ETag header if the version string of the served resource is present. - * Version strings can be resolved by {@link VersionStrategy} implementations and then - * set as a request attribute by {@link VersionResourceResolver}. - * @param request current servlet request - * @param response current servlet response - * @see VersionResourceResolver - */ - protected void setETagHeader(HttpServletRequest request, HttpServletResponse response) { - String versionString = (String) request.getAttribute(VersionResourceResolver.RESOURCE_VERSION_ATTRIBUTE); - if (versionString != null) { - response.setHeader(HttpHeaders.ETAG, "\"" + versionString + "\""); - } - } - /** * Set headers on the given servlet response. * Called for GET requests as well as HEAD requests. @@ -435,15 +419,15 @@ protected void setHeaders(HttpServletResponse response, Resource resource, Media throw new IOException("Resource content too long (beyond Integer.MAX_VALUE): " + resource); } response.setContentLength((int) length); - if (mediaType != null) { response.setContentType(mediaType.toString()); } - if (resource instanceof EncodedResource) { response.setHeader(HttpHeaders.CONTENT_ENCODING, ((EncodedResource) resource).getContentEncoding()); } - + if (resource instanceof VersionedResource) { + response.setHeader(HttpHeaders.ETAG, "\"" + ((VersionedResource) resource).getVersion() + "\""); + } response.setHeader(HttpHeaders.ACCEPT_RANGES, "bytes"); } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionResourceResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionResourceResolver.java index a1ed221dd2..718fdf7de2 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionResourceResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionResourceResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,14 +16,21 @@ package org.springframework.web.servlet.resource; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; + import javax.servlet.http.HttpServletRequest; +import org.springframework.core.io.AbstractResource; import org.springframework.core.io.Resource; import org.springframework.util.AntPathMatcher; import org.springframework.util.StringUtils; @@ -55,10 +62,6 @@ */ public class VersionResourceResolver extends AbstractResourceResolver { - public static final String RESOURCE_VERSION_ATTRIBUTE = - VersionResourceResolver.class.getName() + ".resourceVersion"; - - private AntPathMatcher pathMatcher = new AntPathMatcher(); /** Map from path pattern -> VersionStrategy */ @@ -168,12 +171,9 @@ protected Resource resolveResourceInternal(HttpServletRequest request, String re String actualVersion = versionStrategy.getResourceVersion(baseResource); if (candidateVersion.equals(actualVersion)) { if (logger.isTraceEnabled()) { - logger.trace("Resource matches extracted version ["+ candidateVersion + "]"); + logger.trace("Resource matches extracted version [" + candidateVersion + "]"); } - if (request != null) { - request.setAttribute(RESOURCE_VERSION_ATTRIBUTE, candidateVersion); - } - return baseResource; + return new FileNameVersionedResource(baseResource, candidateVersion); } else { if (logger.isTraceEnabled()) { @@ -225,4 +225,82 @@ protected VersionStrategy getStrategyForPath(String requestPath) { return null; } + private class FileNameVersionedResource extends AbstractResource implements VersionedResource { + + private final Resource original; + + private final String version; + + public FileNameVersionedResource(Resource original, String version) { + this.original = original; + this.version = version; + } + + @Override + public boolean exists() { + return this.original.exists(); + } + + @Override + public boolean isReadable() { + return this.original.isReadable(); + } + + @Override + public boolean isOpen() { + return this.original.isOpen(); + } + + @Override + public URL getURL() throws IOException { + return this.original.getURL(); + } + + @Override + public URI getURI() throws IOException { + return this.original.getURI(); + } + + @Override + public File getFile() throws IOException { + return this.original.getFile(); + } + + @Override + public String getFilename() { + return this.original.getFilename(); + } + + @Override + public long contentLength() throws IOException { + return this.original.contentLength(); + } + + @Override + public long lastModified() throws IOException { + return this.original.lastModified(); + } + + @Override + public Resource createRelative(String relativePath) throws IOException { + return this.original.createRelative(relativePath); + } + + @Override + public String getDescription() { + return original.getDescription(); + } + + @Override + public InputStream getInputStream() throws IOException { + return original.getInputStream(); + } + + @Override + public String getVersion() { + return this.version; + } + + } + } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionedResource.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionedResource.java new file mode 100644 index 0000000000..d6d9807ed5 --- /dev/null +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionedResource.java @@ -0,0 +1,32 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.web.servlet.resource; + +import org.springframework.core.io.Resource; + +/** + * Interface for a resource descriptor that describes its version + * with a version string that can be derived from its content and/or metadata. + * + * @author Brian Clozel + * @since 4.2 + * @see VersionResourceResolver + */ +public interface VersionedResource extends Resource { + + String getVersion(); +} diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandlerTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandlerTests.java index bb5f502231..bfbda11fc9 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandlerTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -87,7 +87,6 @@ public void setUp() throws Exception { @Test public void getResource() throws Exception { this.request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, "foo.css"); - this.request.setAttribute(VersionResourceResolver.RESOURCE_VERSION_ATTRIBUTE, "versionString"); this.handler.handleRequest(this.request, this.response); assertEquals("text/css", this.response.getContentType()); @@ -95,7 +94,6 @@ public void getResource() throws Exception { assertEquals("max-age=3600", this.response.getHeader("Cache-Control")); assertTrue(this.response.containsHeader("Last-Modified")); assertEquals(this.response.getHeader("Last-Modified"), resourceLastModifiedDate("test/foo.css")); - assertEquals("\"versionString\"", this.response.getHeader("ETag")); assertEquals("h1 { color:red; }", this.response.getContentAsString()); } @@ -110,6 +108,19 @@ public void getResourceNoCache() throws Exception { assertEquals(this.response.getHeader("Last-Modified"), resourceLastModifiedDate("test/foo.css")); } + @Test + public void getVersionedResource() throws Exception { + VersionResourceResolver versionResolver = new VersionResourceResolver() + .addFixedVersionStrategy("versionString", "/**"); + this.handler.setResourceResolvers(Arrays.asList(versionResolver, new PathResourceResolver())); + this.handler.afterPropertiesSet(); + + this.request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, "versionString/foo.css"); + this.handler.handleRequest(this.request, this.response); + + assertEquals("\"versionString\"", this.response.getHeader("ETag")); + } + @Test @SuppressWarnings("deprecation") public void getResourceHttp10BehaviorCache() throws Exception { diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/VersionResourceResolverTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/VersionResourceResolverTests.java index c3fd659dbf..3f007b8793 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/VersionResourceResolverTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/VersionResourceResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,6 +28,7 @@ import org.springframework.core.io.Resource; import org.springframework.mock.web.test.MockHttpServletRequest; +import static org.hamcrest.Matchers.instanceOf; import static org.junit.Assert.*; import static org.mockito.BDDMockito.*; @@ -146,9 +147,10 @@ public void resolveResourceSuccess() throws Exception { this.resolver .setStrategyMap(Collections.singletonMap("/**", this.versionStrategy)); Resource actual = this.resolver.resolveResourceInternal(request, versionFile, this.locations, this.chain); - assertEquals(expected, actual); + assertEquals(expected.getFilename(), actual.getFilename()); verify(this.versionStrategy, times(1)).getResourceVersion(expected); - assertEquals(version, request.getAttribute(VersionResourceResolver.RESOURCE_VERSION_ATTRIBUTE)); + assertThat(actual, instanceOf(VersionedResource.class)); + assertEquals(version, ((VersionedResource)actual).getVersion()); } @Test From 3af62bd0ddc39db9e55232bab04315891adaeed0 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Tue, 5 Jan 2016 17:31:30 +0100 Subject: [PATCH 019/344] Polish Issue: SPR-13817 (cherry picked from d681f77d) --- .../springframework/web/servlet/resource/VersionedResource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionedResource.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionedResource.java index d6d9807ed5..8960a54ef6 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionedResource.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionedResource.java @@ -23,7 +23,7 @@ * with a version string that can be derived from its content and/or metadata. * * @author Brian Clozel - * @since 4.2 + * @since 4.2.5 * @see VersionResourceResolver */ public interface VersionedResource extends Resource { From 73df50db3c8870d17ad307e8e486658c93e99518 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Thu, 14 Jan 2016 14:25:30 -0500 Subject: [PATCH 020/344] Fix javadoc issue in ResponseEntityExceptionHandler Issue: SPR-13869 --- .../ResponseEntityExceptionHandler.java | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java index decc3ca131..91cfe65e07 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java @@ -47,21 +47,24 @@ import org.springframework.web.multipart.support.MissingServletRequestPartException; import org.springframework.web.servlet.NoHandlerFoundException; import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException; +import org.springframework.web.util.WebUtils; /** * A convenient base class for {@link ControllerAdvice @ControllerAdvice} classes * that wish to provide centralized exception handling across all * {@code @RequestMapping} methods through {@code @ExceptionHandler} methods. * - *

This base class provides an {@code @ExceptionHandler} method for handling standard - * Spring MVC exceptions that returns a {@code ResponseEntity} to be written with - * {@link HttpMessageConverter message converters}. This is in contrast to + *

This base class provides an {@code @ExceptionHandler} method for handling + * internal Spring MVC exceptions. This method returns a {@code ResponseEntity} + * for writing to the response with a {@link HttpMessageConverter message converter}. + * in contrast to * {@link org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver - * DefaultHandlerExceptionResolver} which returns a {@code ModelAndView} instead. + * DefaultHandlerExceptionResolver} which returns a + * {@link org.springframework.web.servlet.ModelAndView ModelAndView}. * - *

If there is no need to write error content to the response body, or if using - * view resolution (e.g., via {@code ContentNegotiatingViewResolver}), then use - * {@code DefaultHandlerExceptionResolver} instead. + *

If there is no need to write error content to the response body, or when + * using view resolution (e.g., via {@code ContentNegotiatingViewResolver}), + * then {@code DefaultHandlerExceptionResolver} is good enough. * *

Note that in order for an {@code @ControllerAdvice} sub-class to be * detected, {@link ExceptionHandlerExceptionResolver} must be configured. @@ -184,18 +187,20 @@ else if (ex instanceof NoHandlerFoundException) { /** * A single place to customize the response body of all Exception types. - *

This method returns {@code null} by default. + *

The default implementation sets the {@link WebUtils#ERROR_EXCEPTION_ATTRIBUTE} + * request attribute and creates a {@link ResponseEntity} from the given + * body, headers, and status. * @param ex the exception - * @param body the body to use for the response - * @param headers the headers to be written to the response - * @param status the selected response status + * @param body the body for the response + * @param headers the headers for the response + * @param status the response status * @param request the current request */ protected ResponseEntity handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) { if (HttpStatus.INTERNAL_SERVER_ERROR.equals(status)) { - request.setAttribute("javax.servlet.error.exception", ex, WebRequest.SCOPE_REQUEST); + request.setAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE, ex, WebRequest.SCOPE_REQUEST); } return new ResponseEntity(body, headers, status); From 29692fcc2135be20248969332caf30feedf369ca Mon Sep 17 00:00:00 2001 From: Ian Chan Date: Sat, 2 Jan 2016 14:41:10 +1300 Subject: [PATCH 021/344] Catch RejectedExecutionException in WebAsyncManager Issue: SPR-13836 --- .../request/async/WebAsyncManager.java | 43 +++++++++++-------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/context/request/async/WebAsyncManager.java b/spring-web/src/main/java/org/springframework/web/context/request/async/WebAsyncManager.java index 2322d61027..a20cf23d95 100644 --- a/spring-web/src/main/java/org/springframework/web/context/request/async/WebAsyncManager.java +++ b/spring-web/src/main/java/org/springframework/web/context/request/async/WebAsyncManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.Callable; +import java.util.concurrent.RejectedExecutionException; import javax.servlet.http.HttpServletRequest; import org.apache.commons.logging.Log; @@ -306,24 +307,30 @@ public void run() { interceptorChain.applyBeforeConcurrentHandling(this.asyncWebRequest, callable); startAsyncProcessing(processingContext); - - this.taskExecutor.submit(new Runnable() { - @Override - public void run() { - Object result = null; - try { - interceptorChain.applyPreProcess(asyncWebRequest, callable); - result = callable.call(); - } - catch (Throwable ex) { - result = ex; - } - finally { - result = interceptorChain.applyPostProcess(asyncWebRequest, callable, result); + try { + this.taskExecutor.submit(new Runnable() { + @Override + public void run() { + Object result = null; + try { + interceptorChain.applyPreProcess(asyncWebRequest, callable); + result = callable.call(); + } + catch (Throwable ex) { + result = ex; + } + finally { + result = interceptorChain.applyPostProcess(asyncWebRequest, callable, result); + } + setConcurrentResultAndDispatch(result); } - setConcurrentResultAndDispatch(result); - } - }); + }); + } + catch (RejectedExecutionException ex) { + Object result = interceptorChain.applyPostProcess(this.asyncWebRequest, callable, ex); + setConcurrentResultAndDispatch(result); + throw ex; + } } private void setConcurrentResultAndDispatch(Object result) { From ab16adab2e3cd7505cfb68353917bdc42f4ba0d1 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Wed, 20 Jan 2016 17:57:56 -0500 Subject: [PATCH 022/344] Avoid double encoding URI in ServletServerHttpRequest Issue: SPR-13876 --- .../http/server/ServletServerHttpRequest.java | 11 +++++++---- .../server/ServletServerHttpRequestTests.java | 16 +++++++++++++++- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java b/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java index 2b5ac8a93e..a906da520d 100644 --- a/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -88,9 +88,12 @@ public HttpMethod getMethod() { @Override public URI getURI() { try { - return new URI(this.servletRequest.getScheme(), null, this.servletRequest.getServerName(), - this.servletRequest.getServerPort(), this.servletRequest.getRequestURI(), - this.servletRequest.getQueryString(), null); + StringBuffer url = this.servletRequest.getRequestURL(); + String query = this.servletRequest.getQueryString(); + if (StringUtils.hasText(query)) { + url.append('?').append(query); + } + return new URI(url.toString()); } catch (URISyntaxException ex) { throw new IllegalStateException("Could not get HttpServletRequest URI: " + ex.getMessage(), ex); diff --git a/spring-web/src/test/java/org/springframework/http/server/ServletServerHttpRequestTests.java b/spring-web/src/test/java/org/springframework/http/server/ServletServerHttpRequestTests.java index 490b0369b0..906e04352b 100644 --- a/spring-web/src/test/java/org/springframework/http/server/ServletServerHttpRequestTests.java +++ b/spring-web/src/test/java/org/springframework/http/server/ServletServerHttpRequestTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -64,6 +64,20 @@ public void getURI() throws Exception { assertEquals("Invalid uri", uri, request.getURI()); } + // SPR-13876 + + @Test + public void getUriWithEncoding() throws Exception { + URI uri = new URI("https://example.com/%E4%B8%AD%E6%96%87" + + "?redirect=https%3A%2F%2Fgithub.com%2Fspring-projects%2Fspring-framework"); + mockRequest.setScheme(uri.getScheme()); + mockRequest.setServerName(uri.getHost()); + mockRequest.setServerPort(uri.getPort()); + mockRequest.setRequestURI(uri.getRawPath()); + mockRequest.setQueryString(uri.getRawQuery()); + assertEquals("Invalid uri", uri, request.getURI()); + } + @Test public void getHeaders() throws Exception { String headerName = "MyHeader"; From e15908620143f8e852b9584089bfa85700b9b205 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Wed, 20 Jan 2016 21:39:41 -0500 Subject: [PATCH 023/344] Fix failing test --- .../method/annotation/HttpEntityMethodProcessorMockTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessorMockTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessorMockTests.java index 6387365ca4..0e748b031b 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessorMockTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessorMockTests.java @@ -175,7 +175,7 @@ public void resolveArgumentRequestEntity() throws Exception { assertFalse("The requestHandled flag shouldn't change", mavContainer.isRequestHandled()); RequestEntity requestEntity = (RequestEntity) result; assertEquals("Invalid method", HttpMethod.GET, requestEntity.getMethod()); - assertEquals("Invalid url", new URI("http", null, "www.example.com", 80, "/path", null, null), requestEntity.getUrl()); + assertEquals("Invalid url", new URI("http", null, "www.example.com", -1, "/path", null, null), requestEntity.getUrl()); assertEquals("Invalid argument", body, requestEntity.getBody()); } From 33112df23714bef9ce5fcdf483559d93cca15f7f Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Wed, 20 Jan 2016 23:13:34 -0500 Subject: [PATCH 024/344] Fix second failing test --- .../transport/handler/HttpSendingTransportHandlerTests.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/handler/HttpSendingTransportHandlerTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/handler/HttpSendingTransportHandlerTests.java index cfe121a261..8b94177881 100644 --- a/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/handler/HttpSendingTransportHandlerTests.java +++ b/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/handler/HttpSendingTransportHandlerTests.java @@ -31,6 +31,7 @@ import org.springframework.web.socket.sockjs.transport.session.PollingSockJsSession; import org.springframework.web.socket.sockjs.transport.session.StreamingSockJsSession; import org.springframework.web.socket.sockjs.transport.session.StubSockJsServiceConfig; +import org.springframework.web.util.UriUtils; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -114,7 +115,7 @@ private void testJsonpTransport(String callbackValue, boolean expectSuccess) thr setRequest("POST", "/"); if (callbackValue != null) { - this.servletRequest.setQueryString("c=" + callbackValue); + this.servletRequest.setQueryString("c=" + UriUtils.encodeQueryParam(callbackValue, "UTF-8")); this.servletRequest.addParameter("c", callbackValue); } From a4cb3cf90077ac6525c2d2e5b4a9da6d4ec0a212 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Thu, 21 Jan 2016 17:28:29 +0100 Subject: [PATCH 025/344] Fix OutOfBoundsException in ResourceUrlEncodingFilter Prior to this change, the `ResourceUrlEncodingFilter` would try to lookup resources URLs as soon as the given URL would be longer than the expected context+servlet prefix path. This can lead to OutOfBoundsExceptions when the provided URL does not start with that prefix and still has the required length. This commit makes sure that all candidate URLs for resources lookup are prefixed with the cached servlet and context path. This underlines the fact that the `ResourceUrlEncodingFilter` does not support relative URLs for now and delegates to the native servlet implementation in that case. Issue: SPR-13861 cherry-picked from 2f6d86b7 --- .../resource/ResourceUrlEncodingFilter.java | 18 +++++++++------- .../ResourceUrlEncodingFilterTests.java | 21 ++++++++++++++++++- 2 files changed, 31 insertions(+), 8 deletions(-) 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 7299024880..471ae15461 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-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,6 +37,7 @@ * @author Jeremy Grelle * @author Rossen Stoyanchev * @author Sam Brannen + * @author Brian Clozel * @since 4.1 */ public class ResourceUrlEncodingFilter extends OncePerRequestFilter { @@ -56,9 +57,11 @@ private static class ResourceUrlEncodingResponseWrapper extends HttpServletRespo private final HttpServletRequest request; - /* Cache the index of the path within the DispatcherServlet mapping */ + /* 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; @@ -72,15 +75,14 @@ public String encodeURL(String url) { return super.encodeURL(url); } - initIndexLookupPath(resourceUrlProvider); - if (url.length() >= this.indexLookupPath) { - String prefix = url.substring(0, this.indexLookupPath); + 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(prefix + lookupPath + suffix); + return super.encodeURL(this.prefixLookupPath + lookupPath + suffix); } } @@ -92,16 +94,18 @@ private ResourceUrlProvider getResourceUrlProvider() { ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR); } - private void initIndexLookupPath(ResourceUrlProvider urlProvider) { + private void initLookupPath(ResourceUrlProvider urlProvider) { if (this.indexLookupPath == null) { String requestUri = urlProvider.getPathHelper().getRequestUri(this.request); String lookupPath = urlProvider.getPathHelper().getLookupPathForRequest(this.request); this.indexLookupPath = requestUri.lastIndexOf(lookupPath); + this.prefixLookupPath = requestUri.substring(0, this.indexLookupPath); if ("/".equals(lookupPath) && !"/".equals(requestUri)) { String contextPath = urlProvider.getPathHelper().getContextPath(this.request); if (requestUri.equals(contextPath)) { this.indexLookupPath = requestUri.length(); + this.prefixLookupPath = requestUri; } } } 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 964919484a..26dd6ae4ea 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -150,6 +150,25 @@ public void doFilter(ServletRequest request, ServletResponse response) throws IO }); } + // SPR-13847 + @Test + public void encodeUrlPreventStringOutOfBounds() throws Exception { + 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.doFilterInternal(request, response, new FilterChain() { + @Override + public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { + String result = ((HttpServletResponse)response).encodeURL("index?key=value"); + assertEquals("index?key=value", result); + } + }); + } + + protected ResourceUrlProvider createResourceUrlProvider(List resolvers) { ResourceHttpRequestHandler handler = new ResourceHttpRequestHandler(); handler.setLocations(Arrays.asList(new ClassPathResource("test/", getClass()))); From fc34b0c5926a40d2529b8cb4e0fd8f7b1b61a01b Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Fri, 22 Jan 2016 16:04:03 +0100 Subject: [PATCH 026/344] Reorder HTTP headers processing in RequestMappingHandlerAdapter Prior to this change, the `RequestMappingHandlerAdapter` would first add a "Cache-Control" HTTP header to the response (depending on its `WebContentGenerator` configuration and `@SessionAttributes` on the handler class); then, the Adapter would delegate the actual handler the processing of the request. This leads to issues, as the handler does not have full control to the response and has to deal with pre-existing headers in the response. This means that the Adapter and the handler can add incompatible Cache-Control directives without knowing it, since one cannot see the headers added by the other until the response is committed. This commit switches the order of execution: first, the handler is called (possibly adding HTTP headers), then the RMHA processes the response and adds "Cache-Control" directives *only if there's no Cache-Control header already defined*. Issue: SPR-13867 cherry-picked from 8f1d06f19 --- .../RequestMappingHandlerAdapter.java | 21 ++++--- .../servlet/support/WebContentGenerator.java | 63 ++++++++++--------- .../web/servlet/mvc/ControllerTests.java | 4 +- ...MappingHandlerAdapterIntegrationTests.java | 37 +++++++++-- 4 files changed, 80 insertions(+), 45 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java index a1c8e09443..f90b2a5cc6 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java @@ -714,27 +714,30 @@ protected boolean supportsInternal(HandlerMethod handlerMethod) { protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { + ModelAndView mav = null; checkRequest(request); - if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { - applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers); - } - else { - prepareResponse(response); - } - // Execute invokeHandlerMethod in synchronized block if required. if (this.synchronizeOnSession) { HttpSession session = request.getSession(false); if (session != null) { Object mutex = WebUtils.getSessionMutex(session); synchronized (mutex) { - return invokeHandlerMethod(request, response, handlerMethod); + mav = invokeHandlerMethod(request, response, handlerMethod); } } } - return invokeHandlerMethod(request, response, handlerMethod); + mav = invokeHandlerMethod(request, response, handlerMethod); + + if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { + applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers); + } + else { + prepareResponse(response); + } + + return mav; } /** diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/WebContentGenerator.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/WebContentGenerator.java index fda8819180..8bf68bba4a 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/WebContentGenerator.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/WebContentGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import java.util.HashSet; import java.util.Set; import java.util.concurrent.TimeUnit; + import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -329,14 +330,16 @@ protected final void prepareResponse(HttpServletResponse response) { * @since 4.2 */ protected final void applyCacheControl(HttpServletResponse response, CacheControl cacheControl) { - String ccValue = cacheControl.getHeaderValue(); - if (ccValue != null) { - // Set computed HTTP 1.1 Cache-Control header - response.setHeader(HEADER_CACHE_CONTROL, ccValue); - - if (response.containsHeader(HEADER_PRAGMA)) { - // Reset HTTP 1.0 Pragma header if present - response.setHeader(HEADER_PRAGMA, ""); + if (!response.containsHeader(HEADER_CACHE_CONTROL)) { + String ccValue = cacheControl.getHeaderValue(); + if (ccValue != null) { + // Set computed HTTP 1.1 Cache-Control header + response.setHeader(HEADER_CACHE_CONTROL, ccValue); + + if (response.containsHeader(HEADER_PRAGMA)) { + // Reset HTTP 1.0 Pragma header if present + response.setHeader(HEADER_PRAGMA, ""); + } } } } @@ -352,30 +355,32 @@ protected final void applyCacheControl(HttpServletResponse response, CacheContro */ @SuppressWarnings("deprecation") protected final void applyCacheSeconds(HttpServletResponse response, int cacheSeconds) { - if (this.useExpiresHeader || !this.useCacheControlHeader) { - // Deprecated HTTP 1.0 cache behavior, as in previous Spring versions - if (cacheSeconds > 0) { - cacheForSeconds(response, cacheSeconds); - } - else if (cacheSeconds == 0) { - preventCaching(response); - } - } - else { - CacheControl cControl; - if (cacheSeconds > 0) { - cControl = CacheControl.maxAge(cacheSeconds, TimeUnit.SECONDS); - if (this.alwaysMustRevalidate) { - cControl = cControl.mustRevalidate(); + if (!response.containsHeader(HEADER_CACHE_CONTROL)) { + if (this.useExpiresHeader || !this.useCacheControlHeader) { + // Deprecated HTTP 1.0 cache behavior, as in previous Spring versions + if (cacheSeconds > 0) { + cacheForSeconds(response, cacheSeconds); + } + else if (cacheSeconds == 0) { + preventCaching(response); } - } - else if (cacheSeconds == 0) { - cControl = (this.useCacheControlNoStore ? CacheControl.noStore() : CacheControl.noCache()); } else { - cControl = CacheControl.empty(); + CacheControl cControl; + if (cacheSeconds > 0) { + cControl = CacheControl.maxAge(cacheSeconds, TimeUnit.SECONDS); + if (this.alwaysMustRevalidate) { + cControl = cControl.mustRevalidate(); + } + } + else if (cacheSeconds == 0) { + cControl = (this.useCacheControlNoStore ? CacheControl.noStore() : CacheControl.noCache()); + } + else { + cControl = CacheControl.empty(); + } + applyCacheControl(response, cControl); } - applyCacheControl(response, cControl); } } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/ControllerTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/ControllerTests.java index 3ab23141c9..605b129a7b 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/ControllerTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/ControllerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -49,7 +49,7 @@ public void parameterizableViewController() throws Exception { ParameterizableViewController pvc = new ParameterizableViewController(); pvc.setViewName(viewName); // We don't care about the params. - ModelAndView mv = pvc.handleRequest(new MockHttpServletRequest("GET", "foo.html"), null); + ModelAndView mv = pvc.handleRequest(new MockHttpServletRequest("GET", "foo.html"), new MockHttpServletResponse()); assertTrue("model has no data", mv.getModel().size() == 0); assertTrue("model has correct viewname", mv.getViewName().equals(viewName)); assertTrue("getViewName matches", pvc.getViewName().equals(viewName)); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapterIntegrationTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapterIntegrationTests.java index 6ef5cb0188..5ba4bb5499 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapterIntegrationTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapterIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,11 +28,14 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.TimeUnit; + import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; +import org.hamcrest.Matchers; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -40,8 +43,8 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.propertyeditors.CustomDateEditor; import org.springframework.core.MethodParameter; +import org.springframework.http.CacheControl; import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.mock.web.test.MockHttpServletRequest; @@ -85,6 +88,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; /** @@ -262,6 +266,24 @@ public void handleHttpEntity() throws Exception { assertEquals(HttpStatus.ACCEPTED.value(), response.getStatus()); assertEquals("Handled requestBody=[Hello Server]", new String(response.getContentAsByteArray(), "UTF-8")); assertEquals("headerValue", response.getHeader("header")); + // set because of @SesstionAttributes + assertEquals("no-store", response.getHeader("Cache-Control")); + } + + // SPR-13867 + @Test + public void handleHttpEntityWithCacheControl() throws Exception { + Class[] parameterTypes = new Class[] { HttpEntity.class }; + request.addHeader("Content-Type", "text/plain; charset=utf-8"); + request.setContent("Hello Server".getBytes("UTF-8")); + + HandlerMethod handlerMethod = handlerMethod("handleHttpEntityWithCacheControl", parameterTypes); + ModelAndView mav = handlerAdapter.handle(request, response, handlerMethod); + + assertNull(mav); + assertEquals(HttpStatus.OK.value(), response.getStatus()); + assertEquals("Handled requestBody=[Hello Server]", new String(response.getContentAsByteArray(), "UTF-8")); + assertThat(response.getHeaderValues("Cache-Control"), Matchers.contains("max-age=3600")); } @Test @@ -373,10 +395,15 @@ public String handleAndValidateRequestBody(@Valid TestBean modelAttr, Errors err } public ResponseEntity handleHttpEntity(HttpEntity httpEntity) throws Exception { - HttpHeaders responseHeaders = new HttpHeaders(); - responseHeaders.set("header", "headerValue"); String responseBody = "Handled requestBody=[" + new String(httpEntity.getBody(), "UTF-8") + "]"; - return new ResponseEntity(responseBody, responseHeaders, HttpStatus.ACCEPTED); + return ResponseEntity.accepted() + .header("header", "headerValue") + .body(responseBody); + } + + public ResponseEntity handleHttpEntityWithCacheControl(HttpEntity httpEntity) throws Exception { + String responseBody = "Handled requestBody=[" + new String(httpEntity.getBody(), "UTF-8") + "]"; + return ResponseEntity.ok().cacheControl(CacheControl.maxAge(1, TimeUnit.HOURS)).body(responseBody); } public void handleRequestPart(@RequestPart String requestPart, Model model) { From bcebc9db7ed223a351adc69630424bff7a02ae32 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Fri, 22 Jan 2016 18:30:39 +0100 Subject: [PATCH 027/344] Register prefixed path patterns with FixedVersionStrategy Prior to this change, configuring a `FixedVersionStrategy` like so would configure a single "/js/**" path pattern: ``` versionResourceResolver.addFixedVersionStrategy("v1.0.0","/js/**"); ``` This commit makes sure that for each path pattern, its prefixed version is added to the map. For example, the previous configuration also adds "/v1.0.0/js/**". Issue: SPR-13883 cherry-picked from 84fe46cd --- .../resource/VersionResourceResolver.java | 17 +++++++++++++++-- .../resource/VersionResourceResolverTests.java | 17 ++++++++++++++++- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionResourceResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionResourceResolver.java index 718fdf7de2..1e35c830d6 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionResourceResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionResourceResolver.java @@ -22,6 +22,7 @@ import java.net.URI; import java.net.URL; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.LinkedHashMap; @@ -110,14 +111,26 @@ public VersionResourceResolver addContentVersionStrategy(String... pathPatterns) * fetched from a git commit sha, a property file, or environment variable * and set with SpEL expressions in the configuration (e.g. see {@code @Value} * in Java config). + *

If not done already, variants of the given {@code pathPatterns}, prefixed with + * the {@code version} will be also configured. For example, adding a {@code "/js/**"} path pattern + * will also cofigure automatically a {@code "/v1.0.0/js/**"} with {@code "v1.0.0"} the + * {@code version} String given as an argument. * @param version a version string * @param pathPatterns one or more resource URL path patterns * @return the current instance for chained method invocation * @see FixedVersionStrategy */ public VersionResourceResolver addFixedVersionStrategy(String version, String... pathPatterns) { - addVersionStrategy(new FixedVersionStrategy(version), pathPatterns); - return this; + List patternsList = Arrays.asList(pathPatterns); + List prefixedPatterns = new ArrayList(pathPatterns.length); + String versionPrefix = "/" + version; + for(String pattern : patternsList) { + prefixedPatterns.add(pattern); + if(!pattern.startsWith(versionPrefix) && !patternsList.contains(versionPrefix + pattern)) { + prefixedPatterns.add(versionPrefix + pattern); + } + } + return addVersionStrategy(new FixedVersionStrategy(version), prefixedPatterns.toArray(new String[0])); } /** diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/VersionResourceResolverTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/VersionResourceResolverTests.java index 3f007b8793..d423872638 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/VersionResourceResolverTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/VersionResourceResolverTests.java @@ -21,6 +21,7 @@ import java.util.List; import java.util.Map; +import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Test; @@ -28,7 +29,7 @@ import org.springframework.core.io.Resource; import org.springframework.mock.web.test.MockHttpServletRequest; -import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import static org.mockito.BDDMockito.*; @@ -168,4 +169,18 @@ public void getStrategyForPath() throws Exception { assertEquals(jsStrategy, this.resolver.getStrategyForPath("bar/foo.js")); } + // SPR-13883 + @Test + public void shouldConfigureFixedPrefixAutomatically() throws Exception { + + this.resolver.addFixedVersionStrategy("fixedversion", "/js/**", "/css/**", "/fixedversion/css/**"); + + assertThat(this.resolver.getStrategyMap().size(), is(4)); + assertThat(this.resolver.getStrategyForPath("/js/something.js"), Matchers.instanceOf(FixedVersionStrategy.class)); + assertThat(this.resolver.getStrategyForPath("/fixedversion/js/something.js"), Matchers.instanceOf(FixedVersionStrategy.class)); + assertThat(this.resolver.getStrategyForPath("/css/something.css"), Matchers.instanceOf(FixedVersionStrategy.class)); + assertThat(this.resolver.getStrategyForPath("/fixedversion/css/something.css"), Matchers.instanceOf(FixedVersionStrategy.class)); + } + + } From 8516de62e3ee3ee86af2e0349cfac4f692b2f92c Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 13 Jan 2016 12:44:34 +0100 Subject: [PATCH 028/344] FastByteArrayInputStream consistently returns -1 when no data available Issue: SPR-13858 (cherry picked from commit 5d4547d) --- .../util/FastByteArrayOutputStream.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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 6c2b381dfd..c54591f005 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-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -361,7 +361,7 @@ public FastByteArrayInputStream(FastByteArrayOutputStream fastByteArrayOutputStr @Override public int read() { if (this.currentBuffer == null) { - // this stream doesn't have any data in it + // This stream doesn't have any data in it... return -1; } else { @@ -412,8 +412,8 @@ else if (off < 0) { } else { if (this.currentBuffer == null) { - // this stream doesn't have any data in it - return 0; + // This stream doesn't have any data in it... + return -1; } else { if (this.nextIndexInCurrentBuffer < this.currentBufferLength) { @@ -456,7 +456,7 @@ else if (n < 0) { } int len = (int) n; if (this.currentBuffer == null) { - // this stream doesn't have any data in it + // This stream doesn't have any data in it... return 0; } else { @@ -506,7 +506,7 @@ public void updateMessageDigest(MessageDigest messageDigest) { */ public void updateMessageDigest(MessageDigest messageDigest, int len) { if (this.currentBuffer == null) { - // this stream doesn't have any data in it + // This stream doesn't have any data in it... return; } else if (len == 0) { From 77b8f4d6afcd4b0b737415c8175421ddf15954fe Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 13 Jan 2016 12:46:28 +0100 Subject: [PATCH 029/344] ImportStack extends ArrayDeque instead of Stack and relies on standard contains implementation Issue: SPR-13852 (cherry picked from commit e14c2de) --- .../annotation/ConfigurationClassParser.java | 26 ++++--------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java index bd96da51e5..c0541652b2 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,10 +18,12 @@ import java.io.FileNotFoundException; import java.io.IOException; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.Deque; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; @@ -30,7 +32,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.Stack; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -622,7 +623,7 @@ public SourceClass asSourceClass(String className) throws IOException { @SuppressWarnings("serial") - private static class ImportStack extends Stack implements ImportRegistry { + private static class ImportStack extends ArrayDeque implements ImportRegistry { private final MultiValueMap imports = new LinkedMultiValueMap(); @@ -647,23 +648,6 @@ public AnnotationMetadata getImportingClassFor(String importedClass) { return (!CollectionUtils.isEmpty(list) ? list.get(list.size() - 1) : null); } - /** - * Simplified contains() implementation that tests to see if any {@link ConfigurationClass} - * exists within this stack that has the same name as elem. Elem must be of - * type ConfigurationClass. - */ - @Override - public boolean contains(Object elem) { - ConfigurationClass configClass = (ConfigurationClass) elem; - Comparator comparator = new Comparator() { - @Override - public int compare(ConfigurationClass first, ConfigurationClass second) { - return (first.getMetadata().getClassName().equals(second.getMetadata().getClassName()) ? 0 : 1); - } - }; - return (Collections.binarySearch(this, configClass, comparator) != -1); - } - /** * Given a stack containing (in order) *

    @@ -883,7 +867,7 @@ public String toString() { */ private static class CircularImportProblem extends Problem { - public CircularImportProblem(ConfigurationClass attemptedImport, Stack importStack) { + public CircularImportProblem(ConfigurationClass attemptedImport, Deque importStack) { super(String.format("A circular @Import has been detected: " + "Illegal attempt by @Configuration class '%s' to import class '%s' as '%s' is " + "already present in the current import stack %s", importStack.peek().getSimpleName(), From c1f233c7879af6099f29c656c7510736295e6b2a Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 13 Jan 2016 14:11:13 +0100 Subject: [PATCH 030/344] Shared EntityManager does not insist on actualTransactionActive flag anymore Issue: SPR-13838 (cherry picked from commit 50829c9) --- .../springframework/orm/jpa/SharedEntityManagerCreator.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/SharedEntityManagerCreator.java b/spring-orm/src/main/java/org/springframework/orm/jpa/SharedEntityManagerCreator.java index 3bf9e11a35..da262d026c 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/SharedEntityManagerCreator.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/SharedEntityManagerCreator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -272,7 +272,8 @@ else if (method.getName().equals("unwrap")) { else if (transactionRequiringMethods.contains(method.getName())) { // We need a transactional target now, according to the JPA spec. // Otherwise, the operation would get accepted but remain unflushed... - if (target == null || !TransactionSynchronizationManager.isActualTransactionActive()) { + if (target == null || (!TransactionSynchronizationManager.isActualTransactionActive() && + !target.getTransaction().isActive())) { throw new TransactionRequiredException("No EntityManager with actual transaction available " + "for current thread - cannot reliably process '" + method.getName() + "' call"); } From 4fc6ead5487f1ea30a7ac1c3209f153af102fed6 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 15 Jan 2016 17:03:17 +0100 Subject: [PATCH 031/344] SessionDisconnectEvent actually preserves given session user Issue: SPR-13871 (cherry picked from commit cdc9bf7) --- .../socket/messaging/SessionDisconnectEvent.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/messaging/SessionDisconnectEvent.java b/spring-websocket/src/main/java/org/springframework/web/socket/messaging/SessionDisconnectEvent.java index a4167377c4..ba75574014 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/messaging/SessionDisconnectEvent.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/messaging/SessionDisconnectEvent.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -53,15 +53,24 @@ public SessionDisconnectEvent(Object source, Message message, String ses this(source, message, sessionId, closeStatus, null); } + /** + * Create a new SessionDisconnectEvent. + * @param source the component that published the event (never {@code null}) + * @param message the message + * @param sessionId the disconnect message + * @param closeStatus the status object + * @param user the current session user + */ public SessionDisconnectEvent(Object source, Message message, String sessionId, CloseStatus closeStatus, Principal user) { - super(source, message); - Assert.notNull(sessionId, "'sessionId' must not be null"); + super(source, message, user); + Assert.notNull(sessionId, "Session id must not be null"); this.sessionId = sessionId; this.status = closeStatus; } + /** * Return the session id. */ @@ -76,6 +85,7 @@ public CloseStatus getCloseStatus() { return this.status; } + @Override public String toString() { return "SessionDisconnectEvent[sessionId=" + this.sessionId + ", " + From 14babb78b48d9dcb9716e529b2698dd4d9fc0c5a Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 25 Jan 2016 20:23:25 +0100 Subject: [PATCH 032/344] Clarify environment property precedence rules Issue: SPR-13845 (cherry picked from commit ebad8db) --- src/asciidoc/core-beans.adoc | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/asciidoc/core-beans.adoc b/src/asciidoc/core-beans.adoc index 98b5c7ee17..8003041896 100644 --- a/src/asciidoc/core-beans.adoc +++ b/src/asciidoc/core-beans.adoc @@ -7480,7 +7480,7 @@ is populated with additional default property sources including servlet config a context parameters. {api-spring-framework}/web/portlet/context/StandardPortletEnvironment.html[`StandardPortletEnvironment`] similarly has access to portlet config and portlet context parameters as property sources. Both can optionally enable a {api-spring-framework}/jndi/JndiPropertySource.html[`JndiPropertySource`]. -See Javadoc for details. +See the javadocs for details. ==== Concretely, when using the `StandardEnvironment`, the call to `env.containsProperty("foo")` @@ -7492,7 +7492,16 @@ runtime. The search performed is hierarchical. By default, system properties have precedence over environment variables, so if the `foo` property happens to be set in both places during a call to `env.getProperty("foo")`, the system property value will 'win' and be returned -preferentially over the environment variable. +preferentially over the environment variable. Note that property values will not get merged +but rather completely overridden by a preceding entry. + +For a common `StandardServletEnvironment`, the full hierarchy looks as follows, with the +highest-precedence entries at the top: +* ServletConfig parameters (if applicable, e.g. in case of a `DispatcherServlet` context) +* ServletContext parameters (web.xml context-param entries) +* JNDI environment variables ("java:comp/env/" entries) +* JVM system properties ("-D" command-line arguments) +* JVM system environment (operating system environment variables) ==== Most importantly, the entire mechanism is configurable. Perhaps you have a custom source From d152259673973bda3b6c66facefbea710ce71c5d Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 25 Jan 2016 20:24:26 +0100 Subject: [PATCH 033/344] Clarify component scan include-filter semantics Issue: SPR-13844 (cherry picked from commit 837cb75) --- .../context/annotation/ComponentScan.java | 13 ++++++++----- .../context/config/spring-context-4.2.xsd | 3 +++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ComponentScan.java b/spring-context/src/main/java/org/springframework/context/annotation/ComponentScan.java index a1052fd6dc..f7ad662c42 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ComponentScan.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ComponentScan.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -125,10 +125,13 @@ /** * Specifies which types are eligible for component scanning. - *

    Further narrows the set of candidate components from everything in - * {@link #basePackages} to everything in the base packages that matches - * the given filter or filters. - * @see #resourcePattern + *

    Further narrows the set of candidate components from everything in {@link #basePackages} + * to everything in the base packages that matches the given filter or filters. + *

    Note that these filters will be applied in addition to the default filters, if specified. + * Any type under the specified base packages which matches a given filter will be included, + * even if it does not match the default filters (i.e. is not annotated with {@code @Component}). + * @see #resourcePattern() + * @see #useDefaultFilters() */ Filter[] includeFilters() default {}; diff --git a/spring-context/src/main/resources/org/springframework/context/config/spring-context-4.2.xsd b/spring-context/src/main/resources/org/springframework/context/config/spring-context-4.2.xsd index a70b8097c1..ad9700246e 100644 --- a/spring-context/src/main/resources/org/springframework/context/config/spring-context-4.2.xsd +++ b/spring-context/src/main/resources/org/springframework/context/config/spring-context-4.2.xsd @@ -235,6 +235,9 @@ From 0866aa923adb3a952c63ea9d33266220ccb3dd98 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 26 Jan 2016 20:35:30 +0100 Subject: [PATCH 034/344] LocalSessionFactoryBuilder provides ClassLoader to Hibernate BootstrapServiceRegistry Issue: SPR-13879 --- .../orm/hibernate5/LocalSessionFactoryBuilder.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/LocalSessionFactoryBuilder.java b/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/LocalSessionFactoryBuilder.java index def7931f09..7389409089 100644 --- a/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/LocalSessionFactoryBuilder.java +++ b/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/LocalSessionFactoryBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,6 +30,7 @@ import org.hibernate.HibernateException; import org.hibernate.MappingException; +import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder; import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Environment; @@ -106,6 +107,8 @@ public LocalSessionFactoryBuilder(DataSource dataSource, ClassLoader classLoader * @param resourceLoader the ResourceLoader to load application classes from */ public LocalSessionFactoryBuilder(DataSource dataSource, ResourceLoader resourceLoader) { + super(new BootstrapServiceRegistryBuilder().applyClassLoader(resourceLoader.getClassLoader()).build()); + getProperties().put(Environment.CURRENT_SESSION_CONTEXT_CLASS, SpringSessionContext.class.getName()); if (dataSource != null) { getProperties().put(Environment.DATASOURCE, dataSource); From a58eee6ad1bb64bbdc74a0e3132401e2eb95cd73 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 26 Jan 2016 22:07:27 +0100 Subject: [PATCH 035/344] RequestParamMethodArgumentResolver defensively handles MethodParameter nesting level and java.util.Optional access Issue: SPR-13850 --- .../RequestPartMethodArgumentResolver.java | 22 +++-- ...equestPartMethodArgumentResolverTests.java | 90 +++++++++++-------- 2 files changed, 67 insertions(+), 45 deletions(-) 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 59d59d8f99..ba34c2e455 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-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,6 @@ import java.util.Collection; import java.util.List; import java.util.Optional; - import javax.servlet.http.HttpServletRequest; import javax.servlet.http.Part; @@ -123,24 +122,23 @@ else if ("javax.servlet.http.Part".equals(parameter.getParameterType().getName() } @Override - @UsesJava8 public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest request, WebDataBinderFactory binderFactory) throws Exception { HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class); assertIsMultipartRequest(servletRequest); - MultipartHttpServletRequest multipartRequest = WebUtils.getNativeRequest(servletRequest, MultipartHttpServletRequest.class); + String partName = getPartName(parameter); Class paramType = parameter.getParameterType(); boolean optional = paramType.getName().equals("java.util.Optional"); if (optional) { + parameter = new MethodParameter(parameter); parameter.increaseNestingLevel(); paramType = parameter.getNestedParameterType(); } - String partName = getPartName(parameter); Object arg; if (MultipartFile.class == paramType) { @@ -194,7 +192,7 @@ else if (isPartArray(parameter)) { throw new MissingServletRequestPartException(partName); } if (optional) { - arg = Optional.ofNullable(arg); + arg = OptionalResolver.resolveValue(arg); } return arg; @@ -264,4 +262,16 @@ public static Object resolvePart(HttpServletRequest servletRequest) throws Excep } } + + /** + * Inner class to avoid hard-coded dependency on Java 8 Optional type... + */ + @UsesJava8 + private static class OptionalResolver { + + public static Object resolveValue(Object value) { + return Optional.ofNullable(value); + } + } + } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestPartMethodArgumentResolverTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestPartMethodArgumentResolverTests.java index 5b443ff7fd..d1944b1783 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestPartMethodArgumentResolverTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestPartMethodArgumentResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,6 @@ import javax.validation.Valid; import javax.validation.constraints.NotNull; -import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Test; @@ -157,14 +156,12 @@ public void supportsParameter() { @Test public void resolveMultipartFile() throws Exception { Object actual = resolver.resolveArgument(paramMultipartFile, null, webRequest, null); - assertNotNull(actual); assertSame(multipartFile1, actual); } @Test public void resolveMultipartFileList() throws Exception { Object actual = resolver.resolveArgument(paramMultipartFileList, null, webRequest, null); - assertNotNull(actual); assertTrue(actual instanceof List); assertEquals(Arrays.asList(multipartFile1, multipartFile2), actual); } @@ -175,6 +172,7 @@ public void resolveMultipartFileArray() throws Exception { assertNotNull(actual); assertTrue(actual instanceof MultipartFile[]); MultipartFile[] parts = (MultipartFile[]) actual; + assertEquals(2, parts.length); assertEquals(parts[0], multipartFile1); assertEquals(parts[1], multipartFile2); } @@ -202,7 +200,6 @@ public void resolvePartArgument() throws Exception { webRequest = new ServletWebRequest(request); Object result = resolver.resolveArgument(paramPart, null, webRequest, null); - assertTrue(result instanceof Part); assertEquals("Invalid result", expected, result); } @@ -219,7 +216,6 @@ public void resolvePartListArgument() throws Exception { webRequest = new ServletWebRequest(request); Object result = resolver.resolveArgument(paramPartList, null, webRequest, null); - assertTrue(result instanceof List); assertEquals(Arrays.asList(part1, part2), result); } @@ -236,10 +232,9 @@ public void resolvePartArrayArgument() throws Exception { webRequest = new ServletWebRequest(request); Object result = resolver.resolveArgument(paramPartArray, null, webRequest, null); - assertTrue(result instanceof Part[]); Part[] parts = (Part[]) result; - assertThat(parts, Matchers.arrayWithSize(2)); + assertEquals(2, parts.length); assertEquals(parts[0], part1); assertEquals(parts[1], part2); } @@ -302,8 +297,8 @@ public void isMultipartRequest() throws Exception { @Test // SPR-9079 public void isMultipartRequestPut() throws Exception { this.multipartRequest.setMethod("PUT"); - Object actual = resolver.resolveArgument(paramMultipartFile, null, webRequest, null); - assertSame(multipartFile1, actual); + Object actualValue = resolver.resolveArgument(paramMultipartFile, null, webRequest, null); + assertSame(multipartFile1, actualValue); } @Test @@ -313,10 +308,13 @@ public void resolveOptionalMultipartFileArgument() throws Exception { request.addFile(expected); webRequest = new ServletWebRequest(request); - Object result = resolver.resolveArgument(optionalMultipartFile, null, webRequest, null); + Object actualValue = resolver.resolveArgument(optionalMultipartFile, null, webRequest, null); + assertTrue(actualValue instanceof Optional); + assertEquals("Invalid result", expected, ((Optional) actualValue).get()); - assertTrue(result instanceof Optional); - assertEquals("Invalid result", expected, ((Optional) result).get()); + actualValue = resolver.resolveArgument(optionalMultipartFile, null, webRequest, null); + assertTrue(actualValue instanceof Optional); + assertEquals("Invalid result", expected, ((Optional) actualValue).get()); } @Test @@ -324,10 +322,11 @@ public void resolveOptionalMultipartFileArgumentNotPresent() throws Exception { MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest(); webRequest = new ServletWebRequest(request); - Object result = resolver.resolveArgument(optionalMultipartFile, null, webRequest, null); + Object actualValue = resolver.resolveArgument(optionalMultipartFile, null, webRequest, null); + assertEquals("Invalid argument value", Optional.empty(), actualValue); - assertTrue(result instanceof Optional); - assertFalse("Invalid result", ((Optional) result).isPresent()); + actualValue = resolver.resolveArgument(optionalMultipartFile, null, webRequest, null); + assertEquals("Invalid argument value", Optional.empty(), actualValue); } @Test @@ -339,10 +338,13 @@ public void resolveOptionalPartArgument() throws Exception { request.addPart(expected); webRequest = new ServletWebRequest(request); - Object result = resolver.resolveArgument(optionalPart, null, webRequest, null); + Object actualValue = resolver.resolveArgument(optionalPart, null, webRequest, null); + assertTrue(actualValue instanceof Optional); + assertEquals("Invalid result", expected, ((Optional) actualValue).get()); - assertTrue(result instanceof Optional); - assertEquals("Invalid result", expected, ((Optional) result).get()); + actualValue = resolver.resolveArgument(optionalPart, null, webRequest, null); + assertTrue(actualValue instanceof Optional); + assertEquals("Invalid result", expected, ((Optional) actualValue).get()); } @Test @@ -352,22 +354,26 @@ public void resolveOptionalPartArgumentNotPresent() throws Exception { request.setContentType("multipart/form-data"); webRequest = new ServletWebRequest(request); - Object result = resolver.resolveArgument(optionalPart, null, webRequest, null); + Object actualValue = resolver.resolveArgument(optionalPart, null, webRequest, null); + assertEquals("Invalid argument value", Optional.empty(), actualValue); - assertTrue(result instanceof Optional); - assertFalse("Invalid result", ((Optional) result).isPresent()); + actualValue = resolver.resolveArgument(optionalPart, null, webRequest, null); + assertEquals("Invalid argument value", Optional.empty(), actualValue); } @Test public void resolveOptionalRequestPart() throws Exception { SimpleBean simpleBean = new SimpleBean("foo"); - given(messageConverter.canRead(SimpleBean.class, MediaType.TEXT_PLAIN)).willReturn(true); given(messageConverter.read(eq(SimpleBean.class), isA(HttpInputMessage.class))).willReturn(simpleBean); ModelAndViewContainer mavContainer = new ModelAndViewContainer(); + Object actualValue = resolver.resolveArgument(optionalRequestPart, mavContainer, webRequest, new ValidatingBinderFactory()); + assertEquals("Invalid argument value", Optional.of(simpleBean), actualValue); + assertFalse("The requestHandled flag shouldn't change", mavContainer.isRequestHandled()); + actualValue = resolver.resolveArgument(optionalRequestPart, mavContainer, webRequest, new ValidatingBinderFactory()); assertEquals("Invalid argument value", Optional.of(simpleBean), actualValue); assertFalse("The requestHandled flag shouldn't change", mavContainer.isRequestHandled()); } @@ -378,8 +384,12 @@ public void resolveOptionalRequestPartNotPresent() throws Exception { given(messageConverter.read(eq(SimpleBean.class), isA(RequestPartServletServerHttpRequest.class))).willReturn(null); ModelAndViewContainer mavContainer = new ModelAndViewContainer(); + Object actualValue = resolver.resolveArgument(optionalRequestPart, mavContainer, webRequest, new ValidatingBinderFactory()); + assertEquals("Invalid argument value", Optional.empty(), actualValue); + assertFalse("The requestHandled flag shouldn't change", mavContainer.isRequestHandled()); + actualValue = resolver.resolveArgument(optionalRequestPart, mavContainer, webRequest, new ValidatingBinderFactory()); assertEquals("Invalid argument value", Optional.empty(), actualValue); assertFalse("The requestHandled flag shouldn't change", mavContainer.isRequestHandled()); } @@ -390,8 +400,8 @@ private void testResolveArgument(SimpleBean argValue, MethodParameter parameter) given(messageConverter.read(eq(SimpleBean.class), isA(HttpInputMessage.class))).willReturn(argValue); ModelAndViewContainer mavContainer = new ModelAndViewContainer(); - Object actualValue = resolver.resolveArgument(parameter, mavContainer, webRequest, new ValidatingBinderFactory()); + Object actualValue = resolver.resolveArgument(parameter, mavContainer, webRequest, new ValidatingBinderFactory()); assertEquals("Invalid argument value", argValue, actualValue); assertFalse("The requestHandled flag shouldn't change", mavContainer.isRequestHandled()); } @@ -425,22 +435,24 @@ public WebDataBinder createBinder(NativeWebRequest webRequest, Object target, St } } + @SuppressWarnings("unused") - public void handle(@RequestPart SimpleBean requestPart, - @RequestPart(value="requestPart", required=false) SimpleBean namedRequestPart, - @Valid @RequestPart("requestPart") SimpleBean validRequestPart, - @RequestPart("requestPart") MultipartFile multipartFile, - @RequestPart("requestPart") List multipartFileList, - @RequestPart("requestPart") MultipartFile[] multipartFileArray, - int i, - MultipartFile multipartFileNotAnnot, - Part part, - @RequestPart("part") List partList, - @RequestPart("part") Part[] partArray, - @RequestParam MultipartFile requestParamAnnot, - Optional optionalMultipartFile, - Optional optionalPart, - @RequestPart("requestPart") Optional optionalRequestPart) { + public void handle( + @RequestPart SimpleBean requestPart, + @RequestPart(value="requestPart", required=false) SimpleBean namedRequestPart, + @Valid @RequestPart("requestPart") SimpleBean validRequestPart, + @RequestPart("requestPart") MultipartFile multipartFile, + @RequestPart("requestPart") List multipartFileList, + @RequestPart("requestPart") MultipartFile[] multipartFileArray, + int i, + MultipartFile multipartFileNotAnnot, + Part part, + @RequestPart("requestPart") List partList, + @RequestPart("requestPart") Part[] partArray, + @RequestParam MultipartFile requestParamAnnot, + Optional optionalMultipartFile, + Optional optionalPart, + @RequestPart("requestPart") Optional optionalRequestPart) { } } From 30ef893c286cb5369111c7870d61277639945bb5 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 26 Jan 2016 22:23:56 +0100 Subject: [PATCH 036/344] Polishing --- .../UnsatisfiedDependencyException.java | 6 +- .../factory/config/DependencyDescriptor.java | 6 +- .../EnableMBeanExportConfigurationTests.java | 8 +- .../support/EnumToStringConverter.java | 11 +- .../env/PropertySourcesPropertyResolver.java | 6 +- ...inationVariableMethodArgumentResolver.java | 13 +- .../xml/MarshallingHttpMessageConverter.java | 9 +- .../web/bind/annotation/RequestPart.java | 3 +- .../RequestParamMethodArgumentResolver.java | 5 +- .../MissingServletRequestPartException.java | 19 +-- .../RequestPartServletServerHttpRequest.java | 7 +- ...questParamMethodArgumentResolverTests.java | 44 ++---- .../handler/AbstractHandlerMethodMapping.java | 125 +++++++++--------- .../MatrixVariableMethodArgumentResolver.java | 17 ++- .../PathVariableMethodArgumentResolver.java | 30 ++--- .../resource/VersionResourceResolver.java | 3 +- .../servlet/resource/VersionedResource.java | 5 +- .../DefaultHandlerExceptionResolverTests.java | 11 +- 18 files changed, 161 insertions(+), 167 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/UnsatisfiedDependencyException.java b/spring-beans/src/main/java/org/springframework/beans/factory/UnsatisfiedDependencyException.java index f539e70180..9ec35a7e63 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/UnsatisfiedDependencyException.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/UnsatisfiedDependencyException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -56,7 +56,7 @@ public UnsatisfiedDependencyException( public UnsatisfiedDependencyException( String resourceDescription, String beanName, String propertyName, BeansException ex) { - this(resourceDescription, beanName, propertyName, (ex != null ? ": " + ex.getMessage() : "")); + this(resourceDescription, beanName, propertyName, (ex != null ? ex.getMessage() : "")); initCause(ex); } @@ -88,7 +88,7 @@ public UnsatisfiedDependencyException( public UnsatisfiedDependencyException( String resourceDescription, String beanName, int ctorArgIndex, Class ctorArgType, BeansException ex) { - this(resourceDescription, beanName, ctorArgIndex, ctorArgType, (ex != null ? ": " + ex.getMessage() : "")); + this(resourceDescription, beanName, ctorArgIndex, ctorArgType, (ex != null ? ex.getMessage() : "")); initCause(ex); } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java index fd38f364cc..88642eb493 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -196,6 +196,7 @@ public void increaseNestingLevel() { * Optionally set the concrete class that contains this dependency. * This may differ from the class that declares the parameter/field in that * it may be a subclass thereof, potentially substituting type variables. + * @since 4.0 */ public void setContainingClass(Class containingClass) { this.containingClass = containingClass; @@ -206,6 +207,7 @@ public void setContainingClass(Class containingClass) { /** * Build a ResolvableType object for the wrapped parameter/field. + * @since 4.0 */ public ResolvableType getResolvableType() { return (this.field != null ? ResolvableType.forField(this.field, this.nestingLevel, this.containingClass) : @@ -217,6 +219,7 @@ public ResolvableType getResolvableType() { *

    This is {@code false} by default but may be overridden to return {@code true} in order * to suggest to a {@link org.springframework.beans.factory.support.AutowireCandidateResolver} * that a fallback match is acceptable as well. + * @since 4.0 */ public boolean fallbackMatchAllowed() { return false; @@ -224,6 +227,7 @@ public boolean fallbackMatchAllowed() { /** * Return a variant of this descriptor that is intended for a fallback match. + * @since 4.0 * @see #fallbackMatchAllowed() */ public DependencyDescriptor forFallbackMatch() { diff --git a/spring-context/src/test/java/org/springframework/jmx/export/annotation/EnableMBeanExportConfigurationTests.java b/spring-context/src/test/java/org/springframework/jmx/export/annotation/EnableMBeanExportConfigurationTests.java index 6ad47d7eb9..caad398299 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/annotation/EnableMBeanExportConfigurationTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/annotation/EnableMBeanExportConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -73,7 +73,8 @@ public void testOnlyTargetClassIsExposed() throws Exception { ObjectName oname = ObjectNameManager.getInstance("bean:name=testBean4"); assertNotNull(server.getObjectInstance(oname)); assertEquals("TEST", server.getAttribute(oname, "Name")); - } finally { + } + finally { ctx.close(); } } @@ -142,7 +143,8 @@ public void testComponentScan() throws Exception { assertNotNull(server.getObjectInstance(oname)); String name = (String) server.getAttribute(oname, "Name"); assertNull(name); - } finally { + } + finally { ctx.close(); } } diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/EnumToStringConverter.java b/spring-core/src/main/java/org/springframework/core/convert/support/EnumToStringConverter.java index bfd1762d96..02a5f19f29 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/support/EnumToStringConverter.java +++ b/spring-core/src/main/java/org/springframework/core/convert/support/EnumToStringConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,8 +23,9 @@ import org.springframework.util.ClassUtils; /** - * Calls {@link Enum#name()} to convert a source Enum to a String. This converter will - * not match enums with interfaces that can be converterd. + * Calls {@link Enum#name()} to convert a source Enum to a String. + * This converter will not match enums with interfaces that can be converted. + * * @author Keith Donald * @author Phillip Webb * @since 3.0 @@ -33,14 +34,16 @@ final class EnumToStringConverter implements Converter, String>, Conditi private final ConversionService conversionService; + public EnumToStringConverter(ConversionService conversionService) { this.conversionService = conversionService; } + @Override public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { for (Class interfaceType : ClassUtils.getAllInterfacesForClass(sourceType.getType())) { - if (conversionService.canConvert(TypeDescriptor.valueOf(interfaceType), targetType)) { + if (this.conversionService.canConvert(TypeDescriptor.valueOf(interfaceType), targetType)) { return false; } } diff --git a/spring-core/src/main/java/org/springframework/core/env/PropertySourcesPropertyResolver.java b/spring-core/src/main/java/org/springframework/core/env/PropertySourcesPropertyResolver.java index 9e33628448..edf0b72965 100644 --- a/spring-core/src/main/java/org/springframework/core/env/PropertySourcesPropertyResolver.java +++ b/spring-core/src/main/java/org/springframework/core/env/PropertySourcesPropertyResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -80,8 +80,8 @@ protected T getProperty(String key, Class targetValueType, boolean resolv if (debugEnabled) { logger.debug(String.format("Searching for key '%s' in [%s]", key, propertySource.getName())); } - Object value; - if ((value = propertySource.getProperty(key)) != null) { + Object value = propertySource.getProperty(key); + if (value != null) { Class valueType = value.getClass(); if (resolveNestedPlaceholders && value instanceof String) { value = resolveNestedPlaceholders((String) value); diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/DestinationVariableMethodArgumentResolver.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/DestinationVariableMethodArgumentResolver.java index 9ebd81363a..76e6a8e1bf 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/DestinationVariableMethodArgumentResolver.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/DestinationVariableMethodArgumentResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,6 +42,7 @@ public DestinationVariableMethodArgumentResolver(ConversionService cs) { super(cs, null); } + @Override public boolean supportsParameter(MethodParameter parameter) { return parameter.hasParameterAnnotation(DestinationVariable.class); @@ -58,10 +59,9 @@ protected Object resolveArgumentInternal(MethodParameter parameter, Message m throws Exception { @SuppressWarnings("unchecked") - Map vars = (Map) message.getHeaders().get( - DESTINATION_TEMPLATE_VARIABLES_HEADER); - - return (vars != null) ? vars.get(name) : null; + Map vars = + (Map) message.getHeaders().get(DESTINATION_TEMPLATE_VARIABLES_HEADER); + return (vars != null ? vars.get(name) : null); } @Override @@ -77,4 +77,5 @@ private DestinationVariableNamedValueInfo(DestinationVariable annotation) { super(annotation.value(), true, ValueConstants.DEFAULT_NONE); } } -} \ No newline at end of file + +} diff --git a/spring-web/src/main/java/org/springframework/http/converter/xml/MarshallingHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/xml/MarshallingHttpMessageConverter.java index c0c1daa273..797732eff0 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/xml/MarshallingHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/xml/MarshallingHttpMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -55,7 +55,7 @@ public class MarshallingHttpMessageConverter extends AbstractXmlHttpMessageConve /** * Construct a new {@code MarshallingHttpMessageConverter} with no {@link Marshaller} or * {@link Unmarshaller} set. The Marshaller and Unmarshaller must be set after construction - * by invoking {@link #setMarshaller(Marshaller)} and {@link #setUnmarshaller(Unmarshaller)} . + * by invoking {@link #setMarshaller(Marshaller)} and {@link #setUnmarshaller(Unmarshaller)}. */ public MarshallingHttpMessageConverter() { } @@ -104,14 +104,15 @@ public void setUnmarshaller(Unmarshaller unmarshaller) { this.unmarshaller = unmarshaller; } + @Override public boolean canRead(Class clazz, MediaType mediaType) { - return canRead(mediaType) && (this.unmarshaller != null) && this.unmarshaller.supports(clazz); + return (canRead(mediaType) && this.unmarshaller != null && this.unmarshaller.supports(clazz)); } @Override public boolean canWrite(Class clazz, MediaType mediaType) { - return canWrite(mediaType) && (this.marshaller != null) && this.marshaller.supports(clazz); + return (canWrite(mediaType) && this.marshaller != null && this.marshaller.supports(clazz)); } @Override 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 b8bd5c28fa..8f27d5c86f 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-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -55,7 +55,6 @@ * @author Arjen Poutsma * @author Sam Brannen * @since 3.1 - * * @see RequestParam * @see org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter */ 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 e2671f3b08..aba8999a18 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-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -262,7 +262,8 @@ public void contributeMethodArgument(MethodParameter parameter, Object value, } RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class); - String name = (requestParam == null || StringUtils.isEmpty(requestParam.name()) ? parameter.getParameterName() : requestParam.name()); + String name = (requestParam == null || StringUtils.isEmpty(requestParam.name()) ? + parameter.getParameterName() : requestParam.name()); if (value == null) { builder.queryParam(name); diff --git a/spring-web/src/main/java/org/springframework/web/multipart/support/MissingServletRequestPartException.java b/spring-web/src/main/java/org/springframework/web/multipart/support/MissingServletRequestPartException.java index 422517bd63..905525b039 100644 --- a/spring-web/src/main/java/org/springframework/web/multipart/support/MissingServletRequestPartException.java +++ b/spring-web/src/main/java/org/springframework/web/multipart/support/MissingServletRequestPartException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,27 +24,28 @@ * Raised when the part of a "multipart/form-data" request identified by its * name cannot be found. * - *

    This may be because the request is not a multipart/form-data - * - * either because the part is not present in the request, or - * because the web application is not configured correctly for processing - * multipart requests -- e.g. no {@link MultipartResolver}. + *

    This may be because the request is not a multipart/form-data request, + * because the part is not present in the request, or because the web + * application is not configured correctly for processing multipart requests, + * e.g. no {@link MultipartResolver}. * * @author Rossen Stoyanchev * @since 3.1 */ +@SuppressWarnings("serial") public class MissingServletRequestPartException extends ServletException { - private static final long serialVersionUID = -1255077391966870705L; - private final String partName; + public MissingServletRequestPartException(String partName) { - super("Required request part '" + partName + "' is not present."); + super("Required request part '" + partName + "' is not present"); this.partName = partName; } + public String getRequestPartName() { return this.partName; } + } diff --git a/spring-web/src/main/java/org/springframework/web/multipart/support/RequestPartServletServerHttpRequest.java b/spring-web/src/main/java/org/springframework/web/multipart/support/RequestPartServletServerHttpRequest.java index b26637e632..78eab9180e 100644 --- a/spring-web/src/main/java/org/springframework/web/multipart/support/RequestPartServletServerHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/web/multipart/support/RequestPartServletServerHttpRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,7 +33,6 @@ import org.springframework.web.multipart.MultipartResolver; import org.springframework.web.util.WebUtils; - /** * {@link ServerHttpRequest} implementation that accesses one part of a multipart * request. If using {@link MultipartResolver} configuration the part is accessed @@ -54,8 +53,8 @@ public class RequestPartServletServerHttpRequest extends ServletServerHttpReques /** - * Create a new instance. - * @param request the current request + * Create a new {@code RequestPartServletServerHttpRequest} instance. + * @param request the current servlet request * @param partName the name of the part to adapt to the {@link ServerHttpRequest} contract * @throws MissingServletRequestPartException if the request part cannot be found * @throws IllegalArgumentException if MultipartHttpServletRequest cannot be initialized 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 4617715a8c..00b61b5f2b 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-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -87,10 +87,10 @@ public class RequestParamMethodArgumentResolverTests { private MockHttpServletRequest request; + @Before public void setUp() throws Exception { resolver = new RequestParamMethodArgumentResolver(null, true); - ParameterNameDiscoverer paramNameDiscoverer = new LocalVariableTableParameterNameDiscoverer(); Method method = getClass().getMethod("params", String.class, String[].class, @@ -126,6 +126,7 @@ public void setUp() throws Exception { webRequest = new ServletWebRequest(request, new MockHttpServletResponse()); } + @Test public void supportsParameter() { resolver = new RequestParamMethodArgumentResolver(null, true); @@ -154,18 +155,16 @@ public void resolveString() throws Exception { request.addParameter("name", expected); Object result = resolver.resolveArgument(paramNamedDefaultValueString, null, webRequest, null); - assertTrue(result instanceof String); assertEquals("Invalid result", expected, result); } @Test public void resolveStringArray() throws Exception { - String[] expected = new String[]{"foo", "bar"}; + String[] expected = new String[] {"foo", "bar"}; request.addParameter("name", expected); Object result = resolver.resolveArgument(paramNamedStringArray, null, webRequest, null); - assertTrue(result instanceof String[]); assertArrayEquals("Invalid result", expected, (String[]) result); } @@ -178,7 +177,6 @@ public void resolveMultipartFile() throws Exception { webRequest = new ServletWebRequest(request); Object result = resolver.resolveArgument(paramMultipartFile, null, webRequest, null); - assertTrue(result instanceof MultipartFile); assertEquals("Invalid result", expected, result); } @@ -193,7 +191,6 @@ public void resolveMultipartFileList() throws Exception { webRequest = new ServletWebRequest(request); Object result = resolver.resolveArgument(paramMultipartFileList, null, webRequest, null); - assertTrue(result instanceof List); assertEquals(Arrays.asList(expected1, expected2), result); } @@ -208,9 +205,9 @@ public void resolveMultipartFileArray() throws Exception { webRequest = new ServletWebRequest(request); Object result = resolver.resolveArgument(paramMultipartFileArray, null, webRequest, null); - assertTrue(result instanceof MultipartFile[]); MultipartFile[] parts = (MultipartFile[]) result; + assertEquals(2, parts.length); assertEquals(parts[0], expected1); assertEquals(parts[1], expected2); } @@ -242,7 +239,6 @@ public void resolvePartList() throws Exception { webRequest = new ServletWebRequest(request); Object result = resolver.resolveArgument(paramPartList, null, webRequest, null); - assertTrue(result instanceof List); assertEquals(Arrays.asList(expected1, expected2), result); } @@ -259,9 +255,9 @@ public void resolvePartArray() throws Exception { webRequest = new ServletWebRequest(request); Object result = resolver.resolveArgument(paramPartArray, null, webRequest, null); - assertTrue(result instanceof Part[]); Part[] parts = (Part[]) result; + assertEquals(2, parts.length); assertEquals(parts[0], expected1); assertEquals(parts[1], expected2); } @@ -274,7 +270,6 @@ public void resolveMultipartFileNotAnnot() throws Exception { webRequest = new ServletWebRequest(request); Object result = resolver.resolveArgument(paramMultipartFileNotAnnot, null, webRequest, null); - assertTrue(result instanceof MultipartFile); assertEquals("Invalid result", expected, result); } @@ -289,7 +284,6 @@ public void resolveMultipartFileListNotAnnotated() throws Exception { webRequest = new ServletWebRequest(request); Object result = resolver.resolveArgument(paramMultipartFileListNotAnnot, null, webRequest, null); - assertTrue(result instanceof List); assertEquals(Arrays.asList(expected1, expected2), result); } @@ -300,9 +294,7 @@ public void isMultipartRequest() throws Exception { fail("Expected exception: request is not a multipart request"); } - // SPR-9079 - - @Test + @Test // SPR-9079 public void isMultipartRequestHttpPut() throws Exception { MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest(); MultipartFile expected = new MockMultipartFile("multipartFileList", "Hello World".getBytes()); @@ -311,7 +303,6 @@ public void isMultipartRequestHttpPut() throws Exception { webRequest = new ServletWebRequest(request); Object actual = resolver.resolveArgument(paramMultipartFileListNotAnnot, null, webRequest, null); - assertTrue(actual instanceof List); assertEquals(expected, ((List) actual).get(0)); } @@ -334,7 +325,6 @@ public void resolvePartNotAnnot() throws Exception { webRequest = new ServletWebRequest(request); Object result = resolver.resolveArgument(paramPartNotAnnot, null, webRequest, null); - assertTrue(result instanceof Part); assertEquals("Invalid result", expected, result); } @@ -342,7 +332,6 @@ public void resolvePartNotAnnot() throws Exception { @Test public void resolveDefaultValue() throws Exception { Object result = resolver.resolveArgument(paramNamedDefaultValueString, null, webRequest, null); - assertTrue(result instanceof String); assertEquals("Invalid result", "bar", result); } @@ -353,11 +342,8 @@ public void missingRequestParam() throws Exception { fail("Expected exception"); } - // SPR-10578 - - @Test + @Test // SPR-10578 public void missingRequestParamEmptyValueConvertedToNull() throws Exception { - WebDataBinder binder = new WebRequestDataBinder(null); binder.registerCustomEditor(String.class, new StringTrimmerEditor(true)); @@ -367,13 +353,11 @@ public void missingRequestParamEmptyValueConvertedToNull() throws Exception { this.request.addParameter("stringNotAnnot", ""); Object arg = resolver.resolveArgument(paramStringNotAnnot, null, webRequest, binderFactory); - assertNull(arg); } @Test public void missingRequestParamEmptyValueNotRequired() throws Exception { - WebDataBinder binder = new WebRequestDataBinder(null); binder.registerCustomEditor(String.class, new StringTrimmerEditor(true)); @@ -383,7 +367,6 @@ public void missingRequestParamEmptyValueNotRequired() throws Exception { this.request.addParameter("name", ""); Object arg = resolver.resolveArgument(paramNotRequired, null, webRequest, binderFactory); - assertNull(arg); } @@ -396,17 +379,13 @@ public void resolveSimpleTypeParam() throws Exception { assertEquals("plainValue", result); } - // SPR-8561 - - @Test + @Test // SPR-8561 public void resolveSimpleTypeParamToNull() throws Exception { Object result = resolver.resolveArgument(paramStringNotAnnot, null, webRequest, null); assertNull(result); } - // SPR-10180 - - @Test + @Test // SPR-10180 public void resolveEmptyValueToDefault() throws Exception { this.request.addParameter("name", ""); Object result = resolver.resolveArgument(paramNamedDefaultValueString, null, webRequest, null); @@ -429,13 +408,12 @@ public void resolveEmptyValueRequiredWithoutDefault() throws Exception { @Test @SuppressWarnings("rawtypes") - public void resolveOptional() throws Exception { + public void resolveOptionalParamValue() throws Exception { ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer(); initializer.setConversionService(new DefaultConversionService()); WebDataBinderFactory binderFactory = new DefaultDataBinderFactory(initializer); Object result = resolver.resolveArgument(paramOptional, null, webRequest, binderFactory); - assertEquals(Optional.class, result.getClass()); assertEquals(Optional.empty(), result); this.request.addParameter("name", "123"); 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 99386e074a..d84d1601f9 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -152,6 +152,28 @@ MappingRegistry getMappingRegistry() { return this.mappingRegistry; } + /** + * Register the given mapping. + *

    This method may be invoked at runtime after initialization has completed. + * @param mapping the mapping for the handler method + * @param handler the handler + * @param method the method + */ + public void registerMapping(T mapping, Object handler, Method method) { + this.mappingRegistry.register(mapping, handler, method); + } + + /** + * Un-register the given mapping. + *

    This method may be invoked at runtime after initialization has completed. + * @param mapping the mapping to unregister + */ + public void unregisterMapping(T mapping) { + this.mappingRegistry.unregister(mapping); + } + + + // Handler method detection /** * Detects handler methods at initialization. @@ -195,13 +217,6 @@ protected void initHandlerMethods() { handlerMethodsInitialized(getHandlerMethods()); } - /** - * Whether the given type is a handler with handler methods. - * @param beanType the type of the bean being checked - * @return "true" if this a handler type, "false" otherwise. - */ - protected abstract boolean isHandler(Class beanType); - /** * Look for handler methods in a handler. * @param handler the bean name of a handler or a handler instance @@ -227,16 +242,6 @@ public T inspect(Method method) { } } - /** - * Provide the mapping for a handler method. A method for which no - * mapping can be provided is not a handler method. - * @param method the method to provide a mapping for - * @param handlerType the handler type, possibly a sub-type of the method's - * declaring class - * @return the mapping, or {@code null} if the method is not mapped - */ - protected abstract T getMappingForMethod(Method method, Class handlerType); - /** * Register a handler method and its unique mapping. Invoked at startup for * each detected handler method. @@ -269,11 +274,6 @@ protected HandlerMethod createHandlerMethod(Object handler, Method method) { return handlerMethod; } - /** - * Extract and return the URL paths contained in a mapping. - */ - protected abstract Set getMappingPathPatterns(T mapping); - /** * Extract and return the CORS configuration for the mapping. */ @@ -288,25 +288,8 @@ protected CorsConfiguration initCorsConfiguration(Object handler, Method method, protected void handlerMethodsInitialized(Map handlerMethods) { } - /** - * Register the given mapping. - *

    This method may be invoked at runtime after initialization has completed. - * @param mapping the mapping for the handler method - * @param handler the handler - * @param method the method - */ - public void registerMapping(T mapping, Object handler, Method method) { - this.mappingRegistry.register(mapping, handler, method); - } - /** - * Un-register the given mapping. - *

    This method may be invoked at runtime after initialization has completed. - * @param mapping the mapping to unregister - */ - public void unregisterMapping(T mapping) { - this.mappingRegistry.unregister(mapping); - } + // Handler method lookup /** * Look up a handler method for the given request. @@ -392,23 +375,6 @@ private void addMatchingMappings(Collection mappings, List matches, Ht } } - /** - * Check if a mapping matches the current request and return a (potentially - * new) mapping with conditions relevant to the current request. - * @param mapping the mapping to get a match for - * @param request the current HTTP servlet request - * @return the match, or {@code null} if the mapping doesn't match - */ - protected abstract T getMatchingMapping(T mapping, HttpServletRequest request); - - /** - * Return a comparator for sorting matching mappings. - * The returned comparator should sort 'better' matches higher. - * @param request the current request - * @return the comparator (never {@code null}) - */ - protected abstract Comparator getMappingComparator(HttpServletRequest request); - /** * Invoked when a matching mapping is found. * @param mapping the matching mapping @@ -449,6 +415,48 @@ protected CorsConfiguration getCorsConfiguration(Object handler, HttpServletRequ } + // Abstract template methods + + /** + * Whether the given type is a handler with handler methods. + * @param beanType the type of the bean being checked + * @return "true" if this a handler type, "false" otherwise. + */ + protected abstract boolean isHandler(Class beanType); + + /** + * Provide the mapping for a handler method. A method for which no + * mapping can be provided is not a handler method. + * @param method the method to provide a mapping for + * @param handlerType the handler type, possibly a sub-type of the method's + * declaring class + * @return the mapping, or {@code null} if the method is not mapped + */ + protected abstract T getMappingForMethod(Method method, Class handlerType); + + /** + * Extract and return the URL paths contained in a mapping. + */ + protected abstract Set getMappingPathPatterns(T mapping); + + /** + * Check if a mapping matches the current request and return a (potentially + * new) mapping with conditions relevant to the current request. + * @param mapping the mapping to get a match for + * @param request the current HTTP servlet request + * @return the match, or {@code null} if the mapping doesn't match + */ + protected abstract T getMatchingMapping(T mapping, HttpServletRequest request); + + /** + * Return a comparator for sorting matching mappings. + * The returned comparator should sort 'better' matches higher. + * @param request the current request + * @return the comparator (never {@code null}) + */ + protected abstract Comparator getMappingComparator(HttpServletRequest request); + + /** * A registry that maintains all mappings to handler methods, exposing methods * to perform lookups and providing concurrent access. @@ -516,7 +524,6 @@ public void releaseReadLock() { this.readWriteLock.readLock().unlock(); } - public void register(T mapping, Object handler, Method method) { this.readWriteLock.writeLock().lock(); try { diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MatrixVariableMethodArgumentResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MatrixVariableMethodArgumentResolver.java index 31f44a6e64..6e9711767b 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MatrixVariableMethodArgumentResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MatrixVariableMethodArgumentResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -48,6 +48,7 @@ public MatrixVariableMethodArgumentResolver() { super(null); } + @Override public boolean supportsParameter(MethodParameter parameter) { if (!parameter.hasParameterAnnotation(MatrixVariable.class)) { @@ -67,13 +68,10 @@ protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) { } @Override + @SuppressWarnings("unchecked") protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception { - - @SuppressWarnings("unchecked") - Map> pathParameters = - (Map>) request.getAttribute( - HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST); - + Map> pathParameters = (Map>) + request.getAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST); if (CollectionUtils.isEmpty(pathParameters)) { return null; } @@ -95,7 +93,7 @@ protected Object resolveName(String name, MethodParameter parameter, NativeWebRe String paramType = parameter.getParameterType().getName(); throw new ServletRequestBindingException( "Found more than one match for URI path parameter '" + name + - "' for parameter type [" + paramType + "]. Use pathVar attribute to disambiguate."); + "' for parameter type [" + paramType + "]. Use 'pathVar' attribute to disambiguate."); } paramValues.addAll(params.get(name)); found = true; @@ -127,4 +125,5 @@ private MatrixVariableNamedValueInfo(MatrixVariable annotation) { super(annotation.name(), annotation.required(), annotation.defaultValue()); } } -} \ No newline at end of file + +} diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/PathVariableMethodArgumentResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/PathVariableMethodArgumentResolver.java index ec3c7e2431..0475e03b6d 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/PathVariableMethodArgumentResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/PathVariableMethodArgumentResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,7 +33,6 @@ import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver; -import org.springframework.web.method.annotation.RequestParamMapMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.method.support.UriComponentsContributor; import org.springframework.web.servlet.HandlerMapping; @@ -43,22 +42,18 @@ /** * Resolves method arguments annotated with an @{@link PathVariable}. * - *

    An @{@link PathVariable} is a named value that gets resolved from a URI - * template variable. It is always required and does not have a default value - * to fall back on. See the base class + *

    An @{@link PathVariable} is a named value that gets resolved from a URI template variable. + * It is always required and does not have a default value to fall back on. See the base class * {@link org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver} * for more information on how named values are processed. * - *

    If the method parameter type is {@link Map}, the name specified in the - * annotation is used to resolve the URI variable String value. The value is - * then converted to a {@link Map} via type conversion assuming a suitable - * {@link Converter} or {@link PropertyEditor} has been registered. - * Or if the annotation does not specify name the - * {@link RequestParamMapMethodArgumentResolver} is used instead to provide - * access to all URI variables in a map. + *

    If the method parameter type is {@link Map}, the name specified in the annotation is used + * to resolve the URI variable String value. The value is then converted to a {@link Map} via + * type conversion, assuming a suitable {@link Converter} or {@link PropertyEditor} has been + * registered. * - *

    A {@link WebDataBinder} is invoked to apply type conversion to resolved - * path variable values that don't yet match the method parameter type. + *

    A {@link WebDataBinder} is invoked to apply type conversion to resolved path variable + * values that don't yet match the method parameter type. * * @author Rossen Stoyanchev * @author Arjen Poutsma @@ -95,10 +90,9 @@ protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) { @Override @SuppressWarnings("unchecked") protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception { - Map uriTemplateVars = - (Map) request.getAttribute( - HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST); - return (uriTemplateVars != null) ? uriTemplateVars.get(name) : null; + Map uriTemplateVars = (Map) request.getAttribute( + HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST); + return (uriTemplateVars != null ? uriTemplateVars.get(name) : null); } @Override diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionResourceResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionResourceResolver.java index 1e35c830d6..7ccc938ced 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionResourceResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionResourceResolver.java @@ -28,7 +28,6 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; - import javax.servlet.http.HttpServletRequest; import org.springframework.core.io.AbstractResource; @@ -238,6 +237,7 @@ protected VersionStrategy getStrategyForPath(String requestPath) { return null; } + private class FileNameVersionedResource extends AbstractResource implements VersionedResource { private final Resource original; @@ -313,7 +313,6 @@ public InputStream getInputStream() throws IOException { public String getVersion() { return this.version; } - } } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionedResource.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionedResource.java index 8960a54ef6..c780df8c89 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionedResource.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionedResource.java @@ -19,8 +19,8 @@ import org.springframework.core.io.Resource; /** - * Interface for a resource descriptor that describes its version - * with a version string that can be derived from its content and/or metadata. + * Interface for a resource descriptor that describes its version with a + * version string that can be derived from its content and/or metadata. * * @author Brian Clozel * @since 4.2.5 @@ -29,4 +29,5 @@ public interface VersionedResource extends Resource { String getVersion(); + } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolverTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolverTests.java index f7c35c2aee..21bf3834bf 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolverTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/support/DefaultHandlerExceptionResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,7 +47,9 @@ import static org.junit.Assert.*; -/** @author Arjen Poutsma */ +/** + * @author Arjen Poutsma + */ public class DefaultHandlerExceptionResolverTests { private DefaultHandlerExceptionResolver exceptionResolver; @@ -174,7 +176,9 @@ public void handleMissingServletRequestPartException() throws Exception { assertNotNull("No ModelAndView returned", mav); assertTrue("No Empty ModelAndView returned", mav.isEmpty()); assertEquals("Invalid status code", 400, response.getStatus()); - assertEquals("Required request part 'name' is not present.", response.getErrorMessage()); + assertTrue(response.getErrorMessage().contains("request part")); + assertTrue(response.getErrorMessage().contains("name")); + assertTrue(response.getErrorMessage().contains("not present")); } @Test @@ -211,6 +215,7 @@ public void handleConversionNotSupportedException() throws Exception { assertSame(ex, request.getAttribute("javax.servlet.error.exception")); } + @SuppressWarnings("unused") public void handle(String arg) { } From bb12e0ea70dd818f836c9861fcd8546525396064 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 26 Jan 2016 22:25:22 +0100 Subject: [PATCH 037/344] Latest dependency updates (AspectJ 1.8.8, Hibernate ORM 5.0.7, Jackson 2.6.5, Jetty 9.3.7, Undertow 1.3.14, SLF4J 1.7.14) --- build.gradle | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index c2e847ef6a..cd5f401c7f 100644 --- a/build.gradle +++ b/build.gradle @@ -27,7 +27,7 @@ configure(allprojects) { project -> group = "org.springframework" version = qualifyVersionIfNecessary(version) - ext.aspectjVersion = "1.8.7" + ext.aspectjVersion = "1.8.8" ext.eclipselinkVersion = "2.4.2" ext.ehcacheVersion = "2.10.1" ext.ehcachejcacheVersion = "1.0.1" @@ -41,17 +41,17 @@ configure(allprojects) { project -> ext.hamcrestVersion = "1.3" ext.hibernate3Version = "3.6.10.Final" ext.hibernate4Version = "4.3.11.Final" - ext.hibernate5Version = "5.0.6.Final" + ext.hibernate5Version = "5.0.7.Final" ext.hibval4Version = "4.3.2.Final" ext.hibval5Version = "5.2.2.Final" ext.hsqldbVersion = "2.3.3" ext.htmlunitVersion = "2.19" ext.httpasyncVersion = "4.1.1" ext.httpclientVersion = "4.5.1" - ext.jackson2Version = "2.6.4" + ext.jackson2Version = "2.6.5" ext.jasperreportsVersion = "6.2.0" ext.javamailVersion = "1.5.5" - ext.jettyVersion = "9.3.6.v20151106" + ext.jettyVersion = "9.3.7.v20160115" ext.jodaVersion = "2.9.1" ext.jrubyVersion = "1.7.23" // JRuby 9000 only supported through JSR-223 (StandardScriptFactory) ext.jsonassertVersion = "1.2.3" @@ -66,7 +66,7 @@ configure(allprojects) { project -> ext.reactorVersion = "2.0.7.RELEASE" ext.romeVersion = "1.5.1" ext.seleniumVersion = "2.48.2" - ext.slf4jVersion = "1.7.13" + ext.slf4jVersion = "1.7.14" ext.snakeyamlVersion = "1.16" ext.snifferVersion = "1.14" ext.testngVersion = "6.9.10" @@ -74,7 +74,7 @@ configure(allprojects) { project -> ext.tiles3Version = "3.0.5" ext.tomcatVersion = "8.0.30" ext.tyrusVersion = "1.3.5" // constrained by WebLogic 12.1.3 support - ext.undertowVersion = "1.3.11.Final" + ext.undertowVersion = "1.3.14.Final" ext.woodstoxVersion = "5.0.1" ext.xmlunitVersion = "1.6" ext.xstreamVersion = "1.4.8" @@ -1080,7 +1080,7 @@ project("spring-aspects") { dependencies { aspects(project(":spring-orm")) - ajc("org.aspectj:aspectjtools:1.9.0.BETA-2") // for the ability to build on JDK 9, not exposed in the POMs yet + ajc("org.aspectj:aspectjtools:1.9.0.BETA-3") // for the ability to build on JDK 9, not exposed in the POMs yet rt("org.aspectj:aspectjrt:${aspectjVersion}") // regular AspectJ version here, to be exposed in the POMs compile("org.aspectj:aspectjweaver:${aspectjVersion}") provided("org.eclipse.persistence:javax.persistence:2.0.0") From 484dd96606fb0ebbf72d335a3adc4be5213616ee Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Wed, 3 Feb 2016 14:51:13 +0100 Subject: [PATCH 038/344] Throw exception if TxMgr cannot be retrieved for @Transactional test Prior to this commit, a @Transactional integration test would silently be executed without a transaction if the transaction manager could not be retrieved from the application context -- for example, it no such bean was defined or if multiple beans were present but none satisfied the qualifier. This commit addresses this issue by throwing an IllegalStateException if the PlatformTransactionManager cannot be retrieved for a @Transactional test. Issue: SPR-13895 (cherry picked from commit 6d2b9a013602ee2c06d6e30532635f09ae5795d6) --- .../TransactionalTestExecutionListener.java | 6 ++++ ...ansactionalTestExecutionListenerTests.java | 32 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java index 978dced76e..6ae8d4bd9a 100644 --- a/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java +++ b/spring-test/src/main/java/org/springframework/test/context/transaction/TransactionalTestExecutionListener.java @@ -186,6 +186,12 @@ public void beforeTestMethod(final TestContext testContext) throws Exception { } tm = getTransactionManager(testContext, transactionAttribute.getQualifier()); + + if (tm == null) { + throw new IllegalStateException(String.format( + "Failed to retrieve PlatformTransactionManager for @Transactional test for test context %s.", + testContext)); + } } if (tm != null) { diff --git a/spring-test/src/test/java/org/springframework/test/context/transaction/TransactionalTestExecutionListenerTests.java b/spring-test/src/test/java/org/springframework/test/context/transaction/TransactionalTestExecutionListenerTests.java index b01fc608e8..8d4e015f27 100644 --- a/spring-test/src/test/java/org/springframework/test/context/transaction/TransactionalTestExecutionListenerTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/transaction/TransactionalTestExecutionListenerTests.java @@ -155,6 +155,38 @@ public void cleanUpThreadLocalStateForSubsequentTestClassesInSuite() { TransactionContextHolder.removeCurrentTransactionContext(); } + /** + * SPR-13895 + */ + @Test + public void transactionalTestWithoutTransactionManager() throws Exception { + TransactionalTestExecutionListener listener = new TransactionalTestExecutionListener() { + + protected PlatformTransactionManager getTransactionManager(TestContext testContext, String qualifier) { + return null; + } + }; + + Class clazz = TransactionalDeclaredOnClassLocallyTestCase.class; + + BDDMockito.> given(testContext.getTestClass()).willReturn(clazz); + Invocable instance = clazz.newInstance(); + given(testContext.getTestInstance()).willReturn(instance); + given(testContext.getTestMethod()).willReturn(clazz.getDeclaredMethod("transactionalTest")); + + assertFalse(instance.invoked); + TransactionContextHolder.removeCurrentTransactionContext(); + + try { + listener.beforeTestMethod(testContext); + fail("Should have thrown an IllegalStateException"); + } + catch (IllegalStateException e) { + assertTrue(e.getMessage().startsWith( + "Failed to retrieve PlatformTransactionManager for @Transactional test for test context")); + } + } + @Test public void beforeTestMethodWithTransactionalDeclaredOnClassLocally() throws Exception { assertBeforeTestMethodWithTransactionalTestMethod(TransactionalDeclaredOnClassLocallyTestCase.class); From e48315549eb14f1ce5f9f8e65e1263915818b97b Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 4 Feb 2016 19:48:05 +0100 Subject: [PATCH 039/344] AbstractXlsView does not explicitly flush (for compatibility with WebSphere Liberty) Issue: SPR-13910 (cherry picked from commit 28e8af2) --- .../web/servlet/view/document/AbstractXlsView.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/document/AbstractXlsView.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/document/AbstractXlsView.java index 7f06cd70ed..4279bb3e2c 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/document/AbstractXlsView.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/document/AbstractXlsView.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -98,7 +98,6 @@ protected Workbook createWorkbook(Map model, HttpServletRequest protected void renderWorkbook(Workbook workbook, HttpServletResponse response) throws IOException { ServletOutputStream out = response.getOutputStream(); workbook.write(out); - out.flush(); // Closeable only implemented as of POI 3.10 if (workbook instanceof Closeable) { From 44183314299ab9383fe8d7b4db2a46d30532ccc4 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 4 Feb 2016 20:00:00 +0100 Subject: [PATCH 040/344] Polishing (cherry picked from commit e903106) --- ...ReloadableResourceBundleMessageSource.java | 28 +++++++------------ .../support/ResourceBundleMessageSource.java | 15 +++++----- .../util/backoff/ExponentialBackOff.java | 14 +++++----- .../util/backoff/FixedBackOff.java | 8 +++--- .../DefaultMessageListenerContainer.java | 20 ++++++------- 5 files changed, 37 insertions(+), 48 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/support/ReloadableResourceBundleMessageSource.java b/spring-context/src/main/java/org/springframework/context/support/ReloadableResourceBundleMessageSource.java index ab45abf1b0..73de21f4b5 100644 --- a/spring-context/src/main/java/org/springframework/context/support/ReloadableResourceBundleMessageSource.java +++ b/spring-context/src/main/java/org/springframework/context/support/ReloadableResourceBundleMessageSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -50,29 +50,20 @@ * reloading files based on timestamp changes, but also of loading properties files * with a specific character encoding. It will detect XML property files as well. * - *

    In contrast to {@link ResourceBundleMessageSource}, this class supports - * reloading of properties files through the {@link #setCacheSeconds "cacheSeconds"} - * setting, and also through programmatically clearing the properties cache. - * Since application servers typically cache all files loaded from the classpath, - * it is necessary to store resources somewhere else (for example, in the - * "WEB-INF" directory of a web app). Otherwise changes of files in the - * classpath will not be reflected in the application. - * - *

    Note that the base names set as {@link #setBasenames "basenames"} property + *

    Note that the basenames set as {@link #setBasenames "basenames"} property * are treated in a slightly different fashion than the "basenames" property of * {@link ResourceBundleMessageSource}. It follows the basic ResourceBundle rule of not * specifying file extension or language codes, but can refer to any Spring resource * location (instead of being restricted to classpath resources). With a "classpath:" * prefix, resources can still be loaded from the classpath, but "cacheSeconds" values - * other than "-1" (caching forever) will not work in this case. - * - *

    This MessageSource implementation is usually slightly faster than - * {@link ResourceBundleMessageSource}, which builds on {@link java.util.ResourceBundle} - * - in the default mode, i.e. when caching forever. With "cacheSeconds" set to 1, - * message lookup takes about twice as long - with the benefit that changes in - * individual properties files are detected with a maximum delay of 1 second. - * Higher "cacheSeconds" values usually do not make a significant difference. + * other than "-1" (caching forever) might not work reliably in this case. * + *

    For a typical web application, message files could be placed into {@code WEB-INF}: + * e.g. a "WEB-INF/messages" basename would fine a "WEB-INF/messages.properties", + * "WEB-INF/messages_en.properties" etc arrangement as well as "WEB-INF/messages.xml", + * "WEB-INF/messages_en.xml" etc. Note that message definitions in a previous + * resource bundle will override ones in a later bundle, due to sequential lookup. + *

    This MessageSource can easily be used outside of an * {@link org.springframework.context.ApplicationContext}: It will use a * {@link org.springframework.core.io.DefaultResourceLoader} as default, @@ -244,6 +235,7 @@ public void setCacheSeconds(int cacheSeconds) { *

    Default is "true": This behavior is new as of Spring Framework 4.1, * minimizing contention between threads. If you prefer the old behavior, * i.e. to fully block on refresh, switch this flag to "false". + * @since 4.1 * @see #setCacheSeconds */ public void setConcurrentRefresh(boolean concurrentRefresh) { diff --git a/spring-context/src/main/java/org/springframework/context/support/ResourceBundleMessageSource.java b/spring-context/src/main/java/org/springframework/context/support/ResourceBundleMessageSource.java index 23ae8340aa..1f623674c5 100644 --- a/spring-context/src/main/java/org/springframework/context/support/ResourceBundleMessageSource.java +++ b/spring-context/src/main/java/org/springframework/context/support/ResourceBundleMessageSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -51,11 +51,11 @@ * base class. The caching provided by this MessageSource is significantly faster * than the built-in caching of the {@code java.util.ResourceBundle} class. * - *

    Unfortunately, {@code java.util.ResourceBundle} caches loaded bundles - * forever: Reloading a bundle during VM execution is not possible. - * As this MessageSource relies on ResourceBundle, it faces the same limitation. - * Consider {@link ReloadableResourceBundleMessageSource} for an alternative - * that is capable of refreshing the underlying bundle files. + *

    The basenames follow {@link java.util.ResourceBundle} conventions: essentially, + * a fully-qualified classpath location. If it doesn't contain a package qualifier + * (such as {@code org.mypackage}), it will be resolved from the classpath root. + * Note that the JDK's standard ResourceBundle treats dots as package separators: + * This means that "test.theme" is effectively equivalent to "test/theme". * * @author Rod Johnson * @author Juergen Hoeller @@ -404,8 +404,7 @@ protected String getStringOrNull(ResourceBundle bundle, String key) { */ @Override public String toString() { - return getClass().getName() + ": basenames=[" + - StringUtils.arrayToCommaDelimitedString(this.basenames) + "]"; + return getClass().getName() + ": basenames=[" + StringUtils.arrayToCommaDelimitedString(this.basenames) + "]"; } diff --git a/spring-core/src/main/java/org/springframework/util/backoff/ExponentialBackOff.java b/spring-core/src/main/java/org/springframework/util/backoff/ExponentialBackOff.java index b3bfa9c65d..b7c6efd29c 100644 --- a/spring-core/src/main/java/org/springframework/util/backoff/ExponentialBackOff.java +++ b/spring-core/src/main/java/org/springframework/util/backoff/ExponentialBackOff.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -103,6 +103,7 @@ public ExponentialBackOff(long initialInterval, double multiplier) { this.multiplier = multiplier; } + /** * The initial interval in milliseconds. */ @@ -183,12 +184,12 @@ private class ExponentialBackOffExecution implements BackOffExecution { @Override public long nextBackOff() { - if (currentElapsedTime >= maxElapsedTime) { + if (this.currentElapsedTime >= maxElapsedTime) { return STOP; } long nextInterval = computeNextInterval(); - currentElapsedTime += nextInterval; + this.currentElapsedTime += nextInterval; return nextInterval; } @@ -205,7 +206,7 @@ else if (this.currentInterval < 0) { else { this.currentInterval = multiplyInterval(maxInterval); } - return currentInterval; + return this.currentInterval; } private long multiplyInterval(long maxInterval) { @@ -217,9 +218,8 @@ private long multiplyInterval(long maxInterval) { @Override public String toString() { - String i = (this.currentInterval < 0 ? "n/a" : this.currentInterval + "ms"); - final StringBuilder sb = new StringBuilder("ExponentialBackOff{"); - sb.append("currentInterval=").append(i); + StringBuilder sb = new StringBuilder("ExponentialBackOff{"); + sb.append("currentInterval=").append(this.currentInterval < 0 ? "n/a" : this.currentInterval + "ms"); sb.append(", multiplier=").append(getMultiplier()); sb.append('}'); return sb.toString(); diff --git a/spring-core/src/main/java/org/springframework/util/backoff/FixedBackOff.java b/spring-core/src/main/java/org/springframework/util/backoff/FixedBackOff.java index 62566f701c..55c23b5e8a 100644 --- a/spring-core/src/main/java/org/springframework/util/backoff/FixedBackOff.java +++ b/spring-core/src/main/java/org/springframework/util/backoff/FixedBackOff.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -57,6 +57,7 @@ public FixedBackOff(long interval, long maxAttempts) { this.maxAttempts = maxAttempts; } + /** * Set the interval between two attempts in milliseconds. */ @@ -110,14 +111,13 @@ public long nextBackOff() { public String toString() { final StringBuilder sb = new StringBuilder("FixedBackOff{"); sb.append("interval=").append(FixedBackOff.this.interval); - String attemptValue = (FixedBackOff.this.maxAttempts == Long.MAX_VALUE ? "unlimited" - : String.valueOf(FixedBackOff.this.maxAttempts)); + String attemptValue = (FixedBackOff.this.maxAttempts == Long.MAX_VALUE ? + "unlimited" : String.valueOf(FixedBackOff.this.maxAttempts)); sb.append(", currentAttempts=").append(this.currentAttempts); sb.append(", maxAttempts=").append(attemptValue); sb.append('}'); return sb.toString(); } - } } diff --git a/spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java b/spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java index ba55ecc241..c48c3439df 100644 --- a/spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java +++ b/spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -175,7 +175,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe private Executor taskExecutor; - private BackOff backOff = createDefaultBackOff(DEFAULT_RECOVERY_INTERVAL); + private BackOff backOff = new FixedBackOff(DEFAULT_RECOVERY_INTERVAL, Long.MAX_VALUE); private int cacheLevel = CACHE_AUTO; @@ -229,6 +229,7 @@ public void setTaskExecutor(Executor taskExecutor) { * attempt to recover. *

    The {@link #setRecoveryInterval(long) recovery interval} is ignored * when this property is set. + * @since 4.1 */ public void setBackOff(BackOff backOff) { this.backOff = backOff; @@ -244,7 +245,7 @@ public void setBackOff(BackOff backOff) { * @see #handleListenerSetupFailure */ public void setRecoveryInterval(long recoveryInterval) { - this.backOff = createDefaultBackOff(recoveryInterval); + this.backOff = new FixedBackOff(recoveryInterval, Long.MAX_VALUE); } /** @@ -941,7 +942,7 @@ protected void refreshConnectionUntilSuccessful() { StringBuilder msg = new StringBuilder(); msg.append("Stopping container for destination '") .append(getDestinationDescription()) - .append("' - back off policy does not allow ").append("for further attempts."); + .append("': back-off policy does not allow ").append("for further attempts."); logger.error(msg.toString()); stop(); } @@ -968,10 +969,11 @@ protected void refreshDestination() { } /** - * Apply the next back off time using the specified {@link BackOffExecution}. - *

    Return {@code true} if the back off period has been applied and a new + * Apply the next back-off time using the specified {@link BackOffExecution}. + *

    Return {@code true} if the back-off period has been applied and a new * attempt to recover should be made, {@code false} if no further attempt * should be made. + * @since 4.1 */ protected boolean applyBackOffTime(BackOffExecution execution) { long interval = execution.nextBackOff(); @@ -990,10 +992,6 @@ protected boolean applyBackOffTime(BackOffExecution execution) { return true; } - private FixedBackOff createDefaultBackOff(long interval) { - return new FixedBackOff(interval, Long.MAX_VALUE); - } - /** * Return whether this listener container is currently in a recovery attempt. *

    May be used to detect recovery phases but also the end of a recovery phase, @@ -1205,7 +1203,7 @@ private void clearResources() { } /** - * Apply the back off time once. In a regular scenario, the back off is only applied if we + * Apply the back-off time once. In a regular scenario, the back-off is only applied if we * failed to recover with the broker. This additional sleep period avoids a burst retry * scenario when the broker is actually up but something else if failing (i.e. listener * specific). From 39c05c56c45f955a26a8ce86f33f1534da154912 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Thu, 4 Feb 2016 14:59:13 -0500 Subject: [PATCH 041/344] BufferedImage converter writes Content-Type again Issue: SPR-13906 --- .../BufferedImageHttpMessageConverter.java | 23 ++++++++------ .../http/MockHttpOutputMessage.java | 30 ++++++++++++++++--- ...ufferedImageHttpMessageConverterTests.java | 11 +++++-- 3 files changed, 48 insertions(+), 16 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/converter/BufferedImageHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/BufferedImageHttpMessageConverter.java index 66c1432071..359cac6302 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/BufferedImageHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/BufferedImageHttpMessageConverter.java @@ -38,7 +38,6 @@ import javax.imageio.stream.MemoryCacheImageInputStream; import javax.imageio.stream.MemoryCacheImageOutputStream; -import org.springframework.http.HttpHeaders; import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpOutputMessage; import org.springframework.http.MediaType; @@ -209,29 +208,35 @@ public void write(final BufferedImage image, final MediaType contentType, final HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { + final MediaType selectedContentType = getContentType(contentType); + outputMessage.getHeaders().setContentType(selectedContentType); + if (outputMessage instanceof StreamingHttpOutputMessage) { StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage; streamingOutputMessage.setBody(new StreamingHttpOutputMessage.Body() { @Override public void writeTo(OutputStream outputStream) throws IOException { - writeInternal(image, contentType, outputMessage.getHeaders(), outputStream); + writeInternal(image, selectedContentType, outputStream); } }); } else { - writeInternal(image, contentType, outputMessage.getHeaders(), outputMessage.getBody()); + writeInternal(image, selectedContentType, outputMessage.getBody()); } } - private void writeInternal(BufferedImage image, MediaType contentType, HttpHeaders headers, - OutputStream body) throws IOException, HttpMessageNotWritableException { - + private MediaType getContentType(MediaType contentType) { if (contentType == null || contentType.isWildcardType() || contentType.isWildcardSubtype()) { contentType = getDefaultContentType(); } - Assert.notNull(contentType, - "Count not determine Content-Type, set one using the 'defaultContentType' property"); - headers.setContentType(contentType); + Assert.notNull(contentType, "Could not select Content-Type. " + + "Please specify one through the 'defaultContentType' property."); + return contentType; + } + + private void writeInternal(BufferedImage image, MediaType contentType, OutputStream body) + throws IOException, HttpMessageNotWritableException { + ImageOutputStream imageOutputStream = null; ImageWriter imageWriter = null; try { diff --git a/spring-web/src/test/java/org/springframework/http/MockHttpOutputMessage.java b/spring-web/src/test/java/org/springframework/http/MockHttpOutputMessage.java index 9ce157e4b9..e23d739d75 100644 --- a/spring-web/src/test/java/org/springframework/http/MockHttpOutputMessage.java +++ b/spring-web/src/test/java/org/springframework/http/MockHttpOutputMessage.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,10 +21,11 @@ import java.io.OutputStream; import java.nio.charset.Charset; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.spy; /** * @author Arjen Poutsma + * @author Rossen Stoyanchev */ public class MockHttpOutputMessage implements HttpOutputMessage { @@ -34,19 +35,31 @@ public class MockHttpOutputMessage implements HttpOutputMessage { private boolean headersWritten = false; + private final HttpHeaders writtenHeaders = new HttpHeaders(); + + @Override public HttpHeaders getHeaders() { return (this.headersWritten ? HttpHeaders.readOnlyHttpHeaders(this.headers) : this.headers); } + /** + * Return a copy of the actual headers written at the time of the call to + * getBody, i.e. ignoring any further changes that may have been made to + * the underlying headers, e.g. via a previously obtained instance. + */ + public HttpHeaders getWrittenHeaders() { + return writtenHeaders; + } + @Override public OutputStream getBody() throws IOException { - this.headersWritten = true; + writeHeaders(); return body; } public byte[] getBodyAsBytes() { - this.headersWritten = true; + writeHeaders(); return body.toByteArray(); } @@ -54,4 +67,13 @@ public String getBodyAsString(Charset charset) { byte[] bytes = getBodyAsBytes(); return new String(bytes, charset); } + + private void writeHeaders() { + if (this.headersWritten) { + return; + } + this.headersWritten = true; + this.writtenHeaders.putAll(this.headers); + } + } diff --git a/spring-web/src/test/java/org/springframework/http/converter/BufferedImageHttpMessageConverterTests.java b/spring-web/src/test/java/org/springframework/http/converter/BufferedImageHttpMessageConverterTests.java index 5e4f33a7ea..8ca6724c02 100644 --- a/spring-web/src/test/java/org/springframework/http/converter/BufferedImageHttpMessageConverterTests.java +++ b/spring-web/src/test/java/org/springframework/http/converter/BufferedImageHttpMessageConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,6 +33,11 @@ import static org.junit.Assert.*; +/** + * Unit tests for BufferedImageHttpMessageConverter. + * @author Arjen Poutsma + * @author Rossen Stoyanchev + */ public class BufferedImageHttpMessageConverterTests { private BufferedImageHttpMessageConverter converter; @@ -73,7 +78,7 @@ public void write() throws IOException { MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); MediaType contentType = new MediaType("image", "png"); converter.write(body, contentType, outputMessage); - assertEquals("Invalid content type", contentType, outputMessage.getHeaders().getContentType()); + assertEquals("Invalid content type", contentType, outputMessage.getWrittenHeaders().getContentType()); assertTrue("Invalid size", outputMessage.getBodyAsBytes().length > 0); BufferedImage result = ImageIO.read(new ByteArrayInputStream(outputMessage.getBodyAsBytes())); assertEquals("Invalid height", 500, result.getHeight()); @@ -88,7 +93,7 @@ public void writeDefaultContentType() throws IOException { BufferedImage body = ImageIO.read(logo.getFile()); MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); converter.write(body, new MediaType("*", "*"), outputMessage); - assertEquals("Invalid content type", contentType, outputMessage.getHeaders().getContentType()); + assertEquals("Invalid content type", contentType, outputMessage.getWrittenHeaders().getContentType()); assertTrue("Invalid size", outputMessage.getBodyAsBytes().length > 0); BufferedImage result = ImageIO.read(new ByteArrayInputStream(outputMessage.getBodyAsBytes())); assertEquals("Invalid height", 500, result.getHeight()); From f053cdec5158ab7cbf01526f967c42aefa924e11 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Thu, 4 Feb 2016 17:22:36 -0500 Subject: [PATCH 042/344] Improve close in ConcurrentWebSocketSessionDecorator Before this commit the concurrent session wrapper mainly protected the sending of messages. The close itself however may also cause a message to be sent as is the case of the SockJS protocol. This change protects the close and checks if the session has exceeded send time or buffer limits in which case the close status is changed to SESSION_NOT_RELIABLE (introduced in commit cbd5af3a) which in turn signals that extra care should be exercised when closing the session. Issue: SPR-13904 --- .../ConcurrentWebSocketSessionDecorator.java | 29 +++++- ...currentWebSocketSessionDecoratorTests.java | 99 ++++++++++--------- 2 files changed, 81 insertions(+), 47 deletions(-) diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/handler/ConcurrentWebSocketSessionDecorator.java b/spring-websocket/src/main/java/org/springframework/web/socket/handler/ConcurrentWebSocketSessionDecorator.java index b0efe1753d..16de9aa4cc 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/handler/ConcurrentWebSocketSessionDecorator.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/handler/ConcurrentWebSocketSessionDecorator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -169,8 +169,31 @@ private void sessionLimitReached(String reason, CloseStatus status) { @Override public void close(CloseStatus status) throws IOException { - this.shutdownInProgress = true; - super.close(status); + this.closeLock.lock(); + try { + if (this.shutdownInProgress) { + return; + } + if (!CloseStatus.SESSION_NOT_RELIABLE.equals(status)) { + try { + checkSessionLimits(); + } + catch (SessionLimitExceededException ex) { + // Ignore + } + if (this.limitExceeded) { + if (logger.isDebugEnabled()) { + logger.debug("Changing close status " + status + " to SESSION_NOT_RELIABLE."); + } + status = CloseStatus.SESSION_NOT_RELIABLE; + } + } + this.shutdownInProgress = true; + super.close(status); + } + finally { + this.closeLock.unlock(); + } } diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/handler/ConcurrentWebSocketSessionDecoratorTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/handler/ConcurrentWebSocketSessionDecoratorTests.java index 7e73979ab3..c4cdb02a42 100644 --- a/spring-websocket/src/test/java/org/springframework/web/socket/handler/ConcurrentWebSocketSessionDecoratorTests.java +++ b/spring-websocket/src/test/java/org/springframework/web/socket/handler/ConcurrentWebSocketSessionDecoratorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,6 +27,7 @@ import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketMessage; +import org.springframework.web.socket.WebSocketSession; import static org.junit.Assert.*; @@ -186,6 +187,59 @@ public void run() { } } + @Test + public void closeStatusNormal() throws Exception { + + BlockingSession delegate = new BlockingSession(); + delegate.setOpen(true); + WebSocketSession decorator = new ConcurrentWebSocketSessionDecorator(delegate, 10 * 1000, 1024); + + decorator.close(CloseStatus.PROTOCOL_ERROR); + assertEquals(CloseStatus.PROTOCOL_ERROR, delegate.getCloseStatus()); + + decorator.close(CloseStatus.SERVER_ERROR); + assertEquals("Should have been ignored", CloseStatus.PROTOCOL_ERROR, delegate.getCloseStatus()); + } + + @Test + public void closeStatusChangesToSessionNotReliable() throws Exception { + + BlockingSession blockingSession = new BlockingSession(); + blockingSession.setId("123"); + blockingSession.setOpen(true); + CountDownLatch sentMessageLatch = blockingSession.getSentMessageLatch(); + + int sendTimeLimit = 100; + int bufferSizeLimit = 1024; + + final ConcurrentWebSocketSessionDecorator concurrentSession = + new ConcurrentWebSocketSessionDecorator(blockingSession, sendTimeLimit, bufferSizeLimit); + + Executors.newSingleThreadExecutor().submit(new Runnable() { + @Override + public void run() { + TextMessage message = new TextMessage("slow message"); + try { + concurrentSession.sendMessage(message); + } + catch (IOException e) { + e.printStackTrace(); + } + } + }); + + assertTrue(sentMessageLatch.await(5, TimeUnit.SECONDS)); + + // ensure some send time elapses + Thread.sleep(sendTimeLimit + 100); + + concurrentSession.close(CloseStatus.PROTOCOL_ERROR); + + assertEquals("CloseStatus should have changed to SESSION_NOT_RELIABLE", + CloseStatus.SESSION_NOT_RELIABLE, blockingSession.getCloseStatus()); + } + + private static class BlockingSession extends TestWebSocketSession { @@ -220,47 +274,4 @@ private void block() { } -// @Test -// public void sendSessionLimitException() throws IOException, InterruptedException { -// -// BlockingSession blockingSession = new BlockingSession(); -// blockingSession.setOpen(true); -// CountDownLatch sentMessageLatch = blockingSession.getSentMessageLatch(); -// -// int sendTimeLimit = 10 * 1000; -// int bufferSizeLimit = 1024; -// -// final ConcurrentWebSocketSessionDecorator concurrentSession = -// new ConcurrentWebSocketSessionDecorator(blockingSession, sendTimeLimit, bufferSizeLimit); -// -// Executors.newSingleThreadExecutor().submit(new Runnable() { -// @Override -// public void run() { -// TextMessage textMessage = new TextMessage("slow message"); -// try { -// concurrentSession.sendMessage(textMessage); -// } -// catch (IOException e) { -// e.printStackTrace(); -// } -// } -// }); -// -// assertTrue(sentMessageLatch.await(5, TimeUnit.SECONDS)); -// -// StringBuilder sb = new StringBuilder(); -// for (int i=0 ; i < 1023; i++) { -// sb.append("a"); -// } -// -// TextMessage message = new TextMessage(sb.toString()); -// concurrentSession.sendMessage(message); -// -// assertEquals(1023, concurrentSession.getBufferSize()); -// assertTrue(blockingSession.isOpen()); -// -// concurrentSession.sendMessage(message); -// assertFalse(blockingSession.isOpen()); -// } - } From b1a46ccbd9c8af2932e7791ac39aed9143a6ef14 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Thu, 4 Feb 2016 23:32:22 -0500 Subject: [PATCH 043/344] Fix NPE in InvocableHandlerMethod Issue: SPR-13917 --- .../support/InvocableHandlerMethod.java | 3 +- .../support/InvocableHandlerMethodTests.java | 28 ++++++++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java b/spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java index 23972879b5..6758c6b122 100644 --- a/spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java +++ b/spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java @@ -223,7 +223,8 @@ protected Object doInvoke(Object... args) throws Exception { } catch (IllegalArgumentException ex) { assertTargetBean(getBridgedMethod(), getBean(), args); - throw new IllegalStateException(getInvocationErrorMessage(ex.getMessage(), args), ex); + String message = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument"); + throw new IllegalStateException(getInvocationErrorMessage(message, args), ex); } catch (InvocationTargetException ex) { // Unwrap for HandlerExceptionResolvers ... diff --git a/spring-web/src/test/java/org/springframework/web/method/support/InvocableHandlerMethodTests.java b/spring-web/src/test/java/org/springframework/web/method/support/InvocableHandlerMethodTests.java index 4bd60cd508..5dbe548633 100644 --- a/spring-web/src/test/java/org/springframework/web/method/support/InvocableHandlerMethodTests.java +++ b/spring-web/src/test/java/org/springframework/web/method/support/InvocableHandlerMethodTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,6 +29,7 @@ import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.ServletWebRequest; +import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.*; /** @@ -195,6 +196,28 @@ public void invocationTargetException() throws Exception { } } + // SPR-13917 + + @Test + public void invocationErrorMessage() throws Exception { + HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite(); + composite.addResolver(new StubArgumentResolver(double.class, null)); + + Method method = Handler.class.getDeclaredMethod("handle", double.class); + Object handler = new Handler(); + InvocableHandlerMethod hm = new InvocableHandlerMethod(handler, method); + hm.setHandlerMethodArgumentResolvers(composite); + + try { + hm.invokeForRequest(this.webRequest, new ModelAndViewContainer()); + fail(); + } + catch (IllegalStateException ex) { + assertThat(ex.getMessage(), containsString("Illegal argument")); + } + } + + private void invokeExceptionRaisingHandler(Throwable expected) throws Exception { Method method = ExceptionRaisingHandler.class.getDeclaredMethod("raiseException"); Object handler = new ExceptionRaisingHandler(expected); @@ -209,6 +232,9 @@ private static class Handler { public String handle(Integer intArg, String stringArg) { return intArg + "-" + stringArg; } + + public void handle(double amount) { + } } From 6ef2d827c67da49ba7751cae6b9bd9a6ec7f8fbc Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 9 Feb 2016 12:57:02 -0500 Subject: [PATCH 044/344] Reduce log level for @ExceptionHandler failure Issue: SPR-13932 --- .../method/annotation/ExceptionHandlerExceptionResolver.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.java index 590c06b1c4..c897a4196a 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.java @@ -363,8 +363,8 @@ protected ModelAndView doResolveHandlerMethodException(HttpServletRequest reques exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, handlerMethod); } catch (Exception invocationEx) { - if (logger.isErrorEnabled()) { - logger.error("Failed to invoke @ExceptionHandler method: " + exceptionHandlerMethod, invocationEx); + if (logger.isDebugEnabled()) { + logger.debug("Failed to invoke @ExceptionHandler method: " + exceptionHandlerMethod, invocationEx); } return null; } From d8a7b6f0f834ee0abdebaf77a305f130fdd7063d Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 10 Feb 2016 20:40:18 +0100 Subject: [PATCH 045/344] Latest dependency updates (Hibernate Validator 5.2.3, Joda-Time 2.9.2, OkHttp 2.7.4, SLF4J 1.7.15, Netty 4.0.34, Tomcat 8.0.32, Undertow 1.3.17) --- build.gradle | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index cd5f401c7f..9191462bba 100644 --- a/build.gradle +++ b/build.gradle @@ -43,7 +43,7 @@ configure(allprojects) { project -> ext.hibernate4Version = "4.3.11.Final" ext.hibernate5Version = "5.0.7.Final" ext.hibval4Version = "4.3.2.Final" - ext.hibval5Version = "5.2.2.Final" + ext.hibval5Version = "5.2.3.Final" ext.hsqldbVersion = "2.3.3" ext.htmlunitVersion = "2.19" ext.httpasyncVersion = "4.1.1" @@ -52,29 +52,29 @@ configure(allprojects) { project -> ext.jasperreportsVersion = "6.2.0" ext.javamailVersion = "1.5.5" ext.jettyVersion = "9.3.7.v20160115" - ext.jodaVersion = "2.9.1" + ext.jodaVersion = "2.9.2" ext.jrubyVersion = "1.7.23" // JRuby 9000 only supported through JSR-223 (StandardScriptFactory) ext.jsonassertVersion = "1.2.3" ext.jsonpathVersion = "2.1.0" ext.jtaVersion = "1.2" ext.junitVersion = "4.12" - ext.nettyVersion = "4.0.33.Final" - ext.okhttpVersion = "2.7.0" + ext.nettyVersion = "4.0.34.Final" + ext.okhttpVersion = "2.7.4" ext.openjpaVersion = "2.4.0" ext.poiVersion = "3.13" ext.protobufVersion = "2.6.1" ext.reactorVersion = "2.0.7.RELEASE" ext.romeVersion = "1.5.1" ext.seleniumVersion = "2.48.2" - ext.slf4jVersion = "1.7.14" + ext.slf4jVersion = "1.7.15" ext.snakeyamlVersion = "1.16" ext.snifferVersion = "1.14" ext.testngVersion = "6.9.10" ext.tiles2Version = "2.2.2" ext.tiles3Version = "3.0.5" - ext.tomcatVersion = "8.0.30" + ext.tomcatVersion = "8.0.32" ext.tyrusVersion = "1.3.5" // constrained by WebLogic 12.1.3 support - ext.undertowVersion = "1.3.14.Final" + ext.undertowVersion = "1.3.17.Final" ext.woodstoxVersion = "5.0.1" ext.xmlunitVersion = "1.6" ext.xstreamVersion = "1.4.8" From 9e7f6ba0b12b28d90117e2fd2ab216b23b3c4ea7 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 8 Feb 2016 13:18:43 +0100 Subject: [PATCH 046/344] PathMatchingResourcePatternResolver consistently logs retrieval results Issue: SPR-13923 (cherry picked from commit 449f704) --- .../PathMatchingResourcePatternResolver.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) 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 be84de5929..d69433b15c 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-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -156,9 +156,9 @@ * classpath:com/mycompany/**/service-context.xml * * is used to try to resolve it, the resolver will work off the (first) URL - * returned by {@code getResource("com/mycompany");}. If this base package - * node exists in multiple classloader locations, the actual end resource may - * not be underneath. Therefore, preferably, use "{@code classpath*:}" with the same + * returned by {@code getResource("com/mycompany");}. If this base package node + * exists in multiple classloader locations, the actual end resource may not be + * underneath. Therefore, preferably, use "{@code classpath*:}" with the same * Ant-style pattern in such a case, which will search all class path * locations that contain the root package. * @@ -308,6 +308,9 @@ protected Resource[] findAllClassPathResources(String location) throws IOExcepti path = path.substring(1); } Set result = doFindAllClassPathResources(path); + if (logger.isDebugEnabled()) { + logger.debug("Resolved classpath location [" + location + "] to resources " + result); + } return result.toArray(new Resource[result.size()]); } @@ -316,6 +319,7 @@ protected Resource[] findAllClassPathResources(String location) throws IOExcepti * Called by {@link #findAllClassPathResources(String)}. * @param path the absolute path within the classpath (never a leading slash) * @return a mutable Set of matching Resource instances + * @since 4.1.1 */ protected Set doFindAllClassPathResources(String path) throws IOException { Set result = new LinkedHashSet(16); @@ -350,6 +354,7 @@ protected Resource convertClassLoaderURL(URL url) { * given set of resources in the form of pointers to the root of the jar file content. * @param classLoader the ClassLoader to search (including its ancestors) * @param result the set of resources to add jar roots to + * @since 4.1.1 */ protected void addAllClassLoaderJarRoots(ClassLoader classLoader, Set result) { if (classLoader instanceof URLClassLoader) { @@ -381,6 +386,7 @@ protected void addAllClassLoaderJarRoots(ClassLoader classLoader, Set } if (classLoader != null) { try { + // Hierarchy traversal... addAllClassLoaderJarRoots(classLoader.getParent(), result); } catch (Exception ex) { From c960d9733df0ef555a1e2190b3e923a3af17ceba Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 8 Feb 2016 13:20:48 +0100 Subject: [PATCH 047/344] DisposableBeanAdapter ignores bridge method conflicts Issue: SPR-13922 (cherry picked from commit 903ae48) --- .../java/org/springframework/beans/BeanUtils.java | 12 ++++++++---- .../factory/support/DisposableBeanAdapter.java | 4 ++-- .../factory/DefaultListableBeanFactoryTests.java | 14 +++++++++++--- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java b/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java index 1b3993476e..5c735f16fb 100644 --- a/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java +++ b/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -278,8 +278,12 @@ public static Method findMethodWithMinimalParameters(Method[] methods, String me targetMethod = method; numMethodsFoundWithCurrentMinimumArgs = 1; } - else { - if (targetMethod.getParameterTypes().length == numParams) { + else if (!method.isBridge() && targetMethod.getParameterTypes().length == numParams) { + if (targetMethod.isBridge()) { + // Prefer regular method over bridge... + targetMethod = method; + } + else { // Additional candidate with same length numMethodsFoundWithCurrentMinimumArgs++; } @@ -289,7 +293,7 @@ public static Method findMethodWithMinimalParameters(Method[] methods, String me if (numMethodsFoundWithCurrentMinimumArgs > 1) { throw new IllegalArgumentException("Cannot resolve method '" + methodName + "' to a unique method. Attempted to resolve to overloaded method with " + - "the least number of parameters, but there were " + + "the least number of parameters but there were " + numMethodsFoundWithCurrentMinimumArgs + " candidates."); } return targetMethod; diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java index 4cb8f2e12d..d3a6c361c2 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -300,7 +300,7 @@ public Method run() { } } catch (IllegalArgumentException ex) { - throw new BeanDefinitionValidationException("Couldn't find a unique destroy method on bean with name '" + + throw new BeanDefinitionValidationException("Could not find unique destroy method on bean with name '" + this.beanName + ": " + ex.getMessage()); } } diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java index e3f877d373..cbf2f37b92 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -2890,7 +2890,13 @@ public void close() { } - public static class BeanWithDestroyMethod { + public static abstract class BaseClassWithDestroyMethod { + + public abstract BaseClassWithDestroyMethod close(); + } + + + public static class BeanWithDestroyMethod extends BaseClassWithDestroyMethod { private static int closeCount = 0; @@ -2900,8 +2906,10 @@ public void setInner(BeanWithDestroyMethod inner) { this.inner = inner; } - public void close() { + @Override + public BeanWithDestroyMethod close() { closeCount++; + return this; } } From cf3e460a8be291af9fe09189c611bfb1fbb5843a Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 8 Feb 2016 13:23:02 +0100 Subject: [PATCH 048/344] ReflectiveMethodResolver lets local static methods override java.lang.Class methods Issue: SPR-13918 (cherry picked from commit 43bcab9) --- .../spel/support/ReflectiveMethodResolver.java | 5 +++-- .../expression/spel/SpelReproTests.java | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodResolver.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodResolver.java index b78f66e9de..ffd64c7060 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodResolver.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectiveMethodResolver.java @@ -221,14 +221,15 @@ else if (matchRequiringConversion != null) { private Collection getMethods(Class type, Object targetObject) { if (targetObject instanceof Class) { Set result = new LinkedHashSet(); - result.addAll(Arrays.asList(getMethods(targetObject.getClass()))); - // Add these also so that static result are invocable on the type: e.g. Float.valueOf(..) + // Add these so that static methods are invocable on the type: e.g. Float.valueOf(..) Method[] methods = getMethods(type); for (Method method : methods) { if (Modifier.isStatic(method.getModifiers())) { result.add(method); } } + // Also expose methods from java.lang.Class itself + result.addAll(Arrays.asList(getMethods(Class.class))); return result; } else { diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/SpelReproTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/SpelReproTests.java index b1743e5004..8fe757271f 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/SpelReproTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/SpelReproTests.java @@ -19,6 +19,7 @@ import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -36,6 +37,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; + import org.springframework.core.MethodParameter; import org.springframework.core.convert.TypeDescriptor; import org.springframework.expression.AccessException; @@ -2051,6 +2053,17 @@ public void SPR10417_maps() { assertEquals("{X=66}", result.toString()); } + @Test + public void SPR13918() { + EvaluationContext context = new StandardEvaluationContext(); + context.setVariable("encoding", "UTF-8"); + + Expression ex = parser.parseExpression("T(java.nio.charset.Charset).forName(#encoding)"); + Object result = ex.getValue(context); + assertEquals(Charset.forName("UTF-8"), result); + } + + public static class ListOf { private final double value; @@ -2064,6 +2077,7 @@ public double getValue() { } } + public static class BeanClass { private final List list; From 5047e9062527b4bd754e7fc0f0ac85486512eb16 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 10 Feb 2016 16:22:13 +0100 Subject: [PATCH 049/344] AbstractMarshaller defensively uses DocumentBuilderFactory within synchronized block Issue: SPR-13935 (cherry picked from commit f61b998) --- .../org/springframework/oxm/support/AbstractMarshaller.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spring-oxm/src/main/java/org/springframework/oxm/support/AbstractMarshaller.java b/spring-oxm/src/main/java/org/springframework/oxm/support/AbstractMarshaller.java index 86227a0d76..8b92f1c042 100644 --- a/spring-oxm/src/main/java/org/springframework/oxm/support/AbstractMarshaller.java +++ b/spring-oxm/src/main/java/org/springframework/oxm/support/AbstractMarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -130,12 +130,13 @@ public boolean isProcessExternalEntities() { */ protected Document buildDocument() { try { + DocumentBuilder documentBuilder; synchronized (this.documentBuilderFactoryMonitor) { if (this.documentBuilderFactory == null) { this.documentBuilderFactory = createDocumentBuilderFactory(); } + documentBuilder = createDocumentBuilder(this.documentBuilderFactory); } - DocumentBuilder documentBuilder = createDocumentBuilder(this.documentBuilderFactory); return documentBuilder.newDocument(); } catch (ParserConfigurationException ex) { From 19c5462b13248985dcfb1ff2c4609347a073cdda Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 10 Feb 2016 20:48:10 +0100 Subject: [PATCH 050/344] Polishing --- .../DefaultMessageHandlerMethodFactory.java | 4 +- ...andlerMethodArgumentResolverComposite.java | 43 ++++++++++--------- .../invocation/InvocableHandlerMethod.java | 8 ++-- ...andlerMethodArgumentResolverComposite.java | 43 ++++++++++--------- .../support/InvocableHandlerMethod.java | 5 +-- 5 files changed, 52 insertions(+), 51 deletions(-) diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/DefaultMessageHandlerMethodFactory.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/DefaultMessageHandlerMethodFactory.java index 6ff383b235..8c1cbdeab9 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/DefaultMessageHandlerMethodFactory.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/DefaultMessageHandlerMethodFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -144,7 +144,7 @@ public void afterPropertiesSet() { @Override public InvocableHandlerMethod createInvocableHandlerMethod(Object bean, Method method) { InvocableHandlerMethod handlerMethod = new InvocableHandlerMethod(bean, method); - handlerMethod.setMessageMethodArgumentResolvers(argumentResolvers); + handlerMethod.setMessageMethodArgumentResolvers(this.argumentResolvers); return handlerMethod; } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/HandlerMethodArgumentResolverComposite.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/HandlerMethodArgumentResolverComposite.java index 92558e7b1d..1536b3725c 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/HandlerMethodArgumentResolverComposite.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/HandlerMethodArgumentResolverComposite.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,6 +42,26 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu new ConcurrentHashMap(256); + /** + * Add the given {@link HandlerMethodArgumentResolver}. + */ + public HandlerMethodArgumentResolverComposite addResolver(HandlerMethodArgumentResolver argumentResolver) { + this.argumentResolvers.add(argumentResolver); + return this; + } + + /** + * Add the given {@link HandlerMethodArgumentResolver}s. + */ + public HandlerMethodArgumentResolverComposite addResolvers(List argumentResolvers) { + if (argumentResolvers != null) { + for (HandlerMethodArgumentResolver resolver : argumentResolvers) { + this.argumentResolvers.add(resolver); + } + } + return this; + } + /** * Return a read-only list with the contained resolvers, or an empty list. */ @@ -56,6 +76,7 @@ public void clear() { this.argumentResolvers.clear(); } + /** * Whether the given {@linkplain MethodParameter method parameter} is supported by any registered * {@link HandlerMethodArgumentResolver}. @@ -94,24 +115,4 @@ private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parame return result; } - /** - * Add the given {@link HandlerMethodArgumentResolver}. - */ - public HandlerMethodArgumentResolverComposite addResolver(HandlerMethodArgumentResolver argumentResolver) { - this.argumentResolvers.add(argumentResolver); - return this; - } - - /** - * Add the given {@link HandlerMethodArgumentResolver}s. - */ - public HandlerMethodArgumentResolverComposite addResolvers(List argumentResolvers) { - if (argumentResolvers != null) { - for (HandlerMethodArgumentResolver resolver : argumentResolvers) { - this.argumentResolvers.add(resolver); - } - } - return this; - } - } 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 81585d7774..182e6d9224 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,8 +34,7 @@ * Provides a method for invoking the handler method for a given message after resolving its * method argument values through registered {@link HandlerMethodArgumentResolver}s. * - *

    Use {@link #setMessageMethodArgumentResolvers(HandlerMethodArgumentResolver)} - * to customize the list of argument resolvers. + *

    Use {@link #setMessageMethodArgumentResolvers} to customize the list of argument resolvers. * * @author Rossen Stoyanchev * @author Juergen Hoeller @@ -199,7 +198,8 @@ protected Object doInvoke(Object... args) throws Exception { } catch (IllegalArgumentException ex) { assertTargetBean(getBridgedMethod(), getBean(), args); - throw new IllegalStateException(getInvocationErrorMessage(ex.getMessage(), args), ex); + String message = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument"); + throw new IllegalStateException(getInvocationErrorMessage(message, args), ex); } catch (InvocationTargetException ex) { // Unwrap for HandlerExceptionResolvers ... diff --git a/spring-web/src/main/java/org/springframework/web/method/support/HandlerMethodArgumentResolverComposite.java b/spring-web/src/main/java/org/springframework/web/method/support/HandlerMethodArgumentResolverComposite.java index 904af697a3..5492582e59 100644 --- a/spring-web/src/main/java/org/springframework/web/method/support/HandlerMethodArgumentResolverComposite.java +++ b/spring-web/src/main/java/org/springframework/web/method/support/HandlerMethodArgumentResolverComposite.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,6 +47,26 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu new ConcurrentHashMap(256); + /** + * Add the given {@link HandlerMethodArgumentResolver}. + */ + public HandlerMethodArgumentResolverComposite addResolver(HandlerMethodArgumentResolver resolver) { + this.argumentResolvers.add(resolver); + return this; + } + + /** + * Add the given {@link HandlerMethodArgumentResolver}s. + */ + public HandlerMethodArgumentResolverComposite addResolvers(List resolvers) { + if (resolvers != null) { + for (HandlerMethodArgumentResolver resolver : resolvers) { + this.argumentResolvers.add(resolver); + } + } + return this; + } + /** * Return a read-only list with the contained resolvers, or an empty list. */ @@ -54,6 +74,7 @@ public List getResolvers() { return Collections.unmodifiableList(this.argumentResolvers); } + /** * Whether the given {@linkplain MethodParameter method parameter} is supported by any registered * {@link HandlerMethodArgumentResolver}. @@ -99,24 +120,4 @@ private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parame return result; } - /** - * Add the given {@link HandlerMethodArgumentResolver}. - */ - public HandlerMethodArgumentResolverComposite addResolver(HandlerMethodArgumentResolver resolver) { - this.argumentResolvers.add(resolver); - return this; - } - - /** - * Add the given {@link HandlerMethodArgumentResolver}s. - */ - public HandlerMethodArgumentResolverComposite addResolvers(List resolvers) { - if (resolvers != null) { - for (HandlerMethodArgumentResolver resolver : resolvers) { - this.argumentResolvers.add(resolver); - } - } - return this; - } - } diff --git a/spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java b/spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java index 6758c6b122..3a296369e8 100644 --- a/spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java +++ b/spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,8 +39,7 @@ * conversion. Use the {@link #setDataBinderFactory(WebDataBinderFactory)} property to supply * a binder factory to pass to argument resolvers. * - *

    Use {@link #setHandlerMethodArgumentResolvers(HandlerMethodArgumentResolverComposite)} - * to customize the list of argument resolvers. + *

    Use {@link #setHandlerMethodArgumentResolvers} to customize the list of argument resolvers. * * @author Rossen Stoyanchev * @author Juergen Hoeller From 09db26af2b92457fc100c955456486c0a95001a8 Mon Sep 17 00:00:00 2001 From: Sebastien Deleuze Date: Thu, 11 Feb 2016 08:51:42 +0100 Subject: [PATCH 051/344] Improve Jackson 2.7 compatibility This commit introduces a conditional call in AbstractJackson2HttpMessageConverter#getJavaType() in order to avoid calling TypeFactory#constructType() with a null contextClass parameter, since this is not supported by Jackson 2.7 anymore. It allows to use Spring Framework 4.2.x with Jackson 2.7.1+ for most use cases, but with some limitations. For example, with Jackson 2.7 TypeVariable resolution from method parameters does not work. As a consequence, after this commit Spring Framework 4.2.x still fully supports Jackson up to version 2.6, but improves Jackson 2.7 compatibility. Full support for Jackson 2.7 is provided as of Spring Framework 4.3.x. Issue: SPR-13853 --- .../json/AbstractJackson2HttpMessageConverter.java | 10 +++++++--- .../json/MappingJackson2HttpMessageConverter.java | 4 ++-- .../xml/MappingJackson2XmlHttpMessageConverter.java | 4 ++-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java index d648d0d070..04a4b59ac8 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,6 +31,7 @@ import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.ser.FilterProvider; +import com.fasterxml.jackson.databind.type.TypeFactory; import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpOutputMessage; @@ -48,7 +49,7 @@ * Abstract base class for Jackson based and content type independent * {@link HttpMessageConverter} implementations. * - *

    Compatible with Jackson 2.1 and higher. + *

    Compatible with Jackson 2.1 to 2.6. * * @author Arjen Poutsma * @author Keith Donald @@ -308,7 +309,10 @@ protected void writeSuffix(JsonGenerator generator, Object object) throws IOExce * @return the Jackson JavaType */ protected JavaType getJavaType(Type type, Class contextClass) { - return this.objectMapper.getTypeFactory().constructType(type, contextClass); + TypeFactory tf = this.objectMapper.getTypeFactory(); + // Conditional call because Jackson 2.7 does not support null contextClass anymore + // TypeVariable resolution will not work with Jackson 2.7, see SPR-13853 for more details + return (contextClass != null ? tf.constructType(type, contextClass) : tf.constructType(type)); } /** diff --git a/spring-web/src/main/java/org/springframework/http/converter/json/MappingJackson2HttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/json/MappingJackson2HttpMessageConverter.java index 43f2eba97f..e2ef12b935 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/json/MappingJackson2HttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/json/MappingJackson2HttpMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,7 +35,7 @@ * *

    The default constructor uses the default configuration provided by {@link Jackson2ObjectMapperBuilder}. * - *

    Compatible with Jackson 2.1 and higher. + *

    Compatible with Jackson 2.1 to 2.6. * * @author Arjen Poutsma * @author Keith Donald diff --git a/spring-web/src/main/java/org/springframework/http/converter/xml/MappingJackson2XmlHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/xml/MappingJackson2XmlHttpMessageConverter.java index 2f5aaffc10..03084759b4 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/xml/MappingJackson2XmlHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/xml/MappingJackson2XmlHttpMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,7 +35,7 @@ * *

    The default constructor uses the default configuration provided by {@link Jackson2ObjectMapperBuilder}. * - *

    Compatible with Jackson 2.1 and higher. + *

    Compatible with Jackson 2.1 to 2.6. * * @author Sebastien Deleuze * @since 4.1 From d2b9dbf1819d7b6e7dc61bdc3bf6b504717d3ae9 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 19 Feb 2016 14:00:52 +0100 Subject: [PATCH 052/344] BeanMethodInterceptor does not pass on null arguments for singleton beans Issue: SPR-13887 (cherry picked from commit 5ed9046) --- .../ConfigurationClassEnhancer.java | 18 +++++++-- .../ConfigurationClassPostProcessorTests.java | 40 +++++++++++++++---- 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java index 52986ab923..5d7e801cc4 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -364,8 +364,20 @@ public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object if (alreadyInCreation) { beanFactory.setCurrentlyInCreation(beanName, false); } - Object beanInstance = (!ObjectUtils.isEmpty(beanMethodArgs) ? - beanFactory.getBean(beanName, beanMethodArgs) : beanFactory.getBean(beanName)); + boolean useArgs = !ObjectUtils.isEmpty(beanMethodArgs); + if (useArgs && beanFactory.isSingleton(beanName)) { + // Stubbed null arguments just for reference purposes, + // expecting them to be autowired for regular singleton references? + // A safe assumption since @Bean singleton arguments cannot be optional... + for (Object arg : beanMethodArgs) { + if (arg == null) { + useArgs = false; + break; + } + } + } + Object beanInstance = (useArgs ? beanFactory.getBean(beanName, beanMethodArgs) : + beanFactory.getBean(beanName)); if (beanInstance != null && !ClassUtils.isAssignableValue(beanMethod.getReturnType(), beanInstance)) { String msg = String.format("@Bean method %s.%s called as a bean reference " + "for type [%s] but overridden by non-compatible bean instance of type [%s].", 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 6c9a47a458..a1feb216c7 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -592,17 +592,23 @@ public void testCircularDependencyWithApplicationContext() { } @Test - public void testPrototypeArgumentsThroughBeanMethodCall() { + public void testPrototypeArgumentThroughBeanMethodCall() { ApplicationContext ctx = new AnnotationConfigApplicationContext(BeanArgumentConfigWithPrototype.class); ctx.getBean(FooFactory.class).createFoo(new BarArgument()); } @Test - public void testSingletonArgumentsThroughBeanMethodCall() { + public void testSingletonArgumentThroughBeanMethodCall() { ApplicationContext ctx = new AnnotationConfigApplicationContext(BeanArgumentConfigWithSingleton.class); ctx.getBean(FooFactory.class).createFoo(new BarArgument()); } + @Test + public void testNullArgumentThroughBeanMethodCall() { + ApplicationContext ctx = new AnnotationConfigApplicationContext(BeanArgumentConfigWithNull.class); + ctx.getBean("aFoo"); + } + // ------------------------------------------------------------------------- @@ -1137,7 +1143,7 @@ static class BeanArgumentConfigWithPrototype { @Bean @Scope("prototype") - public DependingFoo foo(final BarArgument bar) { + public DependingFoo foo(BarArgument bar) { return new DependingFoo(bar); } @@ -1145,7 +1151,7 @@ public DependingFoo foo(final BarArgument bar) { public FooFactory fooFactory() { return new FooFactory() { @Override - public DependingFoo createFoo(final BarArgument bar) { + public DependingFoo createFoo(BarArgument bar) { return foo(bar); } }; @@ -1156,7 +1162,7 @@ public DependingFoo createFoo(final BarArgument bar) { static class BeanArgumentConfigWithSingleton { @Bean @Lazy - public DependingFoo foo(final BarArgument bar) { + public DependingFoo foo(BarArgument bar) { return new DependingFoo(bar); } @@ -1164,19 +1170,39 @@ public DependingFoo foo(final BarArgument bar) { public FooFactory fooFactory() { return new FooFactory() { @Override - public DependingFoo createFoo(final BarArgument bar) { + public DependingFoo createFoo(BarArgument bar) { return foo(bar); } }; } } + @Configuration + static class BeanArgumentConfigWithNull { + + @Bean + public DependingFoo aFoo() { + return foo(null); + } + + @Bean @Lazy + public DependingFoo foo(BarArgument bar) { + return new DependingFoo(bar); + } + + @Bean + public BarArgument bar() { + return new BarArgument(); + } + } + static class BarArgument { } static class DependingFoo { DependingFoo(BarArgument bar) { + Assert.notNull(bar); } } From 08f2ab942ca9bb982a60a5ae236f4227da814c30 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 16 Feb 2016 22:28:10 +0100 Subject: [PATCH 053/344] DefaultMessageListenerContainer stops if recovery failed after interrupt signal Issue: SPR-11787 (cherry picked from commit 1253b45) --- .../listener/DefaultMessageListenerContainer.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java b/spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java index c48c3439df..06209132b3 100644 --- a/spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java +++ b/spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java @@ -197,6 +197,8 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe private volatile boolean recovering = false; + private volatile boolean interrupted = false; + private Runnable stopCallback; private Object currentRecoveryMarker = new Object(); @@ -893,6 +895,7 @@ protected void recoverAfterListenerSetupFailure() { } finally { this.recovering = false; + this.interrupted = false; } } @@ -976,6 +979,10 @@ protected void refreshDestination() { * @since 4.1 */ protected boolean applyBackOffTime(BackOffExecution execution) { + if (this.recovering && this.interrupted) { + // Interrupted right before and still failing... give up. + return false; + } long interval = execution.nextBackOff(); if (interval == BackOffExecution.STOP) { return false; @@ -987,9 +994,12 @@ protected boolean applyBackOffTime(BackOffExecution execution) { catch (InterruptedException interEx) { // Re-interrupt current thread, to allow other threads to react. Thread.currentThread().interrupt(); + if (this.recovering) { + this.interrupted = true; + } } + return true; } - return true; } /** From 659e44146cd5e4aa513407a962b369bb7955386f Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 17 Feb 2016 16:57:29 +0100 Subject: [PATCH 054/344] Consistent equals implementations across class hierarchies Issue: SPR-13951 (cherry picked from commit ac44f9e) --- .../support/DefaultMessageSourceResolvable.java | 10 +++++----- .../org/springframework/validation/FieldError.java | 6 +++--- .../springframework/validation/ObjectError.java | 4 ++-- .../test/context/MergedContextConfiguration.java | 4 ++-- .../context/web/WebMergedContextConfiguration.java | 14 ++++---------- 5 files changed, 16 insertions(+), 22 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/support/DefaultMessageSourceResolvable.java b/spring-context/src/main/java/org/springframework/context/support/DefaultMessageSourceResolvable.java index e688b5009d..bc10ebc1d4 100644 --- a/spring-context/src/main/java/org/springframework/context/support/DefaultMessageSourceResolvable.java +++ b/spring-context/src/main/java/org/springframework/context/support/DefaultMessageSourceResolvable.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -106,7 +106,7 @@ public String[] getCodes() { * the last one in the codes array. */ public String getCode() { - return (this.codes != null && this.codes.length > 0) ? this.codes[this.codes.length - 1] : null; + return (this.codes != null && this.codes.length > 0 ? this.codes[this.codes.length - 1] : null); } @Override @@ -127,7 +127,7 @@ public String getDefaultMessage() { protected final String resolvableToString() { StringBuilder result = new StringBuilder(); result.append("codes [").append(StringUtils.arrayToDelimitedString(this.codes, ",")); - result.append("]; arguments [" + StringUtils.arrayToDelimitedString(this.arguments, ",")); + result.append("]; arguments [").append(StringUtils.arrayToDelimitedString(this.arguments, ",")); result.append("]; default message [").append(this.defaultMessage).append(']'); return result.toString(); } @@ -153,9 +153,9 @@ public boolean equals(Object other) { return false; } MessageSourceResolvable otherResolvable = (MessageSourceResolvable) other; - return ObjectUtils.nullSafeEquals(getCodes(), otherResolvable.getCodes()) && + return (ObjectUtils.nullSafeEquals(getCodes(), otherResolvable.getCodes()) && ObjectUtils.nullSafeEquals(getArguments(), otherResolvable.getArguments()) && - ObjectUtils.nullSafeEquals(getDefaultMessage(), otherResolvable.getDefaultMessage()); + ObjectUtils.nullSafeEquals(getDefaultMessage(), otherResolvable.getDefaultMessage())); } @Override diff --git a/spring-context/src/main/java/org/springframework/validation/FieldError.java b/spring-context/src/main/java/org/springframework/validation/FieldError.java index 0c76cc1622..26852dadf6 100644 --- a/spring-context/src/main/java/org/springframework/validation/FieldError.java +++ b/spring-context/src/main/java/org/springframework/validation/FieldError.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -112,9 +112,9 @@ public boolean equals(Object other) { return false; } FieldError otherError = (FieldError) other; - return getField().equals(otherError.getField()) && + return (getField().equals(otherError.getField()) && ObjectUtils.nullSafeEquals(getRejectedValue(), otherError.getRejectedValue()) && - isBindingFailure() == otherError.isBindingFailure(); + isBindingFailure() == otherError.isBindingFailure()); } @Override diff --git a/spring-context/src/main/java/org/springframework/validation/ObjectError.java b/spring-context/src/main/java/org/springframework/validation/ObjectError.java index 00b451634b..829e93874b 100644 --- a/spring-context/src/main/java/org/springframework/validation/ObjectError.java +++ b/spring-context/src/main/java/org/springframework/validation/ObjectError.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -78,7 +78,7 @@ public boolean equals(Object other) { if (this == other) { return true; } - if (getClass() != other.getClass() || !super.equals(other)) { + if (other == null || other.getClass() != getClass() || !super.equals(other)) { return false; } ObjectError otherError = (ObjectError) other; diff --git a/spring-test/src/main/java/org/springframework/test/context/MergedContextConfiguration.java b/spring-test/src/main/java/org/springframework/test/context/MergedContextConfiguration.java index 5212a04071..5090d7c5a4 100644 --- a/spring-test/src/main/java/org/springframework/test/context/MergedContextConfiguration.java +++ b/spring-test/src/main/java/org/springframework/test/context/MergedContextConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -401,7 +401,7 @@ public boolean equals(Object other) { if (this == other) { return true; } - if (!(other instanceof MergedContextConfiguration)) { + if (other == null || other.getClass() != getClass()) { return false; } diff --git a/spring-test/src/main/java/org/springframework/test/context/web/WebMergedContextConfiguration.java b/spring-test/src/main/java/org/springframework/test/context/web/WebMergedContextConfiguration.java index 05e476b936..cd1187b535 100644 --- a/spring-test/src/main/java/org/springframework/test/context/web/WebMergedContextConfiguration.java +++ b/spring-test/src/main/java/org/springframework/test/context/web/WebMergedContextConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -161,14 +161,8 @@ public String getResourceBasePath() { */ @Override public boolean equals(Object other) { - if (this == other) { - return true; - } - if (!(other instanceof WebMergedContextConfiguration)) { - return false; - } - WebMergedContextConfiguration otherConfig = (WebMergedContextConfiguration) other; - return super.equals(otherConfig) && this.getResourceBasePath().equals(otherConfig.getResourceBasePath()); + return (this == other || (super.equals(other) && + this.resourceBasePath.equals(((WebMergedContextConfiguration) other).resourceBasePath))); } /** @@ -178,7 +172,7 @@ public boolean equals(Object other) { */ @Override public int hashCode() { - return 31 * super.hashCode() + this.resourceBasePath.hashCode(); + return super.hashCode() * 31 + this.resourceBasePath.hashCode(); } /** From 4320161172f4d50b680a27678d00a7c8b682cbad Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 18 Feb 2016 16:19:09 +0100 Subject: [PATCH 055/344] Shared EntityManager Query unwrapping before passing it to addNamedQuery Issue: SPR-13957 (cherry picked from commit 7fcb277) --- .../jpa/AbstractEntityManagerFactoryBean.java | 21 ++++++++++++++++++- .../orm/jpa/SharedEntityManagerCreator.java | 5 ++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/AbstractEntityManagerFactoryBean.java b/spring-orm/src/main/java/org/springframework/orm/jpa/AbstractEntityManagerFactoryBean.java index 872ba91f37..4043bef01f 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/AbstractEntityManagerFactoryBean.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/AbstractEntityManagerFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,6 +33,7 @@ import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.PersistenceException; +import javax.persistence.Query; import javax.persistence.spi.PersistenceProvider; import javax.persistence.spi.PersistenceUnitInfo; import javax.sql.DataSource; @@ -384,6 +385,24 @@ else if (method.getName().equals("createEntityManager") && args != null && args. return ExtendedEntityManagerCreator.createApplicationManagedEntityManager(rawEntityManager, this, true); } + // Look for Query arguments, primarily JPA 2.1's addNamedQuery(String, Query) + if (args != null) { + for (int i = 0; i < args.length; i++) { + Object arg = args[i]; + if (arg instanceof Query && Proxy.isProxyClass(arg.getClass())) { + // Assumably a Spring-generated proxy from SharedEntityManagerCreator: + // since we're passing it back to the native EntityManagerFactory, + // let's unwrap it to the original Query object from the provider. + try { + args[i] = ((Query) arg).unwrap(null); + } + catch (RuntimeException ex) { + // Ignore - simply proceed with given Query object then + } + } + } + } + // Standard delegation to the native factory, just post-processing EntityManager return values Object retVal = method.invoke(this.nativeEntityManagerFactory, args); if (retVal instanceof EntityManager) { diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/SharedEntityManagerCreator.java b/spring-orm/src/main/java/org/springframework/orm/jpa/SharedEntityManagerCreator.java index da262d026c..cbc5e6be3d 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/SharedEntityManagerCreator.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/SharedEntityManagerCreator.java @@ -355,7 +355,10 @@ else if (method.getName().equals("hashCode")) { else if (method.getName().equals("unwrap")) { // Handle JPA 2.0 unwrap method - could be a proxy match. Class targetClass = (Class) args[0]; - if (targetClass == null || targetClass.isInstance(proxy)) { + if (targetClass == null) { + return this.target; + } + else if (targetClass.isInstance(proxy)) { return proxy; } } From 167ba8387aca00ad9e70c59960f0329007d72a55 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 18 Feb 2016 17:37:22 +0100 Subject: [PATCH 056/344] ScriptUtils.executeSqlScript logs SQLWarnings at debug level Issue: SPR-13959 (cherry picked from commit 9235345) --- .../init/ResourceDatabasePopulator.java | 38 ++++++++++--------- .../jdbc/datasource/init/ScriptUtils.java | 14 +++++-- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ResourceDatabasePopulator.java b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ResourceDatabasePopulator.java index 5673740013..75a668d516 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ResourceDatabasePopulator.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ResourceDatabasePopulator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -80,8 +80,8 @@ public ResourceDatabasePopulator() { /** * Construct a new {@code ResourceDatabasePopulator} with default settings * for the supplied scripts. - * @param scripts the scripts to execute to initialize or clean up the database; - * never {@code null} + * @param scripts the scripts to execute to initialize or clean up the database + * (never {@code null}) * @since 4.0.3 */ public ResourceDatabasePopulator(Resource... scripts) { @@ -97,21 +97,23 @@ public ResourceDatabasePopulator(Resource... scripts) { * statement can be ignored * @param sqlScriptEncoding the encoding for the supplied SQL scripts; may * be {@code null} or empty to indicate platform encoding - * @param scripts the scripts to execute to initialize or clean up the database; - * never {@code null} + * @param scripts the scripts to execute to initialize or clean up the database + * (never {@code null}) * @since 4.0.3 */ - public ResourceDatabasePopulator(boolean continueOnError, boolean ignoreFailedDrops, String sqlScriptEncoding, - Resource... scripts) { + public ResourceDatabasePopulator(boolean continueOnError, boolean ignoreFailedDrops, + String sqlScriptEncoding, Resource... scripts) { + this(scripts); this.continueOnError = continueOnError; this.ignoreFailedDrops = ignoreFailedDrops; setSqlScriptEncoding(sqlScriptEncoding); } + /** * Add a script to execute to initialize or clean up the database. - * @param script the path to an SQL script; never {@code null} + * @param script the path to an SQL script (never {@code null}) */ public void addScript(Resource script) { Assert.notNull(script, "Script must not be null"); @@ -120,7 +122,7 @@ public void addScript(Resource script) { /** * Add multiple scripts to execute to initialize or clean up the database. - * @param scripts the scripts to execute; never {@code null} + * @param scripts the scripts to execute (never {@code null}) */ public void addScripts(Resource... scripts) { assertContentsOfScriptArray(scripts); @@ -130,7 +132,7 @@ public void addScripts(Resource... scripts) { /** * Set the scripts to execute to initialize or clean up the database, * replacing any previously added scripts. - * @param scripts the scripts to execute; never {@code null} + * @param scripts the scripts to execute (never {@code null}) */ public void setScripts(Resource... scripts) { assertContentsOfScriptArray(scripts); @@ -173,8 +175,8 @@ public void setCommentPrefix(String commentPrefix) { * Set the start delimiter that identifies block comments within the SQL * scripts. *

    Defaults to {@code "/*"}. - * @param blockCommentStartDelimiter the start delimiter for block comments; - * never {@code null} or empty + * @param blockCommentStartDelimiter the start delimiter for block comments + * (never {@code null} or empty) * @since 4.0.3 * @see #setBlockCommentEndDelimiter */ @@ -187,8 +189,8 @@ public void setBlockCommentStartDelimiter(String blockCommentStartDelimiter) { * Set the end delimiter that identifies block comments within the SQL * scripts. *

    Defaults to "*/". - * @param blockCommentEndDelimiter the end delimiter for block comments; - * never {@code null} or empty + * @param blockCommentEndDelimiter the end delimiter for block comments + * (never {@code null} or empty) * @since 4.0.3 * @see #setBlockCommentStartDelimiter */ @@ -227,8 +229,8 @@ public void populate(Connection connection) throws ScriptException { Assert.notNull(connection, "Connection must not be null"); for (Resource script : getScripts()) { ScriptUtils.executeSqlScript(connection, encodeScript(script), this.continueOnError, - this.ignoreFailedDrops, this.commentPrefix, this.separator, this.blockCommentStartDelimiter, - this.blockCommentEndDelimiter); + this.ignoreFailedDrops, this.commentPrefix, this.separator, this.blockCommentStartDelimiter, + this.blockCommentEndDelimiter); } } @@ -236,7 +238,7 @@ public void populate(Connection connection) throws ScriptException { * Execute this {@code ResourceDatabasePopulator} against the given * {@link DataSource}. *

    Delegates to {@link DatabasePopulatorUtils#execute}. - * @param dataSource the {@code DataSource} to execute against; never {@code null} + * @param dataSource the {@code DataSource} to execute against (never {@code null}) * @throws ScriptException if an error occurs * @since 4.1 * @see #populate(Connection) @@ -254,7 +256,7 @@ final List getScripts() { * {@link EncodedResource} is not a sub-type of {@link Resource}. Thus we * always need to wrap each script resource in an {@code EncodedResource} * using the configured {@linkplain #setSqlScriptEncoding encoding}. - * @param script the script to wrap; never {@code null} + * @param script the script to wrap (never {@code null}) */ private EncodedResource encodeScript(Resource script) { Assert.notNull(script, "Script must not be null"); diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ScriptUtils.java b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ScriptUtils.java index 6aaaec0405..da4125421b 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ScriptUtils.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ScriptUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import java.io.LineNumberReader; import java.sql.Connection; import java.sql.SQLException; +import java.sql.SQLWarning; import java.sql.Statement; import java.util.LinkedList; import java.util.List; @@ -399,7 +400,7 @@ public static void executeSqlScript(Connection connection, Resource resource) th */ public static void executeSqlScript(Connection connection, EncodedResource resource) throws ScriptException { executeSqlScript(connection, resource, false, false, DEFAULT_COMMENT_PREFIX, DEFAULT_STATEMENT_SEPARATOR, - DEFAULT_BLOCK_COMMENT_START_DELIMITER, DEFAULT_BLOCK_COMMENT_END_DELIMITER); + DEFAULT_BLOCK_COMMENT_START_DELIMITER, DEFAULT_BLOCK_COMMENT_END_DELIMITER); } /** @@ -472,7 +473,14 @@ public static void executeSqlScript(Connection connection, EncodedResource resou stmt.execute(statement); int rowsAffected = stmt.getUpdateCount(); if (logger.isDebugEnabled()) { - logger.debug(rowsAffected + " returned as updateCount for SQL: " + statement); + logger.debug(rowsAffected + " returned as update count for SQL: " + statement); + SQLWarning warningToLog = stmt.getWarnings(); + while (warningToLog != null) { + logger.debug("SQLWarning ignored: SQL state '" + warningToLog.getSQLState() + + "', error code '" + warningToLog.getErrorCode() + + "', message [" + warningToLog.getMessage() + "]"); + warningToLog = warningToLog.getNextWarning(); + } } } catch (SQLException ex) { From a620770470c6ae501a5d54f4f7149745d96cb923 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 19 Feb 2016 16:02:25 +0100 Subject: [PATCH 057/344] Polishing --- .../convert/support/NumberToNumberConverterFactory.java | 6 +++--- .../convert/support/StringToNumberConverterFactory.java | 3 ++- .../java/org/springframework/core/io/AbstractResource.java | 6 +++--- .../http/client/OkHttpClientHttpRequestFactory.java | 4 ++-- .../http/client/OkHttpClientHttpResponse.java | 4 ++-- .../web/servlet/resource/DefaultResourceResolverChain.java | 5 ++--- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/NumberToNumberConverterFactory.java b/spring-core/src/main/java/org/springframework/core/convert/support/NumberToNumberConverterFactory.java index b98af70e31..773721a234 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/support/NumberToNumberConverterFactory.java +++ b/spring-core/src/main/java/org/springframework/core/convert/support/NumberToNumberConverterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,8 +40,7 @@ * @see java.math.BigDecimal * @see NumberUtils */ -final class NumberToNumberConverterFactory implements ConverterFactory, - ConditionalConverter { +final class NumberToNumberConverterFactory implements ConverterFactory, ConditionalConverter { @Override public Converter getConverter(Class targetType) { @@ -53,6 +52,7 @@ public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { return !sourceType.equals(targetType); } + private final static class NumberToNumber implements Converter { private final Class targetType; diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/StringToNumberConverterFactory.java b/spring-core/src/main/java/org/springframework/core/convert/support/StringToNumberConverterFactory.java index 506dcec929..172e8d2d5d 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/support/StringToNumberConverterFactory.java +++ b/spring-core/src/main/java/org/springframework/core/convert/support/StringToNumberConverterFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -45,6 +45,7 @@ public Converter getConverter(Class targetType) return new StringToNumber(targetType); } + private static final class StringToNumber implements Converter { private final Class targetType; diff --git a/spring-core/src/main/java/org/springframework/core/io/AbstractResource.java b/spring-core/src/main/java/org/springframework/core/io/AbstractResource.java index cd00102035..1bc9e1b934 100644 --- a/spring-core/src/main/java/org/springframework/core/io/AbstractResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/AbstractResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -123,8 +123,8 @@ public File getFile() throws IOException { */ @Override public long contentLength() throws IOException { - InputStream is = this.getInputStream(); - Assert.state(is != null, "resource input stream must not be null"); + InputStream is = getInputStream(); + Assert.state(is != null, "Resource InputStream must not be null"); try { long size = 0; byte[] buf = new byte[255]; diff --git a/spring-web/src/main/java/org/springframework/http/client/OkHttpClientHttpRequestFactory.java b/spring-web/src/main/java/org/springframework/http/client/OkHttpClientHttpRequestFactory.java index c5eabc9bf8..9b2674dd41 100644 --- a/spring-web/src/main/java/org/springframework/http/client/OkHttpClientHttpRequestFactory.java +++ b/spring-web/src/main/java/org/springframework/http/client/OkHttpClientHttpRequestFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -54,7 +54,7 @@ public OkHttpClientHttpRequestFactory() { * @param client the client to use */ public OkHttpClientHttpRequestFactory(OkHttpClient client) { - Assert.notNull(client, "'client' must not be null"); + Assert.notNull(client, "OkHttpClient must not be null"); this.client = client; this.defaultClient = false; } diff --git a/spring-web/src/main/java/org/springframework/http/client/OkHttpClientHttpResponse.java b/spring-web/src/main/java/org/springframework/http/client/OkHttpClientHttpResponse.java index d5191441df..392a2d7d9b 100644 --- a/spring-web/src/main/java/org/springframework/http/client/OkHttpClientHttpResponse.java +++ b/spring-web/src/main/java/org/springframework/http/client/OkHttpClientHttpResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,7 +39,7 @@ class OkHttpClientHttpResponse extends AbstractClientHttpResponse { public OkHttpClientHttpResponse(Response response) { - Assert.notNull(response, "'response' must not be null"); + Assert.notNull(response, "Response must not be null"); this.response = response; } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/DefaultResourceResolverChain.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/DefaultResourceResolverChain.java index 7bb638f206..501f2371c4 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/DefaultResourceResolverChain.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/DefaultResourceResolverChain.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -78,12 +78,11 @@ public String resolveUrlPath(String resourcePath, List locat private ResourceResolver getNext() { Assert.state(this.index <= this.resolvers.size(), - "Current index exceeds the number of configured ResourceResolver's"); + "Current index exceeds the number of configured ResourceResolvers"); if (this.index == (this.resolvers.size() - 1)) { return null; } - this.index++; return this.resolvers.get(this.index); } From 99dc7769a659e8750d569f0de5a5ee1afc265562 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 23 Feb 2016 00:04:49 +0100 Subject: [PATCH 058/344] Latest dependency updates (EhCache 3.0 M5, Groovy 2.4.6, Gson 2.6.1, Hibernate ORM 5.0.8, Hibernate Validator 5.2.4, SLF4J 1.7.16, SnakeYAML 1.17, Undertow 1.3.18) --- build.gradle | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/build.gradle b/build.gradle index 9191462bba..9ff39f4baf 100644 --- a/build.gradle +++ b/build.gradle @@ -31,19 +31,19 @@ configure(allprojects) { project -> ext.eclipselinkVersion = "2.4.2" ext.ehcacheVersion = "2.10.1" ext.ehcachejcacheVersion = "1.0.1" - ext.ehcache3Version = "3.0.0.m4" + ext.ehcache3Version = "3.0.0.m5" ext.ejbApiVersion = "3.0" ext.fileuploadVersion = "1.3.1" ext.freemarkerVersion = "2.3.23" - ext.groovyVersion = "2.4.5" - ext.gsonVersion = "2.5" + ext.groovyVersion = "2.4.6" + ext.gsonVersion = "2.6.1" ext.guavaVersion = "19.0" ext.hamcrestVersion = "1.3" ext.hibernate3Version = "3.6.10.Final" ext.hibernate4Version = "4.3.11.Final" - ext.hibernate5Version = "5.0.7.Final" + ext.hibernate5Version = "5.0.8.Final" ext.hibval4Version = "4.3.2.Final" - ext.hibval5Version = "5.2.3.Final" + ext.hibval5Version = "5.2.4.Final" ext.hsqldbVersion = "2.3.3" ext.htmlunitVersion = "2.19" ext.httpasyncVersion = "4.1.1" @@ -66,15 +66,15 @@ configure(allprojects) { project -> ext.reactorVersion = "2.0.7.RELEASE" ext.romeVersion = "1.5.1" ext.seleniumVersion = "2.48.2" - ext.slf4jVersion = "1.7.15" - ext.snakeyamlVersion = "1.16" + ext.slf4jVersion = "1.7.16" + ext.snakeyamlVersion = "1.17" ext.snifferVersion = "1.14" ext.testngVersion = "6.9.10" ext.tiles2Version = "2.2.2" ext.tiles3Version = "3.0.5" ext.tomcatVersion = "8.0.32" ext.tyrusVersion = "1.3.5" // constrained by WebLogic 12.1.3 support - ext.undertowVersion = "1.3.17.Final" + ext.undertowVersion = "1.3.18.Final" ext.woodstoxVersion = "5.0.1" ext.xmlunitVersion = "1.6" ext.xstreamVersion = "1.4.8" From 3f795e64c6d459a750ac60b6b4c667fb957070c8 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 22 Feb 2016 23:21:59 +0100 Subject: [PATCH 059/344] Avoid NPE in case of @Lazy @Autowired(required=false) Issue: SPR-13967 (cherry picked from commit b9fe6d8) --- ...xtAnnotationAutowireCandidateResolver.java | 10 ++++-- ...wiredAnnotationBeanPostProcessorTests.java | 35 ++++++++++++++++++- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ContextAnnotationAutowireCandidateResolver.java b/spring-context/src/main/java/org/springframework/context/annotation/ContextAnnotationAutowireCandidateResolver.java index faf0355fd5..56ba95b68e 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ContextAnnotationAutowireCandidateResolver.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ContextAnnotationAutowireCandidateResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import org.springframework.aop.TargetSource; import org.springframework.aop.framework.ProxyFactory; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver; import org.springframework.beans.factory.config.DependencyDescriptor; import org.springframework.beans.factory.support.DefaultListableBeanFactory; @@ -79,7 +80,12 @@ public boolean isStatic() { } @Override public Object getTarget() { - return beanFactory.doResolveDependency(descriptor, beanName, null, null); + Object target = beanFactory.doResolveDependency(descriptor, beanName, null, null); + if (target == null) { + throw new NoSuchBeanDefinitionException(descriptor.getDependencyType(), + "Optional dependency not present for lazy injection point"); + } + return target; } @Override public void releaseTarget(Object target) { diff --git a/spring-context/src/test/java/org/springframework/context/annotation/LazyAutowiredAnnotationBeanPostProcessorTests.java b/spring-context/src/test/java/org/springframework/context/annotation/LazyAutowiredAnnotationBeanPostProcessorTests.java index 043ed736e7..9c123a41b5 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/LazyAutowiredAnnotationBeanPostProcessorTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/LazyAutowiredAnnotationBeanPostProcessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -118,6 +118,28 @@ public void testLazyResourceInjectionWithNonExistingTarget() { } } + @Test + public void testLazyOptionalResourceInjectionWithNonExistingTarget() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + bf.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); + AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); + bpp.setBeanFactory(bf); + bf.addBeanPostProcessor(bpp); + RootBeanDefinition bd = new RootBeanDefinition(OptionalFieldResourceInjectionBean.class); + bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bf.registerBeanDefinition("annotatedBean", bd); + + OptionalFieldResourceInjectionBean bean = (OptionalFieldResourceInjectionBean) bf.getBean("annotatedBean"); + assertNotNull(bean.getTestBean()); + try { + bean.getTestBean().getName(); + fail("Should have thrown NoSuchBeanDefinitionException"); + } + catch (NoSuchBeanDefinitionException ex) { + // expected; + } + } + public interface TestBeanHolder { @@ -136,6 +158,17 @@ public TestBean getTestBean() { } + public static class OptionalFieldResourceInjectionBean implements TestBeanHolder { + + @Autowired(required = false) @Lazy + private TestBean testBean; + + public TestBean getTestBean() { + return this.testBean; + } + } + + public static class FieldResourceInjectionBeanWithCompositeAnnotation implements TestBeanHolder { @LazyInject From 9f2b3ca8b8ec6149e8937e881476fc6134994b37 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 22 Feb 2016 23:26:41 +0100 Subject: [PATCH 060/344] Polishing (cherry picked from commit 1899fb3) --- .../core/io/support/EncodedResource.java | 8 ++++---- .../core/io/support/PropertiesLoaderUtils.java | 4 ++-- .../springframework/test/context/ActiveProfiles.java | 10 +--------- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/io/support/EncodedResource.java b/spring-core/src/main/java/org/springframework/core/io/support/EncodedResource.java index 638af8ebe5..73c9b158b2 100644 --- a/spring-core/src/main/java/org/springframework/core/io/support/EncodedResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/support/EncodedResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -52,7 +52,7 @@ public class EncodedResource implements InputStreamSource { /** * Create a new {@code EncodedResource} for the given {@code Resource}, * not specifying an explicit encoding or {@code Charset}. - * @param resource the {@code Resource} to hold; never {@code null} + * @param resource the {@code Resource} to hold (never {@code null}) */ public EncodedResource(Resource resource) { this(resource, null, null); @@ -61,7 +61,7 @@ public EncodedResource(Resource resource) { /** * Create a new {@code EncodedResource} for the given {@code Resource}, * using the specified {@code encoding}. - * @param resource the {@code Resource} to hold; never {@code null} + * @param resource the {@code Resource} to hold (never {@code null}) * @param encoding the encoding to use for reading from the resource */ public EncodedResource(Resource resource, String encoding) { @@ -71,7 +71,7 @@ public EncodedResource(Resource resource, String encoding) { /** * Create a new {@code EncodedResource} for the given {@code Resource}, * using the specified {@code Charset}. - * @param resource the {@code Resource} to hold; never {@code null} + * @param resource the {@code Resource} to hold (never {@code null}) * @param charset the {@code Charset} to use for reading from the resource */ public EncodedResource(Resource resource, Charset charset) { diff --git a/spring-core/src/main/java/org/springframework/core/io/support/PropertiesLoaderUtils.java b/spring-core/src/main/java/org/springframework/core/io/support/PropertiesLoaderUtils.java index c501a52fe6..a9fa5d6db5 100644 --- a/spring-core/src/main/java/org/springframework/core/io/support/PropertiesLoaderUtils.java +++ b/spring-core/src/main/java/org/springframework/core/io/support/PropertiesLoaderUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -183,7 +183,7 @@ public static Properties loadAllProperties(String resourceName, ClassLoader clas ResourceUtils.useCachesIfNecessary(con); InputStream is = con.getInputStream(); try { - if (resourceName != null && resourceName.endsWith(XML_FILE_EXTENSION)) { + if (resourceName.endsWith(XML_FILE_EXTENSION)) { props.loadFromXML(is); } else { diff --git a/spring-test/src/main/java/org/springframework/test/context/ActiveProfiles.java b/spring-test/src/main/java/org/springframework/test/context/ActiveProfiles.java index a90b93a071..fc259102ed 100644 --- a/spring-test/src/main/java/org/springframework/test/context/ActiveProfiles.java +++ b/spring-test/src/main/java/org/springframework/test/context/ActiveProfiles.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -51,7 +51,6 @@ /** * Alias for {@link #profiles}. - * *

    This attribute may not be used in conjunction with * {@link #profiles}, but it may be used instead of {@link #profiles}. */ @@ -60,7 +59,6 @@ /** * The bean definition profiles to activate. - * *

    This attribute may not be used in conjunction with * {@link #value}, but it may be used instead of {@link #value}. */ @@ -70,7 +68,6 @@ /** * The type of {@link ActiveProfilesResolver} to use for resolving the active * bean definition profiles programmatically. - * * @since 4.0 * @see ActiveProfilesResolver */ @@ -79,18 +76,15 @@ /** * Whether or not bean definition profiles from superclasses should be * inherited. - * *

    The default value is {@code true}, which means that a test * class will inherit bean definition profiles defined by a * test superclass. Specifically, the bean definition profiles for a test * class will be appended to the list of bean definition profiles * defined by a test superclass. Thus, subclasses have the option of * extending the list of bean definition profiles. - * *

    If {@code inheritProfiles} is set to {@code false}, the bean * definition profiles for the test class will shadow and * effectively replace any bean definition profiles defined by a superclass. - * *

    In the following example, the {@code ApplicationContext} for * {@code BaseTest} will be loaded using only the "base" * bean definition profile; beans defined in the "extended" profile @@ -110,11 +104,9 @@ * // ... * } * - * *

    Note: {@code @ActiveProfiles} can be used when loading an * {@code ApplicationContext} from path-based resource locations or * annotated classes. - * * @see ContextConfiguration#locations * @see ContextConfiguration#classes * @see ContextConfiguration#inheritLocations From 97b017781aa5c6b5e2f85dfb3dcd265c1c9939e9 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 24 Feb 2016 17:11:36 +0100 Subject: [PATCH 061/344] TimerScheduledFuture correctly calculates getDelay result Issue: SPR-13977 (cherry picked from commit a68b910) --- .../scheduling/commonj/TimerManagerTaskScheduler.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/commonj/TimerManagerTaskScheduler.java b/spring-context-support/src/main/java/org/springframework/scheduling/commonj/TimerManagerTaskScheduler.java index b1d4b2207f..2d392dea32 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/commonj/TimerManagerTaskScheduler.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/commonj/TimerManagerTaskScheduler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -134,7 +134,7 @@ public boolean cancel(boolean mayInterruptIfRunning) { @Override public long getDelay(TimeUnit unit) { - return unit.convert(System.currentTimeMillis() - this.timer.getScheduledExecutionTime(), TimeUnit.MILLISECONDS); + return unit.convert(this.timer.getScheduledExecutionTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS); } @Override From 9c5cabf4bae668d3de5bd48378d5671b2a0c7095 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 24 Feb 2016 17:50:14 +0100 Subject: [PATCH 062/344] Refined ApplicationContextInitializer assignability exception (cherry picked from commit ca19920) --- .../java/org/springframework/util/Assert.java | 6 +- .../support/AbstractContextLoader.java | 89 +++++++++---------- .../web/context/ContextLoader.java | 38 ++++---- .../web/servlet/FrameworkServlet.java | 15 ++-- .../web/context/ContextLoaderTests.java | 4 +- 5 files changed, 72 insertions(+), 80 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/util/Assert.java b/spring-core/src/main/java/org/springframework/util/Assert.java index 959d29147b..1d7c477090 100644 --- a/spring-core/src/main/java/org/springframework/util/Assert.java +++ b/spring-core/src/main/java/org/springframework/util/Assert.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of @@ -373,8 +373,8 @@ public static void isAssignable(Class superType, Class subType) { public static void isAssignable(Class superType, Class subType, String message) { notNull(superType, "Type to check against must not be null"); if (subType == null || !superType.isAssignableFrom(subType)) { - throw new IllegalArgumentException((StringUtils.hasLength(message) ? message + " " : "") - + subType + " is not assignable to " + superType); + throw new IllegalArgumentException((StringUtils.hasLength(message) ? message + " " : "") + + subType + " is not assignable to " + superType); } } diff --git a/spring-test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java b/spring-test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java index d8ae5a6611..6994c79821 100644 --- a/spring-test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java +++ b/spring-test/src/main/java/org/springframework/test/context/support/AbstractContextLoader.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ import org.springframework.beans.BeanUtils; import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextException; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.GenericTypeResolver; @@ -86,8 +87,8 @@ public abstract class AbstractContextLoader implements SmartContextLoader { */ @Override public void processContextConfiguration(ContextConfigurationAttributes configAttributes) { - String[] processedLocations = processLocations(configAttributes.getDeclaringClass(), - configAttributes.getLocations()); + String[] processedLocations = + processLocations(configAttributes.getDeclaringClass(), configAttributes.getLocations()); configAttributes.setLocations(processedLocations); } @@ -135,7 +136,9 @@ protected void prepareContext(ConfigurableApplicationContext context, MergedCont @SuppressWarnings("unchecked") private void invokeApplicationContextInitializers(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) { - Set>> initializerClasses = mergedConfig.getContextInitializerClasses(); + + Set>> initializerClasses = + mergedConfig.getContextInitializerClasses(); if (initializerClasses.isEmpty()) { // no ApplicationContextInitializers have been declared -> nothing to do return; @@ -145,13 +148,15 @@ private void invokeApplicationContextInitializers(ConfigurableApplicationContext Class contextClass = context.getClass(); for (Class> initializerClass : initializerClasses) { - Class initializerContextClass = GenericTypeResolver.resolveTypeArgument(initializerClass, - ApplicationContextInitializer.class); - Assert.isAssignable(initializerContextClass, contextClass, String.format( - "Could not add context initializer [%s] since its generic parameter [%s] " - + "is not assignable from the type of application context used by this " - + "context loader [%s]: ", initializerClass.getName(), initializerContextClass.getName(), - contextClass.getName())); + Class initializerContextClass = + GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class); + if (initializerContextClass != null && !initializerContextClass.isInstance(context)) { + throw new ApplicationContextException(String.format( + "Could not apply context initializer [%s] since its generic parameter [%s] " + + "is not assignable from the type of application context used by this " + + "context loader: [%s]", initializerClass.getName(), initializerContextClass.getName(), + contextClass.getName())); + } initializerInstances.add((ApplicationContextInitializer) BeanUtils.instantiateClass(initializerClass)); } @@ -161,6 +166,7 @@ private void invokeApplicationContextInitializers(ConfigurableApplicationContext } } + // --- ContextLoader ------------------------------------------------------- /** @@ -171,7 +177,6 @@ private void invokeApplicationContextInitializers(ConfigurableApplicationContext * and the configured {@linkplain #getResourceSuffixes() resource suffixes}; * otherwise, the supplied {@code locations} will be * {@linkplain #modifyLocations modified} if necessary and returned. - * * @param clazz the class with which the locations are associated: to be * used when generating default locations * @param locations the unmodified locations to use for loading the @@ -186,30 +191,26 @@ private void invokeApplicationContextInitializers(ConfigurableApplicationContext */ @Override public final String[] processLocations(Class clazz, String... locations) { - return (ObjectUtils.isEmpty(locations) && isGenerateDefaultLocations()) ? generateDefaultLocations(clazz) - : modifyLocations(clazz, locations); + return (ObjectUtils.isEmpty(locations) && isGenerateDefaultLocations()) ? + generateDefaultLocations(clazz) : modifyLocations(clazz, locations); } /** * Generate the default classpath resource locations array based on the * supplied class. - * *

    For example, if the supplied class is {@code com.example.MyTest}, * the generated locations will contain a single string with a value of * {@code "classpath:com/example/MyTest"}, where {@code } * is the value of the first configured * {@linkplain #getResourceSuffixes() resource suffix} for which the * generated location actually exists in the classpath. - * *

    As of Spring 3.1, the implementation of this method adheres to the * contract defined in the {@link SmartContextLoader} SPI. Specifically, * this method will preemptively verify that the generated default * location actually exists. If it does not exist, this method will log a * warning and return an empty array. - * *

    Subclasses can override this method to implement a different * default location generation strategy. - * * @param clazz the class for which the default locations are to be generated * @return an array of default application context resource locations * @since 2.5 @@ -224,23 +225,22 @@ protected String[] generateDefaultLocations(Class clazz) { String resourcePath = ClassUtils.convertClassNameToResourcePath(clazz.getName()) + suffix; String prefixedResourcePath = ResourceUtils.CLASSPATH_URL_PREFIX + resourcePath; ClassPathResource classPathResource = new ClassPathResource(resourcePath); - if (classPathResource.exists()) { if (logger.isInfoEnabled()) { logger.info(String.format("Detected default resource location \"%s\" for test class [%s]", - prefixedResourcePath, clazz.getName())); + prefixedResourcePath, clazz.getName())); } - return new String[] { prefixedResourcePath }; + return new String[] {prefixedResourcePath}; } else if (logger.isDebugEnabled()) { - logger.debug(String.format("Did not detect default resource location for test class [%s]: " - + "%s does not exist", clazz.getName(), classPathResource)); + logger.debug(String.format("Did not detect default resource location for test class [%s]: " + + "%s does not exist", clazz.getName(), classPathResource)); } } if (logger.isInfoEnabled()) { - logger.info(String.format("Could not detect default resource locations for test class [%s]: " - + "no resource found for suffixes %s.", clazz.getName(), ObjectUtils.nullSafeToString(suffixes))); + logger.info(String.format("Could not detect default resource locations for test class [%s]: " + + "no resource found for suffixes %s.", clazz.getName(), ObjectUtils.nullSafeToString(suffixes))); } return EMPTY_STRING_ARRAY; @@ -282,36 +282,31 @@ protected boolean isGenerateDefaultLocations() { } /** - * Get the suffix to append to {@link ApplicationContext} resource - * locations when detecting default locations. - * - *

    Subclasses must provide an implementation of this method that - * returns a single suffix. Alternatively subclasses may provide a - * no-op implementation of this method and override - * {@link #getResourceSuffixes()} in order to provide multiple custom - * suffixes. - * - * @return the resource suffix; never {@code null} or empty - * @since 2.5 - * @see #generateDefaultLocations(Class) - * @see #getResourceSuffixes() - */ - protected abstract String getResourceSuffix(); - - /** - * Get the suffixes to append to {@link ApplicationContext} resource - * locations when detecting default locations. - * + * Get the suffixes to append to {@link ApplicationContext} resource locations + * when detecting default locations. *

    The default implementation simply wraps the value returned by * {@link #getResourceSuffix()} in a single-element array, but this * can be overridden by subclasses in order to support multiple suffixes. - * * @return the resource suffixes; never {@code null} or empty * @since 4.1 * @see #generateDefaultLocations(Class) */ protected String[] getResourceSuffixes() { - return new String[] { getResourceSuffix() }; + return new String[] {getResourceSuffix()}; } + /** + * Get the suffix to append to {@link ApplicationContext} resource locations + * when detecting default locations. + *

    Subclasses must provide an implementation of this method that returns + * a single suffix. Alternatively subclasses may provide a no-op + * implementation of this method and override {@link #getResourceSuffixes()} + * in order to provide multiple custom suffixes. + * @return the resource suffix; never {@code null} or empty + * @since 2.5 + * @see #generateDefaultLocations(Class) + * @see #getResourceSuffixes() + */ + protected abstract String getResourceSuffix(); + } diff --git a/spring-web/src/main/java/org/springframework/web/context/ContextLoader.java b/spring-web/src/main/java/org/springframework/web/context/ContextLoader.java index 9da29a744d..595d2908c0 100644 --- a/spring-web/src/main/java/org/springframework/web/context/ContextLoader.java +++ b/spring-web/src/main/java/org/springframework/web/context/ContextLoader.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -49,19 +49,18 @@ * Performs the actual initialization work for the root application context. * Called by {@link ContextLoaderListener}. * - *

    Looks for a {@link #CONTEXT_CLASS_PARAM "contextClass"} parameter - * at the {@code web.xml} context-param level to specify the context - * class type, falling back to the default of - * {@link org.springframework.web.context.support.XmlWebApplicationContext} + *

    Looks for a {@link #CONTEXT_CLASS_PARAM "contextClass"} parameter at the + * {@code web.xml} context-param level to specify the context class type, falling + * back to {@link org.springframework.web.context.support.XmlWebApplicationContext} * if not found. With the default ContextLoader implementation, any context class - * specified needs to implement the ConfigurableWebApplicationContext interface. + * specified needs to implement the {@link ConfigurableWebApplicationContext} interface. * - *

    Processes a {@link #CONFIG_LOCATION_PARAM "contextConfigLocation"} - * context-param and passes its value to the context instance, parsing it into - * potentially multiple file paths which can be separated by any number of - * commas and spaces, e.g. "WEB-INF/applicationContext1.xml, - * WEB-INF/applicationContext2.xml". Ant-style path patterns are supported as well, - * e.g. "WEB-INF/*Context.xml,WEB-INF/spring*.xml" or "WEB-INF/**/*Context.xml". + *

    Processes a {@link #CONFIG_LOCATION_PARAM "contextConfigLocation"} context-param + * and passes its value to the context instance, parsing it into potentially multiple + * file paths which can be separated by any number of commas and spaces, e.g. + * "WEB-INF/applicationContext1.xml, WEB-INF/applicationContext2.xml". + * Ant-style path patterns are supported as well, e.g. + * "WEB-INF/*Context.xml,WEB-INF/spring*.xml" or "WEB-INF/**/*Context.xml". * If not explicitly specified, the context implementation is supposed to use a * default location (with XmlWebApplicationContext: "/WEB-INF/applicationContext.xml"). * @@ -70,10 +69,9 @@ * Spring's default ApplicationContext implementations. This can be leveraged * to deliberately override certain bean definitions via an extra XML file. * - *

    Above and beyond loading the root application context, this class - * can optionally load or obtain and hook up a shared parent context to - * the root application context. See the - * {@link #loadParentContext(ServletContext)} method for more information. + *

    Above and beyond loading the root application context, this class can optionally + * load or obtain and hook up a shared parent context to the root application context. + * See the {@link #loadParentContext(ServletContext)} method for more information. * *

    As of Spring 3.1, {@code ContextLoader} supports injecting the root web * application context via the {@link #ContextLoader(WebApplicationContext)} @@ -470,11 +468,11 @@ protected void customizeContext(ServletContext sc, ConfigurableWebApplicationCon for (Class> initializerClass : initializerClasses) { Class initializerContextClass = GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class); - if (initializerContextClass != null) { - Assert.isAssignable(initializerContextClass, wac.getClass(), String.format( - "Could not add context initializer [%s] since its generic parameter [%s] " + + if (initializerContextClass != null && !initializerContextClass.isInstance(wac)) { + throw new ApplicationContextException(String.format( + "Could not apply context initializer [%s] since its generic parameter [%s] " + "is not assignable from the type of application context used by this " + - "context loader [%s]: ", initializerClass.getName(), initializerContextClass.getName(), + "context loader: [%s]", initializerClass.getName(), initializerContextClass.getName(), wac.getClass().getName())); } this.contextInitializers.add(BeanUtils.instantiateClass(initializerClass)); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java index e3d859fc0a..b880be5050 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,7 +43,6 @@ import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.http.HttpMethod; -import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -738,17 +737,17 @@ private ApplicationContextInitializer loadInitia Class initializerClass = ClassUtils.forName(className, wac.getClassLoader()); Class initializerContextClass = GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class); - if (initializerContextClass != null) { - Assert.isAssignable(initializerContextClass, wac.getClass(), String.format( - "Could not add context initializer [%s] since its generic parameter [%s] " + + if (initializerContextClass != null && !initializerContextClass.isInstance(wac)) { + throw new ApplicationContextException(String.format( + "Could not apply context initializer [%s] since its generic parameter [%s] " + "is not assignable from the type of application context used by this " + - "framework servlet [%s]: ", initializerClass.getName(), initializerContextClass.getName(), + "framework servlet: [%s]", initializerClass.getName(), initializerContextClass.getName(), wac.getClass().getName())); } return BeanUtils.instantiateClass(initializerClass, ApplicationContextInitializer.class); } - catch (Exception ex) { - throw new IllegalArgumentException(String.format("Could not instantiate class [%s] specified " + + catch (ClassNotFoundException ex) { + throw new ApplicationContextException(String.format("Could not load class [%s] specified " + "via 'contextInitializerClasses' init-param", className), ex); } } diff --git a/spring-webmvc/src/test/java/org/springframework/web/context/ContextLoaderTests.java b/spring-webmvc/src/test/java/org/springframework/web/context/ContextLoaderTests.java index c54e84dc9e..55667bb347 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/context/ContextLoaderTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/context/ContextLoaderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -225,7 +225,7 @@ public void testContextLoaderListenerWithUnknownContextInitializer() { listener.contextInitialized(new ServletContextEvent(sc)); fail("expected exception"); } - catch (IllegalArgumentException ex) { + catch (ApplicationContextException ex) { assertTrue(ex.getMessage().contains("not assignable")); } } From cafb99a33d1b644de9012142ab69c7ecd23215a4 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 23 Feb 2016 14:31:09 +0100 Subject: [PATCH 063/344] Polishing (cherry picked from commit 028a690) --- .../springframework/core/OrderComparator.java | 4 +- .../test/context/jdbc/Sql.java | 42 ++-- .../test/context/jdbc/SqlConfig.java | 227 +++++++++--------- .../test/context/jdbc/SqlGroup.java | 4 +- 4 files changed, 139 insertions(+), 138 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/OrderComparator.java b/spring-core/src/main/java/org/springframework/core/OrderComparator.java index 0a121dd227..3d976d94af 100644 --- a/spring-core/src/main/java/org/springframework/core/OrderComparator.java +++ b/spring-core/src/main/java/org/springframework/core/OrderComparator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -203,7 +203,7 @@ else if (value instanceof List) { * Strategy interface to provide an order source for a given object. * @since 4.1 */ - public static interface OrderSourceProvider { + public interface OrderSourceProvider { /** * Return an order source for the specified object, i.e. an object that diff --git a/spring-test/src/main/java/org/springframework/test/context/jdbc/Sql.java b/spring-test/src/main/java/org/springframework/test/context/jdbc/Sql.java index b565912716..e413b7b3e0 100644 --- a/spring-test/src/main/java/org/springframework/test/context/jdbc/Sql.java +++ b/spring-test/src/main/java/org/springframework/test/context/jdbc/Sql.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -68,29 +68,10 @@ @Documented @Inherited @Retention(RUNTIME) -@Target({ TYPE, METHOD }) +@Target({TYPE, METHOD}) @Repeatable(SqlGroup.class) public @interface Sql { - /** - * Enumeration of phases that dictate when SQL scripts are executed. - */ - static enum ExecutionPhase { - - /** - * The configured SQL scripts and statements will be executed - * before the corresponding test method. - */ - BEFORE_TEST_METHOD, - - /** - * The configured SQL scripts and statements will be executed - * after the corresponding test method. - */ - AFTER_TEST_METHOD - } - - /** * Alias for {@link #scripts}. *

    This attribute may not be used in conjunction with @@ -173,4 +154,23 @@ static enum ExecutionPhase { */ SqlConfig config() default @SqlConfig(); + + /** + * Enumeration of phases that dictate when SQL scripts are executed. + */ + enum ExecutionPhase { + + /** + * The configured SQL scripts and statements will be executed + * before the corresponding test method. + */ + BEFORE_TEST_METHOD, + + /** + * The configured SQL scripts and statements will be executed + * after the corresponding test method. + */ + AFTER_TEST_METHOD + } + } diff --git a/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlConfig.java b/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlConfig.java index 6a94a82754..9c0d55a6a0 100644 --- a/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlConfig.java +++ b/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -65,12 +65,122 @@ @Target(TYPE) public @interface SqlConfig { + /** + * The bean name of the {@link javax.sql.DataSource} against which the + * scripts should be executed. + *

    The name is only required if there is more than one bean of type + * {@code DataSource} in the test's {@code ApplicationContext}. If there + * is only one such bean, it is not necessary to specify a bean name. + *

    Defaults to an empty string, requiring that one of the following is + * true: + *

      + *
    1. An explicit bean name is defined in a global declaration of + * {@code @SqlConfig}. + *
    2. The data source can be retrieved from the transaction manager + * by using reflection to invoke a public method named + * {@code getDataSource()} on the transaction manager. + *
    3. There is only one bean of type {@code DataSource} in the test's + * {@code ApplicationContext}.
    4. + *
    5. The {@code DataSource} to use is named {@code "dataSource"}.
    6. + *
    + * @see org.springframework.test.context.transaction.TestContextTransactionUtils#retrieveDataSource + */ + String dataSource() default ""; + + /** + * The bean name of the {@link org.springframework.transaction.PlatformTransactionManager + * PlatformTransactionManager} that should be used to drive transactions. + *

    The name is only used if there is more than one bean of type + * {@code PlatformTransactionManager} in the test's {@code ApplicationContext}. + * If there is only one such bean, it is not necessary to specify a bean name. + *

    Defaults to an empty string, requiring that one of the following is + * true: + *

      + *
    1. An explicit bean name is defined in a global declaration of + * {@code @SqlConfig}. + *
    2. There is only one bean of type {@code PlatformTransactionManager} in + * the test's {@code ApplicationContext}.
    3. + *
    4. {@link org.springframework.transaction.annotation.TransactionManagementConfigurer + * TransactionManagementConfigurer} has been implemented to specify which + * {@code PlatformTransactionManager} bean should be used for annotation-driven + * transaction management.
    5. + *
    6. The {@code PlatformTransactionManager} to use is named + * {@code "transactionManager"}.
    7. + *
    + * @see org.springframework.test.context.transaction.TestContextTransactionUtils#retrieveTransactionManager + */ + String transactionManager() default ""; + + /** + * The mode to use when determining whether SQL scripts should be + * executed within a transaction. + *

    Defaults to {@link TransactionMode#DEFAULT DEFAULT}. + *

    Can be set to {@link TransactionMode#ISOLATED} to ensure that the SQL + * scripts are executed in a new, isolated transaction that will be immediately + * committed. + * @see TransactionMode + */ + TransactionMode transactionMode() default TransactionMode.DEFAULT; + + /** + * The encoding for the supplied SQL scripts, if different from the platform + * encoding. + *

    An empty string denotes that the platform encoding should be used. + */ + String encoding() default ""; + + /** + * The character string used to separate individual statements within the + * SQL scripts. + *

    Implicitly defaults to {@code ";"} if not specified and falls back to + * {@code "\n"} as a last resort. + *

    May be set to + * {@link org.springframework.jdbc.datasource.init.ScriptUtils#EOF_STATEMENT_SEPARATOR} + * to signal that each script contains a single statement without a + * separator. + * @see org.springframework.jdbc.datasource.init.ScriptUtils#DEFAULT_STATEMENT_SEPARATOR + * @see org.springframework.jdbc.datasource.init.ScriptUtils#EOF_STATEMENT_SEPARATOR + */ + String separator() default ""; + + /** + * The prefix that identifies single-line comments within the SQL scripts. + *

    Implicitly defaults to {@code "--"}. + * @see org.springframework.jdbc.datasource.init.ScriptUtils#DEFAULT_COMMENT_PREFIX + */ + String commentPrefix() default ""; + + /** + * The start delimiter that identifies block comments within the SQL scripts. + *

    Implicitly defaults to {@code "/*"}. + * @see #blockCommentEndDelimiter + * @see org.springframework.jdbc.datasource.init.ScriptUtils#DEFAULT_BLOCK_COMMENT_START_DELIMITER + */ + String blockCommentStartDelimiter() default ""; + + /** + * The end delimiter that identifies block comments within the SQL scripts. + *

    Implicitly defaults to "*/". + * @see #blockCommentStartDelimiter + * @see org.springframework.jdbc.datasource.init.ScriptUtils#DEFAULT_BLOCK_COMMENT_END_DELIMITER + */ + String blockCommentEndDelimiter() default ""; + + /** + * The mode to use when an error is encountered while executing an + * SQL statement. + *

    Defaults to {@link ErrorMode#DEFAULT DEFAULT}. + * @see ErrorMode + */ + ErrorMode errorMode() default ErrorMode.DEFAULT; + + /** * Enumeration of modes that dictate whether SQL scripts should be * executed within a transaction and what the transaction propagation behavior * should be. */ - static enum TransactionMode { + enum TransactionMode { /** * Indicates that the default transaction mode should be used. @@ -137,11 +247,12 @@ static enum TransactionMode { ISOLATED } + /** * Enumeration of modes that dictate how errors are handled while * executing SQL statements. */ - static enum ErrorMode { + enum ErrorMode { /** * Indicates that the default error mode should be used. @@ -188,114 +299,4 @@ static enum ErrorMode { IGNORE_FAILED_DROPS } - - /** - * The bean name of the {@link javax.sql.DataSource} against which the - * scripts should be executed. - *

    The name is only required if there is more than one bean of type - * {@code DataSource} in the test's {@code ApplicationContext}. If there - * is only one such bean, it is not necessary to specify a bean name. - *

    Defaults to an empty string, requiring that one of the following is - * true: - *

      - *
    1. An explicit bean name is defined in a global declaration of - * {@code @SqlConfig}. - *
    2. The data source can be retrieved from the transaction manager - * by using reflection to invoke a public method named - * {@code getDataSource()} on the transaction manager. - *
    3. There is only one bean of type {@code DataSource} in the test's - * {@code ApplicationContext}.
    4. - *
    5. The {@code DataSource} to use is named {@code "dataSource"}.
    6. - *
    - * @see org.springframework.test.context.transaction.TestContextTransactionUtils#retrieveDataSource - */ - String dataSource() default ""; - - /** - * The bean name of the {@link org.springframework.transaction.PlatformTransactionManager - * PlatformTransactionManager} that should be used to drive transactions. - *

    The name is only used if there is more than one bean of type - * {@code PlatformTransactionManager} in the test's {@code ApplicationContext}. - * If there is only one such bean, it is not necessary to specify a bean name. - *

    Defaults to an empty string, requiring that one of the following is - * true: - *

      - *
    1. An explicit bean name is defined in a global declaration of - * {@code @SqlConfig}. - *
    2. There is only one bean of type {@code PlatformTransactionManager} in - * the test's {@code ApplicationContext}.
    3. - *
    4. {@link org.springframework.transaction.annotation.TransactionManagementConfigurer - * TransactionManagementConfigurer} has been implemented to specify which - * {@code PlatformTransactionManager} bean should be used for annotation-driven - * transaction management.
    5. - *
    6. The {@code PlatformTransactionManager} to use is named - * {@code "transactionManager"}.
    7. - *
    - * @see org.springframework.test.context.transaction.TestContextTransactionUtils#retrieveTransactionManager - */ - String transactionManager() default ""; - - /** - * The mode to use when determining whether SQL scripts should be - * executed within a transaction. - *

    Defaults to {@link TransactionMode#DEFAULT DEFAULT}. - *

    Can be set to {@link TransactionMode#ISOLATED} to ensure that the SQL - * scripts are executed in a new, isolated transaction that will be immediately - * committed. - * @see TransactionMode - */ - TransactionMode transactionMode() default TransactionMode.DEFAULT; - - /** - * The encoding for the supplied SQL scripts, if different from the platform - * encoding. - *

    An empty string denotes that the platform encoding should be used. - */ - String encoding() default ""; - - /** - * The character string used to separate individual statements within the - * SQL scripts. - *

    Implicitly defaults to {@code ";"} if not specified and falls back to - * {@code "\n"} as a last resort. - *

    May be set to - * {@link org.springframework.jdbc.datasource.init.ScriptUtils#EOF_STATEMENT_SEPARATOR} - * to signal that each script contains a single statement without a - * separator. - * @see org.springframework.jdbc.datasource.init.ScriptUtils#DEFAULT_STATEMENT_SEPARATOR - * @see org.springframework.jdbc.datasource.init.ScriptUtils#EOF_STATEMENT_SEPARATOR - */ - String separator() default ""; - - /** - * The prefix that identifies single-line comments within the SQL scripts. - *

    Implicitly defaults to {@code "--"}. - * @see org.springframework.jdbc.datasource.init.ScriptUtils#DEFAULT_COMMENT_PREFIX - */ - String commentPrefix() default ""; - - /** - * The start delimiter that identifies block comments within the SQL scripts. - *

    Implicitly defaults to {@code "/*"}. - * @see #blockCommentEndDelimiter - * @see org.springframework.jdbc.datasource.init.ScriptUtils#DEFAULT_BLOCK_COMMENT_START_DELIMITER - */ - String blockCommentStartDelimiter() default ""; - - /** - * The end delimiter that identifies block comments within the SQL scripts. - *

    Implicitly defaults to "*/". - * @see #blockCommentStartDelimiter - * @see org.springframework.jdbc.datasource.init.ScriptUtils#DEFAULT_BLOCK_COMMENT_END_DELIMITER - */ - String blockCommentEndDelimiter() default ""; - - /** - * The mode to use when an error is encountered while executing an - * SQL statement. - *

    Defaults to {@link ErrorMode#DEFAULT DEFAULT}. - * @see ErrorMode - */ - ErrorMode errorMode() default ErrorMode.DEFAULT; - } diff --git a/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlGroup.java b/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlGroup.java index fb6a09fd7c..6e052bd607 100644 --- a/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlGroup.java +++ b/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlGroup.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,7 +42,7 @@ @Documented @Inherited @Retention(RUNTIME) -@Target({ TYPE, METHOD }) +@Target({TYPE, METHOD}) public @interface SqlGroup { Sql[] value(); From 2cbc5ff0c8acfb6b43801bde569735f329b0068f Mon Sep 17 00:00:00 2001 From: Spring Buildmaster Date: Thu, 25 Feb 2016 01:36:20 -0800 Subject: [PATCH 064/344] Next Development Version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 16c54f6a1c..dc7301f5c3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=4.2.5.BUILD-SNAPSHOT +version=4.2.6.BUILD-SNAPSHOT From 9129ec262382a6a5dd5488c553077ab1eaf10336 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 25 Feb 2016 21:42:11 +0100 Subject: [PATCH 065/344] LinkedCaseInsensitiveMap provides reliable getOrDefault implementation Issue: SPR-13981 (cherry picked from commit 7a32ce3) --- .../util/LinkedCaseInsensitiveMap.java | 32 +++++++++++++----- .../util/LinkedCaseInsensitiveMapTests.java | 33 +++++++++++++++---- 2 files changed, 49 insertions(+), 16 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/util/LinkedCaseInsensitiveMap.java b/spring-core/src/main/java/org/springframework/util/LinkedCaseInsensitiveMap.java index 62921593a1..cde4e615ef 100644 --- a/spring-core/src/main/java/org/springframework/util/LinkedCaseInsensitiveMap.java +++ b/spring-core/src/main/java/org/springframework/util/LinkedCaseInsensitiveMap.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -114,21 +114,35 @@ public boolean containsKey(Object key) { @Override public V get(Object key) { if (key instanceof String) { - return super.get(this.caseInsensitiveKeys.get(convertKey((String) key))); + String caseInsensitiveKey = this.caseInsensitiveKeys.get(convertKey((String) key)); + if (caseInsensitiveKey != null) { + return super.get(caseInsensitiveKey); + } } - else { - return null; + return null; + } + + // Overridden to avoid LinkedHashMap's own hash computation in its getOrDefault impl + @Override + public V getOrDefault(Object key, V defaultValue) { + if (key instanceof String) { + String caseInsensitiveKey = this.caseInsensitiveKeys.get(convertKey((String) key)); + if (caseInsensitiveKey != null) { + return super.get(caseInsensitiveKey); + } } + return defaultValue; } @Override public V remove(Object key) { - if (key instanceof String ) { - return super.remove(this.caseInsensitiveKeys.remove(convertKey((String) key))); - } - else { - return null; + if (key instanceof String) { + String caseInsensitiveKey = this.caseInsensitiveKeys.remove(convertKey((String) key)); + if (caseInsensitiveKey != null) { + return super.remove(caseInsensitiveKey); + } } + return null; } @Override diff --git a/spring-core/src/test/java/org/springframework/util/LinkedCaseInsensitiveMapTests.java b/spring-core/src/test/java/org/springframework/util/LinkedCaseInsensitiveMapTests.java index 7d3b171ba7..4f4492ef5a 100644 --- a/spring-core/src/test/java/org/springframework/util/LinkedCaseInsensitiveMapTests.java +++ b/spring-core/src/test/java/org/springframework/util/LinkedCaseInsensitiveMapTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,6 @@ package org.springframework.util; -import org.junit.Before; import org.junit.Test; import static org.junit.Assert.*; @@ -26,12 +25,8 @@ */ public class LinkedCaseInsensitiveMapTests { - private LinkedCaseInsensitiveMap map; + private final LinkedCaseInsensitiveMap map = new LinkedCaseInsensitiveMap(); - @Before - public void setUp() { - map = new LinkedCaseInsensitiveMap(); - } @Test public void putAndGet() { @@ -55,4 +50,28 @@ public void putWithOverlappingKeys() { assertEquals("value3", map.get("Key")); } + @Test + public void getOrDefault() { + map.put("key", "value1"); + map.put("KEY", "value2"); + map.put("Key", "value3"); + assertEquals("value3", map.getOrDefault("key", "N")); + assertEquals("value3", map.getOrDefault("KEY", "N")); + assertEquals("value3", map.getOrDefault("Key", "N")); + assertEquals("N", map.getOrDefault("keeeey", "N")); + assertEquals("N", map.getOrDefault(new Object(), "N")); + } + + @Test + public void getOrDefaultWithNullValue() { + map.put("key", null); + map.put("KEY", null); + map.put("Key", null); + assertNull(map.getOrDefault("key", "N")); + assertNull(map.getOrDefault("KEY", "N")); + assertNull(map.getOrDefault("Key", "N")); + assertEquals("N", map.getOrDefault("keeeey", "N")); + assertEquals("N", map.getOrDefault(new Object(), "N")); + } + } From 0349abc565da1523daa84d42d5950ab2ebf12df2 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 29 Feb 2016 11:12:11 +0100 Subject: [PATCH 066/344] RequestMappingHandlerAdapter properly invokes handler method in synchronizeOnSession mode again Issue: SPR-13999 (cherry picked from commit 1815a6a) --- .../RequestMappingHandlerAdapter.java | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java index f90b2a5cc6..9e84d356d9 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -117,8 +117,8 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializingBean { - private static final boolean completionStagePresent = ClassUtils.isPresent("java.util.concurrent.CompletionStage", - RequestMappingHandlerAdapter.class.getClassLoader()); + private static final boolean completionStagePresent = ClassUtils.isPresent( + "java.util.concurrent.CompletionStage", RequestMappingHandlerAdapter.class.getClassLoader()); private List customArgumentResolvers; @@ -444,7 +444,14 @@ public void setSessionAttributeStore(SessionAttributeStore sessionAttributeStore /** * Cache content produced by {@code @SessionAttributes} annotated handlers - * for the given number of seconds. Default is 0, preventing caching completely. + * for the given number of seconds. + *

    Possible values are: + *

      + *
    • -1: no generation of cache-related headers
    • + *
    • 0 (default value): "Cache-Control: no-store" will prevent caching
    • + *
    • 1 or higher: "Cache-Control: max-age=seconds" will ask to cache content; + * not advised when dealing with session attributes
    • + *
    *

    In contrast to the "cacheSeconds" property which will apply to all general * handlers (but not to {@code @SessionAttributes} annotated handlers), * this setting will apply to {@code @SessionAttributes} handlers only. @@ -727,8 +734,9 @@ protected ModelAndView handleInternal(HttpServletRequest request, } } } - - mav = invokeHandlerMethod(request, response, handlerMethod); + else { + mav = invokeHandlerMethod(request, response, handlerMethod); + } if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers); From 8a44560d82b40af65a3b79c3a5c3915ae2e3d5b7 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 29 Feb 2016 15:05:50 +0100 Subject: [PATCH 067/344] RequestMappingHandlerAdapter properly invokes handler method in case of no session as well Issue: SPR-13999 (cherry picked from commit a02fd7c) --- .../method/annotation/RequestMappingHandlerAdapter.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java index 9e84d356d9..cf5c5fce24 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java @@ -721,7 +721,7 @@ protected boolean supportsInternal(HandlerMethod handlerMethod) { protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { - ModelAndView mav = null; + ModelAndView mav; checkRequest(request); // Execute invokeHandlerMethod in synchronized block if required. @@ -733,8 +733,13 @@ protected ModelAndView handleInternal(HttpServletRequest request, mav = invokeHandlerMethod(request, response, handlerMethod); } } + else { + // No HttpSession available -> no mutex necessary + mav = invokeHandlerMethod(request, response, handlerMethod); + } } else { + // No synchronization on session demanded at all... mav = invokeHandlerMethod(request, response, handlerMethod); } From 0ef90df1204069edb5f39b9707fb81231ff86495 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Wed, 2 Mar 2016 16:53:24 +0100 Subject: [PATCH 068/344] Relax SPR-13867 changes for ResourceHttpRequestHandler Prior to this change, SPR-13867 made sure that any class extending WebContentGenerator would not overwrite existing HTTP "Cache-Control" response headers - set by a filter, a Controller handler, etc. This caused issues with resource handling, since specifying a cache configuration there would not overwrite default headers set by filters, for example by Spring Security. This commit restricts the previous changes to the RequestMappingHandlerAdapter, in order to avoid overwriting header set by a filter or a Controller handler in those cases. Issue: SPR-14005 Cherry-picked from 50bcd67fb63 --- .../RequestMappingHandlerAdapter.java | 14 +++-- .../servlet/support/WebContentGenerator.java | 62 +++++++++---------- .../ResourceHttpRequestHandlerTests.java | 11 ++++ 3 files changed, 48 insertions(+), 39 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java index cf5c5fce24..ce031bd5ff 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java @@ -743,11 +743,13 @@ protected ModelAndView handleInternal(HttpServletRequest request, mav = invokeHandlerMethod(request, response, handlerMethod); } - if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { - applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers); - } - else { - prepareResponse(response); + if (!response.containsHeader(HEADER_CACHE_CONTROL)) { + if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { + applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers); + } + else { + prepareResponse(response); + } } return mav; @@ -887,7 +889,7 @@ private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) t } List initBinderMethods = new ArrayList(); // Global methods first - for (Entry> entry : this.initBinderAdviceCache .entrySet()) { + for (Entry> entry : this.initBinderAdviceCache.entrySet()) { if (entry.getKey().isApplicableToBeanType(handlerType)) { Object bean = entry.getKey().resolveBean(); for (Method method : entry.getValue()) { diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/WebContentGenerator.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/WebContentGenerator.java index 8bf68bba4a..903aaa4c7e 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/WebContentGenerator.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/WebContentGenerator.java @@ -71,7 +71,7 @@ public abstract class WebContentGenerator extends WebApplicationObjectSupport { private static final String HEADER_EXPIRES = "Expires"; - private static final String HEADER_CACHE_CONTROL = "Cache-Control"; + protected static final String HEADER_CACHE_CONTROL = "Cache-Control"; /** Set of supported HTTP methods */ @@ -330,16 +330,14 @@ protected final void prepareResponse(HttpServletResponse response) { * @since 4.2 */ protected final void applyCacheControl(HttpServletResponse response, CacheControl cacheControl) { - if (!response.containsHeader(HEADER_CACHE_CONTROL)) { - String ccValue = cacheControl.getHeaderValue(); - if (ccValue != null) { - // Set computed HTTP 1.1 Cache-Control header - response.setHeader(HEADER_CACHE_CONTROL, ccValue); - - if (response.containsHeader(HEADER_PRAGMA)) { - // Reset HTTP 1.0 Pragma header if present - response.setHeader(HEADER_PRAGMA, ""); - } + String ccValue = cacheControl.getHeaderValue(); + if (ccValue != null) { + // Set computed HTTP 1.1 Cache-Control header + response.setHeader(HEADER_CACHE_CONTROL, ccValue); + + if (response.containsHeader(HEADER_PRAGMA)) { + // Reset HTTP 1.0 Pragma header if present + response.setHeader(HEADER_PRAGMA, ""); } } } @@ -355,32 +353,30 @@ protected final void applyCacheControl(HttpServletResponse response, CacheContro */ @SuppressWarnings("deprecation") protected final void applyCacheSeconds(HttpServletResponse response, int cacheSeconds) { - if (!response.containsHeader(HEADER_CACHE_CONTROL)) { - if (this.useExpiresHeader || !this.useCacheControlHeader) { - // Deprecated HTTP 1.0 cache behavior, as in previous Spring versions - if (cacheSeconds > 0) { - cacheForSeconds(response, cacheSeconds); - } - else if (cacheSeconds == 0) { - preventCaching(response); + if (this.useExpiresHeader || !this.useCacheControlHeader) { + // Deprecated HTTP 1.0 cache behavior, as in previous Spring versions + if (cacheSeconds > 0) { + cacheForSeconds(response, cacheSeconds); + } + else if (cacheSeconds == 0) { + preventCaching(response); + } + } + else { + CacheControl cControl; + if (cacheSeconds > 0) { + cControl = CacheControl.maxAge(cacheSeconds, TimeUnit.SECONDS); + if (this.alwaysMustRevalidate) { + cControl = cControl.mustRevalidate(); } } + else if (cacheSeconds == 0) { + cControl = (this.useCacheControlNoStore ? CacheControl.noStore() : CacheControl.noCache()); + } else { - CacheControl cControl; - if (cacheSeconds > 0) { - cControl = CacheControl.maxAge(cacheSeconds, TimeUnit.SECONDS); - if (this.alwaysMustRevalidate) { - cControl = cControl.mustRevalidate(); - } - } - else if (cacheSeconds == 0) { - cControl = (this.useCacheControlNoStore ? CacheControl.noStore() : CacheControl.noCache()); - } - else { - cControl = CacheControl.empty(); - } - applyCacheControl(response, cControl); + cControl = CacheControl.empty(); } + applyCacheControl(response, cControl); } } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandlerTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandlerTests.java index bfbda11fc9..453bdbdcb1 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandlerTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandlerTests.java @@ -503,6 +503,17 @@ public void writeContentInputStreamThrowingNullPointerException() throws Excepti assertEquals(0, this.response.getContentLength()); } + // SPR-14005 + @Test + public void doOverwriteExistingCacheControlHeaders() throws Exception { + this.request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, "foo.css"); + this.response.setHeader("Cache-Control", "no-store"); + + this.handler.handleRequest(this.request, this.response); + + assertEquals("max-age=3600", this.response.getHeader("Cache-Control")); + } + private long dateHeaderAsLong(String responseHeaderName) throws Exception { return dateFormat.parse(this.response.getHeader(responseHeaderName)).getTime(); From b88c39955b23514d1c7816e364401fa9c77d8d61 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Thu, 3 Mar 2016 16:48:17 +0100 Subject: [PATCH 069/344] Add newline at the beginning of textarea JSP tags This commit adds a newline char at the beginning of textarea tags values. As per the HTML 4.01 spec (and browsers behavior), a line break following a start tag is ignored. This can lead to Spring's textarea tag to ignore a line break char at the beginning of a tag value. See https://www.w3.org/TR/html401/appendix/notes.html#notes-line-breaks Issue: SPR-13503 Cherry picked from 44c32128 --- .../web/servlet/tags/form/TextareaTag.java | 4 ++-- .../web/servlet/tags/form/TextareaTagTests.java | 12 +++++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/TextareaTag.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/TextareaTag.java index 09539fbbb8..16987862c2 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/TextareaTag.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/TextareaTag.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -98,7 +98,7 @@ protected int writeTagContent(TagWriter tagWriter) throws JspException { writeOptionalAttribute(tagWriter, COLS_ATTRIBUTE, getCols()); writeOptionalAttribute(tagWriter, ONSELECT_ATTRIBUTE, getOnselect()); String value = getDisplayString(getBoundValue(), getPropertyEditor()); - tagWriter.appendValue(processFieldValue(getName(), value, "textarea")); + tagWriter.appendValue("\r\n" + processFieldValue(getName(), value, "textarea")); tagWriter.endTag(); return SKIP_BODY; } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/tags/form/TextareaTagTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/tags/form/TextareaTagTests.java index 503262d46b..24b686eca5 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/tags/form/TextareaTagTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/tags/form/TextareaTagTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -121,6 +121,16 @@ public void customBind() throws Exception { assertBlockTagContains(output, "12.34f"); } + @Test + public void firstNewLine() throws Exception { + this.tag.setPath("name"); + this.tag.setReadonly(true); + + assertEquals(Tag.SKIP_BODY, this.tag.doStartTag()); + String output = getOutput(); + assertBlockTagContains(output, "\r\nRob"); + } + @Override protected TestBean createTestBean() { // set up test data From f76b38c5066a080ae6916fa8687bd6ab5aff53bd Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Tue, 8 Mar 2016 17:07:59 +0100 Subject: [PATCH 070/344] Fix SpringUrl Velocity macro URI encoding Prior to this commit, the springUrl Velocity macro would only prepend the context to the given URL; this means that the ServletHttpResponse.encodeUri method is not called and neither the ResourceUrlProvider. This commit changes this macro to use RequestContext.getContextUrl which prepends the context and encodes the URI. Issue: SPR-14027 Cherry-picked from 0a56667093 --- .../org/springframework/web/servlet/view/velocity/spring.vm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-webmvc/src/main/resources/org/springframework/web/servlet/view/velocity/spring.vm b/spring-webmvc/src/main/resources/org/springframework/web/servlet/view/velocity/spring.vm index 9064115ee1..91936429ad 100644 --- a/spring-webmvc/src/main/resources/org/springframework/web/servlet/view/velocity/spring.vm +++ b/spring-webmvc/src/main/resources/org/springframework/web/servlet/view/velocity/spring.vm @@ -55,7 +55,7 @@ * Takes a relative URL and makes it absolute from the server root by * adding the context root for the web application. *# -#macro( springUrl $relativeUrl )$springMacroRequestContext.getContextPath()${relativeUrl}#end +#macro( springUrl $relativeUrl )$springMacroRequestContext.getContextUrl(${relativeUrl})#end #** * springBind From bd82acbf99dc71985b9f7af3027aab53d655a235 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Wed, 9 Mar 2016 11:55:20 +0100 Subject: [PATCH 071/344] Support ResolvableTypeProvider on simple event pojo Previously, the generic type of a simple pojo event implementing ResolvableTypeProvider wasn't detected properly. This commit fixes the logic when the generic type is not provided to reuse what PayloadApplicationEvent is already doing anyway. Issue: SPR-14029 --- .../support/AbstractApplicationContext.java | 2 +- .../AnnotationDrivenEventListenerTests.java | 37 ++++++++++++- .../context/event/test/GenericEventPojo.java | 53 +++++++++++++++++++ 3 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 spring-context/src/test/java/org/springframework/context/event/test/GenericEventPojo.java diff --git a/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java b/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java index a3d9778347..d21d28177b 100644 --- a/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java +++ b/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java @@ -369,7 +369,7 @@ protected void publishEvent(Object event, ResolvableType eventType) { else { applicationEvent = new PayloadApplicationEvent(this, event); if (eventType == null) { - eventType = ResolvableType.forClassWithGenerics(PayloadApplicationEvent.class, event.getClass()); + eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType(); } } diff --git a/spring-context/src/test/java/org/springframework/context/event/AnnotationDrivenEventListenerTests.java b/spring-context/src/test/java/org/springframework/context/event/AnnotationDrivenEventListenerTests.java index 7f40e7442c..3e52c58b39 100644 --- a/spring-context/src/test/java/org/springframework/context/event/AnnotationDrivenEventListenerTests.java +++ b/spring-context/src/test/java/org/springframework/context/event/AnnotationDrivenEventListenerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -50,6 +50,7 @@ import org.springframework.context.event.test.AbstractIdentifiable; import org.springframework.context.event.test.AnotherTestEvent; import org.springframework.context.event.test.EventCollector; +import org.springframework.context.event.test.GenericEventPojo; import org.springframework.context.event.test.Identifiable; import org.springframework.context.event.test.TestEvent; import org.springframework.context.support.ClassPathXmlApplicationContext; @@ -443,6 +444,30 @@ public void listenerWithGenericApplicationEvent() { this.eventCollector.assertTotalEventsCount(1); } + @Test + public void listenerWithResolvableTypeEvent() { + load(ResolvableTypeEventListener.class); + ResolvableTypeEventListener listener = this.context.getBean(ResolvableTypeEventListener.class); + + this.eventCollector.assertNoEventReceived(listener); + GenericEventPojo event = new GenericEventPojo<>("TEST"); + this.context.publishEvent(event); + this.eventCollector.assertEvent(listener, event); + this.eventCollector.assertTotalEventsCount(1); + } + + @Test + public void listenerWithResolvableTypeEventWrongGeneric() { + load(ResolvableTypeEventListener.class); + ResolvableTypeEventListener listener = this.context.getBean(ResolvableTypeEventListener.class); + + this.eventCollector.assertNoEventReceived(listener); + GenericEventPojo event = new GenericEventPojo<>(123L); + this.context.publishEvent(event); + this.eventCollector.assertNoEventReceived(listener); + this.eventCollector.assertTotalEventsCount(0); + } + @Test public void conditionMatch() { long timestamp = System.currentTimeMillis(); @@ -791,6 +816,16 @@ public void handleString(PayloadApplicationEvent event) { } + @Component + static class ResolvableTypeEventListener extends AbstractTestEventListener { + + @EventListener + public void handleString(GenericEventPojo value) { + collectEvent(value); + } + } + + @Component static class ConditionalEventListener extends TestEventListener { diff --git a/spring-context/src/test/java/org/springframework/context/event/test/GenericEventPojo.java b/spring-context/src/test/java/org/springframework/context/event/test/GenericEventPojo.java new file mode 100644 index 0000000000..532f08ae01 --- /dev/null +++ b/spring-context/src/test/java/org/springframework/context/event/test/GenericEventPojo.java @@ -0,0 +1,53 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.context.event.test; + +import org.springframework.core.ResolvableType; +import org.springframework.core.ResolvableTypeProvider; + +/** + * A simple POJO that implements {@link ResolvableTypeProvider}. + * + * @author Stephane Nicoll + */ +public class GenericEventPojo implements ResolvableTypeProvider { + private final T value; + + public GenericEventPojo(T value) { + this.value = value; + } + + @Override + public ResolvableType getResolvableType() { + return ResolvableType.forClassWithGenerics(getClass(), ResolvableType.forInstance(this.value)); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + GenericEventPojo that = (GenericEventPojo) o; + + return this.value.equals(that.value); + } + + @Override + public int hashCode() { + return this.value.hashCode(); + } +} From d7062f6291904b86b683cbffe537121506cbffcc Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Mon, 14 Mar 2016 23:00:19 -0400 Subject: [PATCH 072/344] Ensure RedirectModel is initialized This commit fixes an old bug in ModelAndViewContainer where getModel returns a new ModelMap instance that isn't saved and re-used. Issue: SPR-14045 --- .../method/support/ModelAndViewContainer.java | 5 ++++- .../support/ModelAndViewContainerTests.java | 13 ++++++++++++- ...odelAndViewMethodReturnValueHandlerTests.java | 16 +++++++++++++++- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/method/support/ModelAndViewContainer.java b/spring-web/src/main/java/org/springframework/web/method/support/ModelAndViewContainer.java index d485607516..0f141ff6ef 100644 --- a/spring-web/src/main/java/org/springframework/web/method/support/ModelAndViewContainer.java +++ b/spring-web/src/main/java/org/springframework/web/method/support/ModelAndViewContainer.java @@ -126,7 +126,10 @@ public ModelMap getModel() { return this.defaultModel; } else { - return (this.redirectModel != null) ? this.redirectModel : new ModelMap(); + if (this.redirectModel == null) { + this.redirectModel = new ModelMap(); + } + return this.redirectModel; } } diff --git a/spring-web/src/test/java/org/springframework/web/method/support/ModelAndViewContainerTests.java b/spring-web/src/test/java/org/springframework/web/method/support/ModelAndViewContainerTests.java index 114c6712d8..c5f481e0e2 100644 --- a/spring-web/src/test/java/org/springframework/web/method/support/ModelAndViewContainerTests.java +++ b/spring-web/src/test/java/org/springframework/web/method/support/ModelAndViewContainerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -76,4 +76,15 @@ public void ignoreDefaultModel() { assertTrue(this.mavContainer.getModel().isEmpty()); } + @Test // SPR-14045 + public void ignoreDefaultModelAndWithoutRedirectModel() { + this.mavContainer.setIgnoreDefaultModelOnRedirect(true); + this.mavContainer.setRedirectModelScenario(true); + this.mavContainer.addAttribute("name", "value"); + + assertEquals(1, this.mavContainer.getModel().size()); + assertEquals("value", this.mavContainer.getModel().get("name")); + } + + } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewMethodReturnValueHandlerTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewMethodReturnValueHandlerTests.java index 38807604aa..7b245cfaa2 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewMethodReturnValueHandlerTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewMethodReturnValueHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -143,6 +143,20 @@ public void handleRedirectAttributesWithoutRedirect() throws Exception { assertNotSame("RedirectAttributes should not be used if controller doesn't redirect", redirectAttributes, model); } + @Test // SPR-14045 + public void handleRedirectWithIgnoreDefaultModel() throws Exception { + mavContainer.setIgnoreDefaultModelOnRedirect(true); + + RedirectView redirectView = new RedirectView(); + ModelAndView mav = new ModelAndView(redirectView, "name", "value"); + handler.handleReturnValue(mav, returnParamModelAndView, mavContainer, webRequest); + + ModelMap model = mavContainer.getModel(); + assertSame(redirectView, mavContainer.getView()); + assertEquals(1, model.size()); + assertEquals("value", model.get("name")); + } + private MethodParameter getReturnValueParam(String methodName) throws Exception { Method method = getClass().getDeclaredMethod(methodName); From 0c8a40bdbc6d6e25adefbbce873550d3b6f953b8 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Mon, 21 Mar 2016 12:51:14 +0100 Subject: [PATCH 073/344] Fix escaping of # This commit disables the "quotes" substitutions on specific XML examples that require to use the reserved `#` character. Issue: SPR-14074 --- src/asciidoc/core-expressions.adoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/asciidoc/core-expressions.adoc b/src/asciidoc/core-expressions.adoc index 390ead9f89..ec15a381e7 100644 --- a/src/asciidoc/core-expressions.adoc +++ b/src/asciidoc/core-expressions.adoc @@ -442,7 +442,7 @@ form `#{ }`. A property or constructor-arg value can be set using expressions as shown below. [source,xml,indent=0] -[subs="verbatim,quotes"] +[subs="verbatim"] ---- @@ -456,7 +456,7 @@ shown below. Note that you do not have to prefix the predefined variable with th symbol in this context. [source,xml,indent=0] -[subs="verbatim,quotes"] +[subs="verbatim"] ---- @@ -468,7 +468,7 @@ symbol in this context. You can also refer to other bean properties by name, for example. [source,xml,indent=0] -[subs="verbatim,quotes"] +[subs="verbatim"] ---- From 2267b14055f513f70fcd5f57c98bdaa50f7104ee Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Wed, 23 Mar 2016 04:55:07 +0900 Subject: [PATCH 074/344] Add newline at the beginning of textarea macro for FreeMarker and Velocity Issue: SPR-13503 Cherry-picked from 9b2023111d3c6 --- .../web/servlet/view/freemarker/spring.ftl | 3 ++- .../springframework/web/servlet/view/velocity/spring.vm | 3 ++- .../servlet/view/freemarker/FreeMarkerMacroTests.java | 4 ++-- .../web/servlet/view/velocity/VelocityMacroTests.java | 9 ++++++--- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/spring-webmvc/src/main/resources/org/springframework/web/servlet/view/freemarker/spring.ftl b/spring-webmvc/src/main/resources/org/springframework/web/servlet/view/freemarker/spring.ftl index bfb41221f7..00ef2e122d 100644 --- a/spring-webmvc/src/main/resources/org/springframework/web/servlet/view/freemarker/spring.ftl +++ b/spring-webmvc/src/main/resources/org/springframework/web/servlet/view/freemarker/spring.ftl @@ -202,7 +202,8 @@ --> <#macro formTextarea path attributes=""> <@bind path/> - + <#-- diff --git a/spring-webmvc/src/main/resources/org/springframework/web/servlet/view/velocity/spring.vm b/spring-webmvc/src/main/resources/org/springframework/web/servlet/view/velocity/spring.vm index 91936429ad..8ae5fe2aa6 100644 --- a/spring-webmvc/src/main/resources/org/springframework/web/servlet/view/velocity/spring.vm +++ b/spring-webmvc/src/main/resources/org/springframework/web/servlet/view/velocity/spring.vm @@ -161,7 +161,8 @@ *# #macro( springFormTextarea $path $attributes ) #springBind($path) - + #end #** diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/freemarker/FreeMarkerMacroTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/freemarker/FreeMarkerMacroTests.java index 36593225a9..f462250618 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/freemarker/FreeMarkerMacroTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/freemarker/FreeMarkerMacroTests.java @@ -211,12 +211,12 @@ public void testForm2() throws Exception { @Test public void testForm3() throws Exception { - assertEquals("", getMacroOutput("FORM3")); + assertEquals("", getMacroOutput("FORM3")); } @Test public void testForm4() throws Exception { - assertEquals("", getMacroOutput("FORM4")); + assertEquals("", getMacroOutput("FORM4")); } // TODO verify remaining output (fix whitespace) diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/velocity/VelocityMacroTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/velocity/VelocityMacroTests.java index 1c46a1bad0..6409814ea2 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/velocity/VelocityMacroTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/velocity/VelocityMacroTests.java @@ -184,8 +184,10 @@ public void allMacros() throws Exception { if (tokens[i].equals("URL")) assertEquals("/springtest/aftercontext.html", tokens[i + 1]); if (tokens[i].equals("FORM1")) assertEquals("", tokens[i + 1]); if (tokens[i].equals("FORM2")) assertEquals("", tokens[i + 1]); - if (tokens[i].equals("FORM3")) assertEquals("", tokens[i + 1]); - if (tokens[i].equals("FORM4")) assertEquals("", tokens[i + 1]); + if (tokens[i].equals("FORM3")) assertEquals("", tokens[i + 2]); + if (tokens[i].equals("FORM4")) assertEquals("", tokens[i + 2]); //TODO verify remaining output (fix whitespace) if (tokens[i].equals("FORM9")) assertEquals("", tokens[i + 1]); if (tokens[i].equals("FORM10")) assertEquals("", tokens[i + 1]); @@ -248,7 +250,8 @@ public void idContainsBraces() throws Exception { for (int i = 0; i < tokens.length; i++) { if (tokens[i].equals("FORM1")) assertEquals("", tokens[i + 1]); // - if (tokens[i].equals("FORM2")) assertEquals("", tokens[i + 1]); + if (tokens[i].equals("FORM2")) assertEquals("", tokens[i + 2]); if (tokens[i].equals("FORM3")) assertEquals("", tokens[i + 1]); if (tokens[i].equals("FORM5")) assertEquals(" Date: Wed, 23 Mar 2016 16:18:15 +0100 Subject: [PATCH 075/344] Reset Expires HTTP header when caching configured Just like SPR-13252 addressed this issue for the "Pragma" header, this issue resets the HTTP 1.0 "Expires" header. When such a header has been set (by a filter, for example) and HTTP caching has been configured at the WebContentGenerator, this header value is reset to "". In this case, "Cache-Control" and "Expires" might have inconsistent values and we consider that the HTTP caching configuration should take precedence. Depending on the servlet container chosen to deploy the application, this might result in empty "" header values or no header set at all. Issue: SPR-14053 Cherry picked from 15fe8279e6342 --- .../web/servlet/support/WebContentGenerator.java | 8 ++++++++ .../servlet/mvc/WebContentInterceptorTests.java | 15 +++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/WebContentGenerator.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/WebContentGenerator.java index 903aaa4c7e..947d77ab11 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/WebContentGenerator.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/WebContentGenerator.java @@ -339,6 +339,10 @@ protected final void applyCacheControl(HttpServletResponse response, CacheContro // Reset HTTP 1.0 Pragma header if present response.setHeader(HEADER_PRAGMA, ""); } + if (response.containsHeader(HEADER_EXPIRES)) { + // Reset HTTP 1.0 Expires header if present + response.setHeader(HEADER_EXPIRES, ""); + } } } @@ -463,6 +467,10 @@ protected final void cacheForSeconds(HttpServletResponse response, int seconds, // HTTP 1.0 header response.setDateHeader(HEADER_EXPIRES, System.currentTimeMillis() + seconds * 1000L); } + else if (response.containsHeader(HEADER_EXPIRES)) { + // Reset HTTP 1.0 Expires header if present + response.setHeader(HEADER_EXPIRES, ""); + } if (this.useCacheControlHeader) { // HTTP 1.1 header diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/WebContentInterceptorTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/WebContentInterceptorTests.java index a269583f9d..06a844e13a 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/WebContentInterceptorTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/WebContentInterceptorTests.java @@ -16,6 +16,7 @@ package org.springframework.web.servlet.mvc; +import static org.hamcrest.Matchers.is; import static org.junit.Assert.*; import java.util.Properties; @@ -104,20 +105,21 @@ public void emptyCacheConfiguration() throws Exception { assertThat(cacheControlHeaders, Matchers.emptyIterable()); } - // SPR-13252 + // SPR-13252, SPR-14053 @Test public void cachingConfigAndPragmaHeader() throws Exception { WebContentInterceptor interceptor = new WebContentInterceptor(); interceptor.setCacheSeconds(10); response.setHeader("Pragma", "no-cache"); + response.setHeader("Expires", "0"); interceptor.preHandle(request, response, null); - Iterable pragmaHeaders = response.getHeaders("Pragma"); - assertThat(pragmaHeaders, Matchers.contains("")); + assertThat(response.getHeader("Pragma"), is("")); + assertThat(response.getHeader("Expires"), is("")); } - // SPR-13252 + // SPR-13252, SPR-14053 @SuppressWarnings("deprecation") @Test public void http10CachingConfigAndPragmaHeader() throws Exception { @@ -125,11 +127,12 @@ public void http10CachingConfigAndPragmaHeader() throws Exception { interceptor.setCacheSeconds(10); interceptor.setAlwaysMustRevalidate(true); response.setHeader("Pragma", "no-cache"); + response.setHeader("Expires", "0"); interceptor.preHandle(request, response, null); - Iterable pragmaHeaders = response.getHeaders("Pragma"); - assertThat(pragmaHeaders, Matchers.contains("")); + assertThat(response.getHeader("Pragma"), is("")); + assertThat(response.getHeader("Expires"), is("")); } @SuppressWarnings("deprecation") From a5904efbde78b01cbe986d96f61274528ec75d07 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 24 Mar 2016 13:51:03 +0100 Subject: [PATCH 076/344] Upgrade to XStream 1.4.9 Issue: SPR-14084 (cherry picked from commit 7e4563a) --- build.gradle | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/build.gradle b/build.gradle index 9ff39f4baf..7deec82e33 100644 --- a/build.gradle +++ b/build.gradle @@ -31,12 +31,12 @@ configure(allprojects) { project -> ext.eclipselinkVersion = "2.4.2" ext.ehcacheVersion = "2.10.1" ext.ehcachejcacheVersion = "1.0.1" - ext.ehcache3Version = "3.0.0.m5" + ext.ehcache3Version = "3.0.0.rc1" ext.ejbApiVersion = "3.0" ext.fileuploadVersion = "1.3.1" ext.freemarkerVersion = "2.3.23" ext.groovyVersion = "2.4.6" - ext.gsonVersion = "2.6.1" + ext.gsonVersion = "2.6.2" ext.guavaVersion = "19.0" ext.hamcrestVersion = "1.3" ext.hibernate3Version = "3.6.10.Final" @@ -47,26 +47,26 @@ configure(allprojects) { project -> ext.hsqldbVersion = "2.3.3" ext.htmlunitVersion = "2.19" ext.httpasyncVersion = "4.1.1" - ext.httpclientVersion = "4.5.1" + ext.httpclientVersion = "4.5.2" ext.jackson2Version = "2.6.5" - ext.jasperreportsVersion = "6.2.0" + ext.jasperreportsVersion = "6.2.1" ext.javamailVersion = "1.5.5" - ext.jettyVersion = "9.3.7.v20160115" + ext.jettyVersion = "9.3.8.v20160314" ext.jodaVersion = "2.9.2" - ext.jrubyVersion = "1.7.23" // JRuby 9000 only supported through JSR-223 (StandardScriptFactory) + ext.jrubyVersion = "1.7.24" // JRuby 9000 only supported through JSR-223 (StandardScriptFactory) ext.jsonassertVersion = "1.2.3" ext.jsonpathVersion = "2.1.0" ext.jtaVersion = "1.2" ext.junitVersion = "4.12" ext.nettyVersion = "4.0.34.Final" - ext.okhttpVersion = "2.7.4" - ext.openjpaVersion = "2.4.0" + ext.okhttpVersion = "2.7.5" + ext.openjpaVersion = "2.4.1" ext.poiVersion = "3.13" ext.protobufVersion = "2.6.1" ext.reactorVersion = "2.0.7.RELEASE" ext.romeVersion = "1.5.1" ext.seleniumVersion = "2.48.2" - ext.slf4jVersion = "1.7.16" + ext.slf4jVersion = "1.7.19" ext.snakeyamlVersion = "1.17" ext.snifferVersion = "1.14" ext.testngVersion = "6.9.10" @@ -74,10 +74,10 @@ configure(allprojects) { project -> ext.tiles3Version = "3.0.5" ext.tomcatVersion = "8.0.32" ext.tyrusVersion = "1.3.5" // constrained by WebLogic 12.1.3 support - ext.undertowVersion = "1.3.18.Final" + ext.undertowVersion = "1.3.19.Final" ext.woodstoxVersion = "5.0.1" ext.xmlunitVersion = "1.6" - ext.xstreamVersion = "1.4.8" + ext.xstreamVersion = "1.4.9" ext.gradleScriptDir = "${rootProject.projectDir}/gradle" @@ -630,7 +630,7 @@ project("spring-jdbc") { optional("javax.transaction:javax.transaction-api:${jtaVersion}") optional("com.mchange:c3p0:0.9.5.2") optional("org.hsqldb:hsqldb:${hsqldbVersion}") - optional("com.h2database:h2:1.4.190") + optional("com.h2database:h2:1.4.191") optional("org.apache.derby:derby:10.12.1.1") optional("org.apache.derby:derbyclient:10.12.1.1") } From bfe8d15e9793381bc3f35cf15a44d6f7c2f45461 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 11 Mar 2016 12:57:41 +0100 Subject: [PATCH 077/344] Removed invalid quoting of message arguments from MessageSource examples Issue: SPR-14003 (cherry picked from commit 100f3c5) --- src/asciidoc/core-beans.adoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/asciidoc/core-beans.adoc b/src/asciidoc/core-beans.adoc index 8003041896..2214a26d6b 100644 --- a/src/asciidoc/core-beans.adoc +++ b/src/asciidoc/core-beans.adoc @@ -7739,7 +7739,7 @@ are... [subs="verbatim,quotes"] ---- # in exceptions.properties - argument.required=The '{0}' argument is required. + argument.required=The {0} argument is required. ---- A program to execute the `MessageSource` functionality is shown in the next example. @@ -7820,7 +7820,7 @@ The resulting output from the invocation of the `execute()` method will be... The userDao argument is required. ---- -With regard to internationalization (i18n), Spring's various `MessageResource` +With regard to internationalization (i18n), Spring's various `MessageSource` implementations follow the same locale resolution and fallback rules as the standard JDK `ResourceBundle`. In short, and continuing with the example `messageSource` defined previously, if you want to resolve messages against the British (`en-GB`) locale, you @@ -7835,7 +7835,7 @@ resolved is specified manually. [subs="verbatim,quotes"] ---- # in exceptions_en_GB.properties -argument.required=Ebagum lad, the '{0}' argument is required, I say, required. +argument.required=Ebagum lad, the {0} argument is required, I say, required. ---- [source,java,indent=0] From fcd155a92c78b4209c2b0a1f3fab18134206b8de Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 18 Mar 2016 15:15:07 +0100 Subject: [PATCH 078/344] Correct RFC 4648 references in method-level javadoc Issue: SPR-14067 (cherry picked from commit ec7c3aa) --- .../java/org/springframework/util/Base64Utils.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/util/Base64Utils.java b/spring-core/src/main/java/org/springframework/util/Base64Utils.java index 84d2fd8df8..17c48614b8 100644 --- a/spring-core/src/main/java/org/springframework/util/Base64Utils.java +++ b/spring-core/src/main/java/org/springframework/util/Base64Utils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -99,7 +99,7 @@ public static byte[] decode(byte[] src) { } /** - * Base64-encode the given byte array using the RFC 4868 + * Base64-encode the given byte array using the RFC 4648 * "URL and Filename Safe Alphabet". * @param src the original byte array (may be {@code null}) * @return the encoded byte array (or {@code null} if the input was {@code null}) @@ -113,7 +113,7 @@ public static byte[] encodeUrlSafe(byte[] src) { } /** - * Base64-decode the given byte array using the RFC 4868 + * Base64-decode the given byte array using the RFC 4648 * "URL and Filename Safe Alphabet". * @param src the encoded byte array (may be {@code null}) * @return the original byte array (or {@code null} if the input was {@code null}) @@ -174,7 +174,7 @@ public static byte[] decodeFromString(String src) { } /** - * Base64-encode the given byte array to a String using the RFC 4868 + * Base64-encode the given byte array to a String using the RFC 4648 * "URL and Filename Safe Alphabet". * @param src the original byte array (may be {@code null}) * @return the encoded byte array as a UTF-8 String @@ -188,7 +188,7 @@ public static String encodeToUrlSafeString(byte[] src) { } /** - * Base64-decode the given byte array from an UTF-8 String using the RFC 4868 + * Base64-decode the given byte array from an UTF-8 String using the RFC 4648 * "URL and Filename Safe Alphabet". * @param src the encoded UTF-8 String (may be {@code null}) * @return the original byte array (or {@code null} if the input was {@code null}) From 8994498b4477a4cef59dd928b70ed86d99f5f465 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 23 Mar 2016 18:35:26 +0100 Subject: [PATCH 079/344] Revised newline handling tests pass on Windows now Issue: SPR-13503 (cherry picked from commit 311d4c9) --- .../web/servlet/view/freemarker/FreeMarkerMacroTests.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/freemarker/FreeMarkerMacroTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/freemarker/FreeMarkerMacroTests.java index f462250618..6157655214 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/freemarker/FreeMarkerMacroTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/freemarker/FreeMarkerMacroTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -326,6 +326,7 @@ private String getMacroOutput(String name) throws Exception { // tokenize output and ignore whitespace String output = response.getContentAsString(); + output = output.replace("\r\n", "\n"); return output.trim(); } From a19be754c8f1ca67fd302482a6ee91c06f0cfe5e Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 24 Mar 2016 15:08:19 +0100 Subject: [PATCH 080/344] DefaultCorsProcessor checks for existing CORS response before attempting to compare origin Issue: SPR-14080 (cherry picked from commit abe7345) --- .../web/cors/DefaultCorsProcessor.java | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/cors/DefaultCorsProcessor.java b/spring-web/src/main/java/org/springframework/web/cors/DefaultCorsProcessor.java index 72445bb11c..3a564f8082 100644 --- a/spring-web/src/main/java/org/springframework/web/cors/DefaultCorsProcessor.java +++ b/spring-web/src/main/java/org/springframework/web/cors/DefaultCorsProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -58,6 +58,7 @@ public class DefaultCorsProcessor implements CorsProcessor { @Override + @SuppressWarnings("resource") public boolean processRequest(CorsConfiguration config, HttpServletRequest request, HttpServletResponse response) throws IOException { @@ -66,14 +67,14 @@ public boolean processRequest(CorsConfiguration config, HttpServletRequest reque } ServletServerHttpResponse serverResponse = new ServletServerHttpResponse(response); - ServletServerHttpRequest serverRequest = new ServletServerHttpRequest(request); - - if (WebUtils.isSameOrigin(serverRequest)) { - logger.debug("Skip CORS processing, request is a same-origin one"); + if (responseHasCors(serverResponse)) { + logger.debug("Skip CORS processing: response already contains \"Access-Control-Allow-Origin\" header"); return true; } - if (responseHasCors(serverResponse)) { - logger.debug("Skip CORS processing, response already contains \"Access-Control-Allow-Origin\" header"); + + ServletServerHttpRequest serverRequest = new ServletServerHttpRequest(request); + if (WebUtils.isSameOrigin(serverRequest)) { + logger.debug("Skip CORS processing: request is from same origin"); return true; } @@ -92,14 +93,13 @@ public boolean processRequest(CorsConfiguration config, HttpServletRequest reque } private boolean responseHasCors(ServerHttpResponse response) { - boolean hasAllowOrigin = false; try { - hasAllowOrigin = (response.getHeaders().getAccessControlAllowOrigin() != null); + return (response.getHeaders().getAccessControlAllowOrigin() != null); } catch (NullPointerException npe) { // SPR-11919 and https://issues.jboss.org/browse/WFLY-3474 + return false; } - return hasAllowOrigin; } /** @@ -163,7 +163,7 @@ protected boolean handleInternal(ServerHttpRequest request, ServerHttpResponse r /** * Check the origin and determine the origin for the response. The default * implementation simply delegates to - * {@link org.springframework.web.cors.CorsConfiguration#checkOrigin(String)} + * {@link org.springframework.web.cors.CorsConfiguration#checkOrigin(String)}. */ protected String checkOrigin(CorsConfiguration config, String requestOrigin) { return config.checkOrigin(requestOrigin); @@ -172,7 +172,7 @@ protected String checkOrigin(CorsConfiguration config, String requestOrigin) { /** * Check the HTTP method and determine the methods for the response of a * pre-flight request. The default implementation simply delegates to - * {@link org.springframework.web.cors.CorsConfiguration#checkOrigin(String)} + * {@link org.springframework.web.cors.CorsConfiguration#checkOrigin(String)}. */ protected List checkMethods(CorsConfiguration config, HttpMethod requestMethod) { return config.checkHttpMethod(requestMethod); @@ -185,7 +185,7 @@ private HttpMethod getMethodToUse(ServerHttpRequest request, boolean isPreFlight /** * Check the headers and determine the headers for the response of a * pre-flight request. The default implementation simply delegates to - * {@link org.springframework.web.cors.CorsConfiguration#checkOrigin(String)} + * {@link org.springframework.web.cors.CorsConfiguration#checkOrigin(String)}. */ protected List checkHeaders(CorsConfiguration config, List requestHeaders) { return config.checkHeaders(requestHeaders); From 05ab769555fe6c3695a0942831f2405cb2d6ebae Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 11 Mar 2016 12:55:20 +0100 Subject: [PATCH 081/344] Bsh/GroovyScriptFactory reset script cache in case of compilation error Issue: SPR-14007 (cherry picked from commit 0597ff1) --- .../scripting/bsh/BshScriptFactory.java | 51 +++++++++++-------- .../scripting/groovy/GroovyScriptFactory.java | 36 +++++++------ 2 files changed, 49 insertions(+), 38 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/scripting/bsh/BshScriptFactory.java b/spring-context/src/main/java/org/springframework/scripting/bsh/BshScriptFactory.java index 07cffc510e..44921c5282 100644 --- a/spring-context/src/main/java/org/springframework/scripting/bsh/BshScriptFactory.java +++ b/spring-context/src/main/java/org/springframework/scripting/bsh/BshScriptFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -119,9 +119,9 @@ public boolean requiresConfigInterface() { public Object getScriptedObject(ScriptSource scriptSource, Class... actualInterfaces) throws IOException, ScriptCompilationException { - try { - Class clazz; + Class clazz; + try { synchronized (this.scriptClassMonitor) { boolean requiresScriptEvaluation = (this.wasModifiedForTypeCheck && this.scriptClass == null); this.wasModifiedForTypeCheck = false; @@ -145,25 +145,31 @@ public Object getScriptedObject(ScriptSource scriptSource, Class... actualInt } clazz = this.scriptClass; } + } + catch (EvalError ex) { + this.scriptClass = null; + throw new ScriptCompilationException(scriptSource, ex); + } - if (clazz != null) { - // A Class: We need to create an instance for every call. - try { - return clazz.newInstance(); - } - catch (Throwable ex) { - throw new ScriptCompilationException( - scriptSource, "Could not instantiate script class: " + clazz.getName(), ex); - } + if (clazz != null) { + // A Class: We need to create an instance for every call. + try { + return clazz.newInstance(); + } + catch (Throwable ex) { + throw new ScriptCompilationException( + scriptSource, "Could not instantiate script class: " + clazz.getName(), ex); } - else { - // Not a Class: We need to evaluate the script for every call. + } + else { + // Not a Class: We need to evaluate the script for every call. + try { return BshScriptUtils.createBshObject( scriptSource.getScriptAsString(), actualInterfaces, this.beanClassLoader); } - } - catch (EvalError ex) { - throw new ScriptCompilationException(scriptSource, ex); + catch (EvalError ex) { + throw new ScriptCompilationException(scriptSource, ex); + } } } @@ -171,8 +177,8 @@ public Object getScriptedObject(ScriptSource scriptSource, Class... actualInt public Class getScriptedObjectType(ScriptSource scriptSource) throws IOException, ScriptCompilationException { - try { - synchronized (this.scriptClassMonitor) { + synchronized (this.scriptClassMonitor) { + try { if (scriptSource.isModified()) { // New script content: Let's check whether it evaluates to a Class. this.wasModifiedForTypeCheck = true; @@ -181,9 +187,10 @@ public Class getScriptedObjectType(ScriptSource scriptSource) } return this.scriptClass; } - } - catch (EvalError ex) { - throw new ScriptCompilationException(scriptSource, ex); + catch (EvalError ex) { + this.scriptClass = null; + throw new ScriptCompilationException(scriptSource, ex); + } } } diff --git a/spring-context/src/main/java/org/springframework/scripting/groovy/GroovyScriptFactory.java b/spring-context/src/main/java/org/springframework/scripting/groovy/GroovyScriptFactory.java index b13527543c..c082ca4ce1 100644 --- a/spring-context/src/main/java/org/springframework/scripting/groovy/GroovyScriptFactory.java +++ b/spring-context/src/main/java/org/springframework/scripting/groovy/GroovyScriptFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -158,10 +158,9 @@ public boolean requiresConfigInterface() { public Object getScriptedObject(ScriptSource scriptSource, Class... actualInterfaces) throws IOException, ScriptCompilationException { - try { - Class scriptClassToExecute; - - synchronized (this.scriptClassMonitor) { + synchronized (this.scriptClassMonitor) { + try { + Class scriptClassToExecute; this.wasModifiedForTypeCheck = false; if (this.cachedResult != null) { @@ -186,13 +185,15 @@ public Object getScriptedObject(ScriptSource scriptSource, Class... actualInt } } scriptClassToExecute = this.scriptClass; - } - // Process re-execution outside of the synchronized block. - return executeScript(scriptSource, scriptClassToExecute); - } - catch (CompilationFailedException ex) { - throw new ScriptCompilationException(scriptSource, ex); + // Process re-execution outside of the synchronized block. + return executeScript(scriptSource, scriptClassToExecute); + } + catch (CompilationFailedException ex) { + this.scriptClass = null; + this.scriptResultClass = null; + throw new ScriptCompilationException(scriptSource, ex); + } } } @@ -200,8 +201,8 @@ public Object getScriptedObject(ScriptSource scriptSource, Class... actualInt public Class getScriptedObjectType(ScriptSource scriptSource) throws IOException, ScriptCompilationException { - try { - synchronized (this.scriptClassMonitor) { + synchronized (this.scriptClassMonitor) { + try { if (this.scriptClass == null || scriptSource.isModified()) { // New script content... this.wasModifiedForTypeCheck = true; @@ -220,9 +221,12 @@ public Class getScriptedObjectType(ScriptSource scriptSource) } return this.scriptResultClass; } - } - catch (CompilationFailedException ex) { - throw new ScriptCompilationException(scriptSource, ex); + catch (CompilationFailedException ex) { + this.scriptClass = null; + this.scriptResultClass = null; + this.cachedResult = null; + throw new ScriptCompilationException(scriptSource, ex); + } } } From 7c5aac122a0934578de50dce42bcee387d3583ef Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 11 Mar 2016 23:23:58 +0100 Subject: [PATCH 082/344] Restored lazy resolution of default executor for AsyncAnnotationBeanPostProcessor Issue: SPR-14030 (cherry picked from commit 155fa37) --- .../AsyncExecutionAspectSupport.java | 146 ++++++++++++++---- .../AsyncExecutionInterceptor.java | 37 ++++- .../aspectj/AbstractAsyncExecutionAspect.aj | 24 +-- .../aspectj/AnnotationAsyncExecutionAspect.aj | 13 +- .../AnnotationAsyncExecutionInterceptor.java | 10 +- .../annotation/AsyncAnnotationAdvisor.java | 10 +- .../AsyncAnnotationBeanPostProcessor.java | 41 ++--- .../scheduling/annotation/EnableAsync.java | 28 ++-- .../annotation/EnableScheduling.java | 29 +++- 9 files changed, 226 insertions(+), 112 deletions(-) diff --git a/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionAspectSupport.java b/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionAspectSupport.java index 134bc2f3e0..b1cdaa0f20 100644 --- a/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionAspectSupport.java +++ b/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionAspectSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,9 +31,12 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.NoUniqueBeanDefinitionException; import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils; import org.springframework.core.task.AsyncListenableTaskExecutor; import org.springframework.core.task.AsyncTaskExecutor; +import org.springframework.core.task.TaskExecutor; import org.springframework.core.task.support.TaskExecutorAdapter; import org.springframework.lang.UsesJava8; import org.springframework.util.ClassUtils; @@ -58,6 +61,15 @@ */ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware { + /** + * The default name of the {@link TaskExecutor} bean to pick up: "taskExecutor". + *

    Note that the initial lookup happens by type; this is just the fallback + * in case of multiple executor beans found in the context. + * @since 4.2.6 + */ + public static final String DEFAULT_TASK_EXECUTOR_BEAN_NAME = "taskExecutor"; + + // Java 8's CompletableFuture type present? private static final boolean completableFuturePresent = ClassUtils.isPresent( "java.util.concurrent.CompletableFuture", AsyncExecutionInterceptor.class.getClassLoader()); @@ -67,7 +79,7 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware { private final Map executors = new ConcurrentHashMap(16); - private Executor defaultExecutor; + private volatile Executor defaultExecutor; private AsyncUncaughtExceptionHandler exceptionHandler; @@ -75,34 +87,39 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware { /** - * Create a new {@link AsyncExecutionAspectSupport}, using the provided default - * executor unless individual async methods indicate via qualifier that a more - * specific executor should be used. - * @param defaultExecutor the executor to use when executing asynchronous methods - * @param exceptionHandler the {@link AsyncUncaughtExceptionHandler} to use + * Create a new instance with a default {@link AsyncUncaughtExceptionHandler}. + * @param defaultExecutor the {@code Executor} (typically a Spring {@code AsyncTaskExecutor} + * or {@link java.util.concurrent.ExecutorService}) to delegate to, unless a more specific + * executor has been requested via a qualifier on the async method, in which case the + * executor will be looked up at invocation time against the enclosing bean factory */ - public AsyncExecutionAspectSupport(Executor defaultExecutor, AsyncUncaughtExceptionHandler exceptionHandler) { - this.defaultExecutor = defaultExecutor; - this.exceptionHandler = exceptionHandler; + public AsyncExecutionAspectSupport(Executor defaultExecutor) { + this(defaultExecutor, new SimpleAsyncUncaughtExceptionHandler()); } /** - * Create a new instance with a default {@link AsyncUncaughtExceptionHandler}. + * Create a new {@link AsyncExecutionAspectSupport} with the given exception handler. + * @param defaultExecutor the {@code Executor} (typically a Spring {@code AsyncTaskExecutor} + * or {@link java.util.concurrent.ExecutorService}) to delegate to, unless a more specific + * executor has been requested via a qualifier on the async method, in which case the + * executor will be looked up at invocation time against the enclosing bean factory + * @param exceptionHandler the {@link AsyncUncaughtExceptionHandler} to use */ - public AsyncExecutionAspectSupport(Executor defaultExecutor) { - this(defaultExecutor, new SimpleAsyncUncaughtExceptionHandler()); + public AsyncExecutionAspectSupport(Executor defaultExecutor, AsyncUncaughtExceptionHandler exceptionHandler) { + this.defaultExecutor = defaultExecutor; + this.exceptionHandler = exceptionHandler; } /** * Supply the executor to be used when executing async methods. - * @param defaultExecutor the {@code Executor} (typically a Spring {@code - * AsyncTaskExecutor} or {@link java.util.concurrent.ExecutorService}) to delegate to - * unless a more specific executor has been requested via a qualifier on the async - * method, in which case the executor will be looked up at invocation time against the - * enclosing bean factory. - * @see #getExecutorQualifier + * @param defaultExecutor the {@code Executor} (typically a Spring {@code AsyncTaskExecutor} + * or {@link java.util.concurrent.ExecutorService}) to delegate to, unless a more specific + * executor has been requested via a qualifier on the async method, in which case the + * executor will be looked up at invocation time against the enclosing bean factory + * @see #getExecutorQualifier(Method) * @see #setBeanFactory(BeanFactory) + * @see #getDefaultExecutor(BeanFactory) */ public void setExecutor(Executor defaultExecutor) { this.defaultExecutor = defaultExecutor; @@ -117,7 +134,10 @@ public void setExceptionHandler(AsyncUncaughtExceptionHandler exceptionHandler) } /** - * Set the {@link BeanFactory} to be used when looking up executors by qualifier. + * Set the {@link BeanFactory} to be used when looking up executors by qualifier + * or when relying on the default executor lookup algorithm. + * @see #findQualifiedExecutor(BeanFactory, String) + * @see #getDefaultExecutor(BeanFactory) */ @Override public void setBeanFactory(BeanFactory beanFactory) { @@ -128,26 +148,32 @@ public void setBeanFactory(BeanFactory beanFactory) { /** * Determine the specific executor to use when executing the given method. * Should preferably return an {@link AsyncListenableTaskExecutor} implementation. - * @return the executor to use (or {@code null}, but just if no default executor has been set) + * @return the executor to use (or {@code null}, but just if no default executor is available) */ protected AsyncTaskExecutor determineAsyncExecutor(Method method) { AsyncTaskExecutor executor = this.executors.get(method); if (executor == null) { - Executor executorToUse = this.defaultExecutor; + Executor targetExecutor; String qualifier = getExecutorQualifier(method); if (StringUtils.hasLength(qualifier)) { - if (this.beanFactory == null) { - throw new IllegalStateException("BeanFactory must be set on " + getClass().getSimpleName() + - " to access qualified executor '" + qualifier + "'"); + targetExecutor = findQualifiedExecutor(this.beanFactory, qualifier); + } + else { + targetExecutor = this.defaultExecutor; + if (targetExecutor == null) { + synchronized (this.executors) { + if (this.defaultExecutor == null) { + this.defaultExecutor = getDefaultExecutor(this.beanFactory); + } + targetExecutor = this.defaultExecutor; + } } - executorToUse = BeanFactoryAnnotationUtils.qualifiedBeanOfType( - this.beanFactory, Executor.class, qualifier); } - else if (executorToUse == null) { + if (targetExecutor == null) { return null; } - executor = (executorToUse instanceof AsyncListenableTaskExecutor ? - (AsyncListenableTaskExecutor) executorToUse : new TaskExecutorAdapter(executorToUse)); + executor = (targetExecutor instanceof AsyncListenableTaskExecutor ? + (AsyncListenableTaskExecutor) targetExecutor : new TaskExecutorAdapter(targetExecutor)); this.executors.put(method, executor); } return executor; @@ -160,11 +186,69 @@ else if (executorToUse == null) { * been specified and that the {@linkplain #setExecutor(Executor) default executor} * should be used. * @param method the method to inspect for executor qualifier metadata - * @return the qualifier if specified, otherwise empty string or {@code null} + * @return the qualifier if specified, otherwise empty String or {@code null} * @see #determineAsyncExecutor(Method) + * @see #findQualifiedExecutor(BeanFactory, String) */ protected abstract String getExecutorQualifier(Method method); + /** + * Retrieve a target executor for the given qualifier. + * @param qualifier the qualifier to resolve + * @return the target executor, or {@code null} if none available + * @since 4.2.6 + * @see #getExecutorQualifier(Method) + */ + protected Executor findQualifiedExecutor(BeanFactory beanFactory, String qualifier) { + if (beanFactory == null) { + throw new IllegalStateException("BeanFactory must be set on " + getClass().getSimpleName() + + " to access qualified executor '" + qualifier + "'"); + } + return BeanFactoryAnnotationUtils.qualifiedBeanOfType(beanFactory, Executor.class, qualifier); + } + + /** + * Retrieve or build a default executor for this advice instance. + * An executor returned from here will be cached for further use. + *

    The default implementation searches for a unique {@link TaskExecutor} bean + * in the context, or for an {@link Executor} bean named "taskExecutor" otherwise. + * If neither of the two is resolvable, this implementation will return {@code null}. + * @param beanFactory the BeanFactory to use for a default executor lookup + * @return the default executor, or {@code null} if none available + * @since 4.2.6 + * @see #findQualifiedExecutor(BeanFactory, String) + * @see #DEFAULT_TASK_EXECUTOR_BEAN_NAME + */ + protected Executor getDefaultExecutor(BeanFactory beanFactory) { + if (beanFactory != null) { + try { + // Search for TaskExecutor bean... not plain Executor since that would + // match with ScheduledExecutorService as well, which is unusable for + // our purposes here. TaskExecutor is more clearly designed for it. + return beanFactory.getBean(TaskExecutor.class); + } + catch (NoUniqueBeanDefinitionException ex) { + try { + return beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, Executor.class); + } + catch (NoSuchBeanDefinitionException ex2) { + if (logger.isInfoEnabled()) { + logger.info("More than one TaskExecutor bean found within the context, and none is named " + + "'taskExecutor'. Mark one of them as primary or name it 'taskExecutor' (possibly " + + "as an alias) in order to use it for async processing."); + } + } + } + catch (NoSuchBeanDefinitionException ex) { + logger.debug("Could not find default TaskExecutor bean", ex); + // Giving up -> either using local default executor or none at all... + logger.info("No TaskExecutor bean found for async processing"); + } + } + return null; + } + + /** * Delegate for actually executing the given task with the chosen executor. * @param task the task to execute diff --git a/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionInterceptor.java b/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionInterceptor.java index 8aa0c020ac..a758fd2576 100644 --- a/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionInterceptor.java +++ b/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,9 +26,11 @@ import org.aopalliance.intercept.MethodInvocation; import org.springframework.aop.support.AopUtils; +import org.springframework.beans.factory.BeanFactory; import org.springframework.core.BridgeMethodResolver; import org.springframework.core.Ordered; import org.springframework.core.task.AsyncTaskExecutor; +import org.springframework.core.task.SimpleAsyncTaskExecutor; import org.springframework.util.ClassUtils; /** @@ -65,22 +67,27 @@ */ public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport implements MethodInterceptor, Ordered { + /** + * Create a new instance with a default {@link AsyncUncaughtExceptionHandler}. + * @param defaultExecutor the {@link Executor} (typically a Spring {@link AsyncTaskExecutor} + * or {@link java.util.concurrent.ExecutorService}) to delegate to; + * as of 4.2.6, a local executor for this interceptor will be built otherwise + */ + public AsyncExecutionInterceptor(Executor defaultExecutor) { + super(defaultExecutor); + } + /** * Create a new {@code AsyncExecutionInterceptor}. * @param defaultExecutor the {@link Executor} (typically a Spring {@link AsyncTaskExecutor} - * or {@link java.util.concurrent.ExecutorService}) to delegate to + * or {@link java.util.concurrent.ExecutorService}) to delegate to; + * as of 4.2.6, a local executor for this interceptor will be built otherwise * @param exceptionHandler the {@link AsyncUncaughtExceptionHandler} to use */ public AsyncExecutionInterceptor(Executor defaultExecutor, AsyncUncaughtExceptionHandler exceptionHandler) { super(defaultExecutor, exceptionHandler); } - /** - * Create a new instance with a default {@link AsyncUncaughtExceptionHandler}. - */ - public AsyncExecutionInterceptor(Executor defaultExecutor) { - super(defaultExecutor); - } /** * Intercept the given method invocation, submit the actual calling of the method to @@ -136,6 +143,20 @@ protected String getExecutorQualifier(Method method) { return null; } + /** + * This implementation searches for a unique {@link org.springframework.core.task.TaskExecutor} + * bean in the context, or for an {@link Executor} bean named "taskExecutor" otherwise. + * If neither of the two is resolvable (e.g. if no {@code BeanFactory} was configured at all), + * this implementation falls back to a newly created {@link SimpleAsyncTaskExecutor} instance + * for local use if no default could be found. + * @see #DEFAULT_TASK_EXECUTOR_BEAN_NAME + */ + @Override + protected Executor getDefaultExecutor(BeanFactory beanFactory) { + Executor defaultExecutor = super.getDefaultExecutor(beanFactory); + return (defaultExecutor != null ? defaultExecutor : new SimpleAsyncTaskExecutor()); + } + @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; diff --git a/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AbstractAsyncExecutionAspect.aj b/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AbstractAsyncExecutionAspect.aj index f11400f05e..9efd0a4b93 100644 --- a/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AbstractAsyncExecutionAspect.aj +++ b/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AbstractAsyncExecutionAspect.aj @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,6 @@ package org.springframework.scheduling.aspectj; import java.util.concurrent.Callable; -import java.util.concurrent.Executor; import java.util.concurrent.Future; import org.aspectj.lang.annotation.SuppressAjWarnings; @@ -29,33 +28,38 @@ import org.springframework.core.task.AsyncTaskExecutor; /** * Abstract aspect that routes selected methods asynchronously. * - *

    This aspect needs to be injected with an implementation of - * {@link Executor} to activate it for a specific thread pool. - * Otherwise it will simply delegate all calls synchronously. + *

    This aspect needs to be injected with an implementation of a task-oriented + * {@link java.util.concurrent.Executor} to activate it for a specific thread pool, + * or with a {@link org.springframework.beans.factory.BeanFactory} for default + * executor lookup. Otherwise it will simply delegate all calls synchronously. * * @author Ramnivas Laddad * @author Juergen Hoeller * @author Chris Beams * @author Stephane Nicoll * @since 3.0.5 + * @see #setExecutor + * @see #setBeanFactory + * @see #getDefaultExecutor */ public abstract aspect AbstractAsyncExecutionAspect extends AsyncExecutionAspectSupport { /** - * Create an {@code AnnotationAsyncExecutionAspect} with a {@code null} default - * executor, which should instead be set via {@code #aspectOf} and - * {@link #setExecutor(Executor)}. The same applies for {@link #setExceptionHandler} + * Create an {@code AnnotationAsyncExecutionAspect} with a {@code null} + * default executor, which should instead be set via {@code #aspectOf} and + * {@link #setExecutor}. The same applies for {@link #setExceptionHandler}. */ public AbstractAsyncExecutionAspect() { super(null); } + /** * Apply around advice to methods matching the {@link #asyncMethod()} pointcut, * submit the actual calling of the method to the correct task executor and return * immediately to the caller. - * @return {@link Future} if the original method returns {@code Future}; {@code null} - * otherwise. + * @return {@link Future} if the original method returns {@code Future}; + * {@code null} otherwise */ @SuppressAjWarnings("adviceDidNotMatch") Object around() : asyncMethod() { diff --git a/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AnnotationAsyncExecutionAspect.aj b/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AnnotationAsyncExecutionAspect.aj index 6df1662a19..e1a1fd9705 100644 --- a/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AnnotationAsyncExecutionAspect.aj +++ b/spring-aspects/src/main/java/org/springframework/scheduling/aspectj/AnnotationAsyncExecutionAspect.aj @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,9 +33,17 @@ import org.springframework.scheduling.annotation.Async; * on the return type. If, however, a class marked with {@code @Async} contains a method * that violates this constraint, it produces only a warning. * + *

    This aspect needs to be injected with an implementation of a task-oriented + * {@link java.util.concurrent.Executor} to activate it for a specific thread pool, + * or with a {@link org.springframework.beans.factory.BeanFactory} for default + * executor lookup. Otherwise it will simply delegate all calls synchronously. + * * @author Ramnivas Laddad * @author Chris Beams * @since 3.0.5 + * @see #setExecutor + * @see #setBeanFactory + * @see #getDefaultExecutor */ public aspect AnnotationAsyncExecutionAspect extends AbstractAsyncExecutionAspect { @@ -74,7 +82,6 @@ public aspect AnnotationAsyncExecutionAspect extends AbstractAsyncExecutionAspec declare warning: execution(!(void || Future+) (@Async *).*(..)): - "Methods in a class marked with @Async that do not return void or Future will " + - "be routed synchronously"; + "Methods in a class marked with @Async that do not return void or Future will be routed synchronously"; } diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/AnnotationAsyncExecutionInterceptor.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/AnnotationAsyncExecutionInterceptor.java index c3b0ad453e..117de48dfb 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/AnnotationAsyncExecutionInterceptor.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/AnnotationAsyncExecutionInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,7 +42,8 @@ public class AnnotationAsyncExecutionInterceptor extends AsyncExecutionIntercept * Create a new {@code AnnotationAsyncExecutionInterceptor} with the given executor * and a simple {@link AsyncUncaughtExceptionHandler}. * @param defaultExecutor the executor to be used by default if no more specific - * executor has been qualified at the method level using {@link Async#value()} + * executor has been qualified at the method level using {@link Async#value()}; + * as of 4.2.6, a local executor for this interceptor will be built otherwise */ public AnnotationAsyncExecutionInterceptor(Executor defaultExecutor) { super(defaultExecutor); @@ -51,7 +52,8 @@ public AnnotationAsyncExecutionInterceptor(Executor defaultExecutor) { /** * Create a new {@code AnnotationAsyncExecutionInterceptor} with the given executor. * @param defaultExecutor the executor to be used by default if no more specific - * executor has been qualified at the method level using {@link Async#value()} + * executor has been qualified at the method level using {@link Async#value()}; + * as of 4.2.6, a local executor for this interceptor will be built otherwise * @param exceptionHandler the {@link AsyncUncaughtExceptionHandler} to use to * handle exceptions thrown by asynchronous method executions with {@code void} * return type @@ -74,7 +76,7 @@ public AnnotationAsyncExecutionInterceptor(Executor defaultExecutor, AsyncUncaug */ @Override protected String getExecutorQualifier(Method method) { - // maintainer's note: changes made here should also be made in + // Maintainer's note: changes made here should also be made in // AnnotationAsyncExecutionAspect#getExecutorQualifier Async async = AnnotationUtils.findAnnotation(method, Async.class); if (async == null) { diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationAdvisor.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationAdvisor.java index 310c8810ae..ff321cf8e7 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationAdvisor.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationAdvisor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,7 +32,6 @@ import org.springframework.aop.support.annotation.AnnotationMatchingPointcut; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.core.task.SimpleAsyncTaskExecutor; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -73,8 +72,10 @@ public AsyncAnnotationAdvisor() { /** * Create a new {@code AsyncAnnotationAdvisor} for the given task executor. * @param executor the task executor to use for asynchronous methods - * @param exceptionHandler the {@link org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler} to use to + * (can be {@code null} to trigger default executor resolution) + * @param exceptionHandler the {@link AsyncUncaughtExceptionHandler} to use to * handle unexpected exception thrown by asynchronous method executions + * @see AnnotationAsyncExecutionInterceptor#getDefaultExecutor(BeanFactory) */ @SuppressWarnings("unchecked") public AsyncAnnotationAdvisor(Executor executor, AsyncUncaughtExceptionHandler exceptionHandler) { @@ -87,9 +88,6 @@ public AsyncAnnotationAdvisor(Executor executor, AsyncUncaughtExceptionHandler e catch (ClassNotFoundException ex) { // If EJB 3.1 API not present, simply ignore. } - if (executor == null) { - executor = new SimpleAsyncTaskExecutor(); - } if (exceptionHandler != null) { this.exceptionHandler = exceptionHandler; } diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessor.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessor.java index 69c969a576..6d699b3961 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessor.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncAnnotationBeanPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,8 +25,6 @@ import org.springframework.aop.framework.autoproxy.AbstractBeanFactoryAwareAdvisingPostProcessor; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.beans.factory.NoUniqueBeanDefinitionException; import org.springframework.core.task.TaskExecutor; import org.springframework.util.Assert; @@ -68,8 +66,10 @@ public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAd *

    Note that the initial lookup happens by type; this is just the fallback * in case of multiple executor beans found in the context. * @since 4.2 + * @see AnnotationAsyncExecutionInterceptor#DEFAULT_TASK_EXECUTOR_BEAN_NAME */ - public static final String DEFAULT_TASK_EXECUTOR_BEAN_NAME = "taskExecutor"; + public static final String DEFAULT_TASK_EXECUTOR_BEAN_NAME = + AnnotationAsyncExecutionInterceptor.DEFAULT_TASK_EXECUTOR_BEAN_NAME; protected final Log logger = LogFactory.getLog(getClass()); @@ -102,6 +102,13 @@ public void setAsyncAnnotationType(Class asyncAnnotationTy /** * Set the {@link Executor} to use when invoking methods asynchronously. + *

    If not specified, default executor resolution will apply: searching for a + * unique {@link TaskExecutor} bean in the context, or for an {@link Executor} + * bean named "taskExecutor" otherwise. If neither of the two is resolvable, + * a local default executor will be created within the interceptor. + * @see AsyncAnnotationAdvisor#AsyncAnnotationAdvisor(Executor, AsyncUncaughtExceptionHandler) + * @see AnnotationAsyncExecutionInterceptor#getDefaultExecutor(BeanFactory) + * @see #DEFAULT_TASK_EXECUTOR_BEAN_NAME */ public void setExecutor(Executor executor) { this.executor = executor; @@ -121,31 +128,7 @@ public void setExceptionHandler(AsyncUncaughtExceptionHandler exceptionHandler) public void setBeanFactory(BeanFactory beanFactory) { super.setBeanFactory(beanFactory); - Executor executorToUse = this.executor; - if (executorToUse == null) { - try { - // Search for TaskExecutor bean... not plain Executor since that would - // match with ScheduledExecutorService as well, which is unusable for - // our purposes here. TaskExecutor is more clearly designed for it. - executorToUse = beanFactory.getBean(TaskExecutor.class); - } - catch (NoUniqueBeanDefinitionException ex) { - try { - executorToUse = beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, Executor.class); - } - catch (NoSuchBeanDefinitionException ex2) { - logger.info("More than one TaskExecutor bean found within the context, and none is " + - "named 'taskExecutor'. Mark one of them as primary or name it 'taskExecutor' " + - "(possibly as an alias) in order to use it for async annotation processing."); - } - } - catch (NoSuchBeanDefinitionException ex) { - logger.info("No TaskExecutor bean found for async annotation processing."); - // Giving up -> falling back to default executor within the advisor... - } - } - - AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(executorToUse, this.exceptionHandler); + AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler); if (this.asyncAnnotationType != null) { advisor.setAsyncAnnotationType(this.asyncAnnotationType); } diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableAsync.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableAsync.java index ad7c3b0416..ef3c396e64 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableAsync.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableAsync.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,26 +41,28 @@ * @Configuration * @EnableAsync * public class AppConfig { + * * @Bean * public MyAsyncBean asyncBean() { * return new MyAsyncBean(); * } * } * - * *

    The {@link #mode} attribute controls how advice is applied; if the mode is * {@link AdviceMode#PROXY} (the default), then the other attributes control the behavior * of the proxying. * - *

    Note that if the {@linkplain #mode} is set to {@link AdviceMode#ASPECTJ}, then - * the value of the {@link #proxyTargetClass} attribute will be ignored. Note also - * that in this case the {@code spring-aspects} module JAR must be present on the - * classpath. + *

    Note that if the {@linkplain #mode} is set to {@link AdviceMode#ASPECTJ}, then the + * value of the {@link #proxyTargetClass} attribute will be ignored. Note also that in + * this case the {@code spring-aspects} module JAR must be present on the classpath. * - *

    By default, a {@link org.springframework.core.task.SimpleAsyncTaskExecutor - * SimpleAsyncTaskExecutor} will be used to process async method invocations. Besides, - * annotated methods having a {@code void} return type cannot transmit any exception - * back to the caller. By default, such uncaught exceptions are only logged. + *

    By default, Spring will be searching for an associated thread pool definition: + * either a unique {@link org.springframework.core.task.TaskExecutor} bean in the context, + * or an {@link java.util.concurrent.Executor} bean named "taskExecutor" otherwise. If + * neither of the two is resolvable, a {@link org.springframework.core.task.SimpleAsyncTaskExecutor} + * will be used to process async method invocations. Besides, annotated methods having a + * {@code void} return type cannot transmit any exception back to the caller. By default, + * such uncaught exceptions are only logged. * *

    To customize all this, implement {@link AsyncConfigurer} and * provide: @@ -129,6 +131,7 @@ * through direct access to actual componentry. * * @author Chris Beams + * @author Juergen Hoeller * @author Stephane Nicoll * @author Sam Brannen * @since 3.1 @@ -175,9 +178,8 @@ AdviceMode mode() default AdviceMode.PROXY; /** - * Indicate the order in which the - * {@link org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor - * AsyncAnnotationBeanPostProcessor} should be applied. + * Indicate the order in which the {@link AsyncAnnotationBeanPostProcessor} + * should be applied. *

    The default is {@link Ordered#LOWEST_PRECEDENCE} in order to run * after all other post-processors, so that it can add an advisor to * existing proxies rather than double-proxy. diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableScheduling.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableScheduling.java index 944ca9a6bd..b5f643f31b 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableScheduling.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableScheduling.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,16 +37,18 @@ * @Configuration * @EnableScheduling * public class AppConfig { + * * // various @Bean definitions * } * * This enables detection of @{@link Scheduled} annotations on any Spring-managed - * bean in the container. For example, given a class {@code MyTask} + * bean in the container. For example, given a class {@code MyTask} * *

      * package com.myco.tasks;
      *
      * public class MyTask {
    + *
      *     @Scheduled(fixedRate=1000)
      *     public void work() {
      *         // task execution logic
    @@ -60,6 +62,7 @@
      * @Configuration
      * @EnableScheduling
      * public class AppConfig {
    + *
      *     @Bean
      *     public MyTask task() {
      *         return new MyTask();
    @@ -84,14 +87,21 @@
      * @Configuration
      * @EnableScheduling
      * public class AppConfig {
    + *
      *     @Scheduled(fixedRate=1000)
      *     public void work() {
      *         // task execution logic
      *     }
      * }
    * - * In all of the above scenarios, a default single-threaded task executor is used. - * When more control is desired, a {@code @Configuration} class may implement + *

    By default, will be searching for an associated scheduler definition: either + * a unique {@link org.springframework.scheduling.TaskScheduler} bean in the context, + * or a {@code TaskScheduler} bean named "taskScheduler" otherwise; the same lookup + * will also be performed for a {@link java.util.concurrent.ScheduledExecutorService} + * bean. If neither of the two is resolvable, a local single-threaded default + * scheduler will be created and used within the registrar. + * + *

    When more control is desired, a {@code @Configuration} class may implement * {@link SchedulingConfigurer}. This allows access to the underlying * {@link ScheduledTaskRegistrar} instance. For example, the following example * demonstrates how to customize the {@link Executor} used to execute scheduled @@ -101,6 +111,7 @@ * @Configuration * @EnableScheduling * public class AppConfig implements SchedulingConfigurer { + * * @Override * public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { * taskRegistrar.setScheduler(taskExecutor()); @@ -112,11 +123,11 @@ * } * } * - * Note in the example above the use of {@code @Bean(destroyMethod="shutdown")}. This - * ensures that the task executor is properly shut down when the Spring application - * context itself is closed. + *

    Note in the example above the use of {@code @Bean(destroyMethod="shutdown")}. + * This ensures that the task executor is properly shut down when the Spring + * application context itself is closed. * - * Implementing {@code SchedulingConfigurer} also allows for fine-grained + *

    Implementing {@code SchedulingConfigurer} also allows for fine-grained * control over task registration via the {@code ScheduledTaskRegistrar}. * For example, the following configures the execution of a particular bean * method per a custom {@code Trigger} implementation: @@ -125,6 +136,7 @@ * @Configuration * @EnableScheduling * public class AppConfig implements SchedulingConfigurer { + * * @Override * public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { * taskRegistrar.setScheduler(taskScheduler()); @@ -167,6 +179,7 @@ * through direct access to actual componentry.

    * * @author Chris Beams + * @author Juergen Hoeller * @since 3.1 * @see Scheduled * @see SchedulingConfiguration From 2ea7fcde3eee85af6537e7280e15fb24b7436d9c Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 23 Mar 2016 18:39:29 +0100 Subject: [PATCH 083/344] Polishing (cherry picked from commit 9af12d2) --- .../scheduling/annotation/Async.java | 4 +- .../groovy/GroovyScriptFactoryTests.java | 8 +- .../core/annotation/AnnotationUtils.java | 10 +- .../test/annotation/Commit.java | 9 +- .../test/annotation/DirtiesContext.java | 80 ++++++----- .../test/annotation/IfProfileValue.java | 8 +- .../test/annotation/Repeat.java | 11 +- .../test/annotation/Rollback.java | 11 +- .../test/annotation/Timed.java | 11 +- .../test/context/jdbc/MergedSqlConfig.java | 74 +++++----- .../test/context/jdbc/Sql.java | 14 +- .../test/context/jdbc/SqlConfig.java | 9 +- .../test/context/jdbc/SqlGroup.java | 9 +- .../context/transaction/AfterTransaction.java | 9 +- .../transaction/BeforeTransaction.java | 9 +- .../test/context/web/WebAppConfiguration.java | 12 +- .../test/util/MetaAnnotationUtils.java | 11 +- .../TransactionalEventListenerFactory.java | 2 +- .../AbstractHttpMessageConverter.java | 2 +- .../web/method/annotation/ModelFactory.java | 126 +++++++++--------- ...dlerMethodReturnValueHandlerComposite.java | 7 +- .../servlet/support/WebContentGenerator.java | 7 +- .../server/standard/SpringConfigurator.java | 8 +- 23 files changed, 216 insertions(+), 235 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/Async.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/Async.java index b8b32d82fc..bd58fec696 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/Async.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/Async.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -56,7 +56,7 @@ * bean definition. *

    When specified on a class level {@code @Async} annotation, indicates that the * given executor should be used for all methods within the class. Method level use - * of {@link Async#value} always overrides any value set at the class level. + * of {@code Async#value} always overrides any value set at the class level. * @since 3.1.2 */ String value() default ""; diff --git a/spring-context/src/test/java/org/springframework/scripting/groovy/GroovyScriptFactoryTests.java b/spring-context/src/test/java/org/springframework/scripting/groovy/GroovyScriptFactoryTests.java index 4859ad5870..9ab2788443 100644 --- a/spring-context/src/test/java/org/springframework/scripting/groovy/GroovyScriptFactoryTests.java +++ b/spring-context/src/test/java/org/springframework/scripting/groovy/GroovyScriptFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -445,8 +445,7 @@ public void testRefreshableFromTag() throws Exception { assertTrue(ctx.getBeansOfType(Messenger.class).values().contains(messenger)); } - @Test - // Test for SPR-6268 + @Test // SPR-6268 public void testRefreshableFromTagProxyTargetClass() throws Exception { ApplicationContext ctx = new ClassPathXmlApplicationContext("groovy-with-xsd-proxy-target-class.xml", getClass()); @@ -464,8 +463,7 @@ public void testRefreshableFromTagProxyTargetClass() throws Exception { assertNotNull(AnnotationUtils.findAnnotation(messenger.getClass(), Component.class)); } - @Test - // Test for SPR-6268 + @Test // SPR-6268 public void testProxyTargetClassNotAllowedIfNotGroovy() throws Exception { try { new ClassPathXmlApplicationContext("jruby-with-xsd-proxy-target-class.xml", getClass()); diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java index ea634b5cac..c47c801863 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java @@ -71,11 +71,11 @@ * meta-present on the other annotation. * *

    Meta-annotation Support

    - *

    Most {@code find*()} methods and some {@code get*()} methods in this - * class provide support for finding annotations used as meta-annotations. - * Consult the Javadoc for each method in this class for details. For support - * for meta-annotations with attribute overrides in - * composed annotations, use {@link AnnotatedElementUtils} instead. + *

    Most {@code find*()} methods and some {@code get*()} methods in this class + * provide support for finding annotations used as meta-annotations. Consult the + * javadoc for each method in this class for details. For fine-grained support for + * meta-annotations with attribute overrides in composed annotations, + * consider using {@link AnnotatedElementUtils}'s more specific methods instead. * *

    Attribute Aliases

    *

    All public methods in this class that return annotations, arrays of diff --git a/spring-test/src/main/java/org/springframework/test/annotation/Commit.java b/spring-test/src/main/java/org/springframework/test/annotation/Commit.java index 79205810e5..e23c254fba 100644 --- a/spring-test/src/main/java/org/springframework/test/annotation/Commit.java +++ b/spring-test/src/main/java/org/springframework/test/annotation/Commit.java @@ -17,12 +17,11 @@ package org.springframework.test.annotation; import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.*; - /** * {@code @Commit} is a test annotation that is used to indicate that a * test-managed transaction should be committed after @@ -49,9 +48,9 @@ * @see Rollback * @see org.springframework.test.context.transaction.TransactionalTestExecutionListener */ +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) @Documented -@Retention(RUNTIME) -@Target({ TYPE, METHOD }) @Rollback(false) public @interface Commit { } diff --git a/spring-test/src/main/java/org/springframework/test/annotation/DirtiesContext.java b/spring-test/src/main/java/org/springframework/test/annotation/DirtiesContext.java index 73bf379df3..88be564e38 100644 --- a/spring-test/src/main/java/org/springframework/test/annotation/DirtiesContext.java +++ b/spring-test/src/main/java/org/springframework/test/annotation/DirtiesContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -76,19 +76,48 @@ * @see org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener * @see org.springframework.test.context.support.DirtiesContextTestExecutionListener */ +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) @Documented @Inherited -@Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.TYPE, ElementType.METHOD }) public @interface DirtiesContext { + /** + * The mode to use when a test method is annotated with + * {@code @DirtiesContext}. + *

    Defaults to {@link MethodMode#AFTER_METHOD AFTER_METHOD}. + *

    Setting the method mode on an annotated test class has no meaning. + * For class-level control, use {@link #classMode} instead. + * @since 4.2 + */ + MethodMode methodMode() default MethodMode.AFTER_METHOD; + + /** + * The mode to use when a test class is annotated with + * {@code @DirtiesContext}. + *

    Defaults to {@link ClassMode#AFTER_CLASS AFTER_CLASS}. + *

    Setting the class mode on an annotated test method has no meaning. + * For method-level control, use {@link #methodMode} instead. + * @since 3.0 + */ + ClassMode classMode() default ClassMode.AFTER_CLASS; + + /** + * The context cache clearing mode to use when a context is + * configured as part of a hierarchy via + * {@link org.springframework.test.context.ContextHierarchy @ContextHierarchy}. + *

    Defaults to {@link HierarchyMode#EXHAUSTIVE EXHAUSTIVE}. + * @since 3.2.2 + */ + HierarchyMode hierarchyMode() default HierarchyMode.EXHAUSTIVE; + + /** * Defines modes which determine how {@code @DirtiesContext} is * interpreted when used to annotate a test method. - * * @since 4.2 */ - static enum MethodMode { + enum MethodMode { /** * The associated {@code ApplicationContext} will be marked as @@ -103,13 +132,13 @@ static enum MethodMode { AFTER_METHOD; } + /** * Defines modes which determine how {@code @DirtiesContext} is * interpreted when used to annotate a test class. - * * @since 3.0 */ - static enum ClassMode { + enum ClassMode { /** * The associated {@code ApplicationContext} will be marked as @@ -140,15 +169,15 @@ static enum ClassMode { AFTER_CLASS; } + /** * Defines modes which determine how the context cache is cleared * when {@code @DirtiesContext} is used in a test whose context is * configured as part of a hierarchy via * {@link org.springframework.test.context.ContextHierarchy @ContextHierarchy}. - * * @since 3.2.2 */ - static enum HierarchyMode { + enum HierarchyMode { /** * The context cache will be cleared using an exhaustive algorithm @@ -174,37 +203,4 @@ static enum HierarchyMode { CURRENT_LEVEL; } - - /** - * The mode to use when a test method is annotated with - * {@code @DirtiesContext}. - *

    Defaults to {@link MethodMode#AFTER_METHOD AFTER_METHOD}. - *

    Setting the method mode on an annotated test class has no meaning. - * For class-level control, use {@link #classMode} instead. - * - * @since 4.2 - */ - MethodMode methodMode() default MethodMode.AFTER_METHOD; - - /** - * The mode to use when a test class is annotated with - * {@code @DirtiesContext}. - *

    Defaults to {@link ClassMode#AFTER_CLASS AFTER_CLASS}. - *

    Setting the class mode on an annotated test method has no meaning. - * For method-level control, use {@link #methodMode} instead. - * - * @since 3.0 - */ - ClassMode classMode() default ClassMode.AFTER_CLASS; - - /** - * The context cache clearing mode to use when a context is - * configured as part of a hierarchy via - * {@link org.springframework.test.context.ContextHierarchy @ContextHierarchy}. - *

    Defaults to {@link HierarchyMode#EXHAUSTIVE EXHAUSTIVE}. - * - * @since 3.2.2 - */ - HierarchyMode hierarchyMode() default HierarchyMode.EXHAUSTIVE; - } diff --git a/spring-test/src/main/java/org/springframework/test/annotation/IfProfileValue.java b/spring-test/src/main/java/org/springframework/test/annotation/IfProfileValue.java index f16a7d4f07..0e8716e088 100644 --- a/spring-test/src/main/java/org/springframework/test/annotation/IfProfileValue.java +++ b/spring-test/src/main/java/org/springframework/test/annotation/IfProfileValue.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -95,10 +95,10 @@ * @see org.springframework.context.annotation.Profile * @see org.springframework.test.context.ActiveProfiles */ +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) @Documented @Inherited -@Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.TYPE, ElementType.METHOD }) public @interface IfProfileValue { /** @@ -109,7 +109,6 @@ /** * A single, permissible {@code value} of the profile value * for the given {@link #name}. - * *

    Note: Assigning values to both {@link #value} and {@link #values} * will lead to a configuration conflict. */ @@ -118,7 +117,6 @@ /** * A list of all permissible {@code values} of the profile value * for the given {@link #name}. - * *

    Note: Assigning values to both {@link #value} and {@link #values} * will lead to a configuration conflict. */ diff --git a/spring-test/src/main/java/org/springframework/test/annotation/Repeat.java b/spring-test/src/main/java/org/springframework/test/annotation/Repeat.java index 1b04d64fd3..8d649635f5 100644 --- a/spring-test/src/main/java/org/springframework/test/annotation/Repeat.java +++ b/spring-test/src/main/java/org/springframework/test/annotation/Repeat.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,12 +17,11 @@ package org.springframework.test.annotation; import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.*; - /** * Test annotation to indicate that a test method should be invoked repeatedly. * @@ -41,9 +40,9 @@ * @see org.springframework.test.context.junit4.rules.SpringMethodRule * @see org.springframework.test.context.junit4.statements.SpringRepeat */ +@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) @Documented -@Retention(RUNTIME) -@Target({ METHOD, ANNOTATION_TYPE }) public @interface Repeat { /** diff --git a/spring-test/src/main/java/org/springframework/test/annotation/Rollback.java b/spring-test/src/main/java/org/springframework/test/annotation/Rollback.java index 65333a23e3..37bf210279 100644 --- a/spring-test/src/main/java/org/springframework/test/annotation/Rollback.java +++ b/spring-test/src/main/java/org/springframework/test/annotation/Rollback.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,12 +17,11 @@ package org.springframework.test.annotation; import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.*; - /** * {@code @Rollback} is a test annotation that is used to indicate whether * a test-managed transaction should be rolled back after @@ -54,9 +53,9 @@ * @see Commit * @see org.springframework.test.context.transaction.TransactionalTestExecutionListener */ +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) @Documented -@Retention(RUNTIME) -@Target({ TYPE, METHOD }) public @interface Rollback { /** diff --git a/spring-test/src/main/java/org/springframework/test/annotation/Timed.java b/spring-test/src/main/java/org/springframework/test/annotation/Timed.java index f364dab237..7ece58fd9b 100644 --- a/spring-test/src/main/java/org/springframework/test/annotation/Timed.java +++ b/spring-test/src/main/java/org/springframework/test/annotation/Timed.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,12 +17,11 @@ package org.springframework.test.annotation; import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.*; - /** * Test-specific annotation to indicate that a test method has to finish * execution in a {@linkplain #millis() specified time period}. @@ -45,9 +44,9 @@ * @see org.springframework.test.context.junit4.rules.SpringMethodRule * @see org.springframework.test.context.junit4.statements.SpringFailOnTimeout */ +@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) @Documented -@Retention(RUNTIME) -@Target({ METHOD, ANNOTATION_TYPE }) public @interface Timed { /** diff --git a/spring-test/src/main/java/org/springframework/test/context/jdbc/MergedSqlConfig.java b/spring-test/src/main/java/org/springframework/test/context/jdbc/MergedSqlConfig.java index 63dc1c1b71..4a59cad4f9 100644 --- a/spring-test/src/main/java/org/springframework/test/context/jdbc/MergedSqlConfig.java +++ b/spring-test/src/main/java/org/springframework/test/context/jdbc/MergedSqlConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,8 +27,8 @@ /** * {@code MergedSqlConfig} encapsulates the merged {@link SqlConfig @SqlConfig} - * attributes declared locally via {@link Sql#config} and globally as a - * class-level annotation. + * attributes declared locally via {@link Sql#config} and globally as a class-level annotation. + * *

    Explicit local configuration attributes override global configuration attributes. * * @author Sam Brannen @@ -56,23 +56,6 @@ class MergedSqlConfig { private final ErrorMode errorMode; - private static > E getEnum(AnnotationAttributes attributes, String attributeName, - E inheritedOrDefaultValue, E defaultValue) { - E value = attributes.getEnum(attributeName); - if (value == inheritedOrDefaultValue) { - value = defaultValue; - } - return value; - } - - private static String getString(AnnotationAttributes attributes, String attributeName, String defaultValue) { - String value = attributes.getString(attributeName); - if ("".equals(value)) { - value = defaultValue; - } - return value; - } - /** * Construct a {@code MergedSqlConfig} instance by merging the configuration * from the supplied local (potentially method-level) {@code @SqlConfig} annotation @@ -86,8 +69,8 @@ private static String getString(AnnotationAttributes attributes, String attribut Assert.notNull(testClass, "testClass must not be null"); // Get global attributes, if any. - AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(testClass, - SqlConfig.class.getName(), false, false); + AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes( + testClass, SqlConfig.class.getName(), false, false); // Override global attributes with local attributes. if (attributes != null) { @@ -95,7 +78,7 @@ private static String getString(AnnotationAttributes attributes, String attribut Object value = AnnotationUtils.getValue(localSqlConfig, key); if (value != null) { // Is the value explicit (i.e., not a 'default')? - if (!value.equals("") && (value != TransactionMode.DEFAULT) && (value != ErrorMode.DEFAULT)) { + if (!value.equals("") && value != TransactionMode.DEFAULT && value != ErrorMode.DEFAULT) { attributes.put(key, value); } } @@ -113,9 +96,9 @@ private static String getString(AnnotationAttributes attributes, String attribut this.separator = getString(attributes, "separator", ScriptUtils.DEFAULT_STATEMENT_SEPARATOR); this.commentPrefix = getString(attributes, "commentPrefix", ScriptUtils.DEFAULT_COMMENT_PREFIX); this.blockCommentStartDelimiter = getString(attributes, "blockCommentStartDelimiter", - ScriptUtils.DEFAULT_BLOCK_COMMENT_START_DELIMITER); + ScriptUtils.DEFAULT_BLOCK_COMMENT_START_DELIMITER); this.blockCommentEndDelimiter = getString(attributes, "blockCommentEndDelimiter", - ScriptUtils.DEFAULT_BLOCK_COMMENT_END_DELIMITER); + ScriptUtils.DEFAULT_BLOCK_COMMENT_END_DELIMITER); this.errorMode = getEnum(attributes, "errorMode", ErrorMode.DEFAULT, ErrorMode.FAIL_ON_ERROR); } @@ -187,17 +170,36 @@ ErrorMode getErrorMode() { */ @Override public String toString() { - return new ToStringCreator(this)// - .append("dataSource", dataSource)// - .append("transactionManager", transactionManager)// - .append("transactionMode", transactionMode)// - .append("encoding", encoding)// - .append("separator", separator)// - .append("commentPrefix", commentPrefix)// - .append("blockCommentStartDelimiter", blockCommentStartDelimiter)// - .append("blockCommentEndDelimiter", blockCommentEndDelimiter)// - .append("errorMode", errorMode)// - .toString(); + return new ToStringCreator(this) + .append("dataSource", this.dataSource) + .append("transactionManager", this.transactionManager) + .append("transactionMode", this.transactionMode) + .append("encoding", this.encoding) + .append("separator", this.separator) + .append("commentPrefix", this.commentPrefix) + .append("blockCommentStartDelimiter", this.blockCommentStartDelimiter) + .append("blockCommentEndDelimiter", this.blockCommentEndDelimiter) + .append("errorMode", this.errorMode) + .toString(); + } + + + private static > E getEnum(AnnotationAttributes attributes, String attributeName, + E inheritedOrDefaultValue, E defaultValue) { + + E value = attributes.getEnum(attributeName); + if (value == inheritedOrDefaultValue) { + value = defaultValue; + } + return value; + } + + private static String getString(AnnotationAttributes attributes, String attributeName, String defaultValue) { + String value = attributes.getString(attributeName); + if ("".equals(value)) { + value = defaultValue; + } + return value; } } diff --git a/spring-test/src/main/java/org/springframework/test/context/jdbc/Sql.java b/spring-test/src/main/java/org/springframework/test/context/jdbc/Sql.java index e413b7b3e0..65fe0a8313 100644 --- a/spring-test/src/main/java/org/springframework/test/context/jdbc/Sql.java +++ b/spring-test/src/main/java/org/springframework/test/context/jdbc/Sql.java @@ -17,16 +17,15 @@ package org.springframework.test.context.jdbc; import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.core.annotation.AliasFor; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.*; - /** * {@code @Sql} is used to annotate a test class or test method to configure * SQL {@link #scripts} and {@link #statements} to be executed against a given @@ -65,10 +64,10 @@ * @see org.springframework.jdbc.datasource.init.ResourceDatabasePopulator * @see org.springframework.jdbc.datasource.init.ScriptUtils */ +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) @Documented @Inherited -@Retention(RUNTIME) -@Target({TYPE, METHOD}) @Repeatable(SqlGroup.class) public @interface Sql { @@ -88,7 +87,6 @@ * {@link #value}, but it may be used instead of {@link #value}. Similarly, * this attribute may be used in conjunction with or instead of * {@link #statements}. - * *

    Path Resource Semantics

    *

    Each path will be interpreted as a Spring * {@link org.springframework.core.io.Resource Resource}. A plain path @@ -101,7 +99,6 @@ * {@link org.springframework.util.ResourceUtils#CLASSPATH_URL_PREFIX classpath:}, * {@link org.springframework.util.ResourceUtils#FILE_URL_PREFIX file:}, * {@code http:}, etc.) will be loaded using the specified resource protocol. - * *

    Default Script Detection

    *

    If no SQL scripts or {@link #statements} are specified, an attempt will * be made to detect a default script depending on where this @@ -116,7 +113,6 @@ * {@code com.example.MyTest}, the corresponding default script is * {@code "classpath:com/example/MyTest.testMethod.sql"}. * - * * @see #value * @see #statements */ @@ -127,13 +123,11 @@ * Inlined SQL statements to execute. *

    This attribute may be used in conjunction with or instead of * {@link #scripts}. - * *

    Ordering

    *

    Statements declared via this attribute will be executed after * statements loaded from resource {@link #scripts}. If you wish to have * inlined statements executed before scripts, simply declare multiple * instances of {@code @Sql} on the same class or method. - * * @since 4.2 * @see #scripts */ diff --git a/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlConfig.java b/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlConfig.java index 9c0d55a6a0..31b8805b80 100644 --- a/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlConfig.java +++ b/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlConfig.java @@ -17,13 +17,12 @@ package org.springframework.test.context.jdbc; import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.*; - /** * {@code @SqlConfig} defines metadata that is used to determine how to parse * and execute SQL scripts configured via the {@link Sql @Sql} annotation. @@ -59,10 +58,10 @@ * @since 4.1 * @see Sql */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) @Documented @Inherited -@Retention(RUNTIME) -@Target(TYPE) public @interface SqlConfig { /** diff --git a/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlGroup.java b/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlGroup.java index 6e052bd607..e5bfd24e70 100644 --- a/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlGroup.java +++ b/spring-test/src/main/java/org/springframework/test/context/jdbc/SqlGroup.java @@ -17,13 +17,12 @@ package org.springframework.test.context.jdbc; import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.*; - /** * Container annotation that aggregates several {@link Sql @Sql} annotations. * @@ -39,10 +38,10 @@ * @since 4.1 * @see Sql */ +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) @Documented @Inherited -@Retention(RUNTIME) -@Target({TYPE, METHOD}) public @interface SqlGroup { Sql[] value(); diff --git a/spring-test/src/main/java/org/springframework/test/context/transaction/AfterTransaction.java b/spring-test/src/main/java/org/springframework/test/context/transaction/AfterTransaction.java index ea21cc7fb7..04ebe48152 100644 --- a/spring-test/src/main/java/org/springframework/test/context/transaction/AfterTransaction.java +++ b/spring-test/src/main/java/org/springframework/test/context/transaction/AfterTransaction.java @@ -17,12 +17,11 @@ package org.springframework.test.context.transaction; import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.*; - /** *

    Test annotation to indicate that the annotated {@code public void} method * should be executed after a transaction is ended for a test method @@ -39,8 +38,8 @@ * @see org.springframework.transaction.annotation.Transactional * @see BeforeTransaction */ +@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) @Documented -@Retention(RUNTIME) -@Target({ METHOD, ANNOTATION_TYPE }) public @interface AfterTransaction { } diff --git a/spring-test/src/main/java/org/springframework/test/context/transaction/BeforeTransaction.java b/spring-test/src/main/java/org/springframework/test/context/transaction/BeforeTransaction.java index 05106390ca..b7110015bc 100644 --- a/spring-test/src/main/java/org/springframework/test/context/transaction/BeforeTransaction.java +++ b/spring-test/src/main/java/org/springframework/test/context/transaction/BeforeTransaction.java @@ -17,12 +17,11 @@ package org.springframework.test.context.transaction; import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.*; - /** *

    Test annotation to indicate that the annotated {@code public void} method * should be executed before a transaction is started for a test method @@ -39,8 +38,8 @@ * @see org.springframework.transaction.annotation.Transactional * @see AfterTransaction */ +@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) @Documented -@Retention(RUNTIME) -@Target({ METHOD, ANNOTATION_TYPE }) public @interface BeforeTransaction { } diff --git a/spring-test/src/main/java/org/springframework/test/context/web/WebAppConfiguration.java b/spring-test/src/main/java/org/springframework/test/context/web/WebAppConfiguration.java index b2434c5879..e9d176109e 100644 --- a/spring-test/src/main/java/org/springframework/test/context/web/WebAppConfiguration.java +++ b/spring-test/src/main/java/org/springframework/test/context/web/WebAppConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,8 +31,8 @@ * should be a {@link org.springframework.web.context.WebApplicationContext * WebApplicationContext}. * - *

    The mere presence of {@code @WebAppConfiguration} on a test class ensures - * that a {@code WebApplicationContext} will be loaded for the test using a default + *

    The presence of {@code @WebAppConfiguration} on a test class indicates that + * a {@code WebApplicationContext} should be loaded for the test using a default * for the path to the root of the web application. To override the default, * specify an explicit resource path via the {@link #value} attribute. * @@ -49,20 +49,18 @@ * @see org.springframework.test.context.ContextConfiguration * @see ServletTestExecutionListener */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) @Documented @Inherited -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) @BootstrapWith(WebTestContextBootstrapper.class) public @interface WebAppConfiguration { /** * The resource path to the root directory of the web application. - * *

    A path that does not include a Spring resource prefix (e.g., {@code classpath:}, * {@code file:}, etc.) will be interpreted as a file system resource, and a * path should not end with a slash. - * *

    Defaults to {@code "src/main/webapp"} as a file system resource. Note * that this is the standard directory for the root of a web application in * a project that follows the standard Maven project layout for a WAR. diff --git a/spring-test/src/main/java/org/springframework/test/util/MetaAnnotationUtils.java b/spring-test/src/main/java/org/springframework/test/util/MetaAnnotationUtils.java index 2503a998de..9e113f5c18 100644 --- a/spring-test/src/main/java/org/springframework/test/util/MetaAnnotationUtils.java +++ b/spring-test/src/main/java/org/springframework/test/util/MetaAnnotationUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -196,10 +196,10 @@ private static UntypedAnnotationDescriptor findAnnotationDescriptorForTypes(Clas for (Annotation composedAnnotation : clazz.getDeclaredAnnotations()) { if (!AnnotationUtils.isInJavaLangAnnotationPackage(composedAnnotation) && visited.add(composedAnnotation)) { UntypedAnnotationDescriptor descriptor = findAnnotationDescriptorForTypes( - composedAnnotation.annotationType(), visited, annotationTypes); + composedAnnotation.annotationType(), visited, annotationTypes); if (descriptor != null) { return new UntypedAnnotationDescriptor(clazz, descriptor.getDeclaringClass(), composedAnnotation, - descriptor.getAnnotation()); + descriptor.getAnnotation()); } } } @@ -274,13 +274,13 @@ public static class AnnotationDescriptor { private final AnnotationAttributes annotationAttributes; - public AnnotationDescriptor(Class rootDeclaringClass, T annotation) { this(rootDeclaringClass, rootDeclaringClass, null, annotation); } public AnnotationDescriptor(Class rootDeclaringClass, Class declaringClass, Annotation composedAnnotation, T annotation) { + Assert.notNull(rootDeclaringClass, "rootDeclaringClass must not be null"); Assert.notNull(annotation, "annotation must not be null"); this.rootDeclaringClass = rootDeclaringClass; @@ -362,6 +362,7 @@ public UntypedAnnotationDescriptor(Class rootDeclaringClass, Annotation annot public UntypedAnnotationDescriptor(Class rootDeclaringClass, Class declaringClass, Annotation composedAnnotation, Annotation annotation) { + super(rootDeclaringClass, declaringClass, composedAnnotation, annotation); } @@ -374,7 +375,7 @@ public UntypedAnnotationDescriptor(Class rootDeclaringClass, Class declari @Override public Annotation synthesizeAnnotation() { throw new UnsupportedOperationException( - "getMergedAnnotation() is unsupported in UntypedAnnotationDescriptor"); + "getMergedAnnotation() is unsupported in UntypedAnnotationDescriptor"); } } diff --git a/spring-tx/src/main/java/org/springframework/transaction/event/TransactionalEventListenerFactory.java b/spring-tx/src/main/java/org/springframework/transaction/event/TransactionalEventListenerFactory.java index b40fc0f9e0..19d5ab82f8 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/event/TransactionalEventListenerFactory.java +++ b/spring-tx/src/main/java/org/springframework/transaction/event/TransactionalEventListenerFactory.java @@ -41,7 +41,7 @@ public void setOrder(int order) { @Override public int getOrder() { - return order; + return this.order; } diff --git a/spring-web/src/main/java/org/springframework/http/converter/AbstractHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/AbstractHttpMessageConverter.java index 6828953245..1f1e200882 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/AbstractHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/AbstractHttpMessageConverter.java @@ -130,7 +130,7 @@ protected boolean canRead(MediaType mediaType) { @Override public boolean canWrite(Class clazz, MediaType mediaType) { return supports(clazz) && canWrite(mediaType); - } + } /** * Returns {@code true} if the given media type includes any of the diff --git a/spring-web/src/main/java/org/springframework/web/method/annotation/ModelFactory.java b/spring-web/src/main/java/org/springframework/web/method/annotation/ModelFactory.java index 9ee2bbdea3..9eb31c0d36 100644 --- a/spring-web/src/main/java/org/springframework/web/method/annotation/ModelFactory.java +++ b/spring-web/src/main/java/org/springframework/web/method/annotation/ModelFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -45,14 +45,14 @@ import org.springframework.web.method.support.ModelAndViewContainer; /** - * Provides methods to initialize the {@link Model} before controller method - * invocation and to update it afterwards. + * Assist with initialization of the {@link Model} before controller method + * invocation and with updates to it after the invocation. * - *

    On initialization, the model is populated with attributes from the session - * and by invoking methods annotated with {@code @ModelAttribute}. + *

    On initialization the model is populated with attributes temporarily stored + * in the session and through the invocation of {@code @ModelAttribute} methods. * - *

    On update, model attributes are synchronized with the session and also - * {@link BindingResult} attributes are added where missing. + *

    On update model attributes are synchronized with the session and also + * {@link BindingResult} attributes are added if missing. * * @author Rossen Stoyanchev * @since 3.1 @@ -70,51 +70,51 @@ public final class ModelFactory { /** * Create a new instance with the given {@code @ModelAttribute} methods. - * @param invocableMethods the {@code @ModelAttribute} methods to invoke - * @param dataBinderFactory for preparation of {@link BindingResult} attributes - * @param sessionAttributesHandler for access to session attributes + * @param handlerMethods the {@code @ModelAttribute} methods to invoke + * @param binderFactory for preparation of {@link BindingResult} attributes + * @param attributeHandler for access to session attributes */ - public ModelFactory(List invocableMethods, WebDataBinderFactory dataBinderFactory, - SessionAttributesHandler sessionAttributesHandler) { + public ModelFactory(List handlerMethods, + WebDataBinderFactory binderFactory, SessionAttributesHandler attributeHandler) { - if (invocableMethods != null) { - for (InvocableHandlerMethod method : invocableMethods) { - this.modelMethods.add(new ModelMethod(method)); + if (handlerMethods != null) { + for (InvocableHandlerMethod handlerMethod : handlerMethods) { + this.modelMethods.add(new ModelMethod(handlerMethod)); } } - this.dataBinderFactory = dataBinderFactory; - this.sessionAttributesHandler = sessionAttributesHandler; + this.dataBinderFactory = binderFactory; + this.sessionAttributesHandler = attributeHandler; } + /** * Populate the model in the following order: *

      - *
    1. Retrieve "known" session attributes listed as {@code @SessionAttributes}. - *
    2. Invoke {@code @ModelAttribute} methods - *
    3. Find {@code @ModelAttribute} method arguments also listed as - * {@code @SessionAttributes} and ensure they're present in the model raising - * an exception if necessary. + *
    4. Retrieve "known" session attributes listed as {@code @SessionAttributes}. + *
    5. Invoke {@code @ModelAttribute} methods + *
    6. Find {@code @ModelAttribute} method arguments also listed as + * {@code @SessionAttributes} and ensure they're present in the model raising + * an exception if necessary. *
    * @param request the current request - * @param mavContainer a container with the model to be initialized + * @param container a container with the model to be initialized * @param handlerMethod the method for which the model is initialized * @throws Exception may arise from {@code @ModelAttribute} methods */ - public void initModel(NativeWebRequest request, ModelAndViewContainer mavContainer, HandlerMethod handlerMethod) - throws Exception { + public void initModel(NativeWebRequest request, ModelAndViewContainer container, + HandlerMethod handlerMethod) throws Exception { Map sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request); - mavContainer.mergeAttributes(sessionAttributes); - - invokeModelAttributeMethods(request, mavContainer); + container.mergeAttributes(sessionAttributes); + invokeModelAttributeMethods(request, container); for (String name : findSessionAttributeArguments(handlerMethod)) { - if (!mavContainer.containsAttribute(name)) { + if (!container.containsAttribute(name)) { Object value = this.sessionAttributesHandler.retrieveAttribute(request, name); if (value == null) { throw new HttpSessionRequiredException("Expected session attribute '" + name + "'"); } - mavContainer.addAttribute(name, value); + container.addAttribute(name, value); } } } @@ -123,30 +123,29 @@ public void initModel(NativeWebRequest request, ModelAndViewContainer mavContain * Invoke model attribute methods to populate the model. * Attributes are added only if not already present in the model. */ - private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer mavContainer) + private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer container) throws Exception { while (!this.modelMethods.isEmpty()) { - InvocableHandlerMethod attrMethod = getNextModelMethod(mavContainer).getHandlerMethod(); - String modelName = attrMethod.getMethodAnnotation(ModelAttribute.class).value(); - if (mavContainer.containsAttribute(modelName)) { + InvocableHandlerMethod modelMethod = getNextModelMethod(container).getHandlerMethod(); + String modelName = modelMethod.getMethodAnnotation(ModelAttribute.class).value(); + if (container.containsAttribute(modelName)) { continue; } - Object returnValue = attrMethod.invokeForRequest(request, mavContainer); - - if (!attrMethod.isVoid()){ - String returnValueName = getNameForReturnValue(returnValue, attrMethod.getReturnType()); - if (!mavContainer.containsAttribute(returnValueName)) { - mavContainer.addAttribute(returnValueName, returnValue); + Object returnValue = modelMethod.invokeForRequest(request, container); + if (!modelMethod.isVoid()){ + String returnValueName = getNameForReturnValue(returnValue, modelMethod.getReturnType()); + if (!container.containsAttribute(returnValueName)) { + container.addAttribute(returnValueName, returnValue); } } } } - private ModelMethod getNextModelMethod(ModelAndViewContainer mavContainer) { + private ModelMethod getNextModelMethod(ModelAndViewContainer container) { for (ModelMethod modelMethod : this.modelMethods) { - if (modelMethod.checkDependencies(mavContainer)) { + if (modelMethod.checkDependencies(container)) { if (logger.isTraceEnabled()) { logger.trace("Selected @ModelAttribute method " + modelMethod); } @@ -157,7 +156,7 @@ private ModelMethod getNextModelMethod(ModelAndViewContainer mavContainer) { ModelMethod modelMethod = this.modelMethods.get(0); if (logger.isTraceEnabled()) { logger.trace("Selected @ModelAttribute method (not present: " + - modelMethod.getUnresolvedDependencies(mavContainer)+ ") " + modelMethod); + modelMethod.getUnresolvedDependencies(container)+ ") " + modelMethod); } this.modelMethods.remove(modelMethod); return modelMethod; @@ -171,7 +170,8 @@ private List findSessionAttributeArguments(HandlerMethod handlerMethod) for (MethodParameter parameter : handlerMethod.getMethodParameters()) { if (parameter.hasParameterAnnotation(ModelAttribute.class)) { String name = getNameForParameter(parameter); - if (this.sessionAttributesHandler.isHandlerSessionAttribute(name, parameter.getParameterType())) { + Class paramType = parameter.getParameterType(); + if (this.sessionAttributesHandler.isHandlerSessionAttribute(name, paramType)) { result.add(name); } } @@ -182,36 +182,37 @@ private List findSessionAttributeArguments(HandlerMethod handlerMethod) /** * Derives the model attribute name for a method parameter based on: *
      - *
    1. The parameter {@code @ModelAttribute} annotation value - *
    2. The parameter type + *
    3. The parameter {@code @ModelAttribute} annotation value + *
    4. The parameter type *
    * @return the derived name; never {@code null} or an empty string */ public static String getNameForParameter(MethodParameter parameter) { - ModelAttribute annot = parameter.getParameterAnnotation(ModelAttribute.class); - String attrName = (annot != null) ? annot.value() : null; - return StringUtils.hasText(attrName) ? attrName : Conventions.getVariableNameForParameter(parameter); + ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class); + String name = (ann != null ? ann.value() : null); + return StringUtils.hasText(name) ? name : Conventions.getVariableNameForParameter(parameter); } /** * Derive the model attribute name for the given return value using one of: *
      - *
    1. The method {@code ModelAttribute} annotation value - *
    2. The declared return type if it is more specific than {@code Object} - *
    3. The actual return value type + *
    4. The method {@code ModelAttribute} annotation value + *
    5. The declared return type if it is more specific than {@code Object} + *
    6. The actual return value type *
    * @param returnValue the value returned from a method invocation * @param returnType the return type of the method * @return the model name, never {@code null} nor empty */ public static String getNameForReturnValue(Object returnValue, MethodParameter returnType) { - ModelAttribute annotation = returnType.getMethodAnnotation(ModelAttribute.class); - if (annotation != null && StringUtils.hasText(annotation.value())) { - return annotation.value(); + ModelAttribute ann = returnType.getMethodAnnotation(ModelAttribute.class); + if (ann != null && StringUtils.hasText(ann.value())) { + return ann.value(); } else { Method method = returnType.getMethod(); - Class resolvedType = GenericTypeResolver.resolveReturnType(method, returnType.getContainingClass()); + Class containingClass = returnType.getContainingClass(); + Class resolvedType = GenericTypeResolver.resolveReturnType(method, containingClass); return Conventions.getVariableNameForReturnType(method, resolvedType, returnValue); } } @@ -220,18 +221,18 @@ public static String getNameForReturnValue(Object returnValue, MethodParameter r * Promote model attributes listed as {@code @SessionAttributes} to the session. * Add {@link BindingResult} attributes where necessary. * @param request the current request - * @param mavContainer contains the model to update + * @param container contains the model to update * @throws Exception if creating BindingResult attributes fails */ - public void updateModel(NativeWebRequest request, ModelAndViewContainer mavContainer) throws Exception { - ModelMap defaultModel = mavContainer.getDefaultModel(); - if (mavContainer.getSessionStatus().isComplete()){ + public void updateModel(NativeWebRequest request, ModelAndViewContainer container) throws Exception { + ModelMap defaultModel = container.getDefaultModel(); + if (container.getSessionStatus().isComplete()){ this.sessionAttributesHandler.cleanupAttributes(request); } else { this.sessionAttributesHandler.storeAttributes(request, defaultModel); } - if (!mavContainer.isRequestHandled() && mavContainer.getModel() == defaultModel) { + if (!container.isRequestHandled() && container.getModel() == defaultModel) { updateBindingResult(request, defaultModel); } } @@ -248,7 +249,7 @@ private void updateBindingResult(NativeWebRequest request, ModelMap model) throw String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + name; if (!model.containsAttribute(bindingResultKey)) { - WebDataBinder dataBinder = dataBinderFactory.createBinder(request, value, name); + WebDataBinder dataBinder = this.dataBinderFactory.createBinder(request, value, name); model.put(bindingResultKey, dataBinder.getBindingResult()); } } @@ -279,7 +280,6 @@ private static class ModelMethod { private final Set dependencies = new HashSet(); - private ModelMethod(InvocableHandlerMethod handlerMethod) { this.handlerMethod = handlerMethod; for (MethodParameter parameter : handlerMethod.getMethodParameters()) { diff --git a/spring-web/src/main/java/org/springframework/web/method/support/HandlerMethodReturnValueHandlerComposite.java b/spring-web/src/main/java/org/springframework/web/method/support/HandlerMethodReturnValueHandlerComposite.java index 36d2c25d88..847068de2e 100644 --- a/spring-web/src/main/java/org/springframework/web/method/support/HandlerMethodReturnValueHandlerComposite.java +++ b/spring-web/src/main/java/org/springframework/web/method/support/HandlerMethodReturnValueHandlerComposite.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,6 @@ import org.apache.commons.logging.LogFactory; import org.springframework.core.MethodParameter; -import org.springframework.util.Assert; import org.springframework.web.context.request.NativeWebRequest; /** @@ -76,7 +75,9 @@ public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType); - Assert.notNull(handler, "Unknown return value type [" + returnType.getParameterType().getName() + "]"); + if (handler == null) { + throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName()); + } handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest); } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/WebContentGenerator.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/WebContentGenerator.java index 947d77ab11..20cf89d2c0 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/WebContentGenerator.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/WebContentGenerator.java @@ -18,14 +18,15 @@ import java.util.Arrays; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Set; import java.util.concurrent.TimeUnit; - import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.http.CacheControl; +import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.HttpSessionRequiredException; @@ -133,8 +134,8 @@ public WebContentGenerator(String... supportedMethods) { * unrestricted for general controllers and interceptors. */ public final void setSupportedMethods(String... methods) { - if (methods != null) { - this.supportedMethods = new HashSet(Arrays.asList(methods)); + if (!ObjectUtils.isEmpty(methods)) { + this.supportedMethods = new LinkedHashSet(Arrays.asList(methods)); } else { this.supportedMethods = null; diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/SpringConfigurator.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/SpringConfigurator.java index 1bd00abe22..5f13d12a49 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/SpringConfigurator.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/SpringConfigurator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -77,9 +77,9 @@ public T getEndpointInstance(Class endpointClass) throws InstantiationExc return endpoint; } - Component annot = AnnotationUtils.findAnnotation(endpointClass, Component.class); - if ((annot != null) && wac.containsBean(annot.value())) { - T endpoint = wac.getBean(annot.value(), endpointClass); + Component ann = AnnotationUtils.findAnnotation(endpointClass, Component.class); + if (ann != null && wac.containsBean(ann.value())) { + T endpoint = wac.getBean(ann.value(), endpointClass); if (logger.isTraceEnabled()) { logger.trace("Using @ServerEndpoint singleton " + endpoint); } From 0891fbaf979aaa826db4db807a75cf6de54e74af Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 24 Mar 2016 19:54:28 +0100 Subject: [PATCH 084/344] Consistent formatting --- .../aspectj/AspectJAfterReturningAdvice.java | 4 +-- .../config/FieldRetrievingFactoryBean.java | 4 +-- .../beans/CollectingReaderEventListener.java | 33 ++++++++++--------- .../org/springframework/util/DigestUtils.java | 4 +-- .../org/springframework/util/MimeType.java | 4 +-- .../org/springframework/tests/Matchers.java | 2 +- .../org/springframework/tests/TestGroup.java | 3 +- .../expression/spel/CodeFlow.java | 2 +- .../jdbc/core/JdbcTemplate.java | 5 +-- .../cors/UrlBasedCorsConfigurationSource.java | 2 +- .../web/util/HierarchicalUriComponents.java | 6 ++-- .../servlet/resource/ResourceUrlProvider.java | 2 +- .../resource/VersionResourceResolver.java | 4 +-- .../frame/AbstractSockJsMessageCodec.java | 2 +- 14 files changed, 40 insertions(+), 37 deletions(-) diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterReturningAdvice.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterReturningAdvice.java index 5e0b489c63..3a852bdb4f 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterReturningAdvice.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAfterReturningAdvice.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -98,7 +98,7 @@ private boolean matchesReturnValue(Class type, Method method, Object returnVa else if (Object.class == type && void.class == method.getReturnType()) { return true; } - else{ + else { return ClassUtils.isAssignable(type, method.getReturnType()); } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/FieldRetrievingFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/FieldRetrievingFactoryBean.java index aca839616c..6f035c1732 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/FieldRetrievingFactoryBean.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/FieldRetrievingFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -204,7 +204,7 @@ public Object getObject() throws IllegalAccessException { // instance field return this.fieldObject.get(this.targetObject); } - else{ + else { // class field return this.fieldObject.get(null); } diff --git a/spring-beans/src/test/java/org/springframework/tests/beans/CollectingReaderEventListener.java b/spring-beans/src/test/java/org/springframework/tests/beans/CollectingReaderEventListener.java index b7fa67912c..1445c9944e 100644 --- a/spring-beans/src/test/java/org/springframework/tests/beans/CollectingReaderEventListener.java +++ b/spring-beans/src/test/java/org/springframework/tests/beans/CollectingReaderEventListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,20 +36,21 @@ */ public class CollectingReaderEventListener implements ReaderEventListener { - private final List defaults = new LinkedList(); + private final List defaults = new LinkedList<>(); - private final Map componentDefinitions = new LinkedHashMap<>(8); + private final Map componentDefinitions = new LinkedHashMap<>(8); - private final Map aliasMap = new LinkedHashMap<>(8); + private final Map> aliasMap = new LinkedHashMap<>(8); + + private final List imports = new LinkedList<>(); - private final List imports = new LinkedList(); @Override public void defaultsRegistered(DefaultsDefinition defaultsDefinition) { this.defaults.add(defaultsDefinition); } - public List getDefaults() { + public List getDefaults() { return Collections.unmodifiableList(this.defaults); } @@ -59,27 +60,27 @@ public void componentRegistered(ComponentDefinition componentDefinition) { } public ComponentDefinition getComponentDefinition(String name) { - return (ComponentDefinition) this.componentDefinitions.get(name); + return this.componentDefinitions.get(name); } public ComponentDefinition[] getComponentDefinitions() { - Collection collection = this.componentDefinitions.values(); - return (ComponentDefinition[]) collection.toArray(new ComponentDefinition[collection.size()]); + Collection collection = this.componentDefinitions.values(); + return collection.toArray(new ComponentDefinition[collection.size()]); } @Override public void aliasRegistered(AliasDefinition aliasDefinition) { - List aliases = (List) this.aliasMap.get(aliasDefinition.getBeanName()); - if(aliases == null) { - aliases = new ArrayList(); + List aliases = this.aliasMap.get(aliasDefinition.getBeanName()); + if (aliases == null) { + aliases = new ArrayList<>(); this.aliasMap.put(aliasDefinition.getBeanName(), aliases); } aliases.add(aliasDefinition); } - public List getAliases(String beanName) { - List aliases = (List) this.aliasMap.get(beanName); - return aliases == null ? null : Collections.unmodifiableList(aliases); + public List getAliases(String beanName) { + List aliases = this.aliasMap.get(beanName); + return (aliases != null ? Collections.unmodifiableList(aliases) : null); } @Override @@ -87,7 +88,7 @@ public void importProcessed(ImportDefinition importDefinition) { this.imports.add(importDefinition); } - public List getImports() { + public List getImports() { return Collections.unmodifiableList(this.imports); } diff --git a/spring-core/src/main/java/org/springframework/util/DigestUtils.java b/spring-core/src/main/java/org/springframework/util/DigestUtils.java index 87ca6b852e..8de0b513cd 100644 --- a/spring-core/src/main/java/org/springframework/util/DigestUtils.java +++ b/spring-core/src/main/java/org/springframework/util/DigestUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -127,7 +127,7 @@ private static byte[] digest(String algorithm, InputStream inputStream) throws I ((UpdateMessageDigestInputStream) inputStream).updateMessageDigest(messageDigest); return messageDigest.digest(); } - else{ + else { return messageDigest.digest(StreamUtils.copyToByteArray(inputStream)); } } diff --git a/spring-core/src/main/java/org/springframework/util/MimeType.java b/spring-core/src/main/java/org/springframework/util/MimeType.java index 0f770998f2..b3e585fbcc 100644 --- a/spring-core/src/main/java/org/springframework/util/MimeType.java +++ b/spring-core/src/main/java/org/springframework/util/MimeType.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -180,7 +180,7 @@ public MimeType(String type, String subtype, Map parameters) { * @see HTTP 1.1, section 2.2 */ private void checkToken(String token) { - for (int i=0; i < token.length(); i++ ) { + for (int i = 0; i < token.length(); i++ ) { char ch = token.charAt(i); if (!TOKEN.get(ch)) { throw new IllegalArgumentException("Invalid token character '" + ch + "' in token \"" + token + "\""); diff --git a/spring-core/src/test/java/org/springframework/tests/Matchers.java b/spring-core/src/test/java/org/springframework/tests/Matchers.java index 826bd1c732..346836cbb6 100644 --- a/spring-core/src/test/java/org/springframework/tests/Matchers.java +++ b/spring-core/src/test/java/org/springframework/tests/Matchers.java @@ -51,7 +51,7 @@ public static Matcher exceptionCause(final Matcher matcher) { @Override public boolean matches(Object item) { Throwable cause = null; - if(item != null && item instanceof Throwable) { + if (item != null && item instanceof Throwable) { cause = ((Throwable)item).getCause(); } return matcher.matches(cause); diff --git a/spring-core/src/test/java/org/springframework/tests/TestGroup.java b/spring-core/src/test/java/org/springframework/tests/TestGroup.java index aaf310f406..f7b8525bc6 100644 --- a/spring-core/src/test/java/org/springframework/tests/TestGroup.java +++ b/spring-core/src/test/java/org/springframework/tests/TestGroup.java @@ -93,7 +93,8 @@ private static Set parseGroups(String value) { for (String group : value.split(",")) { try { groups.add(valueOf(group.trim().toUpperCase())); - } catch (IllegalArgumentException e) { + } + catch (IllegalArgumentException ex) { throw new IllegalArgumentException(format( "Unable to find test group '%s' when parsing testGroups value: '%s'. " + "Available groups include: [%s]", group.trim(), value, diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java b/spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java index ad8c65daa0..2d20e195d1 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java @@ -918,7 +918,7 @@ public static int arrayCodeFor(String arraytype) { */ public static boolean isReferenceTypeArray(String arraytype) { int length = arraytype.length(); - for (int i=0;i entry : this.corsConfigurations.entrySet()) { + for (Map.Entry entry : this.corsConfigurations.entrySet()) { if (this.pathMatcher.match(entry.getKey(), lookupPath)) { return entry.getValue(); } diff --git a/spring-web/src/main/java/org/springframework/web/util/HierarchicalUriComponents.java b/spring-web/src/main/java/org/springframework/web/util/HierarchicalUriComponents.java index 7966e32fec..e25466ecbd 100644 --- a/spring-web/src/main/java/org/springframework/web/util/HierarchicalUriComponents.java +++ b/spring-web/src/main/java/org/springframework/web/util/HierarchicalUriComponents.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -288,7 +288,7 @@ private static void verifyUriComponent(String source, Type type) { return; } int length = source.length(); - for (int i=0; i < length; i++) { + for (int i = 0; i < length; i++) { char ch = source.charAt(i); if (ch == '%') { if ((i + 2) < length) { @@ -701,9 +701,9 @@ public boolean equals(Object obj) { public int hashCode() { return getPath().hashCode(); } - } + /** * Represents a path backed by a string list (i.e. path segments). */ diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlProvider.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlProvider.java index 5537a48cdc..9ee0c5a8b7 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlProvider.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlProvider.java @@ -218,7 +218,7 @@ public final String getForLookupPath(String lookupPath) { if (!matchingPatterns.isEmpty()) { Comparator patternComparator = getPathMatcher().getPatternComparator(lookupPath); Collections.sort(matchingPatterns, patternComparator); - for(String pattern : matchingPatterns) { + for (String pattern : matchingPatterns) { String pathWithinMapping = getPathMatcher().extractPathWithinPattern(pattern, lookupPath); String pathMapping = lookupPath.substring(0, lookupPath.indexOf(pathWithinMapping)); if (logger.isTraceEnabled()) { diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionResourceResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionResourceResolver.java index 7ccc938ced..f7eb7cb014 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionResourceResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionResourceResolver.java @@ -123,9 +123,9 @@ public VersionResourceResolver addFixedVersionStrategy(String version, String... List patternsList = Arrays.asList(pathPatterns); List prefixedPatterns = new ArrayList(pathPatterns.length); String versionPrefix = "/" + version; - for(String pattern : patternsList) { + for (String pattern : patternsList) { prefixedPatterns.add(pattern); - if(!pattern.startsWith(versionPrefix) && !patternsList.contains(versionPrefix + pattern)) { + if (!pattern.startsWith(versionPrefix) && !patternsList.contains(versionPrefix + pattern)) { prefixedPatterns.add(versionPrefix + pattern); } } diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/frame/AbstractSockJsMessageCodec.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/frame/AbstractSockJsMessageCodec.java index a7481fe768..e2dfcb12d0 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/frame/AbstractSockJsMessageCodec.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/frame/AbstractSockJsMessageCodec.java @@ -32,7 +32,7 @@ public String encode(String... messages) { Assert.notNull(messages, "messages must not be null"); StringBuilder sb = new StringBuilder(); sb.append("a["); - for (int i=0; i < messages.length; i++) { + for (int i = 0; i < messages.length; i++) { sb.append('"'); char[] quotedChars = applyJsonQuoting(messages[i]); sb.append(escapeSockJsSpecialChars(quotedChars)); From ccaa5043783be7de0284ec0d2820506088a17272 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 24 Mar 2016 21:44:16 +0100 Subject: [PATCH 085/344] Polishing (cherry picked from commit 775bccc) --- .../web/servlet/DispatcherServlet.java | 19 +++++++++++-------- .../web/servlet/FrameworkServlet.java | 3 +-- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java index 0143fdc868..7fd9424807 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -328,6 +328,7 @@ public class DispatcherServlet extends FrameworkServlet { /** List of ViewResolvers used by this servlet */ private List viewResolvers; + /** * Create a new {@code DispatcherServlet} that will create its own internal web * application context based on defaults and values provided through servlet @@ -392,6 +393,7 @@ public DispatcherServlet(WebApplicationContext webApplicationContext) { super(webApplicationContext); } + /** * Set whether to detect all HandlerMapping beans in this servlet's context. Otherwise, * just a single bean with name "handlerMapping" will be expected. @@ -463,6 +465,7 @@ public void setCleanupAfterInclude(boolean cleanupAfterInclude) { this.cleanupAfterInclude = cleanupAfterInclude; } + /** * This implementation calls {@link #initStrategies}. */ @@ -547,9 +550,8 @@ private void initThemeResolver(ApplicationContext context) { // We need to use the default. this.themeResolver = getDefaultStrategy(context, ThemeResolver.class); if (logger.isDebugEnabled()) { - logger.debug( - "Unable to locate ThemeResolver with name '" + THEME_RESOLVER_BEAN_NAME + "': using default [" + - this.themeResolver + "]"); + logger.debug("Unable to locate ThemeResolver with name '" + THEME_RESOLVER_BEAN_NAME + + "': using default [" + this.themeResolver + "]"); } } } @@ -737,8 +739,7 @@ private void initViewResolvers(ApplicationContext context) { */ private void initFlashMapManager(ApplicationContext context) { try { - this.flashMapManager = - context.getBean(FLASH_MAP_MANAGER_BEAN_NAME, FlashMapManager.class); + this.flashMapManager = context.getBean(FLASH_MAP_MANAGER_BEAN_NAME, FlashMapManager.class); if (logger.isDebugEnabled()) { logger.debug("Using FlashMapManager [" + this.flashMapManager + "]"); } @@ -838,7 +839,8 @@ protected List getDefaultStrategies(ApplicationContext context, Class /** * Create a default strategy. - *

    The default implementation uses {@link org.springframework.beans.factory.config.AutowireCapableBeanFactory#createBean}. + *

    The default implementation uses + * {@link org.springframework.beans.factory.config.AutowireCapableBeanFactory#createBean}. * @param context the current WebApplicationContext * @param clazz the strategy implementation class to instantiate * @return the fully configured strategy instance @@ -1099,7 +1101,8 @@ else if (request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) instanceof Mul * @see MultipartResolver#cleanupMultipart */ protected void cleanupMultipart(HttpServletRequest request) { - MultipartHttpServletRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class); + MultipartHttpServletRequest multipartRequest = + WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class); if (multipartRequest != null) { this.multipartResolver.cleanupMultipart(multipartRequest); } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java index b880be5050..0cf705a947 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java @@ -46,7 +46,6 @@ import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; -import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.context.ConfigurableWebApplicationContext; import org.springframework.web.context.ConfigurableWebEnvironment; import org.springframework.web.context.ContextLoader; @@ -916,7 +915,7 @@ protected void doOptions(HttpServletRequest request, HttpServletResponse respons @Override public void setHeader(String name, String value) { if ("Allow".equals(name)) { - value = (StringUtils.hasLength(value) ? value + ", " : "") + RequestMethod.PATCH.name(); + value = (StringUtils.hasLength(value) ? value + ", " : "") + HttpMethod.PATCH.name(); } super.setHeader(name, value); } From 756301b7c5a8dc3237fcbeb1fa1d879ddd8a47e0 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 25 Mar 2016 23:28:45 +0100 Subject: [PATCH 086/344] Polishing (cherry picked from commit 94cb778) --- .../cache/interceptor/CacheAspectSupport.java | 12 +++++------ .../expression/AnnotatedElementKey.java | 7 ++++--- .../expression/CachedExpressionEvaluator.java | 11 +++++++--- .../MethodBasedEvaluationContext.java | 10 ++++++---- .../result/JsonPathResultMatchers.java | 4 ++-- .../ResponseEntityExceptionHandler.java | 20 +++++++++---------- .../resource/ResourceHttpRequestHandler.java | 12 +++++------ 7 files changed, 39 insertions(+), 37 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java index ecec4ffda9..6dd21c1d10 100644 --- a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java +++ b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java @@ -374,18 +374,15 @@ private boolean hasCachePut(CacheOperationContexts contexts) { for (CacheOperationContext context : cachePutContexts) { try { if (!context.isConditionPassing(ExpressionEvaluator.RESULT_UNAVAILABLE)) { - excluded.add(context); + excluded.add(context); } } catch (VariableNotAvailableException e) { - // Ignoring failure due to missing result, consider the cache put has - // to proceed + // Ignoring failure due to missing result, consider the cache put has to proceed } } - // check if all puts have been excluded by condition - return cachePutContexts.size() != excluded.size(); - - + // Check if all puts have been excluded by condition + return (cachePutContexts.size() != excluded.size()); } private void processCacheEvicts(Collection contexts, boolean beforeInvocation, Object result) { @@ -710,4 +707,5 @@ public int hashCode() { return (this.cacheOperation.hashCode() * 31 + this.methodCacheKey.hashCode()); } } + } diff --git a/spring-context/src/main/java/org/springframework/context/expression/AnnotatedElementKey.java b/spring-context/src/main/java/org/springframework/context/expression/AnnotatedElementKey.java index 97fc7a5dc1..8f7f88a9c2 100644 --- a/spring-context/src/main/java/org/springframework/context/expression/AnnotatedElementKey.java +++ b/spring-context/src/main/java/org/springframework/context/expression/AnnotatedElementKey.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,7 +27,7 @@ * * @author Costin Leau * @author Stephane Nicoll - * @since 4.2.0 + * @since 4.2 * @see CachedExpressionEvaluator */ public final class AnnotatedElementKey { @@ -36,12 +36,13 @@ public final class AnnotatedElementKey { private final Class targetClass; + /** * Create a new instance with the specified {@link AnnotatedElement} and * optional target {@link Class}. */ public AnnotatedElementKey(AnnotatedElement element, Class targetClass) { - Assert.notNull(element, "AnnotatedElement must be set."); + Assert.notNull(element, "AnnotatedElement must not be null"); this.element = element; this.targetClass = targetClass; } diff --git a/spring-context/src/main/java/org/springframework/context/expression/CachedExpressionEvaluator.java b/spring-context/src/main/java/org/springframework/context/expression/CachedExpressionEvaluator.java index 81d0c55dc8..4bb20b2894 100644 --- a/spring-context/src/main/java/org/springframework/context/expression/CachedExpressionEvaluator.java +++ b/spring-context/src/main/java/org/springframework/context/expression/CachedExpressionEvaluator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,13 +28,14 @@ * are defined on {@link java.lang.reflect.AnnotatedElement}. * * @author Stephane Nicoll - * @since 4.2.0 + * @since 4.2 * @see AnnotatedElementKey */ public abstract class CachedExpressionEvaluator { private final SpelExpressionParser parser; + /** * Create a new instance with the specified {@link SpelExpressionParser}. */ @@ -50,6 +51,7 @@ protected CachedExpressionEvaluator() { this(new SpelExpressionParser()); } + /** * Return the {@link SpelExpressionParser} to use. */ @@ -57,6 +59,7 @@ protected SpelExpressionParser getParser() { return this.parser; } + /** * Return the {@link Expression} for the specified SpEL value *

    Parse the expression if it hasn't been already. @@ -66,6 +69,7 @@ protected SpelExpressionParser getParser() { */ protected Expression getExpression(Map cache, AnnotatedElementKey elementKey, String expression) { + ExpressionKey expressionKey = createKey(elementKey, expression); Expression expr = cache.get(expressionKey); if (expr == null) { @@ -79,6 +83,7 @@ private ExpressionKey createKey(AnnotatedElementKey elementKey, String expressio return new ExpressionKey(elementKey, expression); } + protected static class ExpressionKey { private final AnnotatedElementKey key; @@ -105,7 +110,7 @@ public boolean equals(Object other) { @Override public int hashCode() { - return this.key.hashCode() * 29 + (this.expression != null ? this.expression.hashCode() : 0); + return this.key.hashCode() + (this.expression != null ? this.expression.hashCode() * 29 : 0); } } diff --git a/spring-context/src/main/java/org/springframework/context/expression/MethodBasedEvaluationContext.java b/spring-context/src/main/java/org/springframework/context/expression/MethodBasedEvaluationContext.java index d5c1e21245..b6d783b2d7 100644 --- a/spring-context/src/main/java/org/springframework/context/expression/MethodBasedEvaluationContext.java +++ b/spring-context/src/main/java/org/springframework/context/expression/MethodBasedEvaluationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,8 +25,8 @@ /** * A method-based {@link org.springframework.expression.EvaluationContext} that * provides explicit support for method-based invocations. - *

    - * Expose the actual method arguments using the following aliases: + * + *

    Expose the actual method arguments using the following aliases: *

      *
    1. pX where X is the index of the argument (p0 for the first argument)
    2. *
    3. aX where X is the index of the argument (a1 for the second argument)
    4. @@ -34,7 +34,7 @@ *
    * * @author Stephane Nicoll - * @since 4.2.0 + * @since 4.2 */ public class MethodBasedEvaluationContext extends StandardEvaluationContext { @@ -46,6 +46,7 @@ public class MethodBasedEvaluationContext extends StandardEvaluationContext { private boolean paramLoaded = false; + public MethodBasedEvaluationContext(Object rootObject, Method method, Object[] args, ParameterNameDiscoverer paramDiscoverer) { @@ -55,6 +56,7 @@ public MethodBasedEvaluationContext(Object rootObject, Method method, Object[] a this.paramDiscoverer = paramDiscoverer; } + @Override public Object lookupVariable(String name) { Object variable = super.lookupVariable(name); diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/result/JsonPathResultMatchers.java b/spring-test/src/main/java/org/springframework/test/web/servlet/result/JsonPathResultMatchers.java index 6ce23f8d00..39377075f7 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/result/JsonPathResultMatchers.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/result/JsonPathResultMatchers.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -48,7 +48,7 @@ public class JsonPathResultMatchers { * @param args arguments to parameterize the {@code JsonPath} expression with, * using formatting specifiers defined in {@link String#format(String, Object...)} */ - protected JsonPathResultMatchers(String expression, Object ... args) { + protected JsonPathResultMatchers(String expression, Object... args) { this.jsonPathHelper = new JsonPathExpectationsHelper(expression, args); } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java index 91cfe65e07..0be4cb9bc2 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -71,14 +71,11 @@ * * @author Rossen Stoyanchev * @since 3.2 - * * @see #handleException(Exception, WebRequest) * @see org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver */ public abstract class ResponseEntityExceptionHandler { - protected final Log logger = LogFactory.getLog(getClass()); - /** * Log category to use when no mapped handler is found for a request. * @see #pageNotFoundLogger @@ -86,11 +83,16 @@ public abstract class ResponseEntityExceptionHandler { public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound"; /** - * Additional logger to use when no mapped handler is found for a request. + * Specific logger to use when no mapped handler is found for a request. * @see #PAGE_NOT_FOUND_LOG_CATEGORY */ protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY); + /** + * Common logger for use in subclasses. + */ + protected final Log logger = LogFactory.getLog(getClass()); + /** * Provides handling for standard Spring MVC exceptions. @@ -115,9 +117,7 @@ public abstract class ResponseEntityExceptionHandler { NoHandlerFoundException.class }) public final ResponseEntity handleException(Exception ex, WebRequest request) { - HttpHeaders headers = new HttpHeaders(); - if (ex instanceof NoSuchRequestHandlingMethodException) { HttpStatus status = HttpStatus.NOT_FOUND; return handleNoSuchRequestHandlingMethod((NoSuchRequestHandlingMethodException) ex, headers, status, request); @@ -202,7 +202,6 @@ protected ResponseEntity handleExceptionInternal(Exception ex, Object bo if (HttpStatus.INTERNAL_SERVER_ERROR.equals(status)) { request.setAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE, ex, WebRequest.SCOPE_REQUEST); } - return new ResponseEntity(body, headers, status); } @@ -242,7 +241,6 @@ protected ResponseEntity handleHttpRequestMethodNotSupported(HttpRequest if (!supportedMethods.isEmpty()) { headers.setAllow(supportedMethods); } - return handleExceptionInternal(ex, null, headers, status, request); } @@ -443,8 +441,8 @@ protected ResponseEntity handleBindException(BindException ex, HttpHeade * @return a {@code ResponseEntity} instance * @since 4.0 */ - protected ResponseEntity handleNoHandlerFoundException(NoHandlerFoundException ex, HttpHeaders headers, - HttpStatus status, WebRequest request) { + protected ResponseEntity handleNoHandlerFoundException( + NoHandlerFoundException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { return handleExceptionInternal(ex, null, headers, status, request); } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java index 8d322fed4e..f531989375 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 @@ -187,17 +187,15 @@ public void afterPropertiesSet() throws Exception { } /** - * Look for a {@link org.springframework.web.servlet.resource.PathResourceResolver} - * among the {@link #getResourceResolvers() resource resolvers} and configure - * its {@code "allowedLocations"} to match the value of the - * {@link #setLocations(java.util.List) locations} property unless the "allowed - * locations" of the {@code PathResourceResolver} is non-empty. + * Look for a {@code PathResourceResolver} among the configured resource + * resolvers and set its {@code allowedLocations} property (if empty) to + * match the {@link #setLocations locations} configured on this class. */ protected void initAllowedLocations() { if (CollectionUtils.isEmpty(this.locations)) { return; } - for (int i = getResourceResolvers().size()-1; i >= 0; i--) { + for (int i = getResourceResolvers().size() - 1; i >= 0; i--) { if (getResourceResolvers().get(i) instanceof PathResourceResolver) { PathResourceResolver pathResolver = (PathResourceResolver) getResourceResolvers().get(i); if (ObjectUtils.isEmpty(pathResolver.getAllowedLocations())) { @@ -245,7 +243,7 @@ public void handleRequest(HttpServletRequest request, HttpServletResponse respon // Apply cache settings, if any prepareResponse(response); - // Check the resource's media type + // Check the media type for the resource MediaType mediaType = getMediaType(resource); if (mediaType != null) { if (logger.isTraceEnabled()) { From 8f6ede519d12caf1c4f4215ad07e606ed8b61c7d Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sun, 27 Mar 2016 14:07:25 +0200 Subject: [PATCH 087/344] RequestHeaderMapMethodArgumentResolver defensively checks for non-existing header values Issue: SPR-14091 (cherry picked from commit b2a17ba) --- .../RequestHeaderMapMethodArgumentResolver.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/method/annotation/RequestHeaderMapMethodArgumentResolver.java b/spring-web/src/main/java/org/springframework/web/method/annotation/RequestHeaderMapMethodArgumentResolver.java index 1b8b46fd7c..f2ad4a429e 100644 --- a/spring-web/src/main/java/org/springframework/web/method/annotation/RequestHeaderMapMethodArgumentResolver.java +++ b/spring-web/src/main/java/org/springframework/web/method/annotation/RequestHeaderMapMethodArgumentResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -66,8 +66,11 @@ public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer m } for (Iterator iterator = webRequest.getHeaderNames(); iterator.hasNext();) { String headerName = iterator.next(); - for (String headerValue : webRequest.getHeaderValues(headerName)) { - result.add(headerName, headerValue); + String[] headerValues = webRequest.getHeaderValues(headerName); + if (headerValues != null) { + for (String headerValue : headerValues) { + result.add(headerName, headerValue); + } } } return result; @@ -77,7 +80,9 @@ public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer m for (Iterator iterator = webRequest.getHeaderNames(); iterator.hasNext();) { String headerName = iterator.next(); String headerValue = webRequest.getHeader(headerName); - result.put(headerName, headerValue); + if (headerValue != null) { + result.put(headerName, headerValue); + } } return result; } From 02a6f31807c03602f7679398281502c09aaff6f2 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 29 Mar 2016 15:38:23 +0200 Subject: [PATCH 088/344] ASM ClassReader leniently handles label offset mismatch Issue: SPR-14089 (cherry picked from commit 3910350) --- .../src/main/java/org/springframework/asm/ClassReader.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/spring-core/src/main/java/org/springframework/asm/ClassReader.java b/spring-core/src/main/java/org/springframework/asm/ClassReader.java index 4a6d84b024..25ad9aeb91 100644 --- a/spring-core/src/main/java/org/springframework/asm/ClassReader.java +++ b/spring-core/src/main/java/org/springframework/asm/ClassReader.java @@ -2182,6 +2182,11 @@ private int readFrameType(final Object[] frame, final int index, int v, * @return a non null Label, which must be equal to labels[offset]. */ protected Label readLabel(int offset, Label[] labels) { + // SPRING PATCH: leniently handle offset mismatch + if (offset >= labels.length) { + return new Label(); + } + // END OF PATCH if (labels[offset] == null) { labels[offset] = new Label(); } From 0ed7f9aebbcb0703033fb1665f82e92bea5eb75d Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 4 Apr 2016 14:54:38 +0200 Subject: [PATCH 089/344] SimpleApplicationEventMulticaster leniently handles ClassCastException Issue: SPR-14109 (cherry picked from commit 13f889e) --- .../event/SimpleApplicationEventMulticaster.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/event/SimpleApplicationEventMulticaster.java b/spring-context/src/main/java/org/springframework/context/event/SimpleApplicationEventMulticaster.java index aaba475567..04815d6989 100644 --- a/spring-context/src/main/java/org/springframework/context/event/SimpleApplicationEventMulticaster.java +++ b/spring-context/src/main/java/org/springframework/context/event/SimpleApplicationEventMulticaster.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,8 @@ import java.util.concurrent.Executor; +import org.apache.commons.logging.LogFactory; + import org.springframework.beans.factory.BeanFactory; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; @@ -160,7 +162,13 @@ protected void invokeListener(ApplicationListener listener, ApplicationEvent eve } } else { - listener.onApplicationEvent(event); + try { + listener.onApplicationEvent(event); + } + catch (ClassCastException ex) { + // Possibly a lambda-defined listener which we could not resolve the generic event type for + LogFactory.getLog(getClass()).debug("Non-matching event type for listener: " + listener, ex); + } } } From dc6e512275ef0c58d470c52fd8ee743fa252c890 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 4 Apr 2016 14:56:08 +0200 Subject: [PATCH 090/344] EhCacheManagerFactoryBean logs cache manager name Issue: SPR-14110 (cherry picked from commit 8bfba6a) --- .../cache/ehcache/EhCacheManagerFactoryBean.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheManagerFactoryBean.java b/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheManagerFactoryBean.java index 06c5a5e856..2287995b66 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheManagerFactoryBean.java +++ b/spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheManagerFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -81,7 +81,7 @@ public void setConfigLocation(Resource configLocation) { /** * Set the name of the EhCache CacheManager (if a specific name is desired). - * @see net.sf.ehcache.CacheManager#setName(String) + * @see net.sf.ehcache.config.Configuration#setName(String) */ public void setCacheManagerName(String cacheManagerName) { this.cacheManagerName = cacheManagerName; @@ -126,12 +126,17 @@ public void setShared(boolean shared) { @Override public void afterPropertiesSet() throws CacheException { - logger.info("Initializing EhCache CacheManager"); + if (logger.isInfoEnabled()) { + logger.info("Initializing EhCache CacheManager" + + (this.cacheManagerName != null ? " '" + this.cacheManagerName + "'" : "")); + } + Configuration configuration = (this.configLocation != null ? EhCacheManagerUtils.parseConfiguration(this.configLocation) : ConfigurationFactory.parseConfiguration()); if (this.cacheManagerName != null) { configuration.setName(this.cacheManagerName); } + if (this.shared) { // Old-school EhCache singleton sharing... // No way to find out whether we actually created a new CacheManager @@ -178,7 +183,10 @@ public boolean isSingleton() { @Override public void destroy() { if (this.locallyManaged) { - logger.info("Shutting down EhCache CacheManager"); + if (logger.isInfoEnabled()) { + logger.info("Shutting down EhCache CacheManager" + + (this.cacheManagerName != null ? " '" + this.cacheManagerName + "'" : "")); + } this.cacheManager.shutdown(); } } From 3b7ca7e76ace27aeab7530e19102f984b2d33d36 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 4 Apr 2016 14:58:30 +0200 Subject: [PATCH 091/344] Further tests for getBeanNamesForType(ResolvableType) Issue: SPR-14097 (cherry picked from commit edea66a) --- .../beans/factory/ListableBeanFactory.java | 3 ++- .../AutowiredAnnotationBeanPostProcessorTests.java | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) 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 e9bc88c84f..e8be332289 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-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -109,6 +109,7 @@ public interface ListableBeanFactory extends BeanFactory { * @return the names of beans (or objects created by FactoryBeans) matching * the given object type (including subclasses), or an empty array if none * @since 4.2 + * @see #isTypeMatch(String, ResolvableType) * @see FactoryBean#getObjectType * @see BeanFactoryUtils#beanNamesForTypeIncludingAncestors(ListableBeanFactory, ResolvableType) */ diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java index b6cb43db00..3983807c5b 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java @@ -45,6 +45,7 @@ import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.core.Ordered; +import org.springframework.core.ResolvableType; import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.annotation.Order; import org.springframework.tests.sample.beans.ITestBean; @@ -1397,6 +1398,8 @@ public void testGenericsBasedFieldInjectionWithSimpleMatch() { assertSame(1, bean.stringRepositoryMap.size()); assertSame(repo, bean.repositoryMap.get("repo")); assertSame(repo, bean.stringRepositoryMap.get("repo")); + + assertArrayEquals(new String[] {"repo"}, bf.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class))); } @Test @@ -1777,6 +1780,9 @@ public void testGenericsBasedInjectionIntoTypeVariableSelectingBestMatch() { GenericInterface1Impl bean1 = (GenericInterface1Impl) bf.getBean("bean1"); GenericInterface2Impl bean2 = (GenericInterface2Impl) bf.getBean("bean2"); assertSame(bean2, bean1.gi2); + + assertArrayEquals(new String[] {"bean1"}, bf.getBeanNamesForType(ResolvableType.forClassWithGenerics(GenericInterface1.class, String.class))); + assertArrayEquals(new String[] {"bean2"}, bf.getBeanNamesForType(ResolvableType.forClassWithGenerics(GenericInterface2.class, String.class))); } @Test @@ -2765,7 +2771,7 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl public interface GenericInterface1 { - public String doSomethingGeneric(T o); + String doSomethingGeneric(T o); } From d7fe92d022c390321002f0bbf84a35624aefae3f Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 16 Mar 2016 18:04:02 +0100 Subject: [PATCH 092/344] Abstract(Stax)XMLReader recognizes standard features as not supported Issue: SPR-14056 (cherry picked from commit 35eb52e) --- .../util/xml/AbstractStaxXMLReader.java | 34 +++++---- .../util/xml/AbstractXMLReader.java | 71 +++++++++++-------- 2 files changed, 62 insertions(+), 43 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/util/xml/AbstractStaxXMLReader.java b/spring-core/src/main/java/org/springframework/util/xml/AbstractStaxXMLReader.java index 25dc97b299..77c74d26f6 100644 --- a/spring-core/src/main/java/org/springframework/util/xml/AbstractStaxXMLReader.java +++ b/spring-core/src/main/java/org/springframework/util/xml/AbstractStaxXMLReader.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,6 +35,7 @@ * Abstract base class for SAX {@code XMLReader} implementations that use StAX as a basis. * * @author Arjen Poutsma + * @author Juergen Hoeller * @since 3.0 * @see #setContentHandler(org.xml.sax.ContentHandler) * @see #setDTDHandler(org.xml.sax.DTDHandler) @@ -58,6 +59,7 @@ abstract class AbstractStaxXMLReader extends AbstractXMLReader { private final Map namespaces = new LinkedHashMap(); + @Override public boolean getFeature(String name) throws SAXNotRecognizedException, SAXNotSupportedException { if (NAMESPACES_FEATURE_NAME.equals(name)) { @@ -170,12 +172,13 @@ private void parse() throws SAXException { } /** - * Template-method that parses the StAX reader passed at construction-time. + * Template method that parses the StAX reader passed at construction-time. */ protected abstract void parseInternal() throws SAXException, XMLStreamException; + /** - * Starts the prefix mapping for the given prefix. + * Start the prefix mapping for the given prefix. * @see org.xml.sax.ContentHandler#startPrefixMapping(String, String) */ protected void startPrefixMapping(String prefix, String namespace) throws SAXException { @@ -186,57 +189,58 @@ protected void startPrefixMapping(String prefix, String namespace) throws SAXExc if (!StringUtils.hasLength(namespace)) { return; } - if (!namespace.equals(namespaces.get(prefix))) { + if (!namespace.equals(this.namespaces.get(prefix))) { getContentHandler().startPrefixMapping(prefix, namespace); - namespaces.put(prefix, namespace); + this.namespaces.put(prefix, namespace); } } } /** - * Ends the prefix mapping for the given prefix. + * End the prefix mapping for the given prefix. * @see org.xml.sax.ContentHandler#endPrefixMapping(String) */ protected void endPrefixMapping(String prefix) throws SAXException { if (getContentHandler() != null) { - if (namespaces.containsKey(prefix)) { + if (this.namespaces.containsKey(prefix)) { getContentHandler().endPrefixMapping(prefix); - namespaces.remove(prefix); + this.namespaces.remove(prefix); } } } + /** - * Implementation of the {@code Locator} interface that is based on a StAX {@code Location}. + * Implementation of the {@code Locator} interface based on a given StAX {@code Location}. * @see Locator * @see Location */ private static class StaxLocator implements Locator { - private Location location; + private final Location location; - protected StaxLocator(Location location) { + public StaxLocator(Location location) { this.location = location; } @Override public String getPublicId() { - return location.getPublicId(); + return this.location.getPublicId(); } @Override public String getSystemId() { - return location.getSystemId(); + return this.location.getSystemId(); } @Override public int getLineNumber() { - return location.getLineNumber(); + return this.location.getLineNumber(); } @Override public int getColumnNumber() { - return location.getColumnNumber(); + return this.location.getColumnNumber(); } } diff --git a/spring-core/src/main/java/org/springframework/util/xml/AbstractXMLReader.java b/spring-core/src/main/java/org/springframework/util/xml/AbstractXMLReader.java index 7b24a3c1c2..f759ccba37 100644 --- a/spring-core/src/main/java/org/springframework/util/xml/AbstractXMLReader.java +++ b/spring-core/src/main/java/org/springframework/util/xml/AbstractXMLReader.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,15 +26,16 @@ import org.xml.sax.ext.LexicalHandler; /** - * Abstract base class for SAX {@code XMLReader} implementations. Contains properties as defined in {@link - * XMLReader}, and does not recognize any features. + * Abstract base class for SAX {@code XMLReader} implementations. + * Contains properties as defined in {@link XMLReader}, and does not recognize any features. * * @author Arjen Poutsma + * @author Juergen Hoeller + * @since 3.0 * @see #setContentHandler(org.xml.sax.ContentHandler) * @see #setDTDHandler(org.xml.sax.DTDHandler) * @see #setEntityResolver(org.xml.sax.EntityResolver) * @see #setErrorHandler(org.xml.sax.ErrorHandler) - * @since 3.0 */ abstract class AbstractXMLReader implements XMLReader { @@ -48,10 +49,6 @@ abstract class AbstractXMLReader implements XMLReader { private LexicalHandler lexicalHandler; - @Override - public ContentHandler getContentHandler() { - return contentHandler; - } @Override public void setContentHandler(ContentHandler contentHandler) { @@ -59,18 +56,18 @@ public void setContentHandler(ContentHandler contentHandler) { } @Override - public void setDTDHandler(DTDHandler dtdHandler) { - this.dtdHandler = dtdHandler; + public ContentHandler getContentHandler() { + return this.contentHandler; } @Override - public DTDHandler getDTDHandler() { - return dtdHandler; + public void setDTDHandler(DTDHandler dtdHandler) { + this.dtdHandler = dtdHandler; } @Override - public EntityResolver getEntityResolver() { - return entityResolver; + public DTDHandler getDTDHandler() { + return this.dtdHandler; } @Override @@ -79,8 +76,8 @@ public void setEntityResolver(EntityResolver entityResolver) { } @Override - public ErrorHandler getErrorHandler() { - return errorHandler; + public EntityResolver getEntityResolver() { + return this.entityResolver; } @Override @@ -88,29 +85,46 @@ public void setErrorHandler(ErrorHandler errorHandler) { this.errorHandler = errorHandler; } + @Override + public ErrorHandler getErrorHandler() { + return this.errorHandler; + } + protected LexicalHandler getLexicalHandler() { - return lexicalHandler; + return this.lexicalHandler; } + /** - * Throws a {@code SAXNotRecognizedException} exception. - * - * @throws org.xml.sax.SAXNotRecognizedException - * always + * This implementation throws a {@code SAXNotRecognizedException} exception + * for any feature outside of the "http://xml.org/sax/features/" namespace + * and returns {@code false} for any feature within. */ @Override public boolean getFeature(String name) throws SAXNotRecognizedException, SAXNotSupportedException { - throw new SAXNotRecognizedException(name); + if (name.startsWith("http://xml.org/sax/features/")) { + return false; + } + else { + throw new SAXNotRecognizedException(name); + } } /** - * Throws a {@code SAXNotRecognizedException} exception. - * - * @throws SAXNotRecognizedException always + * This implementation throws a {@code SAXNotRecognizedException} exception + * for any feature outside of the "http://xml.org/sax/features/" namespace + * and accepts a {@code false} value for any feature within. */ @Override public void setFeature(String name, boolean value) throws SAXNotRecognizedException, SAXNotSupportedException { - throw new SAXNotRecognizedException(name); + if (name.startsWith("http://xml.org/sax/features/")) { + if (value) { + throw new SAXNotSupportedException(name); + } + } + else { + throw new SAXNotRecognizedException(name); + } } /** @@ -120,7 +134,7 @@ public void setFeature(String name, boolean value) throws SAXNotRecognizedExcept @Override public Object getProperty(String name) throws SAXNotRecognizedException, SAXNotSupportedException { if ("http://xml.org/sax/properties/lexical-handler".equals(name)) { - return lexicalHandler; + return this.lexicalHandler; } else { throw new SAXNotRecognizedException(name); @@ -134,10 +148,11 @@ public Object getProperty(String name) throws SAXNotRecognizedException, SAXNotS @Override public void setProperty(String name, Object value) throws SAXNotRecognizedException, SAXNotSupportedException { if ("http://xml.org/sax/properties/lexical-handler".equals(name)) { - lexicalHandler = (LexicalHandler) value; + this.lexicalHandler = (LexicalHandler) value; } else { throw new SAXNotRecognizedException(name); } } + } From 01d50e9bde8a1da5e8e869d1287cef6a97907a38 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Mon, 4 Apr 2016 13:57:50 -0400 Subject: [PATCH 093/344] Fix incomplete log message Commit 48236b from 2014 introduced a logging improvement to avoid logging each removed session per line and instead log one line at the end with all removed sessions ids. However that list of removed session ids wasn't populated. This commit fixes that. Issue: SPR-14111 --- .../sockjs/transport/TransportHandlingSockJsService.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/TransportHandlingSockJsService.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/TransportHandlingSockJsService.java index 81d10a0b85..10f40141c5 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/TransportHandlingSockJsService.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/TransportHandlingSockJsService.java @@ -359,14 +359,15 @@ private void scheduleSessionTask() { if (this.sessionCleanupTask != null) { return; } - final List removedSessionIds = new ArrayList(); this.sessionCleanupTask = getTaskScheduler().scheduleAtFixedRate(new Runnable() { @Override public void run() { + List removedIds = new ArrayList(); for (SockJsSession session : sessions.values()) { try { if (session.getTimeSinceLastActive() > getDisconnectDelay()) { sessions.remove(session.getId()); + removedIds.add(session.getId()); session.close(); } } @@ -375,9 +376,8 @@ public void run() { logger.debug("Failed to close " + session, ex); } } - if (logger.isDebugEnabled() && !removedSessionIds.isEmpty()) { - logger.debug("Closed " + removedSessionIds.size() + " sessions " + removedSessionIds); - removedSessionIds.clear(); + if (logger.isDebugEnabled() && !removedIds.isEmpty()) { + logger.debug("Closed " + removedIds.size() + " sessions: " + removedIds); } } }, getDisconnectDelay()); From 9443c1c5dd59397e7ab07723794b61064bb15abe Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Mon, 4 Apr 2016 18:21:10 +0200 Subject: [PATCH 094/344] Fix Auto-Startup for @JmsListener Ignore container's auto-startup once the context is refreshed. Issue: SPR-14015 (cherry picked from commit 996c1cc) --- .../config/JmsListenerEndpointRegistry.java | 67 ++++++++++++------- .../jms/annotation/EnableJmsTests.java | 54 +++++++++++++-- .../JmsListenerContainerTestFactory.java | 11 ++- .../config/MessageListenerTestContainer.java | 19 ++++-- 4 files changed, 115 insertions(+), 36 deletions(-) diff --git a/spring-jms/src/main/java/org/springframework/jms/config/JmsListenerEndpointRegistry.java b/spring-jms/src/main/java/org/springframework/jms/config/JmsListenerEndpointRegistry.java index 5ffa326c4d..4d48fb31fe 100644 --- a/spring-jms/src/main/java/org/springframework/jms/config/JmsListenerEndpointRegistry.java +++ b/spring-jms/src/main/java/org/springframework/jms/config/JmsListenerEndpointRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,7 +29,11 @@ import org.springframework.beans.factory.BeanInitializationException; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ApplicationListener; import org.springframework.context.SmartLifecycle; +import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.jms.listener.MessageListenerContainer; import org.springframework.util.Assert; @@ -53,7 +57,8 @@ * @see MessageListenerContainer * @see JmsListenerContainerFactory */ -public class JmsListenerEndpointRegistry implements DisposableBean, SmartLifecycle { +public class JmsListenerEndpointRegistry implements DisposableBean, SmartLifecycle, + ApplicationContextAware, ApplicationListener { protected final Log logger = LogFactory.getLog(getClass()); @@ -62,6 +67,23 @@ public class JmsListenerEndpointRegistry implements DisposableBean, SmartLifecyc private int phase = Integer.MAX_VALUE; + private ApplicationContext applicationContext; + + private boolean contextRefreshed; + + + @Override + public void setApplicationContext(ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + + @Override + public void onApplicationEvent(ContextRefreshedEvent event) { + if (event.getApplicationContext() == this.applicationContext) { + this.contextRefreshed = true; + } + } + /** * Return the {@link MessageListenerContainer} with the specified id or @@ -92,7 +114,6 @@ public Collection getListenerContainers() { return Collections.unmodifiableCollection(this.listenerContainers.values()); } - /** * Create a message listener container for the given {@link JmsListenerEndpoint}. *

    This create the necessary infrastructure to honor that endpoint @@ -114,8 +135,9 @@ public void registerListenerContainer(JmsListenerEndpoint endpoint, JmsListenerC String id = endpoint.getId(); Assert.notNull(id, "Endpoint id must not be null"); synchronized (this.listenerContainers) { - Assert.state(!this.listenerContainers.containsKey(id), - "Another endpoint is already registered with id '" + id + "'"); + if (this.listenerContainers.containsKey(id)) { + throw new IllegalStateException("Another endpoint is already registered with id '" + id + "'"); + } MessageListenerContainer container = createListenerContainer(endpoint, factory); this.listenerContainers.put(id, container); if (startImmediately) { @@ -166,21 +188,6 @@ protected MessageListenerContainer createListenerContainer(JmsListenerEndpoint e } - @Override - public void destroy() { - for (MessageListenerContainer listenerContainer : getListenerContainers()) { - if (listenerContainer instanceof DisposableBean) { - try { - ((DisposableBean) listenerContainer).destroy(); - } - catch (Throwable ex) { - logger.warn("Failed to destroy message listener container", ex); - } - } - } - } - - // Delegating implementation of SmartLifecycle @Override @@ -228,15 +235,29 @@ public boolean isRunning() { /** * Start the specified {@link MessageListenerContainer} if it should be started - * on startup. + * on startup or when start is called explicitly after startup. * @see MessageListenerContainer#isAutoStartup() */ - private static void startIfNecessary(MessageListenerContainer listenerContainer) { - if (listenerContainer.isAutoStartup()) { + private void startIfNecessary(MessageListenerContainer listenerContainer) { + if (this.contextRefreshed || listenerContainer.isAutoStartup()) { listenerContainer.start(); } } + @Override + public void destroy() { + for (MessageListenerContainer listenerContainer : getListenerContainers()) { + if (listenerContainer instanceof DisposableBean) { + try { + ((DisposableBean) listenerContainer).destroy(); + } + catch (Throwable ex) { + logger.warn("Failed to destroy message listener container", ex); + } + } + } + } + private static class AggregatingCallback implements Runnable { diff --git a/spring-jms/src/test/java/org/springframework/jms/annotation/EnableJmsTests.java b/spring-jms/src/test/java/org/springframework/jms/annotation/EnableJmsTests.java index c2a350e526..a48e401ced 100644 --- a/spring-jms/src/test/java/org/springframework/jms/annotation/EnableJmsTests.java +++ b/spring-jms/src/test/java/org/springframework/jms/annotation/EnableJmsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -102,6 +102,31 @@ public void defaultContainerFactory() { testDefaultContainerFactoryConfiguration(context); } + @Test + public void containerAreStartedByDefault() { + ConfigurableApplicationContext context = new AnnotationConfigApplicationContext( + EnableJmsDefaultContainerFactoryConfig.class, DefaultBean.class); + JmsListenerContainerTestFactory factory = + context.getBean(JmsListenerContainerTestFactory.class); + MessageListenerTestContainer container = factory.getListenerContainers().get(0); + assertTrue(container.isAutoStartup()); + assertTrue(container.isStarted()); + } + + @Test + public void containerCanBeStarterViaTheRegistry() { + ConfigurableApplicationContext context = new AnnotationConfigApplicationContext( + EnableJmsAutoStartupFalseConfig.class, DefaultBean.class); + JmsListenerContainerTestFactory factory = + context.getBean(JmsListenerContainerTestFactory.class); + MessageListenerTestContainer container = factory.getListenerContainers().get(0); + assertFalse(container.isAutoStartup()); + assertFalse(container.isStarted()); + JmsListenerEndpointRegistry registry = context.getBean(JmsListenerEndpointRegistry.class); + registry.start(); + assertTrue(container.isStarted()); + } + @Override @Test public void jmsHandlerMethodFactoryConfiguration() throws JMSException { @@ -132,9 +157,8 @@ public void jmsListeners() { @Test public void unknownFactory() { thrown.expect(BeanCreationException.class); - thrown.expectMessage("customFactory"); // Not found - new AnnotationConfigApplicationContext( - EnableJmsSampleConfig.class, CustomBean.class); + thrown.expectMessage("customFactory"); // not found + new AnnotationConfigApplicationContext(EnableJmsSampleConfig.class, CustomBean.class); } @Test @@ -145,11 +169,11 @@ public void lazyComponent() { context.getBean("jmsListenerContainerFactory", JmsListenerContainerTestFactory.class); assertEquals(0, defaultFactory.getListenerContainers().size()); - context.getBean(LazyBean.class); // trigger lazy resolution + context.getBean(LazyBean.class); // trigger lazy resolution assertEquals(1, defaultFactory.getListenerContainers().size()); MessageListenerTestContainer container = defaultFactory.getListenerContainers().get(0); assertTrue("Should have been started " + container, container.isStarted()); - context.close(); // Close and stop the listeners + context.close(); // close and stop the listeners assertTrue("Should have been stopped " + container, container.isStopped()); } @@ -286,6 +310,24 @@ public JmsListenerContainerTestFactory defaultFactory() { } + @Configuration + @EnableJms + static class EnableJmsAutoStartupFalseConfig implements JmsListenerConfigurer { + + @Override + public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) { + registrar.setContainerFactory(simpleFactory()); + } + + @Bean + public JmsListenerContainerTestFactory simpleFactory() { + JmsListenerContainerTestFactory factory = new JmsListenerContainerTestFactory(); + factory.setAutoStartup(false); + return factory; + } + } + + @Component @Lazy static class LazyBean { diff --git a/spring-jms/src/test/java/org/springframework/jms/config/JmsListenerContainerTestFactory.java b/spring-jms/src/test/java/org/springframework/jms/config/JmsListenerContainerTestFactory.java index d51e06ee63..611b823dcd 100644 --- a/spring-jms/src/test/java/org/springframework/jms/config/JmsListenerContainerTestFactory.java +++ b/spring-jms/src/test/java/org/springframework/jms/config/JmsListenerContainerTestFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,9 +26,17 @@ */ public class JmsListenerContainerTestFactory implements JmsListenerContainerFactory { + private boolean autoStartup = true; + private final Map listenerContainers = new LinkedHashMap<>(); + + public void setAutoStartup(boolean autoStartup) { + this.autoStartup = autoStartup; + } + + public List getListenerContainers() { return new ArrayList<>(this.listenerContainers.values()); } @@ -40,6 +48,7 @@ public MessageListenerTestContainer getListenerContainer(String id) { @Override public MessageListenerTestContainer createListenerContainer(JmsListenerEndpoint endpoint) { MessageListenerTestContainer container = new MessageListenerTestContainer(endpoint); + container.setAutoStartup(this.autoStartup); this.listenerContainers.put(endpoint.getId(), container); return container; } diff --git a/spring-jms/src/test/java/org/springframework/jms/config/MessageListenerTestContainer.java b/spring-jms/src/test/java/org/springframework/jms/config/MessageListenerTestContainer.java index 7c723b7f42..e21b532088 100644 --- a/spring-jms/src/test/java/org/springframework/jms/config/MessageListenerTestContainer.java +++ b/spring-jms/src/test/java/org/springframework/jms/config/MessageListenerTestContainer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,11 +26,12 @@ /** * @author Stephane Nicoll */ -public class MessageListenerTestContainer - implements MessageListenerContainer, InitializingBean, DisposableBean { +public class MessageListenerTestContainer implements MessageListenerContainer, InitializingBean, DisposableBean { private final JmsListenerEndpoint endpoint; + private boolean autoStartup = true; + private boolean startInvoked; private boolean initializationInvoked; @@ -39,10 +40,16 @@ public class MessageListenerTestContainer private boolean destroyInvoked; + MessageListenerTestContainer(JmsListenerEndpoint endpoint) { this.endpoint = endpoint; } + + public void setAutoStartup(boolean autoStartup) { + this.autoStartup = autoStartup; + } + public JmsListenerEndpoint getEndpoint() { return endpoint; } @@ -86,7 +93,7 @@ public int getPhase() { @Override public boolean isAutoStartup() { - return true; + return this.autoStartup; } @Override @@ -127,8 +134,7 @@ public void afterPropertiesSet() { @Override public void destroy() { if (!stopInvoked) { - throw new IllegalStateException("Stop should have been invoked before " + - "destroy on " + this); + throw new IllegalStateException("Stop should have been invoked before " + "destroy on " + this); } destroyInvoked = true; } @@ -144,4 +150,5 @@ public String toString() { sb.append('}'); return sb.toString(); } + } From 6d479b2060f7de185b0af88ad72fc95ffc351cd4 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 4 Apr 2016 20:59:40 +0200 Subject: [PATCH 095/344] Polishing --- .../format/annotation/DateTimeFormat.java | 4 ++-- .../util/ConcurrentReferenceHashMap.java | 10 +++++----- .../transport/TransportHandlingSockJsService.java | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/format/annotation/DateTimeFormat.java b/spring-context/src/main/java/org/springframework/format/annotation/DateTimeFormat.java index 5feb6ee8b0..c889df8019 100644 --- a/spring-context/src/main/java/org/springframework/format/annotation/DateTimeFormat.java +++ b/spring-context/src/main/java/org/springframework/format/annotation/DateTimeFormat.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,7 +41,7 @@ *

    Each attribute is mutually exclusive, so only set one attribute per annotation instance * (the one most convenient one for your formatting needs). * When the pattern attribute is specified, it takes precedence over both the style and ISO attribute. - * When the {@link #iso} attribute is specified, if takes precedence over the style attribute. + * When the {@link #iso} attribute is specified, it takes precedence over the style attribute. * When no annotation attributes are specified, the default format applied is style-based * with a style code of 'SS' (short date, short time). * diff --git a/spring-core/src/main/java/org/springframework/util/ConcurrentReferenceHashMap.java b/spring-core/src/main/java/org/springframework/util/ConcurrentReferenceHashMap.java index 709572f9b6..7710ed273d 100644 --- a/spring-core/src/main/java/org/springframework/util/ConcurrentReferenceHashMap.java +++ b/spring-core/src/main/java/org/springframework/util/ConcurrentReferenceHashMap.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -398,7 +398,7 @@ protected static int calculateShift(int minimumValue, int maximumValue) { /** * Various reference types supported by this map. */ - public static enum ReferenceType { + public enum ReferenceType { /** Use {@link SoftReference}s */ SOFT, @@ -636,7 +636,7 @@ public final int getCount() { * A reference to an {@link Entry} contained in the map. Implementations are usually * wrappers around specific Java reference implementations (e.g., {@link SoftReference}). */ - protected static interface Reference { + protected interface Reference { /** * Returns the referenced entry or {@code null} if the entry is no longer @@ -765,7 +765,7 @@ protected T execute(Reference reference, Entry entry) { /** * Various options supported by a {@code Task}. */ - private static enum TaskOption { + private enum TaskOption { RESTRUCTURE_BEFORE, RESTRUCTURE_AFTER, SKIP_IF_EMPTY, RESIZE } @@ -912,7 +912,7 @@ public void remove() { /** * The types of restructuring that can be performed. */ - protected static enum Restructure { + protected enum Restructure { WHEN_NECESSARY, NEVER } diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/TransportHandlingSockJsService.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/TransportHandlingSockJsService.java index 10f40141c5..79a33b87e1 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/TransportHandlingSockJsService.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/TransportHandlingSockJsService.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -132,7 +132,7 @@ public void setMessageCodec(SockJsMessageCodec messageCodec) { public SockJsMessageCodec getMessageCodec() { Assert.state(this.messageCodec != null, "A SockJsMessageCodec is required but not available: " + - "Add Jackson 2 to the classpath, or configure a custom SockJsMessageCodec."); + "Add Jackson to the classpath, or configure a custom SockJsMessageCodec."); return this.messageCodec; } From 000a0a0006f1e1bdaca6baf44d66e24471f4c512 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Mon, 4 Apr 2016 18:17:11 -0400 Subject: [PATCH 096/344] Call resetRequest after writeFrame for polling sessions Previous refactoring (fcf6ae and also 43d937) in the SockJsSession hierarchy consolidated access to the request and response in the base class AbstractHttpSockJsSession in order to keep synchronization concerns there. However that also unintentionally removed the call to resetRequest() after sending a heartbeat for any of the PollingSockJsSession classes. In general a polling session should call resetRequest after every frame written. This commit brings back the writeFrame override in PollingSockJsSession with an extra call to resetRequest(). Issue: SPR-14107 --- .../sockjs/transport/session/PollingSockJsSession.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/PollingSockJsSession.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/PollingSockJsSession.java index 5bf23b006e..e3cb44882e 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/PollingSockJsSession.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/PollingSockJsSession.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -58,7 +58,6 @@ protected void handleRequestInternal(ServerHttpRequest request, ServerHttpRespon if (initialRequest) { writeFrame(SockJsFrame.openFrame()); - resetRequest(); } else if (!getMessageCache().isEmpty()) { flushCache(); @@ -77,6 +76,11 @@ protected void flushCache() throws SockJsTransportFailureException { SockJsMessageCodec messageCodec = getSockJsServiceConfig().getMessageCodec(); SockJsFrame frame = SockJsFrame.messageFrame(messageCodec, messages); writeFrame(frame); + } + + @Override + protected void writeFrame(SockJsFrame frame) throws SockJsTransportFailureException { + super.writeFrame(frame); resetRequest(); } From 2166f2060f3a56f9ac1205157f3d605c1d9d1761 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 5 Apr 2016 11:38:51 +0200 Subject: [PATCH 097/344] Latest applicable dependency updates (AspectJ 1.8.9, EhCache 3.0 RC2, Hibernate 5.0.9, Joda-Time 2.9.3, Netty 4.0.36, SLF4J 1.7.21, Tomcat 8.0.33, Undertow 1.3.20, Woodstox 5.0.2) --- build.gradle | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/build.gradle b/build.gradle index 7deec82e33..1b71b631d3 100644 --- a/build.gradle +++ b/build.gradle @@ -27,11 +27,11 @@ configure(allprojects) { project -> group = "org.springframework" version = qualifyVersionIfNecessary(version) - ext.aspectjVersion = "1.8.8" + ext.aspectjVersion = "1.8.9" ext.eclipselinkVersion = "2.4.2" ext.ehcacheVersion = "2.10.1" ext.ehcachejcacheVersion = "1.0.1" - ext.ehcache3Version = "3.0.0.rc1" + ext.ehcache3Version = "3.0.0.rc2" ext.ejbApiVersion = "3.0" ext.fileuploadVersion = "1.3.1" ext.freemarkerVersion = "2.3.23" @@ -41,7 +41,7 @@ configure(allprojects) { project -> ext.hamcrestVersion = "1.3" ext.hibernate3Version = "3.6.10.Final" ext.hibernate4Version = "4.3.11.Final" - ext.hibernate5Version = "5.0.8.Final" + ext.hibernate5Version = "5.0.9.Final" ext.hibval4Version = "4.3.2.Final" ext.hibval5Version = "5.2.4.Final" ext.hsqldbVersion = "2.3.3" @@ -52,13 +52,13 @@ configure(allprojects) { project -> ext.jasperreportsVersion = "6.2.1" ext.javamailVersion = "1.5.5" ext.jettyVersion = "9.3.8.v20160314" - ext.jodaVersion = "2.9.2" + ext.jodaVersion = "2.9.3" ext.jrubyVersion = "1.7.24" // JRuby 9000 only supported through JSR-223 (StandardScriptFactory) ext.jsonassertVersion = "1.2.3" ext.jsonpathVersion = "2.1.0" ext.jtaVersion = "1.2" ext.junitVersion = "4.12" - ext.nettyVersion = "4.0.34.Final" + ext.nettyVersion = "4.0.36.Final" ext.okhttpVersion = "2.7.5" ext.openjpaVersion = "2.4.1" ext.poiVersion = "3.13" @@ -66,16 +66,16 @@ configure(allprojects) { project -> ext.reactorVersion = "2.0.7.RELEASE" ext.romeVersion = "1.5.1" ext.seleniumVersion = "2.48.2" - ext.slf4jVersion = "1.7.19" + ext.slf4jVersion = "1.7.21" ext.snakeyamlVersion = "1.17" ext.snifferVersion = "1.14" ext.testngVersion = "6.9.10" ext.tiles2Version = "2.2.2" ext.tiles3Version = "3.0.5" - ext.tomcatVersion = "8.0.32" + ext.tomcatVersion = "8.0.33" ext.tyrusVersion = "1.3.5" // constrained by WebLogic 12.1.3 support - ext.undertowVersion = "1.3.19.Final" - ext.woodstoxVersion = "5.0.1" + ext.undertowVersion = "1.3.20.Final" + ext.woodstoxVersion = "5.0.2" ext.xmlunitVersion = "1.6" ext.xstreamVersion = "1.4.9" From 6b0f26c33553d4724dedc95a8618c957d27ab3af Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 5 Apr 2016 11:12:36 +0200 Subject: [PATCH 098/344] StatementCreatorUtils handles Types.BOOLEAN through PreparedStatement.setBoolean Issue: SPR-14116 (cherry picked from commit 797f5db) --- .../jdbc/core/StatementCreatorUtils.java | 10 +++++- .../jdbc/object/SqlUpdateTests.java | 31 +++++++++++-------- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/StatementCreatorUtils.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/StatementCreatorUtils.java index 4aed272b13..c7b754d25b 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/StatementCreatorUtils.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/StatementCreatorUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -364,6 +364,14 @@ else if (scale != null) { ps.setObject(paramIndex, inValue, sqlType); } } + else if (sqlType == Types.BOOLEAN) { + if (inValue instanceof Boolean) { + ps.setBoolean(paramIndex, (Boolean) inValue); + } + else { + ps.setObject(paramIndex, inValue, Types.BOOLEAN); + } + } else if (sqlType == Types.DATE) { if (inValue instanceof java.util.Date) { if (inValue instanceof java.sql.Date) { diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/object/SqlUpdateTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/object/SqlUpdateTests.java index 7791abe412..dad295b2fa 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/object/SqlUpdateTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/object/SqlUpdateTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -49,16 +49,22 @@ public class SqlUpdateTests { private static final String UPDATE = "update seat_status set booking_id = null"; + private static final String UPDATE_INT = "update seat_status set booking_id = null where performance_id = ?"; + private static final String UPDATE_INT_INT = "update seat_status set booking_id = null where performance_id = ? and price_band_id = ?"; + private static final String UPDATE_NAMED_PARAMETERS = "update seat_status set booking_id = null where performance_id = :perfId and price_band_id = :priceId"; + private static final String UPDATE_STRING = "update seat_status set booking_id = null where name = ?"; + private static final String UPDATE_OBJECTS = "update seat_status set booking_id = null where performance_id = ? and price_band_id = ? and name = ? and confirmed = ?"; + private static final String INSERT_GENERATE_KEYS = "insert into show (name) values(?)"; @@ -66,11 +72,16 @@ public class SqlUpdateTests { public ExpectedException thrown = ExpectedException.none(); private DataSource dataSource; + private Connection connection; + private PreparedStatement preparedStatement; + private ResultSet resultSet; + private ResultSetMetaData resultSetMetaData; + @Before public void setUp() throws Exception { dataSource = mock(DataSource.class); @@ -87,6 +98,7 @@ public void verifyClosed() throws Exception { verify(connection).close(); } + @Test public void testUpdate() throws SQLException { given(preparedStatement.executeUpdate()).willReturn(1); @@ -192,7 +204,7 @@ public void testUpdateMixed() throws SQLException { verify(preparedStatement).setObject(1, 1, Types.NUMERIC); verify(preparedStatement).setObject(2, 1, Types.NUMERIC, 2); verify(preparedStatement).setString(3, "rod"); - verify(preparedStatement).setObject(4, Boolean.TRUE, Types.BOOLEAN); + verify(preparedStatement).setBoolean(4, Boolean.TRUE); } @Test @@ -231,7 +243,7 @@ public void testUpdateConstructor() throws SQLException { verify(preparedStatement).setObject(1, 1, Types.NUMERIC); verify(preparedStatement).setObject(2, 1, Types.NUMERIC); verify(preparedStatement).setString(3, "rod"); - verify(preparedStatement).setObject(4, Boolean.TRUE, Types.BOOLEAN); + verify(preparedStatement).setBoolean(4, Boolean.TRUE); } @Test @@ -360,10 +372,7 @@ public MixedUpdater() { } public int run(int performanceId, int type, String name, boolean confirmed) { - Object[] params = - new Object[] {performanceId, type, name, - new Boolean(confirmed)}; - return update(params); + return update(performanceId, type, name, confirmed); } } @@ -379,8 +388,7 @@ public GeneratedKeysUpdater() { } public int run(String name, KeyHolder generatedKeyHolder) { - Object[] params = new Object[] {name}; - return update(params, generatedKeyHolder); + return update(new Object[] {name}, generatedKeyHolder); } } @@ -394,10 +402,7 @@ public ConstructorUpdater() { } public int run(int performanceId, int type, String name, boolean confirmed) { - Object[] params = - new Object[] { - performanceId, type, name, new Boolean(confirmed)}; - return update(params); + return update(performanceId, type, name, confirmed); } } From 0ecbeafd2b849dd4a9f81ad665a392f9920bd676 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 5 Apr 2016 09:42:41 +0200 Subject: [PATCH 099/344] Polishing (cherry picked from commit d2d528d) --- .../test/context/BootstrapUtils.java | 71 ++++++--------- .../test/context/TestExecutionListener.java | 10 +-- .../test/context/TestExecutionListeners.java | 86 +++++++++--------- .../test/context/TestPropertySource.java | 30 ++----- .../AbstractTestContextBootstrapper.java | 73 ++++++++------- .../test/util/MetaAnnotationUtils.java | 21 +++-- .../context/TestExecutionListenersTests.java | 90 +++++++++---------- 7 files changed, 169 insertions(+), 212 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/context/BootstrapUtils.java b/spring-test/src/main/java/org/springframework/test/context/BootstrapUtils.java index 411b3254dd..c16d7deff7 100644 --- a/spring-test/src/main/java/org/springframework/test/context/BootstrapUtils.java +++ b/spring-test/src/main/java/org/springframework/test/context/BootstrapUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,13 +22,12 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.beans.BeanUtils; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.util.ClassUtils; import org.springframework.util.MultiValueMap; -import static org.springframework.beans.BeanUtils.*; - /** * {@code BootstrapUtils} is a collection of utility methods to assist with * bootstrapping the Spring TestContext Framework. @@ -41,47 +40,41 @@ */ abstract class BootstrapUtils { - private static final String DEFAULT_BOOTSTRAP_CONTEXT_CLASS_NAME = "org.springframework.test.context.support.DefaultBootstrapContext"; + private static final String DEFAULT_BOOTSTRAP_CONTEXT_CLASS_NAME = + "org.springframework.test.context.support.DefaultBootstrapContext"; - private static final String DEFAULT_CACHE_AWARE_CONTEXT_LOADER_DELEGATE_CLASS_NAME = "org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate"; + private static final String DEFAULT_CACHE_AWARE_CONTEXT_LOADER_DELEGATE_CLASS_NAME = + "org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate"; - private static final String DEFAULT_TEST_CONTEXT_BOOTSTRAPPER_CLASS_NAME = "org.springframework.test.context.support.DefaultTestContextBootstrapper"; + private static final String DEFAULT_TEST_CONTEXT_BOOTSTRAPPER_CLASS_NAME = + "org.springframework.test.context.support.DefaultTestContextBootstrapper"; private static final Log logger = LogFactory.getLog(BootstrapUtils.class); - private BootstrapUtils() { - /* no-op */ - } - /** * Create the {@code BootstrapContext} for the specified {@linkplain Class test class}. - * *

    Uses reflection to create a {@link org.springframework.test.context.support.DefaultBootstrapContext} * that uses a {@link org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate}. - * * @param testClass the test class for which the bootstrap context should be created * @return a new {@code BootstrapContext}; never {@code null} */ @SuppressWarnings("unchecked") static BootstrapContext createBootstrapContext(Class testClass) { CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate = createCacheAwareContextLoaderDelegate(); - Class clazz = null; try { - clazz = (Class) ClassUtils.forName(DEFAULT_BOOTSTRAP_CONTEXT_CLASS_NAME, - BootstrapUtils.class.getClassLoader()); - - Constructor constructor = clazz.getConstructor(Class.class, - CacheAwareContextLoaderDelegate.class); - + clazz = (Class) ClassUtils.forName( + DEFAULT_BOOTSTRAP_CONTEXT_CLASS_NAME, BootstrapUtils.class.getClassLoader()); + Constructor constructor = clazz.getConstructor( + Class.class, CacheAwareContextLoaderDelegate.class); if (logger.isDebugEnabled()) { logger.debug(String.format("Instantiating BootstrapContext using constructor [%s]", constructor)); } - return instantiateClass(constructor, testClass, cacheAwareContextLoaderDelegate); + return BeanUtils.instantiateClass(constructor, testClass, cacheAwareContextLoaderDelegate); } - catch (Throwable t) { - throw new IllegalStateException("Could not load BootstrapContext [" + clazz + "]", t); + catch (Throwable ex) { + throw new IllegalStateException("Could not load BootstrapContext [" + clazz + "]", ex); } } @@ -96,10 +89,10 @@ private static CacheAwareContextLoaderDelegate createCacheAwareContextLoaderDele logger.debug(String.format("Instantiating CacheAwareContextLoaderDelegate from class [%s]", clazz.getName())); } - return instantiateClass(clazz, CacheAwareContextLoaderDelegate.class); + return BeanUtils.instantiateClass(clazz, CacheAwareContextLoaderDelegate.class); } - catch (Throwable t) { - throw new IllegalStateException("Could not load CacheAwareContextLoaderDelegate [" + clazz + "]", t); + catch (Throwable ex) { + throw new IllegalStateException("Could not load CacheAwareContextLoaderDelegate [" + clazz + "]", ex); } } @@ -107,13 +100,11 @@ private static CacheAwareContextLoaderDelegate createCacheAwareContextLoaderDele * Resolve the {@link TestContextBootstrapper} type for the test class in the * supplied {@link BootstrapContext}, instantiate it, and provide it a reference * to the {@link BootstrapContext}. - * *

    If the {@link BootstrapWith @BootstrapWith} annotation is present on * the test class, either directly or as a meta-annotation, then its * {@link BootstrapWith#value value} will be used as the bootstrapper type. * Otherwise, the {@link org.springframework.test.context.support.DefaultTestContextBootstrapper * DefaultTestContextBootstrapper} will be used. - * * @param bootstrapContext the bootstrap context to use * @return a fully configured {@code TestContextBootstrapper} */ @@ -123,17 +114,14 @@ static TestContextBootstrapper resolveTestContextBootstrapper(BootstrapContext b Class clazz = null; try { - MultiValueMap attributesMultiMap = AnnotatedElementUtils.getAllAnnotationAttributes( testClass, BootstrapWith.class.getName()); List values = (attributesMultiMap == null ? null : attributesMultiMap.get(AnnotationUtils.VALUE)); if (values != null) { if (values.size() != 1) { - String msg = String.format( - "Configuration error: found multiple declarations of @BootstrapWith on test class [%s] with values %s", - testClass.getName(), values); - throw new IllegalStateException(msg); + throw new IllegalStateException(String.format("Configuration error: found multiple declarations of " + + "@BootstrapWith on test class [%s] with values %s", testClass.getName(), values)); } clazz = (Class) values.get(0); } @@ -144,22 +132,21 @@ static TestContextBootstrapper resolveTestContextBootstrapper(BootstrapContext b if (logger.isDebugEnabled()) { logger.debug(String.format("Instantiating TestContextBootstrapper for test class [%s] from class [%s]", - testClass.getName(), clazz.getName())); + testClass.getName(), clazz.getName())); } - TestContextBootstrapper testContextBootstrapper = instantiateClass(clazz, TestContextBootstrapper.class); + TestContextBootstrapper testContextBootstrapper = + BeanUtils.instantiateClass(clazz, TestContextBootstrapper.class); testContextBootstrapper.setBootstrapContext(bootstrapContext); - return testContextBootstrapper; } - catch (Throwable t) { - if (t instanceof IllegalStateException) { - throw (IllegalStateException) t; + catch (Throwable ex) { + if (ex instanceof IllegalStateException) { + throw (IllegalStateException) ex; } - - throw new IllegalStateException("Could not load TestContextBootstrapper [" + clazz - + "]. Specify @BootstrapWith's 'value' attribute " - + "or make the default bootstrapper class available.", t); + throw new IllegalStateException("Could not load TestContextBootstrapper [" + clazz + + "]. Specify @BootstrapWith's 'value' attribute or make the default bootstrapper class available.", + ex); } } diff --git a/spring-test/src/main/java/org/springframework/test/context/TestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/TestExecutionListener.java index 6274ffd6fc..81d61cb250 100644 --- a/spring-test/src/main/java/org/springframework/test/context/TestExecutionListener.java +++ b/spring-test/src/main/java/org/springframework/test/context/TestExecutionListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,14 +20,17 @@ * {@code TestExecutionListener} defines a listener API for reacting to * test execution events published by the {@link TestContextManager} with which * the listener is registered. + * *

    Concrete implementations must provide a {@code public} no-args constructor, * so that listeners can be instantiated transparently by tools and configuration * mechanisms. + * *

    Implementations may optionally declare the position in which they should * be ordered among the chain of default listeners via the * {@link org.springframework.core.Ordered Ordered} interface or * {@link org.springframework.core.annotation.Order @Order} annotation. See * {@link TestContextBootstrapper#getTestExecutionListeners()} for details. + * *

    Spring provides the following out-of-the-box implementations (all of * which implement {@code Ordered}): *

      @@ -56,7 +59,6 @@ public interface TestExecutionListener { * before class lifecycle callbacks. *

      If a given testing framework does not support before class * lifecycle callbacks, this method will not be called for that framework. - * * @param testContext the test context for the test; never {@code null} * @throws Exception allows any exception to propagate */ @@ -67,7 +69,6 @@ public interface TestExecutionListener { * {@link TestContext test context}, for example by injecting dependencies. *

      This method should be called immediately after instantiation of the test * instance but prior to any framework-specific lifecycle callbacks. - * * @param testContext the test context for the test; never {@code null} * @throws Exception allows any exception to propagate */ @@ -80,7 +81,6 @@ public interface TestExecutionListener { * fixtures. *

      This method should be called immediately prior to framework-specific * before lifecycle callbacks. - * * @param testContext the test context in which the test method will be * executed; never {@code null} * @throws Exception allows any exception to propagate @@ -94,7 +94,6 @@ public interface TestExecutionListener { * fixtures. *

      This method should be called immediately after framework-specific * after lifecycle callbacks. - * * @param testContext the test context in which the test method was * executed; never {@code null} * @throws Exception allows any exception to propagate @@ -108,7 +107,6 @@ public interface TestExecutionListener { * after class lifecycle callbacks. *

      If a given testing framework does not support after class * lifecycle callbacks, this method will not be called for that framework. - * * @param testContext the test context for the test; never {@code null} * @throws Exception allows any exception to propagate */ diff --git a/spring-test/src/main/java/org/springframework/test/context/TestExecutionListeners.java b/spring-test/src/main/java/org/springframework/test/context/TestExecutionListeners.java index 478fcbc33c..d99c48d7aa 100644 --- a/spring-test/src/main/java/org/springframework/test/context/TestExecutionListeners.java +++ b/spring-test/src/main/java/org/springframework/test/context/TestExecutionListeners.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,8 +30,8 @@ * which {@link TestExecutionListener TestExecutionListeners} should be * registered with a {@link TestContextManager}. * - *

      Typically, {@code @TestExecutionListeners} will be used in conjunction with - * {@link ContextConfiguration @ContextConfiguration}. + *

      Typically, {@code @TestExecutionListeners} will be used in conjunction + * with {@link ContextConfiguration @ContextConfiguration}. * *

      As of Spring Framework 4.0, this annotation may be used as a * meta-annotation to create custom composed annotations. @@ -48,42 +48,8 @@ @Target(ElementType.TYPE) public @interface TestExecutionListeners { - /** - * Enumeration of modes that dictate whether or not explicitly - * declared listeners are merged with the default listeners when - * {@code @TestExecutionListeners} is declared on a class that does - * not inherit listeners from a superclass. - * @since 4.1 - */ - static enum MergeMode { - - /** - * Indicates that locally declared listeners should replace the default - * listeners. - */ - REPLACE_DEFAULTS, - - /** - * Indicates that locally declared listeners should be merged with the - * default listeners. - *

      The merging algorithm ensures that duplicates are removed from - * the list and that the resulting set of merged listeners is sorted - * according to the semantics of - * {@link org.springframework.core.annotation.AnnotationAwareOrderComparator - * AnnotationAwareOrderComparator}. If a listener implements - * {@link org.springframework.core.Ordered Ordered} or is annotated - * with {@link org.springframework.core.annotation.Order @Order} it can - * influence the position in which it is merged with the defaults; otherwise, - * locally declared listeners will simply be appended to the list of default - * listeners when merged. - */ - MERGE_WITH_DEFAULTS - } - - /** * Alias for {@link #listeners}. - * *

      This attribute may not be used in conjunction with * {@link #listeners}, but it may be used instead of {@link #listeners}. */ @@ -93,10 +59,8 @@ static enum MergeMode { /** * The {@link TestExecutionListener TestExecutionListeners} to register with * the {@link TestContextManager}. - * *

      This attribute may not be used in conjunction with * {@link #value}, but it may be used instead of {@link #value}. - * * @see org.springframework.test.context.web.ServletTestExecutionListener * @see org.springframework.test.context.support.DependencyInjectionTestExecutionListener * @see org.springframework.test.context.support.DirtiesContextTestExecutionListener @@ -109,7 +73,6 @@ static enum MergeMode { /** * Whether or not {@link #listeners TestExecutionListeners} from superclasses * should be inherited. - * *

      The default value is {@code true}, which means that an annotated * class will inherit the listeners defined by an annotated * superclass. Specifically, the listeners for an annotated class will be @@ -122,21 +85,19 @@ static enum MergeMode { * {@code DependencyInjectionTestExecutionListener}, * {@code DirtiesContextTestExecutionListener}, and * {@code TransactionalTestExecutionListener}, in that order. - * *

       	 * @TestExecutionListeners({
      -	 *    DependencyInjectionTestExecutionListener.class,
      -	 *    DirtiesContextTestExecutionListener.class
      +	 *     DependencyInjectionTestExecutionListener.class,
      +	 *     DirtiesContextTestExecutionListener.class
       	 * })
       	 * public abstract class AbstractBaseTest {
      -	 * 	// ...
      +	 * 	 // ...
       	 * }
       	 *
       	 * @TestExecutionListeners(TransactionalTestExecutionListener.class)
       	 * public class TransactionalTest extends AbstractBaseTest {
      -	 * 	// ...
      +	 * 	 // ...
       	 * }
      - * *

      If {@code inheritListeners} is set to {@code false}, the listeners for * the annotated class will shadow and effectively replace any * listeners defined by a superclass. @@ -158,4 +119,37 @@ static enum MergeMode { */ MergeMode mergeMode() default MergeMode.REPLACE_DEFAULTS; + + /** + * Enumeration of modes that dictate whether or not explicitly + * declared listeners are merged with the default listeners when + * {@code @TestExecutionListeners} is declared on a class that does + * not inherit listeners from a superclass. + * @since 4.1 + */ + enum MergeMode { + + /** + * Indicates that locally declared listeners should replace the default + * listeners. + */ + REPLACE_DEFAULTS, + + /** + * Indicates that locally declared listeners should be merged with the + * default listeners. + *

      The merging algorithm ensures that duplicates are removed from + * the list and that the resulting set of merged listeners is sorted + * according to the semantics of + * {@link org.springframework.core.annotation.AnnotationAwareOrderComparator + * AnnotationAwareOrderComparator}. If a listener implements + * {@link org.springframework.core.Ordered Ordered} or is annotated + * with {@link org.springframework.core.annotation.Order @Order} it can + * influence the position in which it is merged with the defaults; otherwise, + * locally declared listeners will simply be appended to the list of default + * listeners when merged. + */ + MERGE_WITH_DEFAULTS + } + } diff --git a/spring-test/src/main/java/org/springframework/test/context/TestPropertySource.java b/spring-test/src/main/java/org/springframework/test/context/TestPropertySource.java index 3f48102924..ec2528af29 100644 --- a/spring-test/src/main/java/org/springframework/test/context/TestPropertySource.java +++ b/spring-test/src/main/java/org/springframework/test/context/TestPropertySource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -90,10 +90,8 @@ /** * Alias for {@link #locations}. - * *

      This attribute may not be used in conjunction with * {@link #locations}, but it may be used instead of {@link #locations}. - * * @see #locations */ @AliasFor("locations") @@ -104,12 +102,10 @@ * {@code Environment}'s set of {@code PropertySources}. Each location * will be added to the enclosing {@code Environment} as its own property * source, in the order declared. - * *

      Supported File Formats

      *

      Both traditional and XML-based properties file formats are supported * — for example, {@code "classpath:/com/example/test.properties"} * or {@code "file:/path/to/file.xml"}. - * *

      Path Resource Semantics

      *

      Each path will be interpreted as a Spring * {@link org.springframework.core.io.Resource Resource}. A plain path @@ -128,17 +124,13 @@ * in paths (i.e., ${...}) will be * {@linkplain org.springframework.core.env.Environment#resolveRequiredPlaceholders(String) resolved} * against the {@code Environment}. - * *

      Default Properties File Detection

      *

      See the class-level Javadoc for a discussion on detection of defaults. - * *

      Precedence

      *

      Properties loaded from resource locations have lower precedence than * inlined {@link #properties}. - * *

      This attribute may not be used in conjunction with * {@link #value}, but it may be used instead of {@link #value}. - * * @see #inheritLocations * @see #value * @see #properties @@ -150,18 +142,15 @@ /** * Whether or not test property source {@link #locations} from superclasses * should be inherited. - * *

      The default value is {@code true}, which means that a test class will * inherit property source locations defined by a superclass. * Specifically, the property source locations for a test class will be * appended to the list of property source locations defined by a superclass. * Thus, subclasses have the option of extending the list of test * property source locations. - * *

      If {@code inheritLocations} is set to {@code false}, the property * source locations for the test class will shadow and effectively * replace any property source locations defined by a superclass. - * *

      In the following example, the {@code ApplicationContext} for * {@code BaseTest} will be loaded using only the {@code "base.properties"} * file as a test property source. In contrast, the {@code ApplicationContext} @@ -172,13 +161,13 @@ * @TestPropertySource("base.properties") * @ContextConfiguration * public class BaseTest { - * // ... + * // ... * } * * @TestPropertySource("extended.properties") * @ContextConfiguration * public class ExtendedTest extends BaseTest { - * // ... + * // ... * } * * @@ -193,7 +182,6 @@ * {@code ApplicationContext} is loaded for the test. All key-value pairs * will be added to the enclosing {@code Environment} as a single test * {@code PropertySource} with the highest precedence. - * *

      Supported Syntax

      *

      The supported syntax for key-value pairs is the same as the * syntax defined for entries in a Java @@ -203,14 +191,11 @@ *

    • {@code "key:value"}
    • *
    • {@code "key value"}
    • *
    - * *

    Precedence

    *

    Properties declared via this attribute have higher precedence than * properties loaded from resource {@link #locations}. - * *

    This attribute may be used in conjunction with {@link #value} * or {@link #locations}. - * * @see #inheritProperties * @see #locations * @see org.springframework.core.env.PropertySource @@ -220,17 +205,14 @@ /** * Whether or not inlined test {@link #properties} from superclasses should * be inherited. - * *

    The default value is {@code true}, which means that a test class will * inherit inlined properties defined by a superclass. Specifically, * the inlined properties for a test class will be appended to the list of * inlined properties defined by a superclass. Thus, subclasses have the * option of extending the list of inlined test properties. - * *

    If {@code inheritProperties} is set to {@code false}, the inlined * properties for the test class will shadow and effectively * replace any inlined properties defined by a superclass. - * *

    In the following example, the {@code ApplicationContext} for * {@code BaseTest} will be loaded using only the inlined {@code key1} * property. In contrast, the {@code ApplicationContext} for @@ -240,16 +222,14 @@ * @TestPropertySource(properties = "key1 = value1") * @ContextConfiguration * public class BaseTest { - * // ... + * // ... * } - * * @TestPropertySource(properties = "key2 = value2") * @ContextConfiguration * public class ExtendedTest extends BaseTest { - * // ... + * // ... * } * - * * @see #properties */ boolean inheritProperties() default true; diff --git a/spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java b/spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java index e03b89625b..7049115cf1 100644 --- a/spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java +++ b/spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -108,7 +108,7 @@ public BootstrapContext getBootstrapContext() { @Override public TestContext buildTestContext() { return new DefaultTestContext(getBootstrapContext().getTestClass(), buildMergedContextConfiguration(), - getCacheAwareContextLoaderDelegate()); + getCacheAwareContextLoaderDelegate()); } /** @@ -121,14 +121,14 @@ public final List getTestExecutionListeners() { List> classesList = new ArrayList>(); boolean usingDefaults = false; - AnnotationDescriptor descriptor = MetaAnnotationUtils.findAnnotationDescriptor(clazz, - annotationType); + AnnotationDescriptor descriptor = + MetaAnnotationUtils.findAnnotationDescriptor(clazz, annotationType); // Use defaults? if (descriptor == null) { if (logger.isDebugEnabled()) { logger.debug(String.format("@TestExecutionListeners is not present for class [%s]: using defaults.", - clazz.getName())); + clazz.getName())); } usingDefaults = true; classesList.addAll(getDefaultTestExecutionListenerClasses()); @@ -140,21 +140,21 @@ public final List getTestExecutionListeners() { TestExecutionListeners testExecutionListeners = descriptor.synthesizeAnnotation(); if (logger.isTraceEnabled()) { logger.trace(String.format("Retrieved @TestExecutionListeners [%s] for declaring class [%s].", - testExecutionListeners, declaringClass.getName())); + testExecutionListeners, declaringClass.getName())); } boolean inheritListeners = testExecutionListeners.inheritListeners(); - AnnotationDescriptor superDescriptor = MetaAnnotationUtils.findAnnotationDescriptor( - descriptor.getRootDeclaringClass().getSuperclass(), annotationType); + AnnotationDescriptor superDescriptor = + MetaAnnotationUtils.findAnnotationDescriptor( + descriptor.getRootDeclaringClass().getSuperclass(), annotationType); // If there are no listeners to inherit, we might need to merge the // locally declared listeners with the defaults. - if ((!inheritListeners || superDescriptor == null) - && (testExecutionListeners.mergeMode() == MergeMode.MERGE_WITH_DEFAULTS)) { + if ((!inheritListeners || superDescriptor == null) && + testExecutionListeners.mergeMode() == MergeMode.MERGE_WITH_DEFAULTS) { if (logger.isDebugEnabled()) { - logger.debug(String.format( - "Merging default listeners with listeners configured via @TestExecutionListeners for class [%s].", - descriptor.getRootDeclaringClass().getName())); + logger.debug(String.format("Merging default listeners with listeners configured via " + + "@TestExecutionListeners for class [%s].", descriptor.getRootDeclaringClass().getName())); } usingDefaults = true; classesList.addAll(getDefaultTestExecutionListenerClasses()); @@ -204,10 +204,10 @@ private List instantiateListeners(List> getDefaultTestExecutionLis } catch (Throwable ex) { if (logger.isDebugEnabled()) { - logger.debug("Could not load default TestExecutionListener class [" + className - + "]. Specify custom listener classes or make the default listener classes available.", ex); + logger.debug("Could not load default TestExecutionListener class [" + className + + "]. Specify custom listener classes or make the default listener classes available.", ex); } } } @@ -252,12 +252,11 @@ protected Set> getDefaultTestExecutionLis * @see SpringFactoriesLoader#loadFactoryNames */ protected List getDefaultTestExecutionListenerClassNames() { - final List classNames = SpringFactoriesLoader.loadFactoryNames(TestExecutionListener.class, - getClass().getClassLoader()); - + List classNames = + SpringFactoriesLoader.loadFactoryNames(TestExecutionListener.class, getClass().getClassLoader()); if (logger.isInfoEnabled()) { logger.info(String.format("Loaded default TestExecutionListener class names from location [%s]: %s", - SpringFactoriesLoader.FACTORIES_RESOURCE_LOCATION, classNames)); + SpringFactoriesLoader.FACTORIES_RESOURCE_LOCATION, classNames)); } return Collections.unmodifiableList(classNames); } @@ -271,18 +270,18 @@ public final MergedContextConfiguration buildMergedContextConfiguration() { Class testClass = getBootstrapContext().getTestClass(); CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate = getCacheAwareContextLoaderDelegate(); - if (MetaAnnotationUtils.findAnnotationDescriptorForTypes(testClass, ContextConfiguration.class, - ContextHierarchy.class) == null) { + if (MetaAnnotationUtils.findAnnotationDescriptorForTypes( + testClass, ContextConfiguration.class, ContextHierarchy.class) == null) { if (logger.isInfoEnabled()) { - logger.info(String.format( - "Neither @ContextConfiguration nor @ContextHierarchy found for test class [%s]", - testClass.getName())); + logger.info(String.format("Neither @ContextConfiguration nor @ContextHierarchy found for test class [%s]", + testClass.getName())); } return new MergedContextConfiguration(testClass, null, null, null, null); } if (AnnotationUtils.findAnnotation(testClass, ContextHierarchy.class) != null) { - Map> hierarchyMap = ContextLoaderUtils.buildContextHierarchyMap(testClass); + Map> hierarchyMap = + ContextLoaderUtils.buildContextHierarchyMap(testClass); MergedContextConfiguration parentConfig = null; MergedContextConfiguration mergedConfig = null; @@ -296,8 +295,8 @@ public final MergedContextConfiguration buildMergedContextConfiguration() { Assert.notEmpty(reversedList, "ContextConfigurationAttributes list must not be empty"); Class declaringClass = reversedList.get(0).getDeclaringClass(); - mergedConfig = buildMergedContextConfiguration(declaringClass, reversedList, parentConfig, - cacheAwareContextLoaderDelegate); + mergedConfig = buildMergedContextConfiguration( + declaringClass, reversedList, parentConfig, cacheAwareContextLoaderDelegate); parentConfig = mergedConfig; } @@ -306,8 +305,8 @@ public final MergedContextConfiguration buildMergedContextConfiguration() { } else { return buildMergedContextConfiguration(testClass, - ContextLoaderUtils.resolveContextConfigurationAttributes(testClass), null, - cacheAwareContextLoaderDelegate); + ContextLoaderUtils.resolveContextConfigurationAttributes(testClass), + null, cacheAwareContextLoaderDelegate); } } @@ -411,7 +410,7 @@ protected ContextLoader resolveContextLoader(Class testClass, } if (logger.isTraceEnabled()) { logger.trace(String.format("Using ContextLoader class [%s] for test class [%s]", - contextLoaderClass.getName(), testClass.getName())); + contextLoaderClass.getName(), testClass.getName())); } return BeanUtils.instantiateClass(contextLoaderClass, ContextLoader.class); } @@ -443,14 +442,14 @@ protected Class resolveExplicitContextLoaderClass( for (ContextConfigurationAttributes configAttributes : configAttributesList) { if (logger.isTraceEnabled()) { logger.trace(String.format("Resolving ContextLoader for context configuration attributes %s", - configAttributes)); + configAttributes)); } Class contextLoaderClass = configAttributes.getContextLoaderClass(); if (ContextLoader.class != contextLoaderClass) { if (logger.isDebugEnabled()) { logger.debug(String.format( - "Found explicit ContextLoader class [%s] for context configuration attributes %s", - contextLoaderClass.getName(), configAttributes)); + "Found explicit ContextLoader class [%s] for context configuration attributes %s", + contextLoaderClass.getName(), configAttributes)); } return contextLoaderClass; } diff --git a/spring-test/src/main/java/org/springframework/test/util/MetaAnnotationUtils.java b/spring-test/src/main/java/org/springframework/test/util/MetaAnnotationUtils.java index 9e113f5c18..913986a440 100644 --- a/spring-test/src/main/java/org/springframework/test/util/MetaAnnotationUtils.java +++ b/spring-test/src/main/java/org/springframework/test/util/MetaAnnotationUtils.java @@ -98,11 +98,10 @@ public static AnnotationDescriptor findAnnotationDescr * @return the corresponding annotation descriptor if the annotation was found; * otherwise {@code null} */ - private static AnnotationDescriptor findAnnotationDescriptor(Class clazz, - Set visited, Class annotationType) { + private static AnnotationDescriptor findAnnotationDescriptor( + Class clazz, Set visited, Class annotationType) { Assert.notNull(annotationType, "Annotation type must not be null"); - if (clazz == null || Object.class == clazz) { return null; } @@ -115,11 +114,11 @@ private static AnnotationDescriptor findAnnotationDesc // Declared on a composed annotation (i.e., as a meta-annotation)? for (Annotation composedAnnotation : clazz.getDeclaredAnnotations()) { if (!AnnotationUtils.isInJavaLangAnnotationPackage(composedAnnotation) && visited.add(composedAnnotation)) { - AnnotationDescriptor descriptor = findAnnotationDescriptor(composedAnnotation.annotationType(), - visited, annotationType); + AnnotationDescriptor descriptor = findAnnotationDescriptor( + composedAnnotation.annotationType(), visited, annotationType); if (descriptor != null) { - return new AnnotationDescriptor(clazz, descriptor.getDeclaringClass(), composedAnnotation, - descriptor.getAnnotation()); + return new AnnotationDescriptor( + clazz, descriptor.getDeclaringClass(), composedAnnotation, descriptor.getAnnotation()); } } } @@ -287,8 +286,8 @@ public AnnotationDescriptor(Class rootDeclaringClass, Class declaringClass this.declaringClass = declaringClass; this.composedAnnotation = composedAnnotation; this.annotation = annotation; - this.annotationAttributes = AnnotatedElementUtils.findMergedAnnotationAttributes(rootDeclaringClass, - annotation.annotationType().getName(), false, false); + this.annotationAttributes = AnnotatedElementUtils.findMergedAnnotationAttributes( + rootDeclaringClass, annotation.annotationType().getName(), false, false); } public Class getRootDeclaringClass() { @@ -314,8 +313,8 @@ public T getAnnotation() { */ @SuppressWarnings("unchecked") public T synthesizeAnnotation() { - return AnnotationUtils.synthesizeAnnotation(getAnnotationAttributes(), (Class) getAnnotationType(), - getRootDeclaringClass()); + return AnnotationUtils.synthesizeAnnotation( + getAnnotationAttributes(), (Class) getAnnotationType(), getRootDeclaringClass()); } public Class getAnnotationType() { diff --git a/spring-test/src/test/java/org/springframework/test/context/TestExecutionListenersTests.java b/spring-test/src/test/java/org/springframework/test/context/TestExecutionListenersTests.java index 886f252640..648655fb9c 100644 --- a/spring-test/src/test/java/org/springframework/test/context/TestExecutionListenersTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/TestExecutionListenersTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -55,9 +55,9 @@ public class TestExecutionListenersTests { @Test public void defaultListeners() { List> expected = asList(ServletTestExecutionListener.class, - DirtiesContextBeforeModesTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, - DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class, - SqlScriptsTestExecutionListener.class); + DirtiesContextBeforeModesTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, + DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class, + SqlScriptsTestExecutionListener.class); assertRegisteredListeners(DefaultListenersTestCase.class, expected); } @@ -67,9 +67,9 @@ public void defaultListeners() { @Test public void defaultListenersMergedWithCustomListenerPrepended() { List> expected = asList(QuuxTestExecutionListener.class, ServletTestExecutionListener.class, - DirtiesContextBeforeModesTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, - DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class, - SqlScriptsTestExecutionListener.class); + DirtiesContextBeforeModesTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, + DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class, + SqlScriptsTestExecutionListener.class); assertRegisteredListeners(MergedDefaultListenersWithCustomListenerPrependedTestCase.class, expected); } @@ -79,9 +79,9 @@ public void defaultListenersMergedWithCustomListenerPrepended() { @Test public void defaultListenersMergedWithCustomListenerAppended() { List> expected = asList(ServletTestExecutionListener.class, - DirtiesContextBeforeModesTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, - DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class, - SqlScriptsTestExecutionListener.class, BazTestExecutionListener.class); + DirtiesContextBeforeModesTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, + DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class, + SqlScriptsTestExecutionListener.class, BazTestExecutionListener.class); assertRegisteredListeners(MergedDefaultListenersWithCustomListenerAppendedTestCase.class, expected); } @@ -91,9 +91,9 @@ public void defaultListenersMergedWithCustomListenerAppended() { @Test public void defaultListenersMergedWithCustomListenerInserted() { List> expected = asList(ServletTestExecutionListener.class, - DirtiesContextBeforeModesTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, - BarTestExecutionListener.class, DirtiesContextTestExecutionListener.class, - TransactionalTestExecutionListener.class, SqlScriptsTestExecutionListener.class); + DirtiesContextBeforeModesTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, + BarTestExecutionListener.class, DirtiesContextTestExecutionListener.class, + TransactionalTestExecutionListener.class, SqlScriptsTestExecutionListener.class); assertRegisteredListeners(MergedDefaultListenersWithCustomListenerInsertedTestCase.class, expected); } @@ -107,7 +107,7 @@ public void inheritedDefaultListeners() { assertRegisteredListeners(InheritedDefaultListenersTestCase.class, asList(QuuxTestExecutionListener.class)); assertRegisteredListeners(SubInheritedDefaultListenersTestCase.class, asList(QuuxTestExecutionListener.class)); assertRegisteredListeners(SubSubInheritedDefaultListenersTestCase.class, - asList(QuuxTestExecutionListener.class, EnigmaTestExecutionListener.class)); + asList(QuuxTestExecutionListener.class, EnigmaTestExecutionListener.class)); } @Test @@ -172,13 +172,13 @@ private List names(List> classes) { private void assertRegisteredListeners(Class testClass, List> expected) { TestContextManager testContextManager = new TestContextManager(testClass); assertEquals("TELs registered for " + testClass.getSimpleName(), names(expected), - names(classes(testContextManager))); + names(classes(testContextManager))); } private void assertNumRegisteredListeners(Class testClass, int expected) { TestContextManager testContextManager = new TestContextManager(testClass); assertEquals("Num registered TELs for " + testClass, expected, - testContextManager.getTestExecutionListeners().size()); + testContextManager.getTestExecutionListeners().size()); } @@ -187,8 +187,9 @@ private void assertNumRegisteredListeners(Class testClass, int expected) { static class DefaultListenersTestCase { } - @TestExecutionListeners(listeners = { QuuxTestExecutionListener.class, - DependencyInjectionTestExecutionListener.class }, mergeMode = MERGE_WITH_DEFAULTS) + @TestExecutionListeners( + listeners = {QuuxTestExecutionListener.class, DependencyInjectionTestExecutionListener.class}, + mergeMode = MERGE_WITH_DEFAULTS) static class MergedDefaultListenersWithCustomListenerPrependedTestCase { } @@ -211,12 +212,12 @@ static class SubInheritedDefaultListenersTestCase extends InheritedDefaultListen static class SubSubInheritedDefaultListenersTestCase extends SubInheritedDefaultListenersTestCase { } - @TestExecutionListeners(listeners = { QuuxTestExecutionListener.class }, inheritListeners = false) + @TestExecutionListeners(listeners = QuuxTestExecutionListener.class, inheritListeners = false) static class NonInheritedDefaultListenersTestCase extends InheritedDefaultListenersTestCase { } - @TestExecutionListeners({ FooTestExecutionListener.class, BarTestExecutionListener.class, - BazTestExecutionListener.class }) + @TestExecutionListeners( + {FooTestExecutionListener.class, BarTestExecutionListener.class, BazTestExecutionListener.class}) static class ExplicitListenersTestCase { } @@ -232,36 +233,36 @@ static class NonInheritedListenersTestCase extends InheritedListenersTestCase { static class DuplicateListenersConfigTestCase { } - @TestExecutionListeners({// - FooTestExecutionListener.class,// - BarTestExecutionListener.class,// - BazTestExecutionListener.class // + @TestExecutionListeners({ + FooTestExecutionListener.class, + BarTestExecutionListener.class, + BazTestExecutionListener.class }) @Retention(RetentionPolicy.RUNTIME) - static @interface MetaListeners { + @interface MetaListeners { } @TestExecutionListeners(QuuxTestExecutionListener.class) @Retention(RetentionPolicy.RUNTIME) - static @interface MetaInheritedListeners { + @interface MetaInheritedListeners { } @TestExecutionListeners(listeners = QuuxTestExecutionListener.class, inheritListeners = false) @Retention(RetentionPolicy.RUNTIME) - static @interface MetaNonInheritedListeners { + @interface MetaNonInheritedListeners { } @TestExecutionListeners @Retention(RetentionPolicy.RUNTIME) - static @interface MetaListenersWithOverrides { + @interface MetaListenersWithOverrides { - Class[] listeners() default { FooTestExecutionListener.class, - BarTestExecutionListener.class }; + Class[] listeners() default + {FooTestExecutionListener.class, BarTestExecutionListener.class}; } @TestExecutionListeners @Retention(RetentionPolicy.RUNTIME) - static @interface MetaInheritedListenersWithOverrides { + @interface MetaInheritedListenersWithOverrides { Class[] listeners() default QuuxTestExecutionListener.class; @@ -270,7 +271,7 @@ Class[] listeners() default { FooTestExecutionL @TestExecutionListeners @Retention(RetentionPolicy.RUNTIME) - static @interface MetaNonInheritedListenersWithOverrides { + @interface MetaNonInheritedListenersWithOverrides { Class[] listeners() default QuuxTestExecutionListener.class; @@ -289,24 +290,23 @@ static class MetaInheritedListenersTestCase extends MetaTestCase { static class MetaNonInheritedListenersTestCase extends MetaInheritedListenersTestCase { } - @MetaListenersWithOverrides(listeners = {// - FooTestExecutionListener.class,// - BarTestExecutionListener.class,// - BazTestExecutionListener.class // + @MetaListenersWithOverrides(listeners = { + FooTestExecutionListener.class, + BarTestExecutionListener.class, + BazTestExecutionListener.class }) static class MetaWithOverridesTestCase { } - @MetaInheritedListenersWithOverrides(listeners = { FooTestExecutionListener.class, BarTestExecutionListener.class }) + @MetaInheritedListenersWithOverrides(listeners = {FooTestExecutionListener.class, BarTestExecutionListener.class}) static class MetaInheritedListenersWithOverridesTestCase extends MetaWithOverridesTestCase { } - @MetaNonInheritedListenersWithOverrides(listeners = {// - FooTestExecutionListener.class,// - BarTestExecutionListener.class,// - BazTestExecutionListener.class // - },// - inheritListeners = true) + @MetaNonInheritedListenersWithOverrides(listeners = { + FooTestExecutionListener.class, + BarTestExecutionListener.class, + BazTestExecutionListener.class + }, inheritListeners = true) static class MetaNonInheritedListenersWithOverridesTestCase extends MetaInheritedListenersWithOverridesTestCase { } @@ -342,4 +342,4 @@ public int getOrder() { static class EnigmaTestExecutionListener extends AbstractTestExecutionListener { } -} \ No newline at end of file +} From 51f356f5184893182c228d03251822d519dcc636 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 5 Apr 2016 12:18:48 +0200 Subject: [PATCH 100/344] Quartz ResourceLoaderClassLoadHelper explicitly falls back to classpath lookup Issue: SPR-13706 (cherry picked from commit 6db6f23) --- .../quartz/ResourceLoaderClassLoadHelper.java | 48 +++++++++++-------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/ResourceLoaderClassLoadHelper.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/ResourceLoaderClassLoadHelper.java index 18ab755d56..726f9732d7 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/ResourceLoaderClassLoadHelper.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/ResourceLoaderClassLoadHelper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,6 @@ package org.springframework.scheduling.quartz; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.URL; @@ -73,43 +72,50 @@ public void initialize() { } @Override - @SuppressWarnings("rawtypes") - public Class loadClass(String name) throws ClassNotFoundException { + public Class loadClass(String name) throws ClassNotFoundException { return this.resourceLoader.getClassLoader().loadClass(name); } @SuppressWarnings("unchecked") public Class loadClass(String name, Class clazz) throws ClassNotFoundException { - return loadClass(name); + return (Class) loadClass(name); } @Override public URL getResource(String name) { Resource resource = this.resourceLoader.getResource(name); - try { - return resource.getURL(); - } - catch (FileNotFoundException ex) { - return null; + if (resource.exists()) { + try { + return resource.getURL(); + } + catch (IOException ex) { + if (logger.isWarnEnabled()) { + logger.warn("Could not load " + resource); + } + return null; + } } - catch (IOException ex) { - logger.warn("Could not load " + resource); - return null; + else { + return getClassLoader().getResource(name); } } @Override public InputStream getResourceAsStream(String name) { Resource resource = this.resourceLoader.getResource(name); - try { - return resource.getInputStream(); - } - catch (FileNotFoundException ex) { - return null; + if (resource.exists()) { + try { + return resource.getInputStream(); + } + catch (IOException ex) { + if (logger.isWarnEnabled()) { + logger.warn("Could not load " + resource); + } + return null; + } } - catch (IOException ex) { - logger.warn("Could not load " + resource); - return null; + else { + return getClassLoader().getResourceAsStream(name); } } From 5f9113678f2ad0a1dec6b2b5d7a0630138a0104a Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 5 Apr 2016 15:57:05 +0200 Subject: [PATCH 101/344] Consistent use of LinkedHashMap in StaticListableBeanFactory --- .../beans/factory/support/StaticListableBeanFactory.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java index 2d701ce7fa..6e064c5d5e 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,6 @@ import java.lang.annotation.Annotation; import java.util.ArrayList; -import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -59,7 +58,7 @@ public class StaticListableBeanFactory implements ListableBeanFactory { /** Map from bean name to bean instance */ - private final Map beans = new HashMap(); + private final Map beans = new LinkedHashMap(); /** @@ -265,7 +264,7 @@ public Map getBeansOfType(Class type, boolean includeNonSingle throws BeansException { boolean isFactoryType = (type != null && FactoryBean.class.isAssignableFrom(type)); - Map matches = new HashMap(); + Map matches = new LinkedHashMap(); for (Map.Entry entry : this.beans.entrySet()) { String beanName = entry.getKey(); From 7659101843e6d08e5c67663dc4eacfadc8337f24 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 7 Apr 2016 11:35:43 +0200 Subject: [PATCH 102/344] BeanDefinitionBuilder keeps providing addConstructorArg(Object) for Spring Security 2.x compatibility Issue: SPR-14123 (cherry picked from commit 4ea4257) --- .../factory/support/BeanDefinitionBuilder.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionBuilder.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionBuilder.java index ed6bc0661f..a1533835f9 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionBuilder.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -166,6 +166,17 @@ public BeanDefinitionBuilder setFactoryMethod(String factoryMethod) { return this; } + /** + * Add an indexed constructor arg value. The current index is tracked internally + * and all additions are at the present point. + * @deprecated since Spring 2.5, in favor of {@link #addConstructorArgValue}. + * This variant just remains around for Spring Security 2.x compatibility. + */ + @Deprecated + public BeanDefinitionBuilder addConstructorArg(Object value) { + return addConstructorArgValue(value); + } + /** * Add an indexed constructor arg value. The current index is tracked internally * and all additions are at the present point. From 0864834077e4cd645fcecec0d6f63221754d5fae Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 7 Apr 2016 14:27:22 +0200 Subject: [PATCH 103/344] Cleanup of remaining direct BeanWrapper usage Issue: SPR-14121 (cherry picked from commit 157dcab) --- .../AbstractNestablePropertyAccessor.java | 41 +++++++------ .../springframework/beans/BeanWrapper.java | 26 +++++---- .../beans/BeanWrapperImpl.java | 39 +++++++------ .../quartz/SpringBeanJobFactory.java | 6 +- .../config/ExecutorBeanDefinitionParser.java | 5 +- .../config/TaskExecutorFactoryBean.java | 57 +++++++++---------- 6 files changed, 83 insertions(+), 91 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java b/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java index 12b304666c..b987fb56c8 100644 --- a/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -87,10 +87,10 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA } } + private int autoGrowCollectionLimit = Integer.MAX_VALUE; - /** The wrapped object */ - private Object object; + private Object wrappedObject; private String nestedPath = ""; @@ -204,23 +204,23 @@ public void setWrappedInstance(Object object) { public void setWrappedInstance(Object object, String nestedPath, Object rootObject) { Assert.notNull(object, "Target object must not be null"); if (object.getClass() == javaUtilOptionalClass) { - this.object = OptionalUnwrapper.unwrap(object); + this.wrappedObject = OptionalUnwrapper.unwrap(object); } else { - this.object = object; + this.wrappedObject = object; } this.nestedPath = (nestedPath != null ? nestedPath : ""); - this.rootObject = (!"".equals(this.nestedPath) ? rootObject : this.object); + this.rootObject = (!"".equals(this.nestedPath) ? rootObject : this.wrappedObject); this.nestedPropertyAccessors = null; - this.typeConverterDelegate = new TypeConverterDelegate(this, this.object); + this.typeConverterDelegate = new TypeConverterDelegate(this, this.wrappedObject); } public final Object getWrappedInstance() { - return this.object; + return this.wrappedObject; } public final Class getWrappedClass() { - return (this.object != null ? this.object.getClass() : null); + return (this.wrappedObject != null ? this.wrappedObject.getClass() : null); } /** @@ -303,7 +303,7 @@ protected void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) th catch (NotReadablePropertyException ex) { throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName, "Cannot access indexed value in property referenced " + - "in indexed property path '" + propertyName + "'", ex); + "in indexed property path '" + propertyName + "'", ex); } // Set value for last key. String key = tokens.keys[tokens.keys.length - 1]; @@ -318,7 +318,7 @@ protected void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) th else { throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + propertyName, "Cannot access indexed value in property referenced " + - "in indexed property path '" + propertyName + "': returned null"); + "in indexed property path '" + propertyName + "': returned null"); } } if (propValue.getClass().isArray()) { @@ -367,8 +367,8 @@ else if (propValue instanceof List) { catch (NullPointerException ex) { throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName, "Cannot set element with index " + index + " in List of size " + - size + ", accessed using property path '" + propertyName + - "': List does not support filling up gaps with null elements"); + size + ", accessed using property path '" + propertyName + + "': List does not support filling up gaps with null elements"); } } list.add(convertedValue); @@ -405,7 +405,7 @@ else if (propValue instanceof Map) { else { throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName, "Property referenced in indexed property path '" + propertyName + - "' is neither an array nor a List nor a Map; returned value was [" + propValue + "]"); + "' is neither an array nor a List nor a Map; returned value was [" + propValue + "]"); } } @@ -451,7 +451,7 @@ else if (propValue instanceof Map) { } pv.getOriginalPropertyValue().conversionNecessary = (valueToApply != originalValue); } - ph.setValue(object, valueToApply); + ph.setValue(this.wrappedObject, valueToApply); } catch (TypeMismatchException ex) { throw ex; @@ -953,10 +953,9 @@ private PropertyTokenHolder getPropertyNameTokens(String propertyName) { tokens.actualName = (actualName != null ? actualName : propertyName); tokens.canonicalName = tokens.actualName; if (!keys.isEmpty()) { - tokens.canonicalName += - PROPERTY_KEY_PREFIX + - StringUtils.collectionToDelimitedString(keys, PROPERTY_KEY_SUFFIX + PROPERTY_KEY_PREFIX) + - PROPERTY_KEY_SUFFIX; + tokens.canonicalName += PROPERTY_KEY_PREFIX + + StringUtils.collectionToDelimitedString(keys, PROPERTY_KEY_SUFFIX + PROPERTY_KEY_PREFIX) + + PROPERTY_KEY_SUFFIX; tokens.keys = StringUtils.toStringArray(keys); } return tokens; @@ -965,8 +964,8 @@ private PropertyTokenHolder getPropertyNameTokens(String propertyName) { @Override public String toString() { StringBuilder sb = new StringBuilder(getClass().getName()); - if (this.object != null) { - sb.append(": wrapping object [").append(ObjectUtils.identityToString(this.object)).append("]"); + if (this.wrappedObject != null) { + sb.append(": wrapping object [").append(ObjectUtils.identityToString(this.wrappedObject)).append("]"); } else { sb.append(": no wrapped object set"); diff --git a/spring-beans/src/main/java/org/springframework/beans/BeanWrapper.java b/spring-beans/src/main/java/org/springframework/beans/BeanWrapper.java index 7131651c6e..437a450c61 100644 --- a/spring-beans/src/main/java/org/springframework/beans/BeanWrapper.java +++ b/spring-beans/src/main/java/org/springframework/beans/BeanWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -48,6 +48,19 @@ */ public interface BeanWrapper extends ConfigurablePropertyAccessor { + /** + * Specify a limit for array and collection auto-growing. + *

    Default is unlimited on a plain BeanWrapper. + * @since 4.1 + */ + void setAutoGrowCollectionLimit(int autoGrowCollectionLimit); + + /** + * Return the limit for array and collection auto-growing. + * @since 4.1 + */ + int getAutoGrowCollectionLimit(); + /** * Return the bean instance wrapped by this object, if any. * @return the bean instance, or {@code null} if none set @@ -78,15 +91,4 @@ public interface BeanWrapper extends ConfigurablePropertyAccessor { */ PropertyDescriptor getPropertyDescriptor(String propertyName) throws InvalidPropertyException; - /** - * Specify a limit for array and collection auto-growing. - *

    Default is unlimited on a plain BeanWrapper. - */ - void setAutoGrowCollectionLimit(int autoGrowCollectionLimit); - - /** - * Return the limit for array and collection auto-growing. - */ - int getAutoGrowCollectionLimit(); - } diff --git a/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java b/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java index 6625cc1aa4..6615661d4b 100644 --- a/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java +++ b/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -136,23 +136,7 @@ private BeanWrapperImpl(Object object, String nestedPath, BeanWrapperImpl parent @Override public void setWrappedInstance(Object object, String nestedPath, Object rootObject) { super.setWrappedInstance(object, nestedPath, rootObject); - setIntrospectionClass(getWrappedInstance().getClass()); - } - - /** - * Set the security context used during the invocation of the wrapped instance methods. - * Can be null. - */ - public void setSecurityContext(AccessControlContext acc) { - this.acc = acc; - } - - /** - * Return the security context used during the invocation of the wrapped instance methods. - * Can be null. - */ - public AccessControlContext getSecurityContext() { - return this.acc; + setIntrospectionClass(getWrappedClass()); } /** @@ -161,8 +145,7 @@ public AccessControlContext getSecurityContext() { * @param clazz the class to introspect */ protected void setIntrospectionClass(Class clazz) { - if (this.cachedIntrospectionResults != null && - !clazz.equals(this.cachedIntrospectionResults.getBeanClass())) { + if (this.cachedIntrospectionResults != null && this.cachedIntrospectionResults.getBeanClass() != clazz) { this.cachedIntrospectionResults = null; } } @@ -179,6 +162,22 @@ private CachedIntrospectionResults getCachedIntrospectionResults() { return this.cachedIntrospectionResults; } + /** + * Set the security context used during the invocation of the wrapped instance methods. + * Can be null. + */ + public void setSecurityContext(AccessControlContext acc) { + this.acc = acc; + } + + /** + * Return the security context used during the invocation of the wrapped instance methods. + * Can be null. + */ + public AccessControlContext getSecurityContext() { + return this.acc; + } + /** * Convert the given value for the specified property to the latter's type. diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SpringBeanJobFactory.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SpringBeanJobFactory.java index 6a0f737e60..e4dbef0d3e 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SpringBeanJobFactory.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SpringBeanJobFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -72,8 +72,8 @@ public void setSchedulerContext(SchedulerContext schedulerContext) { @Override protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { Object job = super.createJobInstance(bundle); - BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(job); - if (isEligibleForPropertyPopulation(bw.getWrappedInstance())) { + if (isEligibleForPropertyPopulation(job)) { + BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(job); MutablePropertyValues pvs = new MutablePropertyValues(); if (this.schedulerContext != null) { pvs.addPropertyValues(this.schedulerContext); diff --git a/spring-context/src/main/java/org/springframework/scheduling/config/ExecutorBeanDefinitionParser.java b/spring-context/src/main/java/org/springframework/scheduling/config/ExecutorBeanDefinitionParser.java index 1448b3e4aa..98a4684314 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/config/ExecutorBeanDefinitionParser.java +++ b/spring-context/src/main/java/org/springframework/scheduling/config/ExecutorBeanDefinitionParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -61,9 +61,6 @@ private void configureRejectionPolicy(Element element, BeanDefinitionBuilder bui return; } String prefix = "java.util.concurrent.ThreadPoolExecutor."; - if (builder.getRawBeanDefinition().getBeanClassName().contains("backport")) { - prefix = "edu.emory.mathcs.backport." + prefix; - } String policyClassName; if (rejectionPolicy.equals("ABORT")) { policyClassName = prefix + "AbortPolicy"; diff --git a/spring-context/src/main/java/org/springframework/scheduling/config/TaskExecutorFactoryBean.java b/spring-context/src/main/java/org/springframework/scheduling/config/TaskExecutorFactoryBean.java index d655a84e55..7ed6bef329 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/config/TaskExecutorFactoryBean.java +++ b/spring-context/src/main/java/org/springframework/scheduling/config/TaskExecutorFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,8 +16,8 @@ package org.springframework.scheduling.config; -import org.springframework.beans.BeanWrapper; -import org.springframework.beans.BeanWrapperImpl; +import java.util.concurrent.RejectedExecutionHandler; + import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.FactoryBean; @@ -27,8 +27,8 @@ import org.springframework.util.StringUtils; /** - * FactoryBean for creating ThreadPoolTaskExecutor instances, choosing - * between the standard concurrent and the backport-concurrent variant. + * {@link FactoryBean} for creating {@link ThreadPoolTaskExecutor} instances, + * primarily used behind the XML task namespace. * * @author Mark Fisher * @author Juergen Hoeller @@ -41,13 +41,13 @@ public class TaskExecutorFactoryBean implements private Integer queueCapacity; - private Object rejectedExecutionHandler; + private RejectedExecutionHandler rejectedExecutionHandler; private Integer keepAliveSeconds; private String beanName; - private TaskExecutor target; + private ThreadPoolTaskExecutor target; public void setPoolSize(String poolSize) { @@ -58,7 +58,7 @@ public void setQueueCapacity(int queueCapacity) { this.queueCapacity = queueCapacity; } - public void setRejectedExecutionHandler(Object rejectedExecutionHandler) { + public void setRejectedExecutionHandler(RejectedExecutionHandler rejectedExecutionHandler) { this.rejectedExecutionHandler = rejectedExecutionHandler; } @@ -73,28 +73,25 @@ public void setBeanName(String beanName) { @Override - public void afterPropertiesSet() throws Exception { - BeanWrapper bw = new BeanWrapperImpl(ThreadPoolTaskExecutor.class); - determinePoolSizeRange(bw); + public void afterPropertiesSet() { + this.target = new ThreadPoolTaskExecutor(); + determinePoolSizeRange(); if (this.queueCapacity != null) { - bw.setPropertyValue("queueCapacity", this.queueCapacity); + this.target.setQueueCapacity(this.queueCapacity); } if (this.keepAliveSeconds != null) { - bw.setPropertyValue("keepAliveSeconds", this.keepAliveSeconds); + this.target.setKeepAliveSeconds(this.keepAliveSeconds); } if (this.rejectedExecutionHandler != null) { - bw.setPropertyValue("rejectedExecutionHandler", this.rejectedExecutionHandler); + this.target.setRejectedExecutionHandler(this.rejectedExecutionHandler); } if (this.beanName != null) { - bw.setPropertyValue("threadNamePrefix", this.beanName + "-"); - } - this.target = (TaskExecutor) bw.getWrappedInstance(); - if (this.target instanceof InitializingBean) { - ((InitializingBean) this.target).afterPropertiesSet(); + this.target.setThreadNamePrefix(this.beanName + "-"); } + this.target.afterPropertiesSet(); } - private void determinePoolSizeRange(BeanWrapper bw) { + private void determinePoolSizeRange() { if (StringUtils.hasText(this.poolSize)) { try { int corePoolSize; @@ -108,15 +105,15 @@ private void determinePoolSizeRange(BeanWrapper bw) { "Lower bound of pool-size range must not exceed the upper bound"); } if (this.queueCapacity == null) { - // no queue-capacity provided, so unbounded + // No queue-capacity provided, so unbounded if (corePoolSize == 0) { - // actually set 'corePoolSize' to the upper bound of the range - // but allow core threads to timeout - bw.setPropertyValue("allowCoreThreadTimeOut", true); + // Actually set 'corePoolSize' to the upper bound of the range + // but allow core threads to timeout... + this.target.setAllowCoreThreadTimeOut(true); corePoolSize = maxPoolSize; } else { - // non-zero lower bound implies a core-max size range + // Non-zero lower bound implies a core-max size range... throw new IllegalArgumentException( "A non-zero lower bound for the size range requires a queue-capacity value"); } @@ -127,8 +124,8 @@ private void determinePoolSizeRange(BeanWrapper bw) { corePoolSize = value; maxPoolSize = value; } - bw.setPropertyValue("corePoolSize", corePoolSize); - bw.setPropertyValue("maxPoolSize", maxPoolSize); + this.target.setCorePoolSize(corePoolSize); + this.target.setMaxPoolSize(maxPoolSize); } catch (NumberFormatException ex) { throw new IllegalArgumentException("Invalid pool-size value [" + this.poolSize + "]: only single " + @@ -155,10 +152,8 @@ public boolean isSingleton() { @Override - public void destroy() throws Exception { - if (this.target instanceof DisposableBean) { - ((DisposableBean) this.target).destroy(); - } + public void destroy() { + this.target.destroy(); } } From 9c30a1f8dfa57370b3892112c130f1695610179f Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 8 Apr 2016 16:16:29 +0200 Subject: [PATCH 104/344] Latest dependency updates (EhCache 3.0 RC3, Undertow 1.3.21) (cherry picked from commit e365ffb) --- build.gradle | 96 ++++++++++++++++++++++++++-------------------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/build.gradle b/build.gradle index 1b71b631d3..ba3dba33cf 100644 --- a/build.gradle +++ b/build.gradle @@ -31,7 +31,7 @@ configure(allprojects) { project -> ext.eclipselinkVersion = "2.4.2" ext.ehcacheVersion = "2.10.1" ext.ehcachejcacheVersion = "1.0.1" - ext.ehcache3Version = "3.0.0.rc2" + ext.ehcache3Version = "3.0.0.rc3" ext.ejbApiVersion = "3.0" ext.fileuploadVersion = "1.3.1" ext.freemarkerVersion = "2.3.23" @@ -74,7 +74,7 @@ configure(allprojects) { project -> ext.tiles3Version = "3.0.5" ext.tomcatVersion = "8.0.33" ext.tyrusVersion = "1.3.5" // constrained by WebLogic 12.1.3 support - ext.undertowVersion = "1.3.20.Final" + ext.undertowVersion = "1.3.21.Final" ext.woodstoxVersion = "5.0.2" ext.xmlunitVersion = "1.6" ext.xstreamVersion = "1.4.9" @@ -739,52 +739,6 @@ project("spring-web") { } } -project("spring-websocket") { - description = "Spring WebSocket" - - dependencies { - compile(project(":spring-core")) - compile(project(":spring-context")) - compile(project(":spring-web")) - optional(project(":spring-messaging")) - optional(project(":spring-webmvc")) - optional("javax.servlet:javax.servlet-api:3.1.0") - optional("javax.websocket:javax.websocket-api:1.0") - optional("org.apache.tomcat:tomcat-websocket:${tomcatVersion}") { - exclude group: "org.apache.tomcat", module: "tomcat-websocket-api" - exclude group: "org.apache.tomcat", module: "tomcat-servlet-api" - } - optional("org.glassfish.tyrus:tyrus-spi:${tyrusVersion}") - optional("org.glassfish.tyrus:tyrus-core:${tyrusVersion}") - optional("org.glassfish.tyrus:tyrus-server:${tyrusVersion}") - optional("org.glassfish.tyrus:tyrus-container-servlet:${tyrusVersion}") - optional("org.eclipse.jetty:jetty-webapp:${jettyVersion}") { - exclude group: "javax.servlet", module: "javax.servlet" - } - optional("org.eclipse.jetty.websocket:websocket-server:${jettyVersion}") { - exclude group: "javax.servlet", module: "javax.servlet" - } - optional("org.eclipse.jetty.websocket:websocket-client:${jettyVersion}") - optional("org.eclipse.jetty:jetty-client:${jettyVersion}") - optional("io.undertow:undertow-core:${undertowVersion}") - optional("io.undertow:undertow-servlet:${undertowVersion}") { - exclude group: "org.jboss.spec.javax.servlet", module: "jboss-servlet-api_3.1_spec" - exclude group: "org.jboss.spec.javax.annotation", module: "jboss-annotations-api_1.2_spec" - } - optional("io.undertow:undertow-websockets-jsr:${undertowVersion}") { - exclude group: "org.jboss.spec.javax.websocket", module: "jboss-websocket-api_1.1_spec" - } - optional("com.fasterxml.jackson.core:jackson-databind:${jackson2Version}") - testCompile("org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}") - testCompile("org.apache.tomcat.embed:tomcat-embed-websocket:${tomcatVersion}") - testCompile("org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}") - testCompile("io.projectreactor:reactor-net:${reactorVersion}") - testCompile("io.netty:netty-all:${nettyVersion}") - testCompile("log4j:log4j:1.2.17") - testCompile("org.slf4j:slf4j-jcl:${slf4jVersion}") - } -} - project("spring-orm") { description = "Spring Object/Relational Mapping" @@ -991,6 +945,52 @@ project("spring-webmvc-portlet") { } } +project("spring-websocket") { + description = "Spring WebSocket" + + dependencies { + compile(project(":spring-core")) + compile(project(":spring-context")) + compile(project(":spring-web")) + optional(project(":spring-messaging")) + optional(project(":spring-webmvc")) + optional("javax.servlet:javax.servlet-api:3.1.0") + optional("javax.websocket:javax.websocket-api:1.0") + optional("org.apache.tomcat:tomcat-websocket:${tomcatVersion}") { + exclude group: "org.apache.tomcat", module: "tomcat-websocket-api" + exclude group: "org.apache.tomcat", module: "tomcat-servlet-api" + } + optional("org.glassfish.tyrus:tyrus-spi:${tyrusVersion}") + optional("org.glassfish.tyrus:tyrus-core:${tyrusVersion}") + optional("org.glassfish.tyrus:tyrus-server:${tyrusVersion}") + optional("org.glassfish.tyrus:tyrus-container-servlet:${tyrusVersion}") + optional("org.eclipse.jetty:jetty-webapp:${jettyVersion}") { + exclude group: "javax.servlet", module: "javax.servlet" + } + optional("org.eclipse.jetty.websocket:websocket-server:${jettyVersion}") { + exclude group: "javax.servlet", module: "javax.servlet" + } + optional("org.eclipse.jetty.websocket:websocket-client:${jettyVersion}") + optional("org.eclipse.jetty:jetty-client:${jettyVersion}") + optional("io.undertow:undertow-core:${undertowVersion}") + optional("io.undertow:undertow-servlet:${undertowVersion}") { + exclude group: "org.jboss.spec.javax.servlet", module: "jboss-servlet-api_3.1_spec" + exclude group: "org.jboss.spec.javax.annotation", module: "jboss-annotations-api_1.2_spec" + } + optional("io.undertow:undertow-websockets-jsr:${undertowVersion}") { + exclude group: "org.jboss.spec.javax.websocket", module: "jboss-websocket-api_1.1_spec" + } + optional("com.fasterxml.jackson.core:jackson-databind:${jackson2Version}") + testCompile("org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}") + testCompile("org.apache.tomcat.embed:tomcat-embed-websocket:${tomcatVersion}") + testCompile("org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}") + testCompile("io.projectreactor:reactor-net:${reactorVersion}") + testCompile("io.netty:netty-all:${nettyVersion}") + testCompile("log4j:log4j:1.2.17") + testCompile("org.slf4j:slf4j-jcl:${slf4jVersion}") + } +} + project("spring-test") { description = "Spring TestContext Framework" From 35f0fce2ae3339c6309f746b94f71ceead92071c Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Fri, 8 Apr 2016 15:44:22 -0400 Subject: [PATCH 105/344] Remove section on spring-framework-contrib group --- CONTRIBUTING.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2538a2c296..f87099b85f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -29,14 +29,6 @@ do not find something similar, please create a new JIRA issue before submitting a pull request unless the change is truly trivial -- for example: typo fixes, removing compiler warnings, etc. -### Discuss non-trivial contribution ideas with committers - -If you're considering anything more than correcting a typo or fixing a minor -bug, please discuss it on the [spring-framework-contrib][] mailing list before -submitting a pull request. We're happy to provide guidance, but please spend an -hour or two researching the subject on your own, including searching the mailing -list for prior discussions. - ### Sign the Individual Contributor License Agreement (ICLA) If you have not previously done so, please fill out and submit the From 67a65ea600bbbf16e5e361b9ece3c3041090a082 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 19 Jan 2016 18:03:03 +0100 Subject: [PATCH 106/344] Support Quartz trigger without JobDetail This commit allows to create a Quartz trigger implementation via either `CronTriggerFactoryBean` or `SimpleTriggerFactoryBean` even if no job detail is provided. Issue: SPR-13604 (cherry picked from commit 2970065) --- .../quartz/CronTriggerFactoryBean.java | 6 ++- .../quartz/SimpleTriggerFactoryBean.java | 6 ++- .../quartz/CronTriggerFactoryBeanTests.java | 41 ++++++++++++++++++ .../quartz/SimpleTriggerFactoryBeanTests.java | 43 +++++++++++++++++++ 4 files changed, 92 insertions(+), 4 deletions(-) create mode 100644 spring-context-support/src/test/java/org/springframework/scheduling/quartz/CronTriggerFactoryBeanTests.java create mode 100644 spring-context-support/src/test/java/org/springframework/scheduling/quartz/SimpleTriggerFactoryBeanTests.java diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/CronTriggerFactoryBean.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/CronTriggerFactoryBean.java index 666f591e00..03917ad577 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/CronTriggerFactoryBean.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/CronTriggerFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -237,7 +237,9 @@ public void afterPropertiesSet() throws ParseException { CronTriggerImpl cti = new CronTriggerImpl(); cti.setName(this.name); cti.setGroup(this.group); - cti.setJobKey(this.jobDetail.getKey()); + if (this.jobDetail != null) { + cti.setJobKey(this.jobDetail.getKey()); + } cti.setJobDataMap(this.jobDataMap); cti.setStartTime(this.startTime); cti.setCronExpression(this.cronExpression); diff --git a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SimpleTriggerFactoryBean.java b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SimpleTriggerFactoryBean.java index 573fba98c2..a2e85455de 100644 --- a/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SimpleTriggerFactoryBean.java +++ b/spring-context-support/src/main/java/org/springframework/scheduling/quartz/SimpleTriggerFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -228,7 +228,9 @@ public void afterPropertiesSet() { SimpleTriggerImpl sti = new SimpleTriggerImpl(); sti.setName(this.name); sti.setGroup(this.group); - sti.setJobKey(this.jobDetail.getKey()); + if (this.jobDetail != null) { + sti.setJobKey(this.jobDetail.getKey()); + } sti.setJobDataMap(this.jobDataMap); sti.setStartTime(this.startTime); sti.setRepeatInterval(this.repeatInterval); diff --git a/spring-context-support/src/test/java/org/springframework/scheduling/quartz/CronTriggerFactoryBeanTests.java b/spring-context-support/src/test/java/org/springframework/scheduling/quartz/CronTriggerFactoryBeanTests.java new file mode 100644 index 0000000000..5aa0142fc7 --- /dev/null +++ b/spring-context-support/src/test/java/org/springframework/scheduling/quartz/CronTriggerFactoryBeanTests.java @@ -0,0 +1,41 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.scheduling.quartz; + +import java.text.ParseException; + +import org.junit.Test; +import org.quartz.CronTrigger; + +import static org.junit.Assert.*; + +/** + * @author Stephane Nicoll + */ +public class CronTriggerFactoryBeanTests { + + @Test + public void createWithoutJobDetail() throws ParseException { + CronTriggerFactoryBean factory = new CronTriggerFactoryBean(); + factory.setName("myTrigger"); + factory.setCronExpression("0 15 10 ? * *"); + factory.afterPropertiesSet(); + CronTrigger trigger = factory.getObject(); + assertEquals("0 15 10 ? * *", trigger.getCronExpression()); + } + +} diff --git a/spring-context-support/src/test/java/org/springframework/scheduling/quartz/SimpleTriggerFactoryBeanTests.java b/spring-context-support/src/test/java/org/springframework/scheduling/quartz/SimpleTriggerFactoryBeanTests.java new file mode 100644 index 0000000000..7021682b23 --- /dev/null +++ b/spring-context-support/src/test/java/org/springframework/scheduling/quartz/SimpleTriggerFactoryBeanTests.java @@ -0,0 +1,43 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.scheduling.quartz; + +import java.text.ParseException; + +import org.junit.Test; +import org.quartz.SimpleTrigger; + +import static org.junit.Assert.*; + +/** + * @author Stephane Nicoll + */ +public class SimpleTriggerFactoryBeanTests { + + @Test + public void createWithoutJobDetail() throws ParseException { + SimpleTriggerFactoryBean factory = new SimpleTriggerFactoryBean(); + factory.setName("myTrigger"); + factory.setRepeatCount(5); + factory.setRepeatInterval(1000L); + factory.afterPropertiesSet(); + SimpleTrigger trigger = factory.getObject(); + assertEquals(5, trigger.getRepeatCount()); + assertEquals(1000L, trigger.getRepeatInterval()); + } + +} From aa5c12c53435fda934b872f115a988a4c84efde3 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 8 Apr 2016 22:52:36 +0200 Subject: [PATCH 107/344] Polishing --- .../expression/spel/SpelReproTests.java | 18 +++++++++--------- .../method/support/ModelAndViewContainer.java | 3 ++- .../server/ServletServerHttpRequestTests.java | 4 +--- .../support/InvocableHandlerMethodTests.java | 6 ++---- .../support/ModelAndViewContainerTests.java | 7 +++---- ...elAndViewMethodReturnValueHandlerTests.java | 5 ++++- ...wResolverMethodReturnValueHandlerTests.java | 11 ++++++++--- 7 files changed, 29 insertions(+), 25 deletions(-) diff --git a/spring-expression/src/test/java/org/springframework/expression/spel/SpelReproTests.java b/spring-expression/src/test/java/org/springframework/expression/spel/SpelReproTests.java index 8fe757271f..e089bb5f89 100644 --- a/spring-expression/src/test/java/org/springframework/expression/spel/SpelReproTests.java +++ b/spring-expression/src/test/java/org/springframework/expression/spel/SpelReproTests.java @@ -1898,7 +1898,7 @@ public void SPR12522() { } @Test - public void SPR12803() throws Exception { + public void SPR12803() { StandardEvaluationContext sec = new StandardEvaluationContext(); sec.setVariable("iterable", Collections.emptyList()); SpelExpressionParser parser = new SpelExpressionParser(); @@ -1907,7 +1907,7 @@ public void SPR12803() throws Exception { } @Test - public void SPR12808() throws Exception { + public void SPR12808() { SpelExpressionParser parser = new SpelExpressionParser(); Expression expression = parser.parseExpression("T(org.springframework.expression.spel.SpelReproTests.DistanceEnforcer).from(#no)"); StandardEvaluationContext sec = new StandardEvaluationContext(); @@ -2092,9 +2092,9 @@ public List getList() { } - private static enum ABC { A, B, C } + private enum ABC { A, B, C } - private static enum XYZ { X, Y, Z } + private enum XYZ { X, Y, Z } public static class BooleanHolder { @@ -2121,9 +2121,9 @@ public boolean isPrimitiveProperty() { } - private static interface GenericInterface { + private interface GenericInterface { - public T getProperty(); + T getProperty(); } @@ -2148,9 +2148,9 @@ public static class OnlyBridgeMethod extends PackagePrivateClassWithGetter { } - public static interface StaticFinal { + public interface StaticFinal { - public static final String VALUE = "interfaceValue"; + String VALUE = "interfaceValue"; } @@ -2227,7 +2227,7 @@ public Object resolve(EvaluationContext context, String beanName) throws AccessE } - @SuppressWarnings({ "rawtypes", "serial" }) + @SuppressWarnings({"rawtypes", "serial"}) public static class MapWithConstant extends HashMap { public static final int X = 1; diff --git a/spring-web/src/main/java/org/springframework/web/method/support/ModelAndViewContainer.java b/spring-web/src/main/java/org/springframework/web/method/support/ModelAndViewContainer.java index 0f141ff6ef..536d53b0b3 100644 --- a/spring-web/src/main/java/org/springframework/web/method/support/ModelAndViewContainer.java +++ b/spring-web/src/main/java/org/springframework/web/method/support/ModelAndViewContainer.java @@ -147,7 +147,8 @@ private boolean useDefaultModel() { * model (redirect URL preparation). Use of this method may be needed for * advanced cases when access to the "default" model is needed regardless, * e.g. to save model attributes specified via {@code @SessionAttributes}. - * @return the default model, never {@code null} + * @return the default model (never {@code null}) + * @since 4.1.4 */ public ModelMap getDefaultModel() { return this.defaultModel; diff --git a/spring-web/src/test/java/org/springframework/http/server/ServletServerHttpRequestTests.java b/spring-web/src/test/java/org/springframework/http/server/ServletServerHttpRequestTests.java index 906e04352b..2afbbe8e72 100644 --- a/spring-web/src/test/java/org/springframework/http/server/ServletServerHttpRequestTests.java +++ b/spring-web/src/test/java/org/springframework/http/server/ServletServerHttpRequestTests.java @@ -64,9 +64,7 @@ public void getURI() throws Exception { assertEquals("Invalid uri", uri, request.getURI()); } - // SPR-13876 - - @Test + @Test // SPR-13876 public void getUriWithEncoding() throws Exception { URI uri = new URI("https://example.com/%E4%B8%AD%E6%96%87" + "?redirect=https%3A%2F%2Fgithub.com%2Fspring-projects%2Fspring-framework"); diff --git a/spring-web/src/test/java/org/springframework/web/method/support/InvocableHandlerMethodTests.java b/spring-web/src/test/java/org/springframework/web/method/support/InvocableHandlerMethodTests.java index 5dbe548633..b044b257b1 100644 --- a/spring-web/src/test/java/org/springframework/web/method/support/InvocableHandlerMethodTests.java +++ b/spring-web/src/test/java/org/springframework/web/method/support/InvocableHandlerMethodTests.java @@ -29,7 +29,7 @@ import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.ServletWebRequest; -import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; /** @@ -196,9 +196,7 @@ public void invocationTargetException() throws Exception { } } - // SPR-13917 - - @Test + @Test // SPR-13917 public void invocationErrorMessage() throws Exception { HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite(); composite.addResolver(new StubArgumentResolver(double.class, null)); diff --git a/spring-web/src/test/java/org/springframework/web/method/support/ModelAndViewContainerTests.java b/spring-web/src/test/java/org/springframework/web/method/support/ModelAndViewContainerTests.java index c5f481e0e2..8e29239493 100644 --- a/spring-web/src/test/java/org/springframework/web/method/support/ModelAndViewContainerTests.java +++ b/spring-web/src/test/java/org/springframework/web/method/support/ModelAndViewContainerTests.java @@ -16,13 +16,12 @@ package org.springframework.web.method.support; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - import org.junit.Before; import org.junit.Test; + import org.springframework.ui.ModelMap; +import static org.junit.Assert.*; /** * Test fixture for {@link ModelAndViewContainer}. @@ -76,7 +75,7 @@ public void ignoreDefaultModel() { assertTrue(this.mavContainer.getModel().isEmpty()); } - @Test // SPR-14045 + @Test // SPR-14045 public void ignoreDefaultModelAndWithoutRedirectModel() { this.mavContainer.setIgnoreDefaultModelOnRedirect(true); this.mavContainer.setRedirectModelScenario(true); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewMethodReturnValueHandlerTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewMethodReturnValueHandlerTests.java index 7b245cfaa2..fa243ccda4 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewMethodReturnValueHandlerTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewMethodReturnValueHandlerTests.java @@ -47,6 +47,7 @@ public class ModelAndViewMethodReturnValueHandlerTests { private MethodParameter returnParamModelAndView; + @Before public void setUp() throws Exception { this.handler = new ModelAndViewMethodReturnValueHandler(); @@ -55,6 +56,7 @@ public void setUp() throws Exception { this.returnParamModelAndView = getReturnValueParam("modelAndView"); } + @Test public void supportsReturnType() throws Exception { assertTrue(handler.supportsReturnType(returnParamModelAndView)); @@ -143,7 +145,7 @@ public void handleRedirectAttributesWithoutRedirect() throws Exception { assertNotSame("RedirectAttributes should not be used if controller doesn't redirect", redirectAttributes, model); } - @Test // SPR-14045 + @Test // SPR-14045 public void handleRedirectWithIgnoreDefaultModel() throws Exception { mavContainer.setIgnoreDefaultModelOnRedirect(true); @@ -163,6 +165,7 @@ private MethodParameter getReturnValueParam(String methodName) throws Exception return new MethodParameter(method, -1); } + @SuppressWarnings("unused") ModelAndView modelAndView() { return null; diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewResolverMethodReturnValueHandlerTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewResolverMethodReturnValueHandlerTests.java index febaff9135..b2a6d2aa2b 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewResolverMethodReturnValueHandlerTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ModelAndViewResolverMethodReturnValueHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -50,6 +50,7 @@ public class ModelAndViewResolverMethodReturnValueHandlerTests { private ServletWebRequest request; + @Before public void setUp() { mavResolvers = new ArrayList(); @@ -58,6 +59,7 @@ public void setUp() { request = new ServletWebRequest(new MockHttpServletRequest()); } + @Test public void modelAndViewResolver() throws Exception { MethodParameter returnType = new MethodParameter(getClass().getDeclaredMethod("testBeanReturnValue"), -1); @@ -71,7 +73,7 @@ public void modelAndViewResolver() throws Exception { assertFalse(mavContainer.isRequestHandled()); } - @Test(expected=UnsupportedOperationException.class) + @Test(expected = UnsupportedOperationException.class) public void modelAndViewResolverUnresolved() throws Exception { MethodParameter returnType = new MethodParameter(getClass().getDeclaredMethod("intReturnValue"), -1); mavResolvers.add(new TestModelAndViewResolver(TestBean.class)); @@ -88,7 +90,7 @@ public void handleNull() throws Exception { assertTrue(mavContainer.getModel().isEmpty()); } - @Test(expected=UnsupportedOperationException.class) + @Test(expected = UnsupportedOperationException.class) public void handleSimpleType() throws Exception { MethodParameter returnType = new MethodParameter(getClass().getDeclaredMethod("intReturnValue"), -1); handler.handleReturnValue(55, returnType, mavContainer, request); @@ -102,6 +104,7 @@ public void handleNonSimpleType() throws Exception{ assertTrue(mavContainer.containsAttribute("testBean")); } + @SuppressWarnings("unused") private int intReturnValue() { return 0; @@ -112,6 +115,7 @@ private TestBean testBeanReturnValue() { return null; } + private static class TestModelAndViewResolver implements ModelAndViewResolver { private Class returnValueType; @@ -132,4 +136,5 @@ public ModelAndView resolveModelAndView(Method method, Class handlerType, Object } } } + } \ No newline at end of file From 2b6ec6a85eb7d103ffde9df1e757b78521f35220 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sat, 9 Apr 2016 23:12:13 +0200 Subject: [PATCH 108/344] Upgrade to CGLIB 3.2.2 Issue: SPR-13934 (cherry picked from commit dc7ec44) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index ba3dba33cf..5468e0619e 100644 --- a/build.gradle +++ b/build.gradle @@ -291,7 +291,7 @@ project("spring-core") { // both into the spring-core jar. cglib 3.2 itself depends on asm 5.0 and is therefore // further transformed by the JarJar task to depend on org.springframework.asm; this // avoids including two different copies of asm unnecessarily. - def cglibVersion = "3.2.0" + def cglibVersion = "3.2.2" def objenesisVersion = "2.2" configurations { From 053ebb180e141d437ddaa535cafaf81e52024ef4 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sun, 10 Apr 2016 12:37:55 +0200 Subject: [PATCH 109/344] Polishing --- .../springframework/aop/framework/ProxyFactory.java | 8 ++++---- .../springframework/aop/framework/CglibProxyTests.java | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactory.java b/spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactory.java index db1e19a359..854f9122f6 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactory.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,9 +22,9 @@ import org.springframework.util.ClassUtils; /** - * Factory for AOP proxies for programmatic use, rather than via a bean - * factory. This class provides a simple way of obtaining and configuring - * AOP proxies in code. + * Factory for AOP proxies for programmatic use, rather than via declarative + * setup in a bean factory. This class provides a simple way of obtaining + * and configuring AOP proxy instances in custom user code. * * @author Rod Johnson * @author Juergen Hoeller diff --git a/spring-context/src/test/java/org/springframework/aop/framework/CglibProxyTests.java b/spring-context/src/test/java/org/springframework/aop/framework/CglibProxyTests.java index af8b1be5ea..8d997271bd 100644 --- a/spring-context/src/test/java/org/springframework/aop/framework/CglibProxyTests.java +++ b/spring-context/src/test/java/org/springframework/aop/framework/CglibProxyTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -236,18 +236,18 @@ public int hashCode() { @Test public void testMultipleProxiesForIntroductionAdvisor() { - TestBean target = new TestBean(); - target.setAge(20); + TestBean target1 = new TestBean(); + target1.setAge(20); TestBean target2 = new TestBean(); target2.setAge(21); - ITestBean proxy1 = getIntroductionAdvisorProxy(target); + ITestBean proxy1 = getIntroductionAdvisorProxy(target1); ITestBean proxy2 = getIntroductionAdvisorProxy(target2); assertSame("Incorrect duplicate creation of proxy classes", proxy1.getClass(), proxy2.getClass()); } private ITestBean getIntroductionAdvisorProxy(TestBean target) { - ProxyFactory pf = new ProxyFactory(new Class[] {ITestBean.class}); + ProxyFactory pf = new ProxyFactory(ITestBean.class); pf.setProxyTargetClass(true); pf.addAdvisor(new LockMixinAdvisor()); From d303ac94dc5028cc069dd5d07c784339094cc859 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 11 Apr 2016 12:22:37 +0200 Subject: [PATCH 110/344] Polishing --- .../http/client/Netty4ClientHttpRequest.java | 10 +++++----- .../http/client/Netty4ClientHttpResponse.java | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/client/Netty4ClientHttpRequest.java b/spring-web/src/main/java/org/springframework/http/client/Netty4ClientHttpRequest.java index 961255a4b7..929cfb62b8 100644 --- a/spring-web/src/main/java/org/springframework/http/client/Netty4ClientHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/client/Netty4ClientHttpRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,8 +42,8 @@ import org.springframework.util.concurrent.SettableListenableFuture; /** - * {@link org.springframework.http.client.ClientHttpRequest} implementation that uses - * Netty 4 to execute requests. + * {@link org.springframework.http.client.ClientHttpRequest} implementation + * that uses Netty 4 to execute requests. * *

    Created via the {@link Netty4ClientHttpRequestFactory}. * @@ -148,8 +148,8 @@ private FullHttpRequest createFullHttpRequest(HttpHeaders headers) { FullHttpRequest nettyRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, nettyMethod, this.uri.toString(), this.body.buffer()); - nettyRequest.headers().set(HttpHeaders.HOST, uri.getHost()); - nettyRequest.headers().set(HttpHeaders.CONNECTION, io.netty.handler.codec.http.HttpHeaders.Values.CLOSE); + nettyRequest.headers().set(HttpHeaders.HOST, this.uri.getHost()); + nettyRequest.headers().set(HttpHeaders.CONNECTION, "close"); for (Map.Entry> entry : headers.entrySet()) { nettyRequest.headers().add(entry.getKey(), entry.getValue()); diff --git a/spring-web/src/main/java/org/springframework/http/client/Netty4ClientHttpResponse.java b/spring-web/src/main/java/org/springframework/http/client/Netty4ClientHttpResponse.java index 0963d25408..be619f1f8f 100644 --- a/spring-web/src/main/java/org/springframework/http/client/Netty4ClientHttpResponse.java +++ b/spring-web/src/main/java/org/springframework/http/client/Netty4ClientHttpResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,8 +28,8 @@ import org.springframework.util.Assert; /** - * {@link org.springframework.http.client.ClientHttpResponse} implementation that uses - * Netty 4 to execute requests. + * {@link org.springframework.http.client.ClientHttpResponse} implementation + * that uses Netty 4 to parse responses. * * @author Arjen Poutsma * @since 4.1.2 From e0642c77c3195e4a27eecbbcee08291b05bd0289 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 11 Apr 2016 17:45:49 +0200 Subject: [PATCH 111/344] HttpHeaders consistently ignores invalid date header values Issue: SPR-14144 (cherry picked from commit 448621a) --- .../org/springframework/http/HttpHeaders.java | 68 ++++++++++++------- .../http/HttpHeadersTests.java | 25 ++++--- 2 files changed, 59 insertions(+), 34 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/HttpHeaders.java b/spring-web/src/main/java/org/springframework/http/HttpHeaders.java index 886b06a633..1170bb0652 100644 --- a/spring-web/src/main/java/org/springframework/http/HttpHeaders.java +++ b/spring-web/src/main/java/org/springframework/http/HttpHeaders.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,7 +41,7 @@ import org.springframework.util.StringUtils; /** - * Represents HTTP request and response headers, mapping string header names to list of string values. + * Represents HTTP request and response headers, mapping string header names to a list of string values. * *

    In addition to the normal methods defined by {@link Map}, this class offers the following * convenience methods: @@ -51,7 +51,7 @@ *

  • {@link #set(String, String)} sets the header value to a single string value
  • * * - *

    Inspired by {@link com.sun.net.httpserver.Headers}. + *

    Inspired by {@code com.sun.net.httpserver.Headers}. * * @author Arjen Poutsma * @author Sebastien Deleuze @@ -367,9 +367,9 @@ public class HttpHeaders implements MultiValueMap, Serializable * @see Section 7.1.1.1 of RFC 7231 */ private static final String[] DATE_FORMATS = new String[] { - "EEE, dd MMM yyyy HH:mm:ss zzz", - "EEE, dd-MMM-yy HH:mm:ss zzz", - "EEE MMM dd HH:mm:ss yyyy" + "EEE, dd MMM yyyy HH:mm:ss zzz", + "EEE, dd-MMM-yy HH:mm:ss zzz", + "EEE MMM dd HH:mm:ss yyyy" }; private static TimeZone GMT = TimeZone.getTimeZone("GMT"); @@ -779,12 +779,7 @@ public void setExpires(long expires) { * January 1, 1970 GMT. Returns -1 when the date is unknown. */ public long getExpires() { - try { - return getFirstDate(EXPIRES); - } - catch (IllegalArgumentException ex) { - return -1; - } + return getFirstDate(EXPIRES, false); } /** @@ -802,7 +797,7 @@ public void setIfModifiedSince(long ifModifiedSince) { * January 1, 1970 GMT. Returns -1 when the date is unknown. */ public long getIfModifiedSince() { - return getFirstDate(IF_MODIFIED_SINCE); + return getFirstDate(IF_MODIFIED_SINCE, false); } /** @@ -867,7 +862,7 @@ public void setLastModified(long lastModified) { * January 1, 1970 GMT. Returns -1 when the date is unknown. */ public long getLastModified() { - return getFirstDate(LAST_MODIFIED); + return getFirstDate(LAST_MODIFIED, false); } /** @@ -951,24 +946,49 @@ public String getUpgrade() { * Parse the first header value for the given header name as a date, * return -1 if there is no value, or raise {@link IllegalArgumentException} * if the value cannot be parsed as a date. + * @param headerName the header name + * @return the parsed date header, or -1 if none */ public long getFirstDate(String headerName) { + return getFirstDate(headerName, true); + } + + /** + * Parse the first header value for the given header name as a date, + * return -1 if there is no value or also in case of an invalid value + * (if {@code rejectInvalid=false}), or raise {@link IllegalArgumentException} + * if the value cannot be parsed as a date. + * @param headerName the header name + * @param rejectInvalid whether to reject invalid values with an + * {@link IllegalArgumentException} ({@code true}) or rather return -1 + * in that case ({@code false}) + * @return the parsed date header, or -1 if none (or invalid) + */ + private long getFirstDate(String headerName, boolean rejectInvalid) { String headerValue = getFirst(headerName); if (headerValue == null) { + // No header value sent at all return -1; } - for (String dateFormat : DATE_FORMATS) { - SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat, Locale.US); - simpleDateFormat.setTimeZone(GMT); - try { - return simpleDateFormat.parse(headerValue).getTime(); - } - catch (ParseException ex) { - // ignore + if (headerValue.length() >= 3) { + // Short "0" or "-1" like values are never valid HTTP date headers... + // Let's only bother with SimpleDateFormat parsing for long enough values. + for (String dateFormat : DATE_FORMATS) { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat, Locale.US); + simpleDateFormat.setTimeZone(GMT); + try { + return simpleDateFormat.parse(headerValue).getTime(); + } + catch (ParseException ex) { + // ignore + } } } - throw new IllegalArgumentException("Cannot parse date value \"" + headerValue + - "\" for \"" + headerName + "\" header"); + if (rejectInvalid) { + throw new IllegalArgumentException("Cannot parse date value \"" + headerValue + + "\" for \"" + headerName + "\" header"); + } + return -1; } /** diff --git a/spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java b/spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java index d88038b904..9fa0704123 100644 --- a/spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java +++ b/spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,14 +30,9 @@ import java.util.TimeZone; import org.hamcrest.Matchers; - import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; +import static org.junit.Assert.*; /** * Unit tests for {@link org.springframework.http.HttpHeaders}. @@ -215,9 +210,7 @@ public void expires() { assertEquals("Invalid Expires header", "Thu, 18 Dec 2008 10:20:00 GMT", headers.getFirst("expires")); } - // SPR-10648 (example is from INT-3063) - - @Test + @Test // SPR-10648 (example is from INT-3063) public void expiresInvalidDate() { headers.set("Expires", "-1"); assertEquals(-1, headers.getExpires()); @@ -234,6 +227,18 @@ public void ifModifiedSince() { headers.getFirst("if-modified-since")); } + @Test // SPR-14144 + public void invalidIfModifiedSinceHeader() { + headers.set(HttpHeaders.IF_MODIFIED_SINCE, "0"); + assertEquals(-1, headers.getIfModifiedSince()); + + headers.set(HttpHeaders.IF_MODIFIED_SINCE, "-1"); + assertEquals(-1, headers.getIfModifiedSince()); + + headers.set(HttpHeaders.IF_MODIFIED_SINCE, "XXX"); + assertEquals(-1, headers.getIfModifiedSince()); + } + @Test public void pragma() { String pragma = "no-cache"; From b9fd8586ff4068c06091ba4a3f56168035ef6585 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 11 Apr 2016 19:27:06 +0200 Subject: [PATCH 112/344] Fixed XML example for setup of scheduled tasks Issue: SPR-14145 (cherry picked from commit 550a320) --- .../scheduling/annotation/EnableScheduling.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableScheduling.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableScheduling.java index b5f643f31b..9b5b805994 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableScheduling.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableScheduling.java @@ -163,16 +163,25 @@ * *

    For reference, the example above can be compared to the following Spring XML * configuration: + * *

      * {@code
      * 
    + *
      *     
    + *
      *     
    - *     
    + *
    + *     
    + *         
    + *     
    + *
      *     
    + *
      * 
      * }
    - * the examples are equivalent save that in XML a fixed-rate period is used + * + * The examples are equivalent save that in XML a fixed-rate period is used * instead of a custom {@code Trigger} implementation; this is because the * {@code task:} namespace {@code scheduled} cannot easily expose such support. This is * but one demonstration how the code-based approach allows for maximum configurability From b7890ca28ee5cdadf4ab4b49acdc52baa28a99e0 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 11 Apr 2016 20:49:38 +0200 Subject: [PATCH 113/344] Consistent license header (cherry picked from commit 537193a) --- .../cache/annotation/EnableCaching.java | 21 ++++-- .../context/annotation/Bean.java | 17 +++-- .../context/annotation/Configuration.java | 69 ++++++++++++++----- .../annotation/EnableAspectJAutoProxy.java | 9 ++- .../annotation/EnableLoadTimeWeaving.java | 25 +++++-- .../context/annotation/PropertySource.java | 7 +- .../scheduling/annotation/EnableAsync.java | 13 ++-- ...NamespaceHandlerProxyTargetClassTests.java | 16 ++--- .../org/springframework/jmx/IJmxTestBean.java | 16 ++--- .../org/springframework/jmx/JmxTestBean.java | 16 ++--- .../access/MBeanClientInterceptorTests.java | 17 +++-- .../RemoteMBeanClientInterceptorTests.java | 20 +++--- .../jmx/export/CustomDateEditorRegistrar.java | 16 ++--- .../export/CustomEditorConfigurerTests.java | 16 ++--- .../springframework/jmx/export/DateRange.java | 16 ++--- .../jmx/export/ExceptionOnInitBean.java | 16 ++--- .../jmx/export/LazyInitMBeanTests.java | 16 ++--- .../PropertyPlaceholderConfigurerTests.java | 16 ++--- .../AnnotationLazyInitMBeanTests.java | 22 +++--- .../AnnotationMetadataAssemblerTests.java | 41 ++++++++--- .../export/annotation/AnnotationTestBean.java | 16 ++--- .../annotation/JmxUtilsAnnotationTests.java | 16 ++--- .../assembler/AbstractAutodetectTests.java | 20 +++--- .../assembler/AbstractJmxAssemblerTests.java | 16 ++--- ...tractMetadataAssemblerAutodetectTests.java | 16 ++--- .../AbstractMetadataAssemblerTests.java | 16 ++--- .../jmx/export/assembler/ICustomBase.java | 16 ++--- .../jmx/export/assembler/ICustomJmxBean.java | 16 ++--- ...aceBasedMBeanInfoAssemblerCustomTests.java | 17 ++--- ...InterfaceBasedMBeanInfoAssemblerTests.java | 16 ++--- ...ameBasedMBeanInfoAssemblerMappedTests.java | 17 ++--- ...ethodNameBasedMBeanInfoAssemblerTests.java | 17 ++--- .../assembler/ReflectiveAssemblerTests.java | 20 +++--- .../naming/AbstractNamingStrategyTests.java | 20 +++--- .../export/naming/KeyNamingStrategyTests.java | 17 ++--- .../PropertiesFileNamingStrategyTests.java | 16 ++--- .../naming/PropertiesNamingStrategyTests.java | 17 ++--- .../ConnectorServerFactoryBeanTests.java | 19 ++--- .../jmx/support/JmxUtilsTests.java | 35 ++++------ .../support/MBeanServerFactoryBeanTests.java | 17 +++-- .../core/env/CommandLinePropertySource.java | 34 ++++++--- .../java/org/springframework/util/Assert.java | 14 ++-- .../jdbc/object/BatchSqlUpdate.java | 16 ++--- .../jms/annotation/EnableJms.java | 24 +++++-- .../htmlunit/DelegatingWebConnection.java | 16 ++--- .../htmlunit/ForwardRequestPostProcessor.java | 16 ++--- .../servlet/htmlunit/HostRequestMatcher.java | 16 ++--- .../htmlunit/HtmlUnitRequestBuilder.java | 16 ++--- .../htmlunit/MockMvcWebClientBuilder.java | 16 ++--- .../htmlunit/MockMvcWebConnection.java | 14 ++-- .../MockMvcWebConnectionBuilderSupport.java | 14 ++-- .../htmlunit/MockWebResponseBuilder.java | 16 +++-- .../htmlunit/UrlRegexRequestMatcher.java | 22 +++--- .../servlet/htmlunit/WebRequestMatcher.java | 16 ++--- .../MockMvcHtmlUnitDriverBuilder.java | 26 +++---- .../WebConnectionHtmlUnitDriver.java | 53 +++++++------- .../setup/PatternMappingFilterProxy.java | 15 ++-- .../mock/web/MockFilterChainTests.java | 16 +++-- .../AbstractWebRequestMatcherTests.java | 16 ++--- .../DelegatingWebConnectionTests.java | 38 +++++----- .../servlet/htmlunit/ForwardController.java | 16 ++--- .../web/servlet/htmlunit/HelloController.java | 16 ++--- .../htmlunit/HostRequestMatcherTests.java | 16 ++--- .../MockMvcWebClientBuilderTests.java | 40 +++++------ .../htmlunit/MockMvcWebConnectionTests.java | 25 ++++--- .../htmlunit/MockWebResponseBuilderTests.java | 31 +++++---- .../htmlunit/UrlRegexRequestMatcherTests.java | 16 ++--- .../MockMvcHtmlUnitDriverBuilderTests.java | 37 +++++----- .../WebConnectionHtmlUnitDriverTests.java | 27 ++++---- .../samples/standalone/FilterTests.java | 15 ++-- ...ConditionalDelegatingFilterProxyTests.java | 16 +++-- .../setup/DefaultMockMvcBuilderTests.java | 16 +++-- .../EnableTransactionManagement.java | 18 ++++- .../config/annotation/EnableWebMvc.java | 52 +++++++------- .../config/annotation/EnableWebSocket.java | 32 +++++---- .../EnableWebSocketMessageBroker.java | 37 +++++----- 76 files changed, 879 insertions(+), 710 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/cache/annotation/EnableCaching.java b/spring-context/src/main/java/org/springframework/cache/annotation/EnableCaching.java index 5086c1a1df..511e4de60d 100644 --- a/spring-context/src/main/java/org/springframework/cache/annotation/EnableCaching.java +++ b/spring-context/src/main/java/org/springframework/cache/annotation/EnableCaching.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,14 +27,16 @@ import org.springframework.core.Ordered; /** - * Enables Spring's annotation-driven cache management capability, similar to - * the support found in Spring's {@code } XML namespace. To be used together + * Enables Spring's annotation-driven cache management capability, similar to the + * support found in Spring's {@code } XML namespace. To be used together * with @{@link org.springframework.context.annotation.Configuration Configuration} * classes as follows: + * *
      * @Configuration
      * @EnableCaching
      * public class AppConfig {
    + *
      *     @Bean
      *     public MyService myService() {
      *         // configure and return a class having @Cacheable methods
    @@ -52,11 +54,15 @@
      *
      * 

    For reference, the example above can be compared to the following Spring XML * configuration: + * *

      * {@code
      * 
    + *
      *     
    + *
      *     
    + *
      *     
      *         
      *             
    @@ -66,8 +72,10 @@
      *             
      *         
      *     
    + *
      * 
      * }
    + * * In both of the scenarios above, {@code @EnableCaching} and {@code * } are responsible for registering the necessary Spring * components that power annotation-driven cache management, such as the @@ -90,12 +98,14 @@ * *

    For those that wish to establish a more direct relationship between * {@code @EnableCaching} and the exact cache manager bean to be used, - * the {@link CachingConfigurer} callback interface may be implemented - notice the - * the {@code @Override}-annotated methods below: + * the {@link CachingConfigurer} callback interface may be implemented. + * Notice the the {@code @Override}-annotated methods below: + * *

      * @Configuration
      * @EnableCaching
      * public class AppConfig extends CachingConfigurerSupport {
    + *
      *     @Bean
      *     public MyService myService() {
      *         // configure and return a class having @Cacheable methods
    @@ -118,6 +128,7 @@
      *         return new MyKeyGenerator();
      *     }
      * }
    + * * This approach may be desirable simply because it is more explicit, or it may be * necessary in order to distinguish between two {@code CacheManager} beans present in the * same container. diff --git a/spring-context/src/main/java/org/springframework/context/annotation/Bean.java b/spring-context/src/main/java/org/springframework/context/annotation/Bean.java index 5c0fcd6805..2763560088 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/Bean.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/Bean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,7 +39,8 @@ * public MyBean myBean() { * // instantiate and configure MyBean obj * return obj; - * }
    + * } + * * *

    Bean Names

    * @@ -55,7 +56,8 @@ * public MyBean myBean() { * // instantiate and configure MyBean obj * return obj; - * } + * } + * * *

    Scope, DependsOn, Primary, and Lazy

    * @@ -70,7 +72,8 @@ * public MyBean myBean() { * // instantiate and configure MyBean obj * return obj; - * } + * } + * * *

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

    * @@ -87,14 +90,17 @@ *
      * @Configuration
      * public class AppConfig {
    + *
      *     @Bean
      *     public FooService fooService() {
      *         return new FooService(fooRepository());
      *     }
    + *
      *     @Bean
      *     public FooRepository fooRepository() {
      *         return new JdbcFooRepository(dataSource());
      *     }
    + *
      *     // ...
      * }
    * @@ -152,7 +158,8 @@ * @Bean * public static PropertyPlaceholderConfigurer ppc() { * // instantiate, configure and return ppc ... - * } + * } + * * * By marking this method as {@code static}, it can be invoked without causing instantiation of its * declaring {@code @Configuration} class, thus avoiding the above-mentioned lifecycle conflicts. diff --git a/spring-context/src/main/java/org/springframework/context/annotation/Configuration.java b/spring-context/src/main/java/org/springframework/context/annotation/Configuration.java index c8a8b75124..5d3b67779d 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/Configuration.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/Configuration.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,12 +27,14 @@ import org.springframework.stereotype.Component; /** - * Indicates that a class declares one or more {@link Bean @Bean} methods and may be processed - * by the Spring container to generate bean definitions and service requests for those - * beans at runtime, for example: + * Indicates that a class declares one or more {@link Bean @Bean} methods and + * may be processed by the Spring container to generate bean definitions and + * service requests for those beans at runtime, for example: + * *
      * @Configuration
      * public class AppConfig {
    + *
      *     @Bean
      *     public MyBean myBean() {
      *         // instantiate, configure and return bean ...
    @@ -40,25 +42,28 @@
      * }
    * *

    Bootstrapping {@code @Configuration} classes

    + * *

    Via {@code AnnotationConfigApplicationContext}

    + * * {@code @Configuration} classes are typically bootstrapped using either * {@link AnnotationConfigApplicationContext} or its web-capable variant, * {@link org.springframework.web.context.support.AnnotationConfigWebApplicationContext - * AnnotationConfigWebApplicationContext}. - * A simple example with the former follows: + * AnnotationConfigWebApplicationContext}. A simple example with the former follows: + * *
    - * AnnotationConfigApplicationContext ctx =
    - *     new AnnotationConfigApplicationContext();
    + * AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
      * ctx.register(AppConfig.class);
      * ctx.refresh();
      * MyBean myBean = ctx.getBean(MyBean.class);
    - * // use myBean ...
    + * // use myBean ... + * * * See {@link AnnotationConfigApplicationContext} Javadoc for further details and see * {@link org.springframework.web.context.support.AnnotationConfigWebApplicationContext * AnnotationConfigWebApplicationContext} for {@code web.xml} configuration instructions. * *

    Via Spring {@code } XML

    + * *

    As an alternative to registering {@code @Configuration} classes directly against an * {@code AnnotationConfigApplicationContext}, {@code @Configuration} classes may be * declared as normal {@code } definitions within Spring XML files: @@ -74,6 +79,7 @@ * post processors that facilitate handling {@code @Configuration} classes. * *

    Via component scanning

    + * *

    {@code @Configuration} is meta-annotated with {@link Component @Component}, therefore * {@code @Configuration} classes are candidates for component scanning (typically using * Spring XML's {@code } element) and therefore may also take @@ -82,6 +88,7 @@ *

    {@code @Configuration} classes may not only be bootstrapped using * component scanning, but may also themselves configure component scanning using * the {@link ComponentScan @ComponentScan} annotation: + * *

      * @Configuration
      * @ComponentScan("com.acme.app.services")
    @@ -89,18 +96,20 @@
      *     // various @Bean definitions ...
      * }
    * - * See {@link ComponentScan @ComponentScan} Javadoc for details. - * + * See the {@link ComponentScan @ComponentScan} javadoc for details. * *

    Working with externalized values

    + * *

    Using the {@code Environment} API

    + * * Externalized values may be looked up by injecting the Spring - * {@link org.springframework.core.env.Environment Environment} into a - * {@code @Configuration} class using the {@code @Autowired} or the {@code @Inject} - * annotation: + * {@link org.springframework.core.env.Environment} into a {@code @Configuration} + * class using the {@code @Autowired} or the {@code @Inject} annotation: + * *
      * @Configuration
      * public class AppConfig {
    + *
      *     @Inject Environment env;
      *
      *     @Bean
    @@ -115,10 +124,12 @@
      * source" objects, and {@code @Configuration} classes may contribute property sources to
      * the {@code Environment} object using
      * the {@link org.springframework.core.env.PropertySources @PropertySources} annotation:
    + *
      * 
      * @Configuration
      * @PropertySource("classpath:/com/acme/app.properties")
      * public class AppConfig {
    + *
      *     @Inject Environment env;
      *
      *     @Bean
    @@ -131,12 +142,15 @@
      * and {@link PropertySource @PropertySource} Javadoc for further details.
      *
      * 

    Using the {@code @Value} annotation

    + * * Externalized values may be 'wired into' {@code @Configuration} classes using * the {@link Value @Value} annotation: + * *
      * @Configuration
      * @PropertySource("classpath:/com/acme/app.properties")
      * public class AppConfig {
    + *
      *     @Value("${bean.name}") String beanName;
      *
      *     @Bean
    @@ -155,14 +169,18 @@
      * {@code PropertySourcesPlaceholderConfigurer}.
      *
      * 

    Composing {@code @Configuration} classes

    + * *

    With the {@code @Import} annotation

    + * *

    {@code @Configuration} classes may be composed using the {@link Import @Import} annotation, * not unlike the way that {@code } works in Spring XML. Because * {@code @Configuration} objects are managed as Spring beans within the container, * imported configurations may be injected using {@code @Autowired} or {@code @Inject}: + * *

      * @Configuration
      * public class DatabaseConfig {
    + *
      *     @Bean
      *     public DataSource dataSource() {
      *         // instantiate, configure and return DataSource
    @@ -172,6 +190,7 @@
      * @Configuration
      * @Import(DatabaseConfig.class)
      * public class AppConfig {
    + *
      *     @Inject DatabaseConfig dataConfig;
      *
      *     @Bean
    @@ -188,13 +207,15 @@
      * new AnnotationConfigApplicationContext(AppConfig.class);
    * *

    With the {@code @Profile} annotation

    + * * {@code @Configuration} classes may be marked with the {@link Profile @Profile} annotation to - * indicate they should be processed only if a given profile or profiles are - * active: + * indicate they should be processed only if a given profile or profiles are active: + * *
      * @Profile("embedded")
      * @Configuration
      * public class EmbeddedDatabaseConfig {
    + *
      *     @Bean
      *     public DataSource dataSource() {
      *         // instantiate, configure and return embedded DataSource
    @@ -204,25 +225,29 @@
      * @Profile("production")
      * @Configuration
      * public class ProductionDatabaseConfig {
    + *
      *     @Bean
      *     public DataSource dataSource() {
      *         // instantiate, configure and return production DataSource
      *     }
      * }
    * - * See {@link Profile @Profile} and {@link org.springframework.core.env.Environment Environment} - * Javadoc for further details. + * See the {@link Profile @Profile} and {@link org.springframework.core.env.Environment} + * javadocs for further details. * *

    With Spring XML using the {@code @ImportResource} annotation

    + * * As mentioned above, {@code @Configuration} classes may be declared as regular Spring * {@code } definitions within Spring XML files. It is also possible to * import Spring XML configuration files into {@code @Configuration} classes using * the {@link ImportResource @ImportResource} annotation. Bean definitions imported from XML can be * injected using {@code @Autowired} or {@code @Inject}: + * *
      * @Configuration
      * @ImportResource("classpath:/com/acme/database-config.xml")
      * public class AppConfig {
    + *
      *     @Inject DataSource dataSource; // from XML
      *
      *     @Bean
    @@ -233,10 +258,13 @@
      * }
    * *

    With nested {@code @Configuration} classes

    + * * {@code @Configuration} classes may be nested within one another as follows: + * *
      * @Configuration
      * public class AppConfig {
    + *
      *     @Inject DataSource dataSource;
      *
      *     @Bean
    @@ -264,6 +292,7 @@
      * enclosing {@code @Configuration} class.
      *
      * 

    Configuring lazy initialization

    + * *

    By default, {@code @Bean} methods will be eagerly instantiated at container * bootstrap time. To avoid this, {@code @Configuration} may be used in conjunction with * the {@link Lazy @Lazy} annotation to indicate that all {@code @Bean} methods declared within @@ -271,9 +300,11 @@ * individual {@code @Bean} methods as well. * *

    Testing support for {@code @Configuration} classes

    + * * The Spring TestContext framework available in the {@code spring-test} module * provides the {@code @ContextConfiguration} annotation, which as of Spring 3.1 can * accept an array of {@code @Configuration} {@code Class} objects: + * *
      * @RunWith(SpringJUnit4ClassRunner.class)
      * @ContextConfiguration(classes={AppConfig.class, DatabaseConfig.class})
    @@ -292,6 +323,7 @@
      * See TestContext framework reference documentation for details.
      *
      * 

    Enabling built-in Spring features using {@code @Enable} annotations

    + * * Spring features such as asynchronous method execution, scheduled task execution, * annotation driven transaction management, and even Spring MVC can be enabled and * configured from {@code @Configuration} @@ -304,6 +336,7 @@ * for details. * *

    Constraints when authoring {@code @Configuration} classes

    + * *
      *
    • @Configuration classes must be non-final *
    • @Configuration classes must be non-local (may not be declared within a method) diff --git a/spring-context/src/main/java/org/springframework/context/annotation/EnableAspectJAutoProxy.java b/spring-context/src/main/java/org/springframework/context/annotation/EnableAspectJAutoProxy.java index effcec5df9..e2d652c875 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/EnableAspectJAutoProxy.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/EnableAspectJAutoProxy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,6 +31,7 @@ * @Configuration * @EnableAspectJAutoProxy * public class AppConfig { + * * @Bean * public FooService fooService() { * return new FooService(); @@ -47,12 +48,14 @@ * *
        * public class FooService {
      + *
        *     // various methods
        * }
      * *
        * @Aspect
        * public class MyAspect {
      + *
        *     @Before("execution(* FooService+.*(..))")
        *     public void advice() {
        *         // advise FooService methods as appropriate
      @@ -66,6 +69,7 @@
        * 

      Users can control the type of proxy that gets created for {@code FooService} using * the {@link #proxyTargetClass()} attribute. The following enables CGLIB-style 'subclass' * proxies as opposed to the default interface-based JDK proxy approach. + * *

        * @Configuration
        * @EnableAspectJAutoProxy(proxyTargetClass=true)
      @@ -75,6 +79,7 @@
        *
        * 

      Note that {@code @Aspect} beans may be component-scanned like any other. Simply * mark the aspect with both {@code @Aspect} and {@code @Component}: + * *

        * package com.foo;
        *
      @@ -86,11 +91,13 @@
        * public class MyAspect { ... }
      * * Then use the @{@link ComponentScan} annotation to pick both up: + * *
        * @Configuration
        * @ComponentScan("com.foo")
        * @EnableAspectJAutoProxy
        * public class AppConfig {
      + *
        *     // no explicit @Bean definitions required
        * }
      * diff --git a/spring-context/src/main/java/org/springframework/context/annotation/EnableLoadTimeWeaving.java b/spring-context/src/main/java/org/springframework/context/annotation/EnableLoadTimeWeaving.java index eb8776cf9b..17fda815e0 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/EnableLoadTimeWeaving.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/EnableLoadTimeWeaving.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,22 +29,28 @@ * Activates a Spring {@link LoadTimeWeaver} for this application context, available as * a bean with the name "loadTimeWeaver", similar to the {@code } * element in Spring XML. - * To be used - * on @{@link org.springframework.context.annotation.Configuration Configuration} classes; + * + *

      To be used on @{@link org.springframework.context.annotation.Configuration Configuration} classes; * the simplest possible example of which follows: + * *

        * @Configuration
        * @EnableLoadTimeWeaving
        * public class AppConfig {
      + *
        *     // application-specific @Bean definitions ...
        * }
      * * The example above is equivalent to the following Spring XML configuration: + * *
        * {@code
        * 
      + *
        *     
      + *
        *     
      + *
        * 
        * }
      * @@ -61,10 +67,12 @@ * {@code @EnableLoadTimeWeaving} may also implement the {@link LoadTimeWeavingConfigurer} * interface and return a custom {@code LoadTimeWeaver} instance through the * {@code #getLoadTimeWeaver} method: + * *
        * @Configuration
        * @EnableLoadTimeWeaving
        * public class AppConfig implements LoadTimeWeavingConfigurer {
      + *
        *     @Override
        *     public LoadTimeWeaver getLoadTimeWeaver() {
        *         MyLoadTimeWeaver ltw = new MyLoadTimeWeaver();
      @@ -75,10 +83,13 @@
        * }
      * *

      The example above can be compared to the following Spring XML configuration: + * *

        * {@code
        * 
      + *
        *     
      + *
        * 
        * }
      * @@ -94,6 +105,7 @@ * be registered through {@link LoadTimeWeaver#addTransformer}. AspectJ weaving will be * activated by default if a "META-INF/aop.xml" resource is present on the classpath. * Example: + * *
        * @Configuration
        * @EnableLoadTimeWeaving(aspectjWeaving=ENABLED)
      @@ -101,10 +113,13 @@
        * }
      * *

      The example above can be compared to the following Spring XML configuration: + * *

        * {@code
        * 
      + *
        *     
      + *
        * 
        * }
      * @@ -131,7 +146,8 @@ */ AspectJWeaving aspectjWeaving() default AspectJWeaving.AUTODETECT; - public enum AspectJWeaving { + + enum AspectJWeaving { /** * Switches on Spring-based AspectJ load-time weaving. @@ -151,4 +167,5 @@ public enum AspectJWeaving { */ AUTODETECT; } + } diff --git a/spring-context/src/main/java/org/springframework/context/annotation/PropertySource.java b/spring-context/src/main/java/org/springframework/context/annotation/PropertySource.java index 115766c687..908f70a78c 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/PropertySource.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/PropertySource.java @@ -30,6 +30,7 @@ * conjunction with @{@link Configuration} classes. * *

      Example usage

      + * *

      Given a file {@code app.properties} containing the key/value pair * {@code testbean.name=myTestBean}, the following {@code @Configuration} class * uses {@code @PropertySource} to contribute {@code app.properties} to the @@ -56,6 +57,7 @@ * the configuration above, a call to {@code testBean.getName()} will return "myTestBean". * *

      Resolving ${...} placeholders in {@code } and {@code @Value} annotations

      + * * In order to resolve ${...} placeholders in {@code } definitions or {@code @Value} * annotations using properties from a {@code PropertySource}, one must register * a {@code PropertySourcesPlaceholderConfigurer}. This happens automatically when using @@ -66,9 +68,11 @@ * for details and examples. * *

      Resolving ${...} placeholders within {@code @PropertySource} resource locations

      + * * Any ${...} placeholders present in a {@code @PropertySource} {@linkplain #value() * resource location} will be resolved against the set of property sources already - * registered against the environment. For example: + * registered against the environment. For example: + * *
        * @Configuration
        * @PropertySource("classpath:/com/${my.placeholder:default/path}/app.properties")
      @@ -92,6 +96,7 @@
        * IllegalArgumentException} will be thrown.
        *
        * 

      A note on property overriding with @PropertySource

      + * * In cases where a given property key exists in more than one {@code .properties} * file, the last {@code @PropertySource} annotation processed will 'win' and override. * diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableAsync.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableAsync.java index ef3c396e64..e4910e9f1b 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableAsync.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableAsync.java @@ -64,8 +64,7 @@ * {@code void} return type cannot transmit any exception back to the caller. By default, * such uncaught exceptions are only logged. * - *

      To customize all this, implement {@link AsyncConfigurer} and - * provide: + *

      To customize all this, implement {@link AsyncConfigurer} and provide: *

        *
      • your own {@link java.util.concurrent.Executor Executor} through the * {@link AsyncConfigurer#getAsyncExecutor getAsyncExecutor()} method, and
      • @@ -114,13 +113,19 @@ * *

        For reference, the example above can be compared to the following Spring XML * configuration: + * *

          * {@code
          * 
        + *
          *     
        + *
          *     
        + *
          *     
        + *
          *     
        + *
          * 
          * }
        * @@ -148,8 +153,8 @@ /** * Indicate the 'async' annotation type to be detected at either class * or method level. - *

        By default, both Spring's @{@link Async} annotation and the EJB - * 3.1 {@code @javax.ejb.Asynchronous} annotation will be detected. + *

        By default, both Spring's @{@link Async} annotation and the EJB 3.1 + * {@code @javax.ejb.Asynchronous} annotation will be detected. *

        This attribute exists so that developers can provide their own * custom annotation type to indicate that a method (or all methods of * a given class) should be invoked asynchronously. diff --git a/spring-context/src/test/java/org/springframework/aop/config/AopNamespaceHandlerProxyTargetClassTests.java b/spring-context/src/test/java/org/springframework/aop/config/AopNamespaceHandlerProxyTargetClassTests.java index a6627eb377..66a54b2e3c 100644 --- a/spring-context/src/test/java/org/springframework/aop/config/AopNamespaceHandlerProxyTargetClassTests.java +++ b/spring-context/src/test/java/org/springframework/aop/config/AopNamespaceHandlerProxyTargetClassTests.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.aop.config; diff --git a/spring-context/src/test/java/org/springframework/jmx/IJmxTestBean.java b/spring-context/src/test/java/org/springframework/jmx/IJmxTestBean.java index be001e23c4..ca6a1cd796 100644 --- a/spring-context/src/test/java/org/springframework/jmx/IJmxTestBean.java +++ b/spring-context/src/test/java/org/springframework/jmx/IJmxTestBean.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.jmx; diff --git a/spring-context/src/test/java/org/springframework/jmx/JmxTestBean.java b/spring-context/src/test/java/org/springframework/jmx/JmxTestBean.java index b1a773128d..005fcf574e 100644 --- a/spring-context/src/test/java/org/springframework/jmx/JmxTestBean.java +++ b/spring-context/src/test/java/org/springframework/jmx/JmxTestBean.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.jmx; diff --git a/spring-context/src/test/java/org/springframework/jmx/access/MBeanClientInterceptorTests.java b/spring-context/src/test/java/org/springframework/jmx/access/MBeanClientInterceptorTests.java index 90fb6f9633..b2f4569b6e 100644 --- a/spring-context/src/test/java/org/springframework/jmx/access/MBeanClientInterceptorTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/access/MBeanClientInterceptorTests.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.jmx.access; @@ -22,7 +22,6 @@ import java.net.BindException; import java.util.HashMap; import java.util.Map; - import javax.management.Descriptor; import javax.management.MBeanServerConnection; import javax.management.remote.JMXConnectorServer; diff --git a/spring-context/src/test/java/org/springframework/jmx/access/RemoteMBeanClientInterceptorTests.java b/spring-context/src/test/java/org/springframework/jmx/access/RemoteMBeanClientInterceptorTests.java index d34d620419..218fb8e9bc 100644 --- a/spring-context/src/test/java/org/springframework/jmx/access/RemoteMBeanClientInterceptorTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/access/RemoteMBeanClientInterceptorTests.java @@ -1,24 +1,23 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.jmx.access; import java.net.BindException; import java.net.MalformedURLException; - import javax.management.MBeanServerConnection; import javax.management.remote.JMXConnector; import javax.management.remote.JMXConnectorFactory; @@ -43,6 +42,7 @@ public class RemoteMBeanClientInterceptorTests extends MBeanClientInterceptorTests { private static final int SERVICE_PORT; + private static final String SERVICE_URL; static { @@ -50,10 +50,12 @@ public class RemoteMBeanClientInterceptorTests extends MBeanClientInterceptorTes SERVICE_URL = "service:jmx:jmxmp://localhost:" + SERVICE_PORT; } + private JMXConnectorServer connectorServer; private JMXConnector connector; + @Override public void onSetUp() throws Exception { runTests = false; diff --git a/spring-context/src/test/java/org/springframework/jmx/export/CustomDateEditorRegistrar.java b/spring-context/src/test/java/org/springframework/jmx/export/CustomDateEditorRegistrar.java index e85640fdc5..c7c4f7aea7 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/CustomDateEditorRegistrar.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/CustomDateEditorRegistrar.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.jmx.export; diff --git a/spring-context/src/test/java/org/springframework/jmx/export/CustomEditorConfigurerTests.java b/spring-context/src/test/java/org/springframework/jmx/export/CustomEditorConfigurerTests.java index ff04ff43c7..3e3efbb32b 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/CustomEditorConfigurerTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/CustomEditorConfigurerTests.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.jmx.export; diff --git a/spring-context/src/test/java/org/springframework/jmx/export/DateRange.java b/spring-context/src/test/java/org/springframework/jmx/export/DateRange.java index 64f3f03b89..c3fb705e84 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/DateRange.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/DateRange.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.jmx.export; diff --git a/spring-context/src/test/java/org/springframework/jmx/export/ExceptionOnInitBean.java b/spring-context/src/test/java/org/springframework/jmx/export/ExceptionOnInitBean.java index a59d950a8d..89a4fb42df 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/ExceptionOnInitBean.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/ExceptionOnInitBean.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.jmx.export; diff --git a/spring-context/src/test/java/org/springframework/jmx/export/LazyInitMBeanTests.java b/spring-context/src/test/java/org/springframework/jmx/export/LazyInitMBeanTests.java index 9914be229c..674d105d9a 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/LazyInitMBeanTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/LazyInitMBeanTests.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.jmx.export; diff --git a/spring-context/src/test/java/org/springframework/jmx/export/PropertyPlaceholderConfigurerTests.java b/spring-context/src/test/java/org/springframework/jmx/export/PropertyPlaceholderConfigurerTests.java index 47091ef0cf..4f334afff9 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/PropertyPlaceholderConfigurerTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/PropertyPlaceholderConfigurerTests.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.jmx.export; diff --git a/spring-context/src/test/java/org/springframework/jmx/export/annotation/AnnotationLazyInitMBeanTests.java b/spring-context/src/test/java/org/springframework/jmx/export/annotation/AnnotationLazyInitMBeanTests.java index 93f54eadf4..334dd7d5cf 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/annotation/AnnotationLazyInitMBeanTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/annotation/AnnotationLazyInitMBeanTests.java @@ -1,32 +1,32 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.jmx.export.annotation; -import org.junit.Test; - import javax.management.MBeanServer; import javax.management.ObjectName; -import static org.junit.Assert.*; +import org.junit.Test; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.jmx.support.ObjectNameManager; +import static org.junit.Assert.*; + /** * @author Rob Harrop * @author Juergen Hoeller diff --git a/spring-context/src/test/java/org/springframework/jmx/export/annotation/AnnotationMetadataAssemblerTests.java b/spring-context/src/test/java/org/springframework/jmx/export/annotation/AnnotationMetadataAssemblerTests.java index 8eb3068983..83f77457c6 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/annotation/AnnotationMetadataAssemblerTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/annotation/AnnotationMetadataAssemblerTests.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.jmx.export.annotation; @@ -36,6 +36,7 @@ public class AnnotationMetadataAssemblerTests extends AbstractMetadataAssemblerT private static final String OBJECT_NAME = "bean:name=testBean4"; + @Test public void testAttributeFromInterface() throws Exception { ModelMBeanInfo inf = getMBeanInfoFromAssembler(); @@ -58,6 +59,30 @@ public void testOperationOnGetter() throws Exception { assertNotNull(op); } + @Test + public void testRegistrationOnInterface() throws Exception { + Object bean = getContext().getBean("testInterfaceBean"); + ModelMBeanInfo inf = getAssembler().getMBeanInfo(bean, "bean:name=interfaceTestBean"); + assertNotNull(inf); + assertEquals("My Managed Bean", inf.getDescription()); + + ModelMBeanOperationInfo op = inf.getOperation("foo"); + assertNotNull("foo operation not exposed", op); + assertEquals("invoke foo", op.getDescription()); + + assertNull("doNotExpose operation should not be exposed", inf.getOperation("doNotExpose")); + + ModelMBeanAttributeInfo attr = inf.getAttribute("Bar"); + assertNotNull("bar attribute not exposed", attr); + assertEquals("Bar description", attr.getDescription()); + + ModelMBeanAttributeInfo attr2 = inf.getAttribute("CacheEntries"); + assertNotNull("cacheEntries attribute not exposed", attr2); + assertEquals("Metric Type should be COUNTER", "COUNTER", + attr2.getDescriptor().getFieldValue("metricType")); + } + + @Override protected JmxAttributeSource getAttributeSource() { return new AnnotationJmxAttributeSource(); diff --git a/spring-context/src/test/java/org/springframework/jmx/export/annotation/AnnotationTestBean.java b/spring-context/src/test/java/org/springframework/jmx/export/annotation/AnnotationTestBean.java index 0618e8422c..559b644976 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/annotation/AnnotationTestBean.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/annotation/AnnotationTestBean.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.jmx.export.annotation; diff --git a/spring-context/src/test/java/org/springframework/jmx/export/annotation/JmxUtilsAnnotationTests.java b/spring-context/src/test/java/org/springframework/jmx/export/annotation/JmxUtilsAnnotationTests.java index 75052e84d6..8ed38cb6fd 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/annotation/JmxUtilsAnnotationTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/annotation/JmxUtilsAnnotationTests.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.jmx.export.annotation; diff --git a/spring-context/src/test/java/org/springframework/jmx/export/assembler/AbstractAutodetectTests.java b/spring-context/src/test/java/org/springframework/jmx/export/assembler/AbstractAutodetectTests.java index a5234fd0a1..80ad217dda 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/assembler/AbstractAutodetectTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/assembler/AbstractAutodetectTests.java @@ -1,27 +1,27 @@ /* - * Copyright 2002-2005 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.jmx.export.assembler; import org.junit.Test; -import static org.junit.Assert.*; - import org.springframework.jmx.JmxTestBean; +import static org.junit.Assert.*; + /** * @author Rob Harrop */ diff --git a/spring-context/src/test/java/org/springframework/jmx/export/assembler/AbstractJmxAssemblerTests.java b/spring-context/src/test/java/org/springframework/jmx/export/assembler/AbstractJmxAssemblerTests.java index bbb35f56ed..4a9a45b0b4 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/assembler/AbstractJmxAssemblerTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/assembler/AbstractJmxAssemblerTests.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.jmx.export.assembler; diff --git a/spring-context/src/test/java/org/springframework/jmx/export/assembler/AbstractMetadataAssemblerAutodetectTests.java b/spring-context/src/test/java/org/springframework/jmx/export/assembler/AbstractMetadataAssemblerAutodetectTests.java index 68811d712a..69c11603ef 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/assembler/AbstractMetadataAssemblerAutodetectTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/assembler/AbstractMetadataAssemblerAutodetectTests.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.jmx.export.assembler; diff --git a/spring-context/src/test/java/org/springframework/jmx/export/assembler/AbstractMetadataAssemblerTests.java b/spring-context/src/test/java/org/springframework/jmx/export/assembler/AbstractMetadataAssemblerTests.java index 6919ff676c..4bcbdc57af 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/assembler/AbstractMetadataAssemblerTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/assembler/AbstractMetadataAssemblerTests.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.jmx.export.assembler; diff --git a/spring-context/src/test/java/org/springframework/jmx/export/assembler/ICustomBase.java b/spring-context/src/test/java/org/springframework/jmx/export/assembler/ICustomBase.java index 808168de68..5b7f01e873 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/assembler/ICustomBase.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/assembler/ICustomBase.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2005 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.jmx.export.assembler; diff --git a/spring-context/src/test/java/org/springframework/jmx/export/assembler/ICustomJmxBean.java b/spring-context/src/test/java/org/springframework/jmx/export/assembler/ICustomJmxBean.java index 311b7c67a5..37841ac5af 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/assembler/ICustomJmxBean.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/assembler/ICustomJmxBean.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.jmx.export.assembler; diff --git a/spring-context/src/test/java/org/springframework/jmx/export/assembler/InterfaceBasedMBeanInfoAssemblerCustomTests.java b/spring-context/src/test/java/org/springframework/jmx/export/assembler/InterfaceBasedMBeanInfoAssemblerCustomTests.java index e7bb1d68c9..024b647b79 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/assembler/InterfaceBasedMBeanInfoAssemblerCustomTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/assembler/InterfaceBasedMBeanInfoAssemblerCustomTests.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.jmx.export.assembler; @@ -31,6 +31,7 @@ public class InterfaceBasedMBeanInfoAssemblerCustomTests extends AbstractJmxAsse protected static final String OBJECT_NAME = "bean:name=testBean5"; + @Override protected String getObjectName() { return OBJECT_NAME; diff --git a/spring-context/src/test/java/org/springframework/jmx/export/assembler/InterfaceBasedMBeanInfoAssemblerTests.java b/spring-context/src/test/java/org/springframework/jmx/export/assembler/InterfaceBasedMBeanInfoAssemblerTests.java index 76b8ad5639..3f49e70f92 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/assembler/InterfaceBasedMBeanInfoAssemblerTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/assembler/InterfaceBasedMBeanInfoAssemblerTests.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.jmx.export.assembler; diff --git a/spring-context/src/test/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssemblerMappedTests.java b/spring-context/src/test/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssemblerMappedTests.java index 3dd58bf546..21efe0ba52 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssemblerMappedTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssemblerMappedTests.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.jmx.export.assembler; @@ -33,6 +33,7 @@ public class MethodNameBasedMBeanInfoAssemblerMappedTests extends AbstractJmxAss protected static final String OBJECT_NAME = "bean:name=testBean4"; + @Test public void testGetAgeIsReadOnly() throws Exception { ModelMBeanInfo info = getMBeanInfoFromAssembler(); diff --git a/spring-context/src/test/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssemblerTests.java b/spring-context/src/test/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssemblerTests.java index d85f42b508..3b51e92bc8 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssemblerTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssemblerTests.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.jmx.export.assembler; @@ -33,6 +33,7 @@ public class MethodNameBasedMBeanInfoAssemblerTests extends AbstractJmxAssembler protected static final String OBJECT_NAME = "bean:name=testBean5"; + @Override protected String getObjectName() { return OBJECT_NAME; diff --git a/spring-context/src/test/java/org/springframework/jmx/export/assembler/ReflectiveAssemblerTests.java b/spring-context/src/test/java/org/springframework/jmx/export/assembler/ReflectiveAssemblerTests.java index 729e784464..4b52c9127c 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/assembler/ReflectiveAssemblerTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/assembler/ReflectiveAssemblerTests.java @@ -1,24 +1,21 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.jmx.export.assembler; - - - /** * @author Rob Harrop */ @@ -26,6 +23,7 @@ public class ReflectiveAssemblerTests extends AbstractJmxAssemblerTests { protected static final String OBJECT_NAME = "bean:name=testBean1"; + @Override protected String getObjectName() { return OBJECT_NAME; diff --git a/spring-context/src/test/java/org/springframework/jmx/export/naming/AbstractNamingStrategyTests.java b/spring-context/src/test/java/org/springframework/jmx/export/naming/AbstractNamingStrategyTests.java index 3be1715b4e..9ec912daf0 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/naming/AbstractNamingStrategyTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/naming/AbstractNamingStrategyTests.java @@ -1,25 +1,25 @@ /* - * Copyright 2002-2005 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.jmx.export.naming; -import org.junit.Test; - import javax.management.ObjectName; +import org.junit.Test; + import static org.junit.Assert.*; /** diff --git a/spring-context/src/test/java/org/springframework/jmx/export/naming/KeyNamingStrategyTests.java b/spring-context/src/test/java/org/springframework/jmx/export/naming/KeyNamingStrategyTests.java index df11e8b4bb..2893e7644d 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/naming/KeyNamingStrategyTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/naming/KeyNamingStrategyTests.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.jmx.export.naming; @@ -23,6 +23,7 @@ public class KeyNamingStrategyTests extends AbstractNamingStrategyTests { private static final String OBJECT_NAME = "spring:name=test"; + @Override protected ObjectNamingStrategy getStrategy() throws Exception { return new KeyNamingStrategy(); diff --git a/spring-context/src/test/java/org/springframework/jmx/export/naming/PropertiesFileNamingStrategyTests.java b/spring-context/src/test/java/org/springframework/jmx/export/naming/PropertiesFileNamingStrategyTests.java index 3050b6f8e8..958b5f1128 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/naming/PropertiesFileNamingStrategyTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/naming/PropertiesFileNamingStrategyTests.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.jmx.export.naming; diff --git a/spring-context/src/test/java/org/springframework/jmx/export/naming/PropertiesNamingStrategyTests.java b/spring-context/src/test/java/org/springframework/jmx/export/naming/PropertiesNamingStrategyTests.java index 3521619c22..f8b6038433 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/naming/PropertiesNamingStrategyTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/naming/PropertiesNamingStrategyTests.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.jmx.export.naming; @@ -26,6 +26,7 @@ public class PropertiesNamingStrategyTests extends AbstractNamingStrategyTests { private static final String OBJECT_NAME = "bean:name=namingTest"; + @Override protected ObjectNamingStrategy getStrategy() throws Exception { KeyNamingStrategy strat = new KeyNamingStrategy(); diff --git a/spring-context/src/test/java/org/springframework/jmx/support/ConnectorServerFactoryBeanTests.java b/spring-context/src/test/java/org/springframework/jmx/support/ConnectorServerFactoryBeanTests.java index 754e4d2238..d1b6758d17 100644 --- a/spring-context/src/test/java/org/springframework/jmx/support/ConnectorServerFactoryBeanTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/support/ConnectorServerFactoryBeanTests.java @@ -1,24 +1,23 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.jmx.support; import java.io.IOException; import java.net.MalformedURLException; - import javax.management.InstanceNotFoundException; import javax.management.MBeanServer; import javax.management.MBeanServerConnection; @@ -49,6 +48,7 @@ public class ConnectorServerFactoryBeanTests extends AbstractMBeanServerTests { private static final String OBJECT_NAME = "spring:type=connector,name=test"; + @Override protected void onSetUp() throws Exception { Assume.group(TestGroup.JMXMP); @@ -60,6 +60,7 @@ public void tearDown() throws Exception { Assume.group(TestGroup.JMXMP, () -> super.tearDown()); } + @Test public void startupWithLocatedServer() throws Exception { ConnectorServerFactoryBean bean = new ConnectorServerFactoryBean(); diff --git a/spring-context/src/test/java/org/springframework/jmx/support/JmxUtilsTests.java b/spring-context/src/test/java/org/springframework/jmx/support/JmxUtilsTests.java index 30b0083461..99a1e6f012 100644 --- a/spring-context/src/test/java/org/springframework/jmx/support/JmxUtilsTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/support/JmxUtilsTests.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.jmx.support; @@ -193,7 +193,7 @@ public void dontExposeMe() { } - public static interface FooMBean { + public interface FooMBean { String getName(); } @@ -208,7 +208,7 @@ public String getName() { } - public static interface FooMXBean { + public interface FooMXBean { String getName(); } @@ -224,37 +224,30 @@ public String getName() { public static class Bar extends Foo { - } public static class Abc extends Bar { - } - private static interface JmxInterfaceMBean { - + private interface JmxInterfaceMBean { } - private static interface JmxInterface extends JmxInterfaceMBean { - + private interface JmxInterface extends JmxInterfaceMBean { } - private static interface SpecializedJmxInterface extends JmxInterface { - + private interface SpecializedJmxInterface extends JmxInterface { } - private static interface JmxClassMBean { - + private interface JmxClassMBean { } private static class JmxClass implements JmxClassMBean { - } } diff --git a/spring-context/src/test/java/org/springframework/jmx/support/MBeanServerFactoryBeanTests.java b/spring-context/src/test/java/org/springframework/jmx/support/MBeanServerFactoryBeanTests.java index f8d0bf740c..4dbeccb146 100644 --- a/spring-context/src/test/java/org/springframework/jmx/support/MBeanServerFactoryBeanTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/support/MBeanServerFactoryBeanTests.java @@ -1,24 +1,23 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.jmx.support; import java.lang.management.ManagementFactory; import java.util.List; - import javax.management.MBeanServer; import javax.management.MBeanServerFactory; diff --git a/spring-core/src/main/java/org/springframework/core/env/CommandLinePropertySource.java b/spring-core/src/main/java/org/springframework/core/env/CommandLinePropertySource.java index c71630fdbb..1a47e0ebb5 100644 --- a/spring-core/src/main/java/org/springframework/core/env/CommandLinePropertySource.java +++ b/spring-core/src/main/java/org/springframework/core/env/CommandLinePropertySource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,6 +29,7 @@ * {@code OptionSet} in the case of {@link JOptCommandLinePropertySource}. * *

        Purpose and General Usage

        + * * For use in standalone Spring-based applications, i.e. those that are bootstrapped via * a traditional {@code main} method accepting a {@code String[]} of arguments from the * command line. In many cases, processing command-line arguments directly within the @@ -38,6 +39,7 @@ * will typically be added to the {@link Environment} of the Spring * {@code ApplicationContext}, at which point all command line arguments become available * through the {@link Environment#getProperty(String)} family of methods. For example: + * *
          * public static void main(String[] args) {
          *     CommandLinePropertySource clps = ...;
        @@ -46,11 +48,14 @@
          *     ctx.register(AppConfig.class);
          *     ctx.refresh();
          * }
        + * * With the bootstrap logic above, the {@code AppConfig} class may {@code @Inject} the * Spring {@code Environment} and query it directly for properties: + * *
          * @Configuration
          * public class AppConfig {
        + *
          *     @Inject Environment env;
          *
          *     @Bean
        @@ -63,6 +68,7 @@
          *         return dataSource;
          *     }
          * }
        + * * Because the {@code CommandLinePropertySource} was added to the {@code Environment}'s * set of {@link MutablePropertySources} using the {@code #addFirst} method, it has * highest search precedence, meaning that while "db.hostname" and other properties may @@ -75,9 +81,11 @@ * annotation may be used to inject these properties, given that a {@link * PropertySourcesPropertyResolver} bean has been registered, either directly or through * using the {@code } element. For example: + * *
          * @Component
          * public class MyComponent {
        + *
          *     @Value("my.property:defaultVal")
          *     private String myProperty;
          *
        @@ -94,10 +102,12 @@
          * {@link PropertySource#getProperty(String)} and
          * {@link PropertySource#containsProperty(String)} methods. For example, given the
          * following command line:
        - * 
        - * --o1=v1 --o2
        + * + *
        --o1=v1 --o2
        + * * 'o1' and 'o2' are treated as "option arguments", and the following assertions would * evaluate true: + * *
          * CommandLinePropertySource ps = ...
          * assert ps.containsProperty("o1") == true;
        @@ -105,7 +115,8 @@
          * assert ps.containsProperty("o3") == false;
          * assert ps.getProperty("o1").equals("v1");
          * assert ps.getProperty("o2").equals("");
        - * assert ps.getProperty("o3") == null;
        + * assert ps.getProperty("o3") == null; + *
        * * Note that the 'o2' option has no argument, but {@code getProperty("o2")} resolves to * empty string ({@code ""}) as opposed to {@code null}, while {@code getProperty("o3")} @@ -129,11 +140,13 @@ * CommandLinePropertySource} and at the same time lends itself to conversion when used * in conjunction with the Spring {@link Environment} and its built-in {@code * ConversionService}. Consider the following example: - *
        - * --o1=v1 --o2=v2 /path/to/file1 /path/to/file2
        + * + *
        --o1=v1 --o2=v2 /path/to/file1 /path/to/file2
        + * * In this example, "o1" and "o2" would be considered "option arguments", while the two * filesystem paths qualify as "non-option arguments". As such, the following assertions * will evaluate true: + * *
          * CommandLinePropertySource ps = ...
          * assert ps.containsProperty("o1") == true;
        @@ -141,22 +154,26 @@
          * assert ps.containsProperty("nonOptionArgs") == true;
          * assert ps.getProperty("o1").equals("v1");
          * assert ps.getProperty("o2").equals("v2");
        - * assert ps.getProperty("nonOptionArgs").equals("/path/to/file1,/path/to/file2");
        + * assert ps.getProperty("nonOptionArgs").equals("/path/to/file1,/path/to/file2"); + *
      * *

      As mentioned above, when used in conjunction with the Spring {@code Environment} * abstraction, this comma-delimited string may easily be converted to a String array or * list: + * *

        * Environment env = applicationContext.getEnvironment();
        * String[] nonOptionArgs = env.getProperty("nonOptionArgs", String[].class);
        * assert nonOptionArgs[0].equals("/path/to/file1");
      - * assert nonOptionArgs[1].equals("/path/to/file2");
      + * assert nonOptionArgs[1].equals("/path/to/file2"); + *
      * *

      The name of the special "non-option arguments" property may be customized through * the {@link #setNonOptionArgsPropertyName(String)} method. Doing so is recommended as * it gives proper semantic value to non-option arguments. For example, if filesystem * paths are being specified as non-option arguments, it is likely preferable to refer to * these as something like "file.locations" than the default of "nonOptionArgs": + * *

        * public static void main(String[] args) {
        *     CommandLinePropertySource clps = ...;
      @@ -169,6 +186,7 @@
        * }
      * *

      Limitations

      + * * This abstraction is not intended to expose the full power of underlying command line * parsing APIs such as JOpt or Commons CLI. It's intent is rather just the opposite: to * provide the simplest possible abstraction for accessing command line arguments diff --git a/spring-core/src/main/java/org/springframework/util/Assert.java b/spring-core/src/main/java/org/springframework/util/Assert.java index 1d7c477090..76b45186be 100644 --- a/spring-core/src/main/java/org/springframework/util/Assert.java +++ b/spring-core/src/main/java/org/springframework/util/Assert.java @@ -1,17 +1,17 @@ /* * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.util; diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/object/BatchSqlUpdate.java b/spring-jdbc/src/main/java/org/springframework/jdbc/object/BatchSqlUpdate.java index 6372990866..c8b0484652 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/object/BatchSqlUpdate.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/object/BatchSqlUpdate.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.jdbc.object; diff --git a/spring-jms/src/main/java/org/springframework/jms/annotation/EnableJms.java b/spring-jms/src/main/java/org/springframework/jms/annotation/EnableJms.java index a896e5f49b..a8767010c6 100644 --- a/spring-jms/src/main/java/org/springframework/jms/annotation/EnableJms.java +++ b/spring-jms/src/main/java/org/springframework/jms/annotation/EnableJms.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,6 +35,7 @@ * @Configuration * @EnableJms * public class AppConfig { + * * @Bean * public DefaultJmsListenerContainerFactory myJmsListenerContainerFactory() { * DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory(); @@ -43,6 +44,7 @@ * factory.setConcurrency("5"); * return factory; * } + * * // other @Bean definitions * }
      * @@ -59,6 +61,7 @@ * package com.acme.foo; * * public class MyService { + * * @JmsListener(containerFactory = "myJmsListenerContainerFactory", destination="myQueue") * public void process(String msg) { * // process incoming message @@ -78,6 +81,7 @@ * @Configuration * @EnableJms * public class AppConfig { + * * @Bean * public MyService myService() { * return new MyService(); @@ -112,10 +116,9 @@ * // process incoming message * }
    * - * These features are abstracted by the {@link org.springframework.messaging.handler.annotation.support.MessageHandlerMethodFactory - * MessageHandlerMethodFactory} that is responsible to build the necessary invoker to process - * the annotated method. By default, {@link org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory - * DefaultMessageHandlerMethodFactory} is used. + * These features are abstracted by the {@link org.springframework.messaging.handler.annotation.support.MessageHandlerMethodFactory} + * that is responsible to build the necessary invoker to process the annotated method. By default, + * {@link org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory} is used. * *

    When more control is desired, a {@code @Configuration} class may implement * {@link JmsListenerConfigurer}. This allows access to the underlying @@ -127,6 +130,7 @@ * @Configuration * @EnableJms * public class AppConfig implements JmsListenerConfigurer { + * * @Override * public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) { * registrar.setContainerFactory(myJmsListenerContainerFactory()); @@ -145,16 +149,18 @@ * * For reference, the example above can be compared to the following Spring XML * configuration: + * *

      * {@code 
    + *
      *     
      *
    - *     
    + *     
      *           // factory settings
      *     
      *
      *     
    + *
      * 
      * }
    * @@ -169,6 +175,7 @@ * @Configuration * @EnableJms * public class AppConfig implements JmsListenerConfigurer { + * * @Override * public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) { * registrar.setEndpointRegistry(myJmsListenerEndpointRegistry()); @@ -197,6 +204,7 @@ * configuration: *
      * {@code 
    + *
      *     
      *
      *     
    + *
      * 
      * }
    * @@ -222,6 +231,7 @@ * @Configuration * @EnableJms * public class AppConfig implements JmsListenerConfigurer { + * * @Override * public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) { * SimpleJmsListenerEndpoint myEndpoint = new SimpleJmsListenerEndpoint(); diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/DelegatingWebConnection.java b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/DelegatingWebConnection.java index 091681a86d..3db5711ddc 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/DelegatingWebConnection.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/DelegatingWebConnection.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.test.web.servlet.htmlunit; diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/ForwardRequestPostProcessor.java b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/ForwardRequestPostProcessor.java index f6bc223140..d8d69d94c1 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/ForwardRequestPostProcessor.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/ForwardRequestPostProcessor.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.test.web.servlet.htmlunit; diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/HostRequestMatcher.java b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/HostRequestMatcher.java index e6cee1e665..28f9f0b4fa 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/HostRequestMatcher.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/HostRequestMatcher.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.test.web.servlet.htmlunit; diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/HtmlUnitRequestBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/HtmlUnitRequestBuilder.java index 3cd0218a13..771e8c542a 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/HtmlUnitRequestBuilder.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/HtmlUnitRequestBuilder.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.test.web.servlet.htmlunit; diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebClientBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebClientBuilder.java index 5dc12a2118..497bf4abc8 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebClientBuilder.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebClientBuilder.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.test.web.servlet.htmlunit; diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnection.java b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnection.java index d7a6fe6087..2f19cea7d6 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnection.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnection.java @@ -1,17 +1,17 @@ /* * Copyright 2002-2015 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.test.web.servlet.htmlunit; diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnectionBuilderSupport.java b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnectionBuilderSupport.java index 6575e6a26e..cc957dcab8 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnectionBuilderSupport.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnectionBuilderSupport.java @@ -1,17 +1,17 @@ /* * Copyright 2002-2015 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.test.web.servlet.htmlunit; diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockWebResponseBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockWebResponseBuilder.java index 935d77453c..7ead8e13ce 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockWebResponseBuilder.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockWebResponseBuilder.java @@ -1,17 +1,17 @@ /* * Copyright 2002-2015 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.test.web.servlet.htmlunit; @@ -40,6 +40,7 @@ final class MockWebResponseBuilder { private static final String DEFAULT_STATUS_MESSAGE = "N/A"; + private final long startTime; private final WebRequest webRequest; @@ -55,6 +56,7 @@ public MockWebResponseBuilder(long startTime, WebRequest webRequest, MockHttpSer this.response = response; } + public WebResponse build() throws IOException { WebResponseData webResponseData = webResponseData(); long endTime = System.currentTimeMillis(); diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/UrlRegexRequestMatcher.java b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/UrlRegexRequestMatcher.java index 13d6b6e546..9ab6736448 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/UrlRegexRequestMatcher.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/UrlRegexRequestMatcher.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.test.web.servlet.htmlunit; @@ -27,7 +27,9 @@ *

    For example, if you would like to match on the domain {@code code.jquery.com}, * you might want to use the following. * - *

    WebRequestMatcher cdnMatcher = new UrlRegexRequestMatcher(".*?//code.jquery.com/.*");
    + *
    + * WebRequestMatcher cdnMatcher = new UrlRegexRequestMatcher(".*?//code.jquery.com/.*");
    + * 
    * * @author Rob Winch * @author Sam Brannen @@ -38,6 +40,7 @@ public final class UrlRegexRequestMatcher implements WebRequestMatcher { private final Pattern pattern; + public UrlRegexRequestMatcher(String regex) { this.pattern = Pattern.compile(regex); } @@ -46,6 +49,7 @@ public UrlRegexRequestMatcher(Pattern pattern) { this.pattern = pattern; } + @Override public boolean matches(WebRequest request) { String url = request.getUrl().toExternalForm(); diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/WebRequestMatcher.java b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/WebRequestMatcher.java index e022528cf3..9f34a449de 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/WebRequestMatcher.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/WebRequestMatcher.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.test.web.servlet.htmlunit; diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/webdriver/MockMvcHtmlUnitDriverBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/webdriver/MockMvcHtmlUnitDriverBuilder.java index 3b8ded7168..9454c0a3ee 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/webdriver/MockMvcHtmlUnitDriverBuilder.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/webdriver/MockMvcHtmlUnitDriverBuilder.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.test.web.servlet.htmlunit.webdriver; @@ -69,7 +69,7 @@ protected MockMvcHtmlUnitDriverBuilder(WebApplicationContext context, MockMvcCon /** * Create a new {@code MockMvcHtmlUnitDriverBuilder} based on the supplied * {@link MockMvc} instance. - * @param mockMvc the {@code MockMvc} instance to use; never {@code null} + * @param mockMvc the {@code MockMvc} instance to use (never {@code null}) * @return the MockMvcHtmlUnitDriverBuilder to customize */ public static MockMvcHtmlUnitDriverBuilder mockMvcSetup(MockMvc mockMvc) { @@ -81,7 +81,7 @@ public static MockMvcHtmlUnitDriverBuilder mockMvcSetup(MockMvc mockMvc) { * Create a new {@code MockMvcHtmlUnitDriverBuilder} based on the supplied * {@link WebApplicationContext}. * @param context the {@code WebApplicationContext} to create a {@link MockMvc} - * instance from; never {@code null} + * instance from (never {@code null}) * @return the MockMvcHtmlUnitDriverBuilder to customize */ public static MockMvcHtmlUnitDriverBuilder webAppContextSetup(WebApplicationContext context) { @@ -93,8 +93,8 @@ public static MockMvcHtmlUnitDriverBuilder webAppContextSetup(WebApplicationCont * Create a new {@code MockMvcHtmlUnitDriverBuilder} based on the supplied * {@link WebApplicationContext} and {@link MockMvcConfigurer}. * @param context the {@code WebApplicationContext} to create a {@link MockMvc} - * instance from; never {@code null} - * @param configurer the {@code MockMvcConfigurer} to apply; never {@code null} + * instance from (never {@code null}) + * @param configurer the {@code MockMvcConfigurer} to apply (never {@code null}) * @return the MockMvcHtmlUnitDriverBuilder to customize */ public static MockMvcHtmlUnitDriverBuilder webAppContextSetup(WebApplicationContext context, @@ -122,7 +122,7 @@ public MockMvcHtmlUnitDriverBuilder javascriptEnabled(boolean javascriptEnabled) * {@linkplain #build built} by this builder should delegate to when * processing non-{@linkplain WebRequestMatcher matching} requests. * @param driver the {@code WebConnectionHtmlUnitDriver} to delegate to - * for requests that do not match; never {@code null} + * for requests that do not match (never {@code null}) * @return this builder for further customizations * @see #build() */ diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/webdriver/WebConnectionHtmlUnitDriver.java b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/webdriver/WebConnectionHtmlUnitDriver.java index ab04baf253..bdb67b87f5 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/webdriver/WebConnectionHtmlUnitDriver.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/webdriver/WebConnectionHtmlUnitDriver.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.test.web.servlet.htmlunit.webdriver; @@ -42,11 +42,11 @@ public class WebConnectionHtmlUnitDriver extends HtmlUnitDriver { private WebClient webClient; - public WebConnectionHtmlUnitDriver(BrowserVersion browserVersion) { - super(browserVersion); + public WebConnectionHtmlUnitDriver() { } - public WebConnectionHtmlUnitDriver() { + public WebConnectionHtmlUnitDriver(BrowserVersion browserVersion) { + super(browserVersion); } public WebConnectionHtmlUnitDriver(boolean enableJavascript) { @@ -59,12 +59,10 @@ public WebConnectionHtmlUnitDriver(Capabilities capabilities) { /** - * Modify the supplied {@link WebClient} and retain a reference to it - * so that its {@link WebConnection} is {@linkplain #getWebConnection - * accessible} for later use. - *

    Delegates to {@link HtmlUnitDriver#modifyWebClient(WebClient)} - * for default behavior and to {@link #modifyWebClientInternal(WebClient)} - * for further customization. + * Modify the supplied {@link WebClient} and retain a reference to it so that its + * {@link WebConnection} is {@linkplain #getWebConnection accessible} for later use. + *

    Delegates to {@link HtmlUnitDriver#modifyWebClient} for default behavior + * and to {@link #modifyWebClientInternal} for further customization. * @param webClient the client to modify * @return the modified client * @see HtmlUnitDriver#modifyWebClient(WebClient) @@ -79,8 +77,7 @@ protected final WebClient modifyWebClient(WebClient webClient) { /** * Modify the supplied {@link WebClient}. - *

    The default implementation simply returns the supplied client - * unmodified. + *

    The default implementation simply returns the supplied client unmodified. *

    Subclasses can override this method to customize the {@code WebClient} * that the {@link HtmlUnitDriver} uses. * @param webClient the client to modify @@ -90,21 +87,21 @@ protected WebClient modifyWebClientInternal(WebClient webClient) { return webClient; } - /** - * Access the current {@link WebConnection} for the {@link WebClient}. - * @return the current {@code WebConnection} - */ - public WebConnection getWebConnection() { - return this.webClient.getWebConnection(); - } - /** * Set the {@link WebConnection} to be used with the {@link WebClient}. - * @param webConnection the {@code WebConnection} to use; never {@code null} + * @param webConnection the {@code WebConnection} to use (never {@code null}) */ public void setWebConnection(WebConnection webConnection) { Assert.notNull(webConnection, "WebConnection must not be null"); this.webClient.setWebConnection(webConnection); } + /** + * Access the current {@link WebConnection} for the {@link WebClient}. + * @return the current {@code WebConnection} + */ + public WebConnection getWebConnection() { + return this.webClient.getWebConnection(); + } + } diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/PatternMappingFilterProxy.java b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/PatternMappingFilterProxy.java index 0b72aafa5f..7ae98a634b 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/PatternMappingFilterProxy.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/PatternMappingFilterProxy.java @@ -1,14 +1,17 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.test.web.servlet.setup; diff --git a/spring-test/src/test/java/org/springframework/mock/web/MockFilterChainTests.java b/spring-test/src/test/java/org/springframework/mock/web/MockFilterChainTests.java index 99fe6cca80..bbafd49814 100644 --- a/spring-test/src/test/java/org/springframework/mock/web/MockFilterChainTests.java +++ b/spring-test/src/test/java/org/springframework/mock/web/MockFilterChainTests.java @@ -1,15 +1,19 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + package org.springframework.mock.web; import java.io.IOException; diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/AbstractWebRequestMatcherTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/AbstractWebRequestMatcherTests.java index e3fae7c1bb..9ca8fe50cc 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/AbstractWebRequestMatcherTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/AbstractWebRequestMatcherTests.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.test.web.servlet.htmlunit; diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/DelegatingWebConnectionTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/DelegatingWebConnectionTests.java index 867d47be67..beab76685f 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/DelegatingWebConnectionTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/DelegatingWebConnectionTests.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.test.web.servlet.htmlunit; @@ -19,10 +19,17 @@ import java.net.URL; import java.util.Collections; +import com.gargoylesoftware.htmlunit.HttpWebConnection; +import com.gargoylesoftware.htmlunit.Page; +import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.WebConnection; +import com.gargoylesoftware.htmlunit.WebRequest; +import com.gargoylesoftware.htmlunit.WebResponse; +import com.gargoylesoftware.htmlunit.WebResponseData; +import com.gargoylesoftware.htmlunit.util.NameValuePair; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; - import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; @@ -33,20 +40,11 @@ import org.springframework.tests.Assume; import org.springframework.tests.TestGroup; -import com.gargoylesoftware.htmlunit.HttpWebConnection; -import com.gargoylesoftware.htmlunit.Page; -import com.gargoylesoftware.htmlunit.WebClient; -import com.gargoylesoftware.htmlunit.WebConnection; -import com.gargoylesoftware.htmlunit.WebRequest; -import com.gargoylesoftware.htmlunit.WebResponse; -import com.gargoylesoftware.htmlunit.WebResponseData; -import com.gargoylesoftware.htmlunit.util.NameValuePair; - import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.sameInstance; -import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.*; import static org.hamcrest.core.IsNot.not; -import static org.junit.Assert.assertThat; +import static org.junit.Assert.*; import static org.mockito.Mockito.*; /** diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/ForwardController.java b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/ForwardController.java index 367b717738..9ab342bcd5 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/ForwardController.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/ForwardController.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.test.web.servlet.htmlunit; diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/HelloController.java b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/HelloController.java index 8180898a03..f016a38040 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/HelloController.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/HelloController.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.test.web.servlet.htmlunit; diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/HostRequestMatcherTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/HostRequestMatcherTests.java index 081b94d090..6fa082ee35 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/HostRequestMatcherTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/HostRequestMatcherTests.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.test.web.servlet.htmlunit; diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebClientBuilderTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebClientBuilderTests.java index 0f68d9075f..a7473c185d 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebClientBuilderTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebClientBuilderTests.java @@ -1,26 +1,28 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.test.web.servlet.htmlunit; import java.io.IOException; import java.net.URL; - import javax.servlet.http.HttpServletRequest; +import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.WebRequest; +import com.gargoylesoftware.htmlunit.WebResponse; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -39,13 +41,8 @@ import org.springframework.web.context.WebApplicationContext; import org.springframework.web.servlet.config.annotation.EnableWebMvc; -import com.gargoylesoftware.htmlunit.WebClient; -import com.gargoylesoftware.htmlunit.WebRequest; -import com.gargoylesoftware.htmlunit.WebResponse; - import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; -import static org.springframework.test.web.servlet.htmlunit.MockMvcWebClientBuilder.*; /** * Integration tests for {@link MockMvcWebClientBuilder}. @@ -59,32 +56,33 @@ @WebAppConfiguration public class MockMvcWebClientBuilderTests { - private WebClient webClient; - @Autowired private WebApplicationContext wac; private MockMvc mockMvc; + private WebClient webClient; + @Before public void setup() { this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); } + @Test(expected = IllegalArgumentException.class) public void mockMvcSetupNull() { - mockMvcSetup(null); + MockMvcWebClientBuilder.mockMvcSetup(null); } @Test(expected = IllegalArgumentException.class) public void webAppContextSetupNull() { - webAppContextSetup(null); + MockMvcWebClientBuilder.webAppContextSetup(null); } @Test public void mockMvcSetupWithDefaultWebClientDelegate() throws Exception { - this.webClient = mockMvcSetup(this.mockMvc).build(); + this.webClient = MockMvcWebClientBuilder.mockMvcSetup(this.mockMvc).build(); assertMvcProcessed("http://localhost/test"); Assume.group(TestGroup.PERFORMANCE, () -> assertDelegateProcessed("http://example.com/")); @@ -92,8 +90,8 @@ public void mockMvcSetupWithDefaultWebClientDelegate() throws Exception { @Test public void mockMvcSetupWithCustomWebClientDelegate() throws Exception { - WebClient preconfiguredWebClient = new WebClient(); - this.webClient = mockMvcSetup(this.mockMvc).withDelegate(preconfiguredWebClient).build(); + WebClient otherClient = new WebClient(); + this.webClient = MockMvcWebClientBuilder.mockMvcSetup(this.mockMvc).withDelegate(otherClient).build(); assertMvcProcessed("http://localhost/test"); Assume.group(TestGroup.PERFORMANCE, () -> assertDelegateProcessed("http://example.com/")); diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnectionTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnectionTests.java index 726c4f0bb6..b74a22f2ff 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnectionTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnectionTests.java @@ -1,34 +1,33 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.test.web.servlet.htmlunit; import java.io.IOException; +import com.gargoylesoftware.htmlunit.Page; +import com.gargoylesoftware.htmlunit.WebClient; import org.junit.Before; import org.junit.Test; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import com.gargoylesoftware.htmlunit.Page; -import com.gargoylesoftware.htmlunit.WebClient; - -import static org.hamcrest.CoreMatchers.equalTo; -import static org.junit.Assert.assertThat; +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; /** * Integration tests for {@link MockMvcWebConnection}. diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockWebResponseBuilderTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockWebResponseBuilderTests.java index 363718f00f..f367153cb5 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockWebResponseBuilderTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockWebResponseBuilderTests.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.test.web.servlet.htmlunit; @@ -19,16 +19,16 @@ import java.net.URL; import java.util.List; -import org.junit.Before; -import org.junit.Test; -import org.springframework.mock.web.MockHttpServletResponse; - import com.gargoylesoftware.htmlunit.WebRequest; import com.gargoylesoftware.htmlunit.WebResponse; import com.gargoylesoftware.htmlunit.util.NameValuePair; +import org.junit.Before; +import org.junit.Test; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.junit.Assert.assertThat; +import org.springframework.mock.web.MockHttpServletResponse; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; /** * Tests for {@link MockWebResponseBuilder}. @@ -48,10 +48,10 @@ public class MockWebResponseBuilderTests { @Before public void setUp() throws Exception { this.webRequest = new WebRequest(new URL("http://example.com:80/test/this/here")); - this.responseBuilder = new MockWebResponseBuilder(System.currentTimeMillis(), this.webRequest, this.response); } + // --- constructor @Test(expected = IllegalArgumentException.class) @@ -64,6 +64,7 @@ public void constructorWithNullResponse() throws Exception { new MockWebResponseBuilder(0L, new WebRequest(new URL("http://example.com:80/test/this/here")), null); } + // --- build @Test diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/UrlRegexRequestMatcherTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/UrlRegexRequestMatcherTests.java index 2baab69521..acabeef40d 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/UrlRegexRequestMatcherTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/UrlRegexRequestMatcherTests.java @@ -1,17 +1,17 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.test.web.servlet.htmlunit; diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/webdriver/MockMvcHtmlUnitDriverBuilderTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/webdriver/MockMvcHtmlUnitDriverBuilderTests.java index 9fe37224bc..85e41b02f6 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/webdriver/MockMvcHtmlUnitDriverBuilderTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/webdriver/MockMvcHtmlUnitDriverBuilderTests.java @@ -1,29 +1,27 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.test.web.servlet.htmlunit.webdriver; import java.io.IOException; - import javax.servlet.http.HttpServletRequest; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; - import org.openqa.selenium.htmlunit.HtmlUnitDriver; import org.springframework.beans.factory.annotation.Autowired; @@ -42,7 +40,6 @@ import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; -import static org.springframework.test.web.servlet.htmlunit.webdriver.MockMvcHtmlUnitDriverBuilder.*; /** * Integration tests for {@link MockMvcHtmlUnitDriverBuilder}. @@ -65,25 +62,27 @@ public class MockMvcHtmlUnitDriverBuilderTests { private HtmlUnitDriver driver; + @Before public void setup() { this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); } + @Test(expected = IllegalArgumentException.class) public void webAppContextSetupNull() { - webAppContextSetup(null); + MockMvcHtmlUnitDriverBuilder.webAppContextSetup(null); } @Test(expected = IllegalArgumentException.class) public void mockMvcSetupNull() { - mockMvcSetup(null); + MockMvcHtmlUnitDriverBuilder.mockMvcSetup(null); } @Test public void mockMvcSetupWithCustomDriverDelegate() throws Exception { - WebConnectionHtmlUnitDriver preconfiguredDriver = new WebConnectionHtmlUnitDriver(); - this.driver = mockMvcSetup(this.mockMvc).withDelegate(preconfiguredDriver).build(); + WebConnectionHtmlUnitDriver otherDriver = new WebConnectionHtmlUnitDriver(); + this.driver = MockMvcHtmlUnitDriverBuilder.mockMvcSetup(this.mockMvc).withDelegate(otherDriver).build(); assertMvcProcessed("http://localhost/test"); Assume.group(TestGroup.PERFORMANCE, () -> assertDelegateProcessed("http://example.com/")); @@ -91,7 +90,7 @@ public void mockMvcSetupWithCustomDriverDelegate() throws Exception { @Test public void mockMvcSetupWithDefaultDriverDelegate() throws Exception { - this.driver = mockMvcSetup(this.mockMvc).build(); + this.driver = MockMvcHtmlUnitDriverBuilder.mockMvcSetup(this.mockMvc).build(); assertMvcProcessed("http://localhost/test"); Assume.group(TestGroup.PERFORMANCE, () -> assertDelegateProcessed("http://example.com/")); @@ -99,15 +98,13 @@ public void mockMvcSetupWithDefaultDriverDelegate() throws Exception { @Test public void javaScriptEnabledByDefault() { - this.driver = mockMvcSetup(this.mockMvc).build(); - + this.driver = MockMvcHtmlUnitDriverBuilder.mockMvcSetup(this.mockMvc).build(); assertTrue(this.driver.isJavascriptEnabled()); } @Test public void javaScriptDisabled() { - this.driver = mockMvcSetup(this.mockMvc).javascriptEnabled(false).build(); - + this.driver = MockMvcHtmlUnitDriverBuilder.mockMvcSetup(this.mockMvc).javascriptEnabled(false).build(); assertFalse(this.driver.isJavascriptEnabled()); } diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/webdriver/WebConnectionHtmlUnitDriverTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/webdriver/WebConnectionHtmlUnitDriverTests.java index 45118ce49e..5bed444fea 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/webdriver/WebConnectionHtmlUnitDriverTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/webdriver/WebConnectionHtmlUnitDriverTests.java @@ -1,39 +1,36 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.test.web.servlet.htmlunit.webdriver; +import com.gargoylesoftware.htmlunit.WebConnection; +import com.gargoylesoftware.htmlunit.WebRequest; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; - import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; -import com.gargoylesoftware.htmlunit.WebConnection; -import com.gargoylesoftware.htmlunit.WebRequest; - import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.notNullValue; - import static org.mockito.Matchers.any; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; /** * Unit tests for {@link WebConnectionHtmlUnitDriver}. diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/FilterTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/FilterTests.java index cc04fb6be2..e122f71760 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/FilterTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/FilterTests.java @@ -1,14 +1,17 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.test.web.servlet.samples.standalone; diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/setup/ConditionalDelegatingFilterProxyTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/setup/ConditionalDelegatingFilterProxyTests.java index fa3857e860..6231898a53 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/setup/ConditionalDelegatingFilterProxyTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/setup/ConditionalDelegatingFilterProxyTests.java @@ -1,15 +1,19 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + package org.springframework.test.web.servlet.setup; import javax.servlet.Filter; diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/setup/DefaultMockMvcBuilderTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/setup/DefaultMockMvcBuilderTests.java index bf07bb9e3a..d30ad4180a 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/setup/DefaultMockMvcBuilderTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/setup/DefaultMockMvcBuilderTests.java @@ -1,15 +1,19 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + package org.springframework.test.web.servlet.setup; import org.junit.Rule; diff --git a/spring-tx/src/main/java/org/springframework/transaction/annotation/EnableTransactionManagement.java b/spring-tx/src/main/java/org/springframework/transaction/annotation/EnableTransactionManagement.java index 565e857f15..5e3e229463 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/annotation/EnableTransactionManagement.java +++ b/spring-tx/src/main/java/org/springframework/transaction/annotation/EnableTransactionManagement.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,13 +28,15 @@ /** * Enables Spring's annotation-driven transaction management capability, similar to - * the support found in Spring's {@code } XML namespace. To be used - * on @{@link org.springframework.context.annotation.Configuration Configuration} classes + * the support found in Spring's {@code } XML namespace. To be used on + * @{@link org.springframework.context.annotation.Configuration Configuration} classes * as follows: + * *

      * @Configuration
      * @EnableTransactionManagement
      * public class AppConfig {
    + *
      *     @Bean
      *     public FooRepository fooRepository() {
      *         // configure and return a class having @Transactional methods
    @@ -54,19 +56,26 @@
      *
      * 

    For reference, the example above can be compared to the following Spring XML * configuration: + * *

      * {@code
      * 
    + *
      *     
    + *
      *     
      *         
      *     
    + *
      *     
    + *
      *     
      *         
      *     
    + *
      * 
      * }
    + * * In both of the scenarios above, {@code @EnableTransactionManagement} and {@code * } are responsible for registering the necessary Spring * components that power annotation-driven transaction management, such as the @@ -87,10 +96,12 @@ * {@code @EnableTransactionManagement} and the exact transaction manager bean to be used, * the {@link TransactionManagementConfigurer} callback interface may be implemented - * notice the {@code implements} clause and the {@code @Override}-annotated method below: + * *
      * @Configuration
      * @EnableTransactionManagement
      * public class AppConfig implements TransactionManagementConfigurer {
    + *
      *     @Bean
      *     public FooRepository fooRepository() {
      *         // configure and return a class having @Transactional methods
    @@ -112,6 +123,7 @@
      *         return txManager();
      *     }
      * }
    + * * This approach may be desirable simply because it is more explicit, or it may be * necessary in order to distinguish between two {@code PlatformTransactionManager} beans * present in the same container. As the name suggests, the diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/EnableWebMvc.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/EnableWebMvc.java index 02cfa98b72..8de751cc15 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/EnableWebMvc.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/EnableWebMvc.java @@ -1,15 +1,19 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + package org.springframework.web.servlet.config.annotation; import java.lang.annotation.Documented; @@ -43,17 +47,17 @@ * @ComponentScan(basePackageClasses = { MyConfiguration.class }) * public class MyConfiguration extends WebMvcConfigurerAdapter { * - * @Override - * public void addFormatters(FormatterRegistry formatterRegistry) { - * formatterRegistry.addConverter(new MyConverter()); - * } + * @Override + * public void addFormatters(FormatterRegistry formatterRegistry) { + * formatterRegistry.addConverter(new MyConverter()); + * } * - * @Override - * public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { - * converters.add(new MyHttpMessageConverter()); - * } + * @Override + * public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { + * converters.add(new MyHttpMessageConverter()); + * } * - * // More overridden methods ... + * // More overridden methods ... * } *
    * @@ -67,16 +71,16 @@ * @ComponentScan(basePackageClasses = { MyConfiguration.class }) * public class MyConfiguration extends WebMvcConfigurationSupport { * - * @Override - * public void addFormatters(FormatterRegistry formatterRegistry) { - * formatterRegistry.addConverter(new MyConverter()); - * } + * @Override + * public void addFormatters(FormatterRegistry formatterRegistry) { + * formatterRegistry.addConverter(new MyConverter()); + * } * - * @Bean - * public RequestMappingHandlerAdapter requestMappingHandlerAdapter() { - * // Create or delegate to "super" to create and - * // customize properties of RequestMappingHandlerAdapter - * } + * @Bean + * public RequestMappingHandlerAdapter requestMappingHandlerAdapter() { + * // Create or delegate to "super" to create and + * // customize properties of RequestMappingHandlerAdapter + * } * } *
    * diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/EnableWebSocket.java b/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/EnableWebSocket.java index d695cfad8f..af8d056dfc 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/EnableWebSocket.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/EnableWebSocket.java @@ -1,14 +1,17 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.web.socket.config.annotation; @@ -32,6 +35,7 @@ * * } *
    + * *

    Customize the imported configuration by implementing the * {@link WebSocketConfigurer} interface: * @@ -40,15 +44,15 @@ * @EnableWebSocket * public class MyConfiguration implements WebSocketConfigurer { * - * @Override - * public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { - * registry.addHandler(echoWebSocketHandler(), "/echo").withSockJS(); - * } + * @Override + * public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { + * registry.addHandler(echoWebSocketHandler(), "/echo").withSockJS(); + * } * - * @Bean - * public WebSocketHandler echoWebSocketHandler() { - * return new EchoWebSocketHandler(); - * } + * @Bean + * public WebSocketHandler echoWebSocketHandler() { + * return new EchoWebSocketHandler(); + * } * } *

    * diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/EnableWebSocketMessageBroker.java b/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/EnableWebSocketMessageBroker.java index dfed7660b4..716489ee26 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/EnableWebSocketMessageBroker.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/EnableWebSocketMessageBroker.java @@ -1,14 +1,17 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.web.socket.config.annotation; @@ -32,8 +35,8 @@ * * } *
    - *

    - * Customize the imported configuration by implementing the + * + *

    Customize the imported configuration by implementing the * {@link WebSocketMessageBrokerConfigurer} interface or more likely extend the * convenient base class {@link AbstractWebSocketMessageBrokerConfigurer}: * @@ -42,16 +45,16 @@ * @EnableWebSocketMessageBroker * public class MyConfiguration extends AbstractWebSocketMessageBrokerConfigurer { * - * @Override - * public void registerStompEndpoints(StompEndpointRegistry registry) { - * registry.addEndpoint("/portfolio").withSockJS(); - * } + * @Override + * public void registerStompEndpoints(StompEndpointRegistry registry) { + * registry.addEndpoint("/portfolio").withSockJS(); + * } * - * @Bean - * public void configureMessageBroker(MessageBrokerRegistry registry) { - * registry.enableStompBrokerRelay("/queue/", "/topic/"); - * registry.setApplicationDestinationPrefixes("/app/"); - * } + * @Bean + * public void configureMessageBroker(MessageBrokerRegistry registry) { + * registry.enableStompBrokerRelay("/queue/", "/topic/"); + * registry.setApplicationDestinationPrefixes("/app/"); + * } * } * * From e5477ea19d856fb6f39ec4d71971e53e33ef7795 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Mon, 18 Jan 2016 14:14:17 +0100 Subject: [PATCH 114/344] Add interface-based detection test Add a test that explicitely validates that `@ManagedResource` and friends can be set on an interface. Issue: SPR-13138 (cherry picked from commit eb49f3c) --- .../annotation/AnotherAnnotationTestBean.java | 38 ++++++++++++++ .../AnotherAnnotationTestBeanImpl.java | 49 +++++++++++++++++++ .../jmx/export/annotation/annotations.xml | 20 +++----- 3 files changed, 95 insertions(+), 12 deletions(-) create mode 100644 spring-context/src/test/java/org/springframework/jmx/export/annotation/AnotherAnnotationTestBean.java create mode 100644 spring-context/src/test/java/org/springframework/jmx/export/annotation/AnotherAnnotationTestBeanImpl.java diff --git a/spring-context/src/test/java/org/springframework/jmx/export/annotation/AnotherAnnotationTestBean.java b/spring-context/src/test/java/org/springframework/jmx/export/annotation/AnotherAnnotationTestBean.java new file mode 100644 index 0000000000..59d084f414 --- /dev/null +++ b/spring-context/src/test/java/org/springframework/jmx/export/annotation/AnotherAnnotationTestBean.java @@ -0,0 +1,38 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.jmx.export.annotation; + +import org.springframework.jmx.support.MetricType; + +/** + * @author Stephane Nicoll + */ +@ManagedResource(objectName = "bean:name=interfaceTestBean", description = "My Managed Bean") +public interface AnotherAnnotationTestBean { + + @ManagedOperation(description = "invoke foo") + void foo(); + + @ManagedAttribute(description = "Bar description") + String getBar(); + + void setBar(String bar); + + @ManagedMetric(description = "a metric", metricType = MetricType.COUNTER) + int getCacheEntries(); + +} diff --git a/spring-context/src/test/java/org/springframework/jmx/export/annotation/AnotherAnnotationTestBeanImpl.java b/spring-context/src/test/java/org/springframework/jmx/export/annotation/AnotherAnnotationTestBeanImpl.java new file mode 100644 index 0000000000..1acaefb032 --- /dev/null +++ b/spring-context/src/test/java/org/springframework/jmx/export/annotation/AnotherAnnotationTestBeanImpl.java @@ -0,0 +1,49 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.jmx.export.annotation; + +/** + * @author Stephane Nicoll + */ +class AnotherAnnotationTestBeanImpl implements AnotherAnnotationTestBean { + + private String bar; + + @Override + public void foo() { + } + + public void doNotExpose() { + + } + + @Override + public String getBar() { + return this.bar; + } + + @Override + public void setBar(String bar) { + this.bar = bar; + } + + @Override + public int getCacheEntries() { + return 42; + } + +} diff --git a/spring-context/src/test/resources/org/springframework/jmx/export/annotation/annotations.xml b/spring-context/src/test/resources/org/springframework/jmx/export/annotation/annotations.xml index c9c15cf771..0124173f05 100644 --- a/spring-context/src/test/resources/org/springframework/jmx/export/annotation/annotations.xml +++ b/spring-context/src/test/resources/org/springframework/jmx/export/annotation/annotations.xml @@ -20,26 +20,22 @@ - - TEST - - - 100 - + + + + + + - - - + - - - + From e9382b2c0bb7584fe200257e21b628e6fd7a2d42 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 12 Apr 2016 16:02:36 +0200 Subject: [PATCH 115/344] SimpleTransactionScope properly suspends and resumes scoped objects Issue: SPR-14148 (cherry picked from commit 831f09c) --- .../support/SimpleTransactionScope.java | 27 +++++-- .../support/SimpleTransactionScopeTests.java | 73 ++++++++++++++++++- 2 files changed, 93 insertions(+), 7 deletions(-) diff --git a/spring-tx/src/main/java/org/springframework/transaction/support/SimpleTransactionScope.java b/spring-tx/src/main/java/org/springframework/transaction/support/SimpleTransactionScope.java index 60ad8426a1..84a6668c75 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/support/SimpleTransactionScope.java +++ b/spring-tx/src/main/java/org/springframework/transaction/support/SimpleTransactionScope.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -46,7 +46,7 @@ public Object get(String name, ObjectFactory objectFactory) { ScopedObjectsHolder scopedObjects = (ScopedObjectsHolder) TransactionSynchronizationManager.getResource(this); if (scopedObjects == null) { scopedObjects = new ScopedObjectsHolder(); - TransactionSynchronizationManager.registerSynchronization(new CleanupSynchronization()); + TransactionSynchronizationManager.registerSynchronization(new CleanupSynchronization(scopedObjects)); TransactionSynchronizationManager.bindResource(this, scopedObjects); } Object scopedObject = scopedObjects.scopedInstances.get(name); @@ -98,13 +98,30 @@ static class ScopedObjectsHolder { private class CleanupSynchronization extends TransactionSynchronizationAdapter { + private final ScopedObjectsHolder scopedObjects; + + public CleanupSynchronization(ScopedObjectsHolder scopedObjects) { + this.scopedObjects = scopedObjects; + } + + @Override + public void suspend() { + TransactionSynchronizationManager.unbindResource(SimpleTransactionScope.this); + } + + @Override + public void resume() { + TransactionSynchronizationManager.bindResource(SimpleTransactionScope.this, this.scopedObjects); + } + @Override public void afterCompletion(int status) { - ScopedObjectsHolder scopedObjects = (ScopedObjectsHolder) - TransactionSynchronizationManager.unbindResourceIfPossible(SimpleTransactionScope.this); - for (Runnable callback : scopedObjects.destructionCallbacks.values()) { + TransactionSynchronizationManager.unbindResourceIfPossible(SimpleTransactionScope.this); + for (Runnable callback : this.scopedObjects.destructionCallbacks.values()) { callback.run(); } + this.scopedObjects.destructionCallbacks.clear(); + this.scopedObjects.scopedInstances.clear(); } } diff --git a/spring-tx/src/test/java/org/springframework/transaction/support/SimpleTransactionScopeTests.java b/spring-tx/src/test/java/org/springframework/transaction/support/SimpleTransactionScopeTests.java index c8a16a41c4..8d7f3f37f2 100644 --- a/spring-tx/src/test/java/org/springframework/transaction/support/SimpleTransactionScopeTests.java +++ b/spring-tx/src/test/java/org/springframework/transaction/support/SimpleTransactionScopeTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,9 @@ package org.springframework.transaction.support; +import java.util.HashSet; +import java.util.Set; + import org.junit.Test; import org.springframework.beans.factory.BeanCreationException; @@ -23,6 +26,7 @@ import org.springframework.context.support.GenericApplicationContext; import org.springframework.tests.sample.beans.DerivedTestBean; import org.springframework.tests.sample.beans.TestBean; +import org.springframework.tests.transaction.CallCountingTransactionManager; import static org.junit.Assert.*; @@ -93,7 +97,6 @@ public void getFromScope() throws Exception { bean2b = context.getBean(DerivedTestBean.class); assertSame(bean2b, context.getBean(DerivedTestBean.class)); - assertNotSame(bean2, bean2a); assertNotSame(bean2, bean2b); assertNotSame(bean2a, bean2b); } @@ -125,4 +128,70 @@ public void getFromScope() throws Exception { } } + @Test + public void getWithTransactionManager() throws Exception { + GenericApplicationContext context = new GenericApplicationContext(); + context.getBeanFactory().registerScope("tx", new SimpleTransactionScope()); + + GenericBeanDefinition bd1 = new GenericBeanDefinition(); + bd1.setBeanClass(TestBean.class); + bd1.setScope("tx"); + bd1.setPrimary(true); + context.registerBeanDefinition("txScopedObject1", bd1); + + GenericBeanDefinition bd2 = new GenericBeanDefinition(); + bd2.setBeanClass(DerivedTestBean.class); + bd2.setScope("tx"); + context.registerBeanDefinition("txScopedObject2", bd2); + + context.refresh(); + + CallCountingTransactionManager tm = new CallCountingTransactionManager(); + TransactionTemplate tt = new TransactionTemplate(tm); + Set finallyDestroy = new HashSet(); + + tt.execute(status -> { + TestBean bean1 = context.getBean(TestBean.class); + assertSame(bean1, context.getBean(TestBean.class)); + + DerivedTestBean bean2 = context.getBean(DerivedTestBean.class); + assertSame(bean2, context.getBean(DerivedTestBean.class)); + context.getBeanFactory().destroyScopedBean("txScopedObject2"); + assertFalse(TransactionSynchronizationManager.hasResource("txScopedObject2")); + assertTrue(bean2.wasDestroyed()); + + DerivedTestBean bean2a = context.getBean(DerivedTestBean.class); + assertSame(bean2a, context.getBean(DerivedTestBean.class)); + assertNotSame(bean2, bean2a); + context.getBeanFactory().getRegisteredScope("tx").remove("txScopedObject2"); + assertFalse(TransactionSynchronizationManager.hasResource("txScopedObject2")); + assertFalse(bean2a.wasDestroyed()); + + DerivedTestBean bean2b = context.getBean(DerivedTestBean.class); + finallyDestroy.add(bean2b); + assertSame(bean2b, context.getBean(DerivedTestBean.class)); + assertNotSame(bean2, bean2b); + assertNotSame(bean2a, bean2b); + + Set immediatelyDestroy = new HashSet(); + TransactionTemplate tt2 = new TransactionTemplate(tm); + tt2.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED); + tt2.execute(status2 -> { + DerivedTestBean bean2c = context.getBean(DerivedTestBean.class); + immediatelyDestroy.add(bean2c); + assertSame(bean2c, context.getBean(DerivedTestBean.class)); + assertNotSame(bean2, bean2c); + assertNotSame(bean2a, bean2c); + assertNotSame(bean2b, bean2c); + return null; + }); + assertTrue(immediatelyDestroy.iterator().next().wasDestroyed()); + assertFalse(bean2b.wasDestroyed()); + + return null; + }); + + assertTrue(finallyDestroy.iterator().next().wasDestroyed()); + } + } From 3829a778947857f0de2a466413d3a36e0847ea16 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 12 Apr 2016 16:03:57 +0200 Subject: [PATCH 116/344] Polishing (cherry picked from commit 74608e6) --- .../annotation/AnnotationAttributesTests.java | 40 +++++++++++-------- .../jdbc/core/ConnectionCallback.java | 5 +-- .../jdbc/core/PreparedStatementCallback.java | 6 +-- .../jdbc/core/StatementCallback.java | 6 +-- .../jdbc/support/lob/OracleLobHandler.java | 4 +- .../orm/hibernate4/HibernateCallback.java | 4 +- .../orm/hibernate5/HibernateCallback.java | 4 +- .../orm/hibernate3/HibernateCallback.java | 5 +-- .../jca/cci/core/ConnectionCallback.java | 5 +-- .../jca/cci/core/InteractionCallback.java | 5 +-- .../support/TransactionCallback.java | 21 +++++----- 11 files changed, 42 insertions(+), 63 deletions(-) diff --git a/spring-core/src/test/java/org/springframework/core/annotation/AnnotationAttributesTests.java b/spring-core/src/test/java/org/springframework/core/annotation/AnnotationAttributesTests.java index cb7af8502f..57cdd7ab2e 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/AnnotationAttributesTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/AnnotationAttributesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -54,23 +54,23 @@ public void typeSafeAttributeAccess() { nestedAttributes.put("name", "algernon"); attributes.put("name", "dave"); - attributes.put("names", new String[] { "dave", "frank", "hal" }); + attributes.put("names", new String[] {"dave", "frank", "hal"}); attributes.put("bool1", true); attributes.put("bool2", false); attributes.put("color", Color.RED); attributes.put("class", Integer.class); - attributes.put("classes", new Class[] { Number.class, Short.class, Integer.class }); + attributes.put("classes", new Class[] {Number.class, Short.class, Integer.class}); attributes.put("number", 42); attributes.put("anno", nestedAttributes); - attributes.put("annoArray", new AnnotationAttributes[] { nestedAttributes }); + attributes.put("annoArray", new AnnotationAttributes[] {nestedAttributes}); assertThat(attributes.getString("name"), equalTo("dave")); - assertThat(attributes.getStringArray("names"), equalTo(new String[] { "dave", "frank", "hal" })); + assertThat(attributes.getStringArray("names"), equalTo(new String[] {"dave", "frank", "hal"})); assertThat(attributes.getBoolean("bool1"), equalTo(true)); assertThat(attributes.getBoolean("bool2"), equalTo(false)); assertThat(attributes.getEnum("color"), equalTo(Color.RED)); assertTrue(attributes.getClass("class").equals(Integer.class)); - assertThat(attributes.getClassArray("classes"), equalTo(new Class[] { Number.class, Short.class, Integer.class })); + assertThat(attributes.getClassArray("classes"), equalTo(new Class[] {Number.class, Short.class, Integer.class})); assertThat(attributes.getNumber("number"), equalTo(42)); assertThat(attributes.getAnnotation("anno").getNumber("value"), equalTo(10)); assertThat(attributes.getAnnotationArray("annoArray")[0].getString("name"), equalTo("algernon")); @@ -99,8 +99,8 @@ public void singleElementToSingleElementArrayConversionSupport() throws Exceptio attributes.put("filters", filter); // Get back arrays of single elements - assertThat(attributes.getStringArray("names"), equalTo(new String[] { "Dogbert" })); - assertThat(attributes.getClassArray("classes"), equalTo(new Class[] { Number.class })); + assertThat(attributes.getStringArray("names"), equalTo(new String[] {"Dogbert"})); + assertThat(attributes.getClassArray("classes"), equalTo(new Class[] {Number.class})); AnnotationAttributes[] array = attributes.getAnnotationArray("nestedAttributes"); assertNotNull(array); @@ -118,7 +118,7 @@ public void nestedAnnotations() throws Exception { Filter filter = FilteredClass.class.getAnnotation(Filter.class); attributes.put("filter", filter); - attributes.put("filters", new Filter[] { filter, filter }); + attributes.put("filters", new Filter[] {filter, filter}); Filter retrievedFilter = attributes.getAnnotation("filter", Filter.class); assertThat(retrievedFilter, equalTo(filter)); @@ -257,7 +257,7 @@ private String getAliasedStringWithImplicitAliases(String attributeName) { @Test public void getAliasedStringArray() { - final String[] INPUT = new String[] { "test.xml" }; + final String[] INPUT = new String[] {"test.xml"}; final String[] EMPTY = new String[0]; attributes.clear(); @@ -297,7 +297,7 @@ public void getAliasedStringArray() { @Test public void getAliasedStringArrayWithImplicitAliases() { - final String[] INPUT = new String[] { "test.xml" }; + final String[] INPUT = new String[] {"test.xml"}; final String[] EMPTY = new String[0]; final List aliases = Arrays.asList("value", "location1", "location2", "location3", "xmlFile", "groovyScript"); @@ -352,8 +352,8 @@ public void getAliasedStringArrayWithMissingAliasedAttributes() { @Test public void getAliasedStringArrayWithDifferentAliasedValues() { - attributes.put("location", new String[] { "1.xml" }); - attributes.put("value", new String[] { "2.xml" }); + attributes.put("location", new String[] {"1.xml"}); + attributes.put("value", new String[] {"2.xml"}); exception.expect(AnnotationConfigurationException.class); exception.expectMessage(containsString("In annotation [" + ContextConfig.class.getName() + "]")); @@ -382,7 +382,7 @@ private String[] getAliasedStringArrayWithImplicitAliases(String attributeName) @Test public void getAliasedClassArray() { - final Class[] INPUT = new Class[] { String.class }; + final Class[] INPUT = new Class[] {String.class}; final Class[] EMPTY = new Class[0]; attributes.clear(); @@ -422,7 +422,7 @@ public void getAliasedClassArray() { @Test public void getAliasedClassArrayWithImplicitAliases() { - final Class[] INPUT = new Class[] { String.class }; + final Class[] INPUT = new Class[] {String.class}; final Class[] EMPTY = new Class[0]; final List aliases = Arrays.asList("value", "location1", "location2", "location3", "xmlFile", "groovyScript"); @@ -465,8 +465,8 @@ public void getAliasedClassArrayWithMissingAliasedAttributes() { @Test public void getAliasedClassArrayWithDifferentAliasedValues() { - attributes.put("classes", new Class[] { String.class }); - attributes.put("value", new Class[] { Number.class }); + attributes.put("classes", new Class[] {String.class}); + attributes.put("value", new Class[] {Number.class}); exception.expect(AnnotationConfigurationException.class); exception.expectMessage(containsString("In annotation [" + Filter.class.getName() + "]")); @@ -477,6 +477,7 @@ public void getAliasedClassArrayWithDifferentAliasedValues() { getAliasedClassArray("classes"); } + private Class[] getAliasedClassArray(String attributeName) { return attributes.getAliasedClassArray(attributeName, Filter.class, null); } @@ -491,9 +492,11 @@ private Class[] getAliasedClassArrayWithImplicitAliases(String attributeName) enum Color { + RED, WHITE, BLUE } + @Retention(RetentionPolicy.RUNTIME) @interface Filter { @@ -506,10 +509,12 @@ enum Color { String pattern(); } + @Filter(pattern = "foo") static class FilteredClass { } + /** * Mock of {@code org.springframework.context.annotation.Scope}. */ @@ -523,6 +528,7 @@ static class FilteredClass { String name() default "singleton"; } + @Scope(name = "custom") static class ScopedComponent { } diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/ConnectionCallback.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/ConnectionCallback.java index 04ae1ccacc..55a63039c3 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/ConnectionCallback.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/ConnectionCallback.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,19 +43,16 @@ public interface ConnectionCallback { * Gets called by {@code JdbcTemplate.execute} with an active JDBC * Connection. Does not need to care about activating or closing the * Connection, or handling transactions. - * *

    If called without a thread-bound JDBC transaction (initiated by * DataSourceTransactionManager), the code will simply get executed on the * JDBC connection with its transactional semantics. If JdbcTemplate is * configured to use a JTA-aware DataSource, the JDBC Connection and thus * the callback code will be transactional if a JTA transaction is active. - * *

    Allows for returning a result object created within the callback, i.e. * a domain object or a collection of domain objects. Note that there's special * support for single step actions: see {@code JdbcTemplate.queryForObject} * etc. A thrown RuntimeException is treated as application exception: * it gets propagated to the caller of the template. - * * @param con active JDBC Connection * @return a result object, or {@code null} if none * @throws SQLException if thrown by a JDBC method, to be auto-converted diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/PreparedStatementCallback.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/PreparedStatementCallback.java index bd82ddf9e9..9c2cd0e9be 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/PreparedStatementCallback.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/PreparedStatementCallback.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,26 +47,22 @@ public interface PreparedStatementCallback { * PreparedStatement. Does not need to care about closing the Statement * or the Connection, or about handling transactions: this will all be * handled by Spring's JdbcTemplate. - * *

    NOTE: Any ResultSets opened should be closed in finally blocks * within the callback implementation. Spring will close the Statement * object after the callback returned, but this does not necessarily imply * that the ResultSet resources will be closed: the Statement objects might * get pooled by the connection pool, with {@code close} calls only * returning the object to the pool but not physically closing the resources. - * *

    If called without a thread-bound JDBC transaction (initiated by * DataSourceTransactionManager), the code will simply get executed on the * JDBC connection with its transactional semantics. If JdbcTemplate is * configured to use a JTA-aware DataSource, the JDBC connection and thus * the callback code will be transactional if a JTA transaction is active. - * *

    Allows for returning a result object created within the callback, i.e. * a domain object or a collection of domain objects. Note that there's * special support for single step actions: see JdbcTemplate.queryForObject etc. * A thrown RuntimeException is treated as application exception, it gets * propagated to the caller of the template. - * * @param ps active JDBC PreparedStatement * @return a result object, or {@code null} if none * @throws SQLException if thrown by a JDBC method, to be auto-converted diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/StatementCallback.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/StatementCallback.java index c786232ca9..3edd3c2b67 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/StatementCallback.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/StatementCallback.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,26 +40,22 @@ public interface StatementCallback { * Statement. Does not need to care about closing the Statement or the * Connection, or about handling transactions: this will all be handled * by Spring's JdbcTemplate. - * *

    NOTE: Any ResultSets opened should be closed in finally blocks * within the callback implementation. Spring will close the Statement * object after the callback returned, but this does not necessarily imply * that the ResultSet resources will be closed: the Statement objects might * get pooled by the connection pool, with {@code close} calls only * returning the object to the pool but not physically closing the resources. - * *

    If called without a thread-bound JDBC transaction (initiated by * DataSourceTransactionManager), the code will simply get executed on the * JDBC connection with its transactional semantics. If JdbcTemplate is * configured to use a JTA-aware DataSource, the JDBC connection and thus * the callback code will be transactional if a JTA transaction is active. - * *

    Allows for returning a result object created within the callback, i.e. * a domain object or a collection of domain objects. Note that there's * special support for single step actions: see JdbcTemplate.queryForObject etc. * A thrown RuntimeException is treated as application exception, it gets * propagated to the caller of the template. - * * @param stmt active JDBC Statement * @return a result object, or {@code null} if none * @throws SQLException if thrown by a JDBC method, to be auto-converted diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/support/lob/OracleLobHandler.java b/spring-jdbc/src/main/java/org/springframework/jdbc/support/lob/OracleLobHandler.java index 0861b92d22..df836abf1c 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/support/lob/OracleLobHandler.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/support/lob/OracleLobHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -613,7 +613,7 @@ public void close() { /** * Internal callback interface for use with createLob. */ - protected static interface LobCallback { + protected interface LobCallback { /** * Populate the given BLOB or CLOB instance with content. diff --git a/spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/HibernateCallback.java b/spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/HibernateCallback.java index a6cca5ab8e..66f78d6678 100644 --- a/spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/HibernateCallback.java +++ b/spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/HibernateCallback.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,12 +36,10 @@ public interface HibernateCallback { * Gets called by {@code HibernateTemplate.execute} with an active * Hibernate {@code Session}. Does not need to care about activating * or closing the {@code Session}, or handling transactions. - * *

    Allows for returning a result object created within the callback, * i.e. a domain object or a collection of domain objects. * A thrown custom RuntimeException is treated as an application exception: * It gets propagated to the caller of the template. - * * @param session active Hibernate session * @return a result object, or {@code null} if none * @throws HibernateException if thrown by the Hibernate API diff --git a/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateCallback.java b/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateCallback.java index 78074360ca..54cee88152 100644 --- a/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateCallback.java +++ b/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateCallback.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,12 +36,10 @@ public interface HibernateCallback { * Gets called by {@code HibernateTemplate.execute} with an active * Hibernate {@code Session}. Does not need to care about activating * or closing the {@code Session}, or handling transactions. - * *

    Allows for returning a result object created within the callback, * i.e. a domain object or a collection of domain objects. * A thrown custom RuntimeException is treated as an application exception: * It gets propagated to the caller of the template. - * * @param session active Hibernate session * @return a result object, or {@code null} if none * @throws HibernateException if thrown by the Hibernate API diff --git a/spring-orm/src/main/java/org/springframework/orm/hibernate3/HibernateCallback.java b/spring-orm/src/main/java/org/springframework/orm/hibernate3/HibernateCallback.java index 8bf4dbeffe..b0545f15ff 100644 --- a/spring-orm/src/main/java/org/springframework/orm/hibernate3/HibernateCallback.java +++ b/spring-orm/src/main/java/org/springframework/orm/hibernate3/HibernateCallback.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,18 +47,15 @@ public interface HibernateCallback { * Gets called by {@code HibernateTemplate.execute} with an active * Hibernate {@code Session}. Does not need to care about activating * or closing the {@code Session}, or handling transactions. - * *

    If called without a thread-bound Hibernate transaction (initiated * by HibernateTransactionManager), the code will simply get executed on the * underlying JDBC connection with its transactional semantics. If Hibernate * is configured to use a JTA-aware DataSource, the JDBC connection and thus * the callback code will be transactional if a JTA transaction is active. - * *

    Allows for returning a result object created within the callback, * i.e. a domain object or a collection of domain objects. * A thrown custom RuntimeException is treated as an application exception: * It gets propagated to the caller of the template. - * * @param session active Hibernate session * @return a result object, or {@code null} if none * @throws HibernateException if thrown by the Hibernate API diff --git a/spring-tx/src/main/java/org/springframework/jca/cci/core/ConnectionCallback.java b/spring-tx/src/main/java/org/springframework/jca/cci/core/ConnectionCallback.java index d6dad2a2e4..3be2710f15 100644 --- a/spring-tx/src/main/java/org/springframework/jca/cci/core/ConnectionCallback.java +++ b/spring-tx/src/main/java/org/springframework/jca/cci/core/ConnectionCallback.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -46,19 +46,16 @@ public interface ConnectionCallback { * Gets called by {@code CciTemplate.execute} with an active CCI Connection. * Does not need to care about activating or closing the Connection, or handling * transactions. - * *

    If called without a thread-bound CCI transaction (initiated by * CciLocalTransactionManager), the code will simply get executed on the CCI * Connection with its transactional semantics. If CciTemplate is configured * to use a JTA-aware ConnectionFactory, the CCI Connection and thus the callback * code will be transactional if a JTA transaction is active. - * *

    Allows for returning a result object created within the callback, i.e. * a domain object or a collection of domain objects. Note that there's special * support for single step actions: see the {@code CciTemplate.execute} * variants. A thrown RuntimeException is treated as application exception: * it gets propagated to the caller of the template. - * * @param connection active CCI Connection * @param connectionFactory the CCI ConnectionFactory that the Connection was * created with (gives access to RecordFactory and ResourceAdapterMetaData) diff --git a/spring-tx/src/main/java/org/springframework/jca/cci/core/InteractionCallback.java b/spring-tx/src/main/java/org/springframework/jca/cci/core/InteractionCallback.java index ace6ad0989..38db29348d 100644 --- a/spring-tx/src/main/java/org/springframework/jca/cci/core/InteractionCallback.java +++ b/spring-tx/src/main/java/org/springframework/jca/cci/core/InteractionCallback.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,19 +47,16 @@ public interface InteractionCallback { * Gets called by {@code CciTemplate.execute} with an active CCI Interaction. * Does not need to care about activating or closing the Interaction, or * handling transactions. - * *

    If called without a thread-bound CCI transaction (initiated by * CciLocalTransactionManager), the code will simply get executed on the CCI * Interaction with its transactional semantics. If CciTemplate is configured * to use a JTA-aware ConnectionFactory, the CCI Interaction and thus the callback * code will be transactional if a JTA transaction is active. - * *

    Allows for returning a result object created within the callback, i.e. * a domain object or a collection of domain objects. Note that there's special * support for single step actions: see the {@code CciTemplate.execute} * variants. A thrown RuntimeException is treated as application exception: * it gets propagated to the caller of the template. - * * @param interaction active CCI Interaction * @param connectionFactory the CCI ConnectionFactory that the Connection was * created with (gives access to RecordFactory and ResourceAdapterMetaData) diff --git a/spring-tx/src/main/java/org/springframework/transaction/support/TransactionCallback.java b/spring-tx/src/main/java/org/springframework/transaction/support/TransactionCallback.java index 98841f3cbe..80e8bde72a 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/support/TransactionCallback.java +++ b/spring-tx/src/main/java/org/springframework/transaction/support/TransactionCallback.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,17 +36,14 @@ public interface TransactionCallback { /** * Gets called by {@link TransactionTemplate#execute} within a transactional context. - * Does not need to care about transactions itself, although it can retrieve - * and influence the status of the current transaction via the given status - * object, e.g. setting rollback-only. - * - *

    Allows for returning a result object created within the transaction, i.e. - * a domain object or a collection of domain objects. A RuntimeException thrown - * by the callback is treated as application exception that enforces a rollback. - * Any such exception will be propagated to the caller of the template, unless - * there is a problem rolling back, in which case a TransactionException will be - * thrown. - * + * Does not need to care about transactions itself, although it can retrieve and + * influence the status of the current transaction via the given status object, + * e.g. setting rollback-only. + *

    Allows for returning a result object created within the transaction, i.e. a + * domain object or a collection of domain objects. A RuntimeException thrown by the + * callback is treated as application exception that enforces a rollback. Any such + * exception will be propagated to the caller of the template, unless there is a + * problem rolling back, in which case a TransactionException will be thrown. * @param status associated transaction status * @return a result object, or {@code null} * @see TransactionTemplate#execute From 33dcef358305865b54e40e48199174ce34081034 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 12 Apr 2016 23:19:51 +0200 Subject: [PATCH 117/344] SpringCacheAnnotationParser properly accepts empty @Caching annotation Issue: SPR-14162 (cherry picked from commit da11261) --- .../SpringCacheAnnotationParser.java | 7 +- .../AnnotationCacheOperationSourceTests.java | 95 +++++++++---------- 2 files changed, 50 insertions(+), 52 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/cache/annotation/SpringCacheAnnotationParser.java b/spring-context/src/main/java/org/springframework/cache/annotation/SpringCacheAnnotationParser.java index 3fd43c091d..d6992bd263 100644 --- a/spring-context/src/main/java/org/springframework/cache/annotation/SpringCacheAnnotationParser.java +++ b/spring-context/src/main/java/org/springframework/cache/annotation/SpringCacheAnnotationParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -86,7 +86,10 @@ protected Collection parseCacheAnnotations(DefaultCacheConfig ca if (cachings != null) { ops = lazyInit(ops); for (Caching caching : cachings) { - ops.addAll(parseCachingAnnotation(ae, cachingConfig, caching)); + Collection cachingOps = parseCachingAnnotation(ae, cachingConfig, caching); + if (cachingOps != null) { + ops.addAll(cachingOps); + } } } diff --git a/spring-context/src/test/java/org/springframework/cache/annotation/AnnotationCacheOperationSourceTests.java b/spring-context/src/test/java/org/springframework/cache/annotation/AnnotationCacheOperationSourceTests.java index 35091c83a3..42430474c8 100644 --- a/spring-context/src/test/java/org/springframework/cache/annotation/AnnotationCacheOperationSourceTests.java +++ b/spring-context/src/test/java/org/springframework/cache/annotation/AnnotationCacheOperationSourceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,10 +23,8 @@ import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.Iterator; -import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -37,7 +35,6 @@ import org.springframework.core.annotation.AliasFor; import org.springframework.util.ReflectionUtils; -import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; /** @@ -50,7 +47,7 @@ public class AnnotationCacheOperationSourceTests { @Rule public final ExpectedException exception = ExpectedException.none(); - private AnnotationCacheOperationSource source = new AnnotationCacheOperationSource(); + private final AnnotationCacheOperationSource source = new AnnotationCacheOperationSource(); private Collection getOps(Class target, String name, int expectedNumberOfOperations) { @@ -86,6 +83,11 @@ public void caching() throws Exception { assertTrue(it.next() instanceof CacheEvictOperation); } + @Test + public void emptyCaching() throws Exception { + Collection ops = getOps(AnnotatedClass.class, "emptyCaching", 0); + } + @Test public void singularStereotype() throws Exception { Collection ops = getOps(AnnotatedClass.class, "singleStereotype", 1); @@ -105,36 +107,6 @@ public void multipleStereotypes() throws Exception { assertTrue(next.getCacheNames().contains("bar")); } - // TODO [SPR-13475] Enable test once @Cache* is supported as a composed annotation. - @Ignore("Disabled until SPR-13475 is resolved") - @Test - public void singleComposedAnnotation() throws Exception { - Collection ops = getOps(AnnotatedClass.class, "singleComposed", 1); - CacheOperation cacheOperation = ops.iterator().next(); - assertThat(cacheOperation, instanceOf(CacheableOperation.class)); - assertThat(cacheOperation.getCacheNames(), equalTo(Collections.singleton("composed"))); - } - - // TODO [SPR-13475] Enable test once @Cache* is supported as a composed annotation. - @Ignore("Disabled until SPR-13475 is resolved") - @Test - public void multipleComposedAnnotations() throws Exception { - Collection ops = getOps(AnnotatedClass.class, "multipleComposed", 3); - Iterator it = ops.iterator(); - - CacheOperation cacheOperation = it.next(); - assertThat(cacheOperation, instanceOf(CacheableOperation.class)); - assertThat(cacheOperation.getCacheNames(), equalTo(Collections.singleton("composedCache"))); - - cacheOperation = it.next(); - assertThat(cacheOperation, instanceOf(CacheableOperation.class)); - assertThat(cacheOperation.getCacheNames(), equalTo(Collections.singleton("foo"))); - - cacheOperation = it.next(); - assertThat(cacheOperation, instanceOf(CacheEvictOperation.class)); - assertThat(cacheOperation.getCacheNames(), equalTo(Collections.singleton("composedCache"))); - } - @Test public void customKeyGenerator() { Collection ops = getOps(AnnotatedClass.class, "customKeyGenerator", 1); @@ -261,17 +233,20 @@ public void partialClassLevelWithNoCustomization() { assertSharedConfig(cacheOperation, "classKeyGenerator", "classCacheManager", "", "classCacheName"); } + private void assertSharedConfig(CacheOperation actual, String keyGenerator, String cacheManager, - String cacheResolver, String... cacheNames) { + String cacheResolver, String... cacheNames) { + assertEquals("Wrong key manager", keyGenerator, actual.getKeyGenerator()); assertEquals("Wrong cache manager", cacheManager, actual.getCacheManager()); assertEquals("Wrong cache resolver", cacheResolver, actual.getCacheResolver()); assertEquals("Wrong number of cache names", cacheNames.length, actual.getCacheNames().size()); Arrays.stream(cacheNames).forEach( - cacheName -> assertTrue("Cache '" + cacheName + "' not found in " + actual.getCacheNames(), - actual.getCacheNames().contains(cacheName))); + cacheName -> assertTrue("Cache '" + cacheName + "' not found in " + actual.getCacheNames(), + actual.getCacheNames().contains(cacheName))); } + private static class AnnotatedClass { @Cacheable("test") @@ -287,6 +262,10 @@ public void multiple() { public void caching() { } + @Caching + public void emptyCaching() { + } + @Cacheable(cacheNames = "test", keyGenerator = "custom") public void customKeyGenerator() { } @@ -309,13 +288,15 @@ public void singleStereotype() { public void multipleStereotype() { } - @ComposedCacheable("composed") + @Cacheable("directly declared") + @ComposedCacheable(cacheNames = "composedCache", key = "composedKey") public void singleComposed() { } + @Cacheable("directly declared") @ComposedCacheable(cacheNames = "composedCache", key = "composedKey") @CacheableFoo - @ComposedCacheEvict(cacheNames = "composedCache", key = "composedKey") + @ComposedCacheEvict(cacheNames = "composedCacheEvict", key = "composedEvictionKey") public void multipleComposed() { } @@ -348,6 +329,7 @@ public void noCacheNameSpecified() { } } + @CacheConfig(cacheNames = "classCacheName", keyGenerator = "classKeyGenerator", cacheManager = "classCacheManager", cacheResolver = "classCacheResolver") @@ -370,6 +352,7 @@ public void methodLevelCacheResolver() { } } + @CacheConfigFoo private static class AnnotatedClassWithCustomDefault { @@ -378,6 +361,7 @@ public void methodLevelCacheName() { } } + @CacheConfig(cacheNames = "classCacheName", keyGenerator = "classKeyGenerator", cacheManager = "classCacheManager") @@ -396,6 +380,7 @@ public void noCustomization() { } } + @CacheConfigFoo @CacheConfig(cacheNames = "myCache") // multiple sources private static class MultipleCacheConfig { @@ -405,77 +390,87 @@ public void multipleCacheConfig() { } } + @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Cacheable("foo") public @interface CacheableFoo { } + @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Cacheable(cacheNames = "foo", keyGenerator = "custom") public @interface CacheableFooCustomKeyGenerator { } + @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Cacheable(cacheNames = "foo", cacheManager = "custom") public @interface CacheableFooCustomCacheManager { } + @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Cacheable(cacheNames = "foo", cacheResolver = "custom") public @interface CacheableFooCustomCacheResolver { } + @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @CacheEvict("foo") public @interface EvictFoo { } + @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @CacheEvict("bar") public @interface EvictBar { } + @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @CacheConfig(keyGenerator = "classKeyGenerator", - cacheManager = "classCacheManager", cacheResolver = "classCacheResolver") + cacheManager = "classCacheManager", + cacheResolver = "classCacheResolver") public @interface CacheConfigFoo { } + @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD, ElementType.TYPE }) @Cacheable(cacheNames = "shadowed cache name", key = "shadowed key") - public @interface ComposedCacheable { + @interface ComposedCacheable { - @AliasFor(annotation = Cacheable.class, attribute = "cacheNames") + @AliasFor(annotation = Cacheable.class) String[] value() default {}; - @AliasFor(annotation = Cacheable.class, attribute = "cacheNames") + @AliasFor(annotation = Cacheable.class) String[] cacheNames() default {}; - @AliasFor(annotation = Cacheable.class, attribute = "key") + @AliasFor(annotation = Cacheable.class) String key() default ""; } + @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD, ElementType.TYPE }) @CacheEvict(cacheNames = "shadowed cache name", key = "shadowed key") - public @interface ComposedCacheEvict { + @interface ComposedCacheEvict { - @AliasFor(annotation = Cacheable.class, attribute = "cacheNames") + @AliasFor(annotation = CacheEvict.class) String[] value() default {}; - @AliasFor(annotation = Cacheable.class, attribute = "cacheNames") + @AliasFor(annotation = CacheEvict.class) String[] cacheNames() default {}; - @AliasFor(annotation = Cacheable.class, attribute = "key") + @AliasFor(annotation = CacheEvict.class) String key() default ""; } -} \ No newline at end of file +} From b1c70729c2c6830cb27794713fc82c086dc465e7 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 12 Apr 2016 23:33:58 +0200 Subject: [PATCH 118/344] Polishing --- .../ReflectiveLoadTimeWeaver.java | 7 ++- .../WebSphereClassLoaderAdapter.java | 45 +++++++++++-------- .../core/OverridingClassLoader.java | 19 ++++---- .../ClassFileTransformerAdapter.java | 19 ++++---- 4 files changed, 49 insertions(+), 41 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/instrument/classloading/ReflectiveLoadTimeWeaver.java b/spring-context/src/main/java/org/springframework/instrument/classloading/ReflectiveLoadTimeWeaver.java index 45b6836fd3..f4fa2cc642 100644 --- a/spring-context/src/main/java/org/springframework/instrument/classloading/ReflectiveLoadTimeWeaver.java +++ b/spring-context/src/main/java/org/springframework/instrument/classloading/ReflectiveLoadTimeWeaver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -97,15 +97,14 @@ public ReflectiveLoadTimeWeaver(ClassLoader classLoader) { Assert.notNull(classLoader, "ClassLoader must not be null"); this.classLoader = classLoader; this.addTransformerMethod = ClassUtils.getMethodIfAvailable( - this.classLoader.getClass(), ADD_TRANSFORMER_METHOD_NAME, - new Class[] {ClassFileTransformer.class}); + this.classLoader.getClass(), ADD_TRANSFORMER_METHOD_NAME, ClassFileTransformer.class); if (this.addTransformerMethod == null) { throw new IllegalStateException( "ClassLoader [" + classLoader.getClass().getName() + "] does NOT provide an " + "'addTransformer(ClassFileTransformer)' method."); } this.getThrowawayClassLoaderMethod = ClassUtils.getMethodIfAvailable( - this.classLoader.getClass(), GET_THROWAWAY_CLASS_LOADER_METHOD_NAME, new Class[0]); + this.classLoader.getClass(), GET_THROWAWAY_CLASS_LOADER_METHOD_NAME); // getThrowawayClassLoader method is optional if (this.getThrowawayClassLoaderMethod == null) { if (logger.isInfoEnabled()) { diff --git a/spring-context/src/main/java/org/springframework/instrument/classloading/websphere/WebSphereClassLoaderAdapter.java b/spring-context/src/main/java/org/springframework/instrument/classloading/websphere/WebSphereClassLoaderAdapter.java index 3ee521c3be..00e8bbafab 100644 --- a/spring-context/src/main/java/org/springframework/instrument/classloading/websphere/WebSphereClassLoaderAdapter.java +++ b/spring-context/src/main/java/org/springframework/instrument/classloading/websphere/WebSphereClassLoaderAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,47 +29,57 @@ /** * - * Reflective wrapper around a WebSphere 7 class loader. Used to + * Reflective wrapper around a WebSphere 7+ class loader. Used to * encapsulate the classloader-specific methods (discovered and * called through reflection) from the load-time weaver. * * @author Costin Leau + * @author Juergen Hoeller * @since 3.1 */ class WebSphereClassLoaderAdapter { private static final String COMPOUND_CLASS_LOADER_NAME = "com.ibm.ws.classloader.CompoundClassLoader"; + private static final String CLASS_PRE_PROCESSOR_NAME = "com.ibm.websphere.classloader.ClassLoaderInstancePreDefinePlugin"; + private static final String PLUGINS_FIELD = "preDefinePlugins"; private ClassLoader classLoader; + private Class wsPreProcessorClass; + private Method addPreDefinePlugin; + private Constructor cloneConstructor; + private Field transformerList; + public WebSphereClassLoaderAdapter(ClassLoader classLoader) { - Class wsCompoundClassLoaderClass = null; + Class wsCompoundClassLoaderClass; try { wsCompoundClassLoaderClass = classLoader.loadClass(COMPOUND_CLASS_LOADER_NAME); - cloneConstructor = classLoader.getClass().getDeclaredConstructor(wsCompoundClassLoaderClass); - cloneConstructor.setAccessible(true); + this.cloneConstructor = classLoader.getClass().getDeclaredConstructor(wsCompoundClassLoaderClass); + this.cloneConstructor.setAccessible(true); - wsPreProcessorClass = classLoader.loadClass(CLASS_PRE_PROCESSOR_NAME); - addPreDefinePlugin = classLoader.getClass().getMethod("addPreDefinePlugin", wsPreProcessorClass); - transformerList = wsCompoundClassLoaderClass.getDeclaredField(PLUGINS_FIELD); - transformerList.setAccessible(true); + this.wsPreProcessorClass = classLoader.loadClass(CLASS_PRE_PROCESSOR_NAME); + this.addPreDefinePlugin = classLoader.getClass().getMethod("addPreDefinePlugin", this.wsPreProcessorClass); + this.transformerList = wsCompoundClassLoaderClass.getDeclaredField(PLUGINS_FIELD); + this.transformerList.setAccessible(true); } catch (Exception ex) { throw new IllegalStateException( - "Could not initialize WebSphere LoadTimeWeaver because WebSphere 7 API classes are not available", - ex); + "Could not initialize WebSphere LoadTimeWeaver because WebSphere API classes are not available", ex); + } + + if (!wsCompoundClassLoaderClass.isInstance(classLoader)) { + throw new IllegalArgumentException("ClassLoader must be instance of [" + COMPOUND_CLASS_LOADER_NAME + "]"); } - Assert.isInstanceOf(wsCompoundClassLoaderClass, classLoader, - "ClassLoader must be instance of [" + COMPOUND_CLASS_LOADER_NAME + "]"); this.classLoader = classLoader; } + public ClassLoader getClassLoader() { return this.classLoader; } @@ -79,9 +89,8 @@ public void addTransformer(ClassFileTransformer transformer) { try { InvocationHandler adapter = new WebSphereClassPreDefinePlugin(transformer); Object adapterInstance = Proxy.newProxyInstance(this.wsPreProcessorClass.getClassLoader(), - new Class[] { this.wsPreProcessorClass }, adapter); + new Class[] {this.wsPreProcessorClass}, adapter); this.addPreDefinePlugin.invoke(this.classLoader, adapterInstance); - } catch (InvocationTargetException ex) { throw new IllegalStateException("WebSphere addPreDefinePlugin method threw exception", ex.getCause()); @@ -93,9 +102,9 @@ public void addTransformer(ClassFileTransformer transformer) { public ClassLoader getThrowawayClassLoader() { try { - ClassLoader loader = cloneConstructor.newInstance(getClassLoader()); - // clear out the transformers (copied as well) - List list = (List) transformerList.get(loader); + ClassLoader loader = this.cloneConstructor.newInstance(getClassLoader()); + // Clear out the transformers (copied as well) + List list = (List) this.transformerList.get(loader); list.clear(); return loader; } diff --git a/spring-core/src/main/java/org/springframework/core/OverridingClassLoader.java b/spring-core/src/main/java/org/springframework/core/OverridingClassLoader.java index c60a4c8b4b..684d8561e1 100644 --- a/spring-core/src/main/java/org/springframework/core/OverridingClassLoader.java +++ b/spring-core/src/main/java/org/springframework/core/OverridingClassLoader.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -63,19 +63,16 @@ public OverridingClassLoader(ClassLoader parent) { @Override protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { - Class result = null; if (isEligibleForOverriding(name)) { - result = loadClassForOverriding(name); - } - if (result != null) { - if (resolve) { - resolveClass(result); + Class result = loadClassForOverriding(name); + if (result != null) { + if (resolve) { + resolveClass(result); + } + return result; } - return result; - } - else { - return super.loadClass(name, resolve); } + return super.loadClass(name, resolve); } /** diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/ClassFileTransformerAdapter.java b/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/ClassFileTransformerAdapter.java index 9a62f076a1..c7c150e7a2 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/ClassFileTransformerAdapter.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/persistenceunit/ClassFileTransformerAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,10 +27,11 @@ /** * Simple adapter that implements the {@code java.lang.instrument.ClassFileTransformer} - * interface based on a JPA ClassTransformer which a JPA PersistenceProvider asks the - * PersistenceUnitInfo to install in the current runtime. + * interface based on a JPA {@code ClassTransformer} which a JPA PersistenceProvider + * asks the {@code PersistenceUnitInfo} to install in the current runtime. * * @author Rod Johnson + * @author Juergen Hoeller * @since 2.0 * @see javax.persistence.spi.PersistenceUnitInfo#addTransformer(javax.persistence.spi.ClassTransformer) */ @@ -63,14 +64,16 @@ public byte[] transform( return transformed; } catch (ClassCircularityError ex) { - logger.error("Error weaving class [" + className + "] with " + - "transformer of class [" + this.classTransformer.getClass().getName() + "]", ex); - throw new IllegalStateException("Could not weave class [" + className + "]", ex); + if (logger.isErrorEnabled()) { + logger.error("Circularity error while weaving class [" + className + "] with " + + "transformer of class [" + this.classTransformer.getClass().getName() + "]", ex); + } + throw new IllegalStateException("Failed to weave class [" + className + "]", ex); } catch (Throwable ex) { if (logger.isWarnEnabled()) { - logger.warn("Error weaving class [" + className + "] with " + - "transformer of class [" + this.classTransformer.getClass().getName() + "]", ex); + logger.warn("Error weaving class [" + className + "] with transformer of class [" + + this.classTransformer.getClass().getName() + "]", ex); } // The exception will be ignored by the class loader, anyway... throw new IllegalStateException("Could not weave class [" + className + "]", ex); From e6ae43703893fad2c18f94f86034ba86440e8a53 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 13 Apr 2016 13:13:11 +0200 Subject: [PATCH 119/344] Latest dependency updates (AspectJ 1.8.9, Rhino 1.7.7.1, WebJars Locator 0.30) --- build.gradle | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/build.gradle b/build.gradle index 5468e0619e..33397827a0 100644 --- a/build.gradle +++ b/build.gradle @@ -867,9 +867,9 @@ project("spring-webmvc") { exclude group: "org.slf4j", module: "jcl-over-slf4j" exclude group: "org.springframework", module: "spring-web" } - optional('org.webjars:webjars-locator:0.28') + optional('org.webjars:webjars-locator:0.30') testCompile(project(":spring-aop")) - testCompile("rhino:js:1.7R1") + testCompile("org.mozilla:rhino:1.7.7.1") testCompile("xmlunit:xmlunit:${xmlunitVersion}") testCompile("dom4j:dom4j:1.6.1") { exclude group: "xml-apis", module: "xml-apis" @@ -1018,11 +1018,11 @@ project("spring-test") { optional("org.aspectj:aspectjweaver:${aspectjVersion}") optional("org.codehaus.groovy:groovy-all:${groovyVersion}") optional("org.hamcrest:hamcrest-core:${hamcrestVersion}") - optional("com.jayway.jsonpath:json-path:${jsonpathVersion}") - optional("org.skyscreamer:jsonassert:${jsonassertVersion}") - optional("xmlunit:xmlunit:${xmlunitVersion}") - optional("net.sourceforge.htmlunit:htmlunit:${htmlunitVersion}") optional("org.seleniumhq.selenium:selenium-htmlunit-driver:${seleniumVersion}") + optional("net.sourceforge.htmlunit:htmlunit:${htmlunitVersion}") + optional("xmlunit:xmlunit:${xmlunitVersion}") + optional("org.skyscreamer:jsonassert:${jsonassertVersion}") + optional("com.jayway.jsonpath:json-path:${jsonpathVersion}") testCompile(project(":spring-context-support")) testCompile(project(":spring-oxm")) testCompile("javax.mail:javax.mail-api:${javamailVersion}") @@ -1080,8 +1080,8 @@ project("spring-aspects") { dependencies { aspects(project(":spring-orm")) - ajc("org.aspectj:aspectjtools:1.9.0.BETA-3") // for the ability to build on JDK 9, not exposed in the POMs yet - rt("org.aspectj:aspectjrt:${aspectjVersion}") // regular AspectJ version here, to be exposed in the POMs + ajc("org.aspectj:aspectjtools:${aspectjVersion}") + rt("org.aspectj:aspectjrt:${aspectjVersion}") compile("org.aspectj:aspectjweaver:${aspectjVersion}") provided("org.eclipse.persistence:javax.persistence:2.0.0") optional(project(":spring-aop")) // for @Async support @@ -1099,8 +1099,7 @@ project("spring-aspects") { eclipse.project { natures += "org.eclipse.ajdt.ui.ajnature" - buildCommands = [new org.gradle.plugins.ide.eclipse.model. - BuildCommand("org.eclipse.ajdt.core.ajbuilder")] + buildCommands = [new org.gradle.plugins.ide.eclipse.model.BuildCommand("org.eclipse.ajdt.core.ajbuilder")] } } From 09b7735b0f5faa81226fe93a41126f50f728c22d Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 13 Apr 2016 13:33:39 +0200 Subject: [PATCH 120/344] Polishing --- .../AnnotationCacheOperationSourceTests.java | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/spring-context/src/test/java/org/springframework/cache/annotation/AnnotationCacheOperationSourceTests.java b/spring-context/src/test/java/org/springframework/cache/annotation/AnnotationCacheOperationSourceTests.java index 42430474c8..bfafce1de1 100644 --- a/spring-context/src/test/java/org/springframework/cache/annotation/AnnotationCacheOperationSourceTests.java +++ b/spring-context/src/test/java/org/springframework/cache/annotation/AnnotationCacheOperationSourceTests.java @@ -33,7 +33,6 @@ import org.springframework.cache.interceptor.CacheOperation; import org.springframework.cache.interceptor.CacheableOperation; import org.springframework.core.annotation.AliasFor; -import org.springframework.util.ReflectionUtils; import static org.junit.Assert.*; @@ -50,17 +49,6 @@ public class AnnotationCacheOperationSourceTests { private final AnnotationCacheOperationSource source = new AnnotationCacheOperationSource(); - private Collection getOps(Class target, String name, int expectedNumberOfOperations) { - Collection result = getOps(target, name); - assertEquals("Wrong number of operation(s) for '" + name + "'", expectedNumberOfOperations, result.size()); - return result; - } - - private Collection getOps(Class target, String name) { - Method method = ReflectionUtils.findMethod(target, name); - return source.getCacheOperations(method, target); - } - @Test public void singularAnnotation() throws Exception { Collection ops = getOps(AnnotatedClass.class, "singular", 1); @@ -234,6 +222,22 @@ public void partialClassLevelWithNoCustomization() { } + private Collection getOps(Class target, String name, int expectedNumberOfOperations) { + Collection result = getOps(target, name); + assertEquals("Wrong number of operation(s) for '" + name + "'", expectedNumberOfOperations, result.size()); + return result; + } + + private Collection getOps(Class target, String name) { + try { + Method method = target.getMethod(name); + return source.getCacheOperations(method, target); + } + catch (NoSuchMethodException ex) { + throw new IllegalStateException(ex); + } + } + private void assertSharedConfig(CacheOperation actual, String keyGenerator, String cacheManager, String cacheResolver, String... cacheNames) { @@ -241,8 +245,8 @@ private void assertSharedConfig(CacheOperation actual, String keyGenerator, Stri assertEquals("Wrong cache manager", cacheManager, actual.getCacheManager()); assertEquals("Wrong cache resolver", cacheResolver, actual.getCacheResolver()); assertEquals("Wrong number of cache names", cacheNames.length, actual.getCacheNames().size()); - Arrays.stream(cacheNames).forEach( - cacheName -> assertTrue("Cache '" + cacheName + "' not found in " + actual.getCacheNames(), + Arrays.stream(cacheNames).forEach(cacheName -> + assertTrue("Cache '" + cacheName + "' not found in " + actual.getCacheNames(), actual.getCacheNames().contains(cacheName))); } From 863bae7d8666dcde0bb52e40898cdb0a479bc2d4 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 13 Apr 2016 13:58:34 +0200 Subject: [PATCH 121/344] Drop outdated support for building on JDK 9 (not compatible with JDK 9 b111+) --- build.gradle | 7 ------- 1 file changed, 7 deletions(-) diff --git a/build.gradle b/build.gradle index 33397827a0..49c2c8572b 100644 --- a/build.gradle +++ b/build.gradle @@ -130,8 +130,6 @@ configure(allprojects) { project -> repositories { maven { url "https://repo.spring.io/libs-release" } - maven { url "https://repo.spring.io/milestone" } - maven { url "https://repo.spring.io/snapshot" } // reactor 2.0.6 snapshot } dependencies { @@ -566,11 +564,6 @@ project("spring-oxm") { targetCompatibility = 1.6 } - if (!System.getProperty("java.version").contains("1.8.")) { - // necessary because castor and xjc tasks cannot find the JDK's compiler on JDK 9 - compileTestJava.enabled = false - } - dependencies { compile(project(":spring-beans")) compile(project(":spring-core")) From bf3cadb90810fc93e356f3148bc3f98adf098797 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 13 Apr 2016 18:34:22 +0200 Subject: [PATCH 122/344] AbstractJackson2HttpMessageConverter's canRead/canWrite checks media type first before delegating to Jackson Issue: SPR-14163 (cherry picked from commit e366746) --- .../json/AbstractJackson2HttpMessageConverter.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java index 04a4b59ac8..a98222d4e9 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java @@ -145,11 +145,14 @@ public boolean canRead(Class clazz, MediaType mediaType) { @Override public boolean canRead(Type type, Class contextClass, MediaType mediaType) { JavaType javaType = getJavaType(type, contextClass); + if (!canRead(mediaType)) { + return false; + } if (!jackson23Available || !logger.isWarnEnabled()) { - return (this.objectMapper.canDeserialize(javaType) && canRead(mediaType)); + return this.objectMapper.canDeserialize(javaType); } AtomicReference causeRef = new AtomicReference(); - if (this.objectMapper.canDeserialize(javaType, causeRef) && canRead(mediaType)) { + if (this.objectMapper.canDeserialize(javaType, causeRef)) { return true; } Throwable cause = causeRef.get(); @@ -167,11 +170,14 @@ public boolean canRead(Type type, Class contextClass, MediaType mediaType) { @Override public boolean canWrite(Class clazz, MediaType mediaType) { + if (!canWrite(mediaType)) { + return false; + } if (!jackson23Available || !logger.isWarnEnabled()) { - return (this.objectMapper.canSerialize(clazz) && canWrite(mediaType)); + return this.objectMapper.canSerialize(clazz); } AtomicReference causeRef = new AtomicReference(); - if (this.objectMapper.canSerialize(clazz, causeRef) && canWrite(mediaType)) { + if (this.objectMapper.canSerialize(clazz, causeRef)) { return true; } Throwable cause = causeRef.get(); From 7f438dc9aa93053b844b4394557039a93fdb805a Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 13 Apr 2016 18:44:56 +0200 Subject: [PATCH 123/344] Consistent headers.getContentLength() checks --- .../client/AbstractBufferingAsyncClientHttpRequest.java | 4 ++-- .../http/client/AbstractBufferingClientHttpRequest.java | 4 ++-- .../http/converter/AbstractHttpMessageConverter.java | 8 ++++---- .../http/server/ServletServerHttpRequest.java | 2 +- .../java/org/springframework/web/client/RestTemplate.java | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/client/AbstractBufferingAsyncClientHttpRequest.java b/spring-web/src/main/java/org/springframework/http/client/AbstractBufferingAsyncClientHttpRequest.java index cf83983de5..2bcbcdb298 100644 --- a/spring-web/src/main/java/org/springframework/http/client/AbstractBufferingAsyncClientHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/client/AbstractBufferingAsyncClientHttpRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,7 +43,7 @@ protected OutputStream getBodyInternal(HttpHeaders headers) throws IOException { @Override protected ListenableFuture executeInternal(HttpHeaders headers) throws IOException { byte[] bytes = this.bufferedOutput.toByteArray(); - if (headers.getContentLength() == -1) { + if (headers.getContentLength() < 0) { headers.setContentLength(bytes.length); } ListenableFuture result = executeInternal(headers, bytes); diff --git a/spring-web/src/main/java/org/springframework/http/client/AbstractBufferingClientHttpRequest.java b/spring-web/src/main/java/org/springframework/http/client/AbstractBufferingClientHttpRequest.java index 4af7348798..d468b5877b 100644 --- a/spring-web/src/main/java/org/springframework/http/client/AbstractBufferingClientHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/client/AbstractBufferingClientHttpRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,7 +42,7 @@ protected OutputStream getBodyInternal(HttpHeaders headers) throws IOException { @Override protected ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException { byte[] bytes = this.bufferedOutput.toByteArray(); - if (headers.getContentLength() == -1) { + if (headers.getContentLength() < 0) { headers.setContentLength(bytes.length); } ClientHttpResponse result = executeInternal(headers, bytes); diff --git a/spring-web/src/main/java/org/springframework/http/converter/AbstractHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/AbstractHttpMessageConverter.java index 1f1e200882..42a9119282 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/AbstractHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/AbstractHttpMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -68,7 +68,7 @@ protected AbstractHttpMessageConverter(MediaType supportedMediaType) { } /** - * Construct an {@code AbstractHttpMessageConverter} with multiple supported media type. + * Construct an {@code AbstractHttpMessageConverter} with multiple supported media types. * @param supportedMediaTypes the supported media types */ protected AbstractHttpMessageConverter(MediaType... supportedMediaTypes) { @@ -101,7 +101,7 @@ public boolean canRead(Class clazz, MediaType mediaType) { } /** - * Returns true if any of the {@linkplain #setSupportedMediaTypes(List) + * Returns {@code true} if any of the {@linkplain #setSupportedMediaTypes(List) * supported} media types {@link MediaType#includes(MediaType) include} the * given media type. * @param mediaType the media type to read, can be {@code null} if not specified. @@ -217,7 +217,7 @@ else if (MediaType.APPLICATION_OCTET_STREAM.equals(contentType)) { headers.setContentType(contentTypeToUse); } } - if (headers.getContentLength() == -1) { + if (headers.getContentLength() < 0) { Long contentLength = getContentLength(t, headers.getContentType()); if (contentLength != null) { headers.setContentLength(contentLength); diff --git a/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java b/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java index a906da520d..6fda91d29b 100644 --- a/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java @@ -132,7 +132,7 @@ public HttpHeaders getHeaders() { this.headers.setContentType(newContentType); } } - if (this.headers.getContentLength() == -1) { + if (this.headers.getContentLength() < 0) { int requestContentLength = this.servletRequest.getContentLength(); if (requestContentLength != -1) { this.headers.setContentLength(requestContentLength); diff --git a/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java b/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java index 2601d2f916..8c7c04334f 100644 --- a/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java +++ b/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -773,7 +773,7 @@ public void doWithRequest(ClientHttpRequest httpRequest) throws IOException { if (!requestHeaders.isEmpty()) { httpHeaders.putAll(requestHeaders); } - if (httpHeaders.getContentLength() == -1) { + if (httpHeaders.getContentLength() < 0) { httpHeaders.setContentLength(0L); } } From 416a24c6bc0a9453ad5b8a20eb4f33d47da2eb92 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 13 Apr 2016 18:52:52 +0200 Subject: [PATCH 124/344] Avoid unnecessary GenericHttpMessageConverter re-declaration (cherry picked from commit 96875fe) --- .../json/AbstractJackson2HttpMessageConverter.java | 4 +--- .../http/converter/json/GsonHttpMessageConverter.java | 8 +++----- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java index a98222d4e9..2bad6fc440 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java @@ -37,7 +37,6 @@ import org.springframework.http.HttpOutputMessage; import org.springframework.http.MediaType; import org.springframework.http.converter.AbstractGenericHttpMessageConverter; -import org.springframework.http.converter.GenericHttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.converter.HttpMessageNotWritableException; @@ -58,8 +57,7 @@ * @author Sebastien Deleuze * @since 4.1 */ -public abstract class AbstractJackson2HttpMessageConverter extends AbstractGenericHttpMessageConverter - implements GenericHttpMessageConverter { +public abstract class AbstractJackson2HttpMessageConverter extends AbstractGenericHttpMessageConverter { public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); diff --git a/spring-web/src/main/java/org/springframework/http/converter/json/GsonHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/json/GsonHttpMessageConverter.java index 1cfe37ead3..38f7f43289 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/json/GsonHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/json/GsonHttpMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,7 +33,6 @@ import org.springframework.http.HttpOutputMessage; import org.springframework.http.MediaType; import org.springframework.http.converter.AbstractGenericHttpMessageConverter; -import org.springframework.http.converter.GenericHttpMessageConverter; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.converter.HttpMessageNotWritableException; import org.springframework.util.Assert; @@ -48,15 +47,14 @@ * By default, it supports {@code application/json} and {@code application/*+json} with * {@code UTF-8} character set. * - *

    Tested against Gson 2.3; compatible with Gson 2.0 and higher. + *

    Tested against Gson 2.6; compatible with Gson 2.0 and higher. * * @author Roy Clarkson * @since 4.1 * @see #setGson * @see #setSupportedMediaTypes */ -public class GsonHttpMessageConverter extends AbstractGenericHttpMessageConverter - implements GenericHttpMessageConverter { +public class GsonHttpMessageConverter extends AbstractGenericHttpMessageConverter { public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); From 334b4a99b05cfaf3b09910a0f502770a3917eeab Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 14 Apr 2016 14:22:23 +0200 Subject: [PATCH 125/344] Jackson-based message converters consistently check media type first Issue: SPR-14163 --- .../MappingJackson2MessageConverter.java | 19 +++++++++++-------- .../AbstractJackson2HttpMessageConverter.java | 6 +++--- ...pingJackson2HttpMessageConverterTests.java | 12 +++++++----- 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/spring-messaging/src/main/java/org/springframework/messaging/converter/MappingJackson2MessageConverter.java b/spring-messaging/src/main/java/org/springframework/messaging/converter/MappingJackson2MessageConverter.java index cf2b676084..f549f5e317 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/converter/MappingJackson2MessageConverter.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/converter/MappingJackson2MessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -142,20 +142,20 @@ private void configurePrettyPrint() { @Override protected boolean canConvertFrom(Message message, Class targetClass) { - if (targetClass == null) { + if (targetClass == null || !supportsMimeType(message.getHeaders())) { return false; } JavaType javaType = this.objectMapper.constructType(targetClass); if (!jackson23Available || !logger.isWarnEnabled()) { - return (this.objectMapper.canDeserialize(javaType) && supportsMimeType(message.getHeaders())); + return this.objectMapper.canDeserialize(javaType); } AtomicReference causeRef = new AtomicReference(); - if (this.objectMapper.canDeserialize(javaType, causeRef) && supportsMimeType(message.getHeaders())) { + if (this.objectMapper.canDeserialize(javaType, causeRef)) { return true; } Throwable cause = causeRef.get(); if (cause != null) { - String msg = "Failed to evaluate deserialization for type " + javaType; + String msg = "Failed to evaluate Jackson deserialization for type " + javaType; if (logger.isDebugEnabled()) { logger.warn(msg, cause); } @@ -168,16 +168,19 @@ protected boolean canConvertFrom(Message message, Class targetClass) { @Override protected boolean canConvertTo(Object payload, MessageHeaders headers) { + if (payload == null || !supportsMimeType(headers)) { + return false; + } if (!jackson23Available || !logger.isWarnEnabled()) { - return (this.objectMapper.canSerialize(payload.getClass()) && supportsMimeType(headers)); + return this.objectMapper.canSerialize(payload.getClass()); } AtomicReference causeRef = new AtomicReference(); - if (this.objectMapper.canSerialize(payload.getClass(), causeRef) && supportsMimeType(headers)) { + if (this.objectMapper.canSerialize(payload.getClass(), causeRef)) { return true; } Throwable cause = causeRef.get(); if (cause != null) { - String msg = "Failed to evaluate serialization for type [" + payload.getClass() + "]"; + String msg = "Failed to evaluate Jackson serialization for type [" + payload.getClass() + "]"; if (logger.isDebugEnabled()) { logger.warn(msg, cause); } diff --git a/spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java index 2bad6fc440..f29dc80677 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.java @@ -142,10 +142,10 @@ public boolean canRead(Class clazz, MediaType mediaType) { @Override public boolean canRead(Type type, Class contextClass, MediaType mediaType) { - JavaType javaType = getJavaType(type, contextClass); if (!canRead(mediaType)) { return false; } + JavaType javaType = getJavaType(type, contextClass); if (!jackson23Available || !logger.isWarnEnabled()) { return this.objectMapper.canDeserialize(javaType); } @@ -155,7 +155,7 @@ public boolean canRead(Type type, Class contextClass, MediaType mediaType) { } Throwable cause = causeRef.get(); if (cause != null) { - String msg = "Failed to evaluate deserialization for type " + javaType; + String msg = "Failed to evaluate Jackson deserialization for type " + javaType; if (logger.isDebugEnabled()) { logger.warn(msg, cause); } @@ -180,7 +180,7 @@ public boolean canWrite(Class clazz, MediaType mediaType) { } Throwable cause = causeRef.get(); if (cause != null) { - String msg = "Failed to evaluate serialization for type [" + clazz + "]"; + String msg = "Failed to evaluate Jackson serialization for type [" + clazz + "]"; if (logger.isDebugEnabled()) { logger.warn(msg, cause); } diff --git a/spring-web/src/test/java/org/springframework/http/converter/json/MappingJackson2HttpMessageConverterTests.java b/spring-web/src/test/java/org/springframework/http/converter/json/MappingJackson2HttpMessageConverterTests.java index 51a04ee602..e2b15b0861 100644 --- a/spring-web/src/test/java/org/springframework/http/converter/json/MappingJackson2HttpMessageConverterTests.java +++ b/spring-web/src/test/java/org/springframework/http/converter/json/MappingJackson2HttpMessageConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,7 +31,6 @@ import com.fasterxml.jackson.databind.ser.FilterProvider; import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; - import org.junit.Test; import org.springframework.core.ParameterizedTypeReference; @@ -68,9 +67,7 @@ public void canWrite() { assertTrue(converter.canWrite(Map.class, new MediaType("application", "json"))); } - // SPR-7905 - - @Test + @Test // SPR-7905 public void canReadAndWriteMicroformats() { assertTrue(converter.canRead(MyBean.class, new MediaType("application", "vnd.test-micro-type+json"))); assertTrue(converter.canWrite(MyBean.class, new MediaType("application", "vnd.test-micro-type+json"))); @@ -439,9 +436,12 @@ public void setName(String name) { } } + private interface MyJacksonView1 {}; + private interface MyJacksonView2 {}; + @SuppressWarnings("unused") private static class JacksonViewBean { @@ -478,11 +478,13 @@ public void setWithoutView(String withoutView) { } } + @JsonFilter("myJacksonFilter") @SuppressWarnings("unused") private static class JacksonFilteredBean { private String property1; + private String property2; public String getProperty1() { From fd954ef1d6f63f8b5a9539b09b451d227bbcf3ac Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 14 Apr 2016 14:48:57 +0200 Subject: [PATCH 126/344] Upgrade to Jackson 2.6.6 and Protobuf Java Format 1.4 --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 49c2c8572b..f97ec2274b 100644 --- a/build.gradle +++ b/build.gradle @@ -48,7 +48,7 @@ configure(allprojects) { project -> ext.htmlunitVersion = "2.19" ext.httpasyncVersion = "4.1.1" ext.httpclientVersion = "4.5.2" - ext.jackson2Version = "2.6.5" + ext.jackson2Version = "2.6.6" ext.jasperreportsVersion = "6.2.1" ext.javamailVersion = "1.5.5" ext.jettyVersion = "9.3.8.v20160314" @@ -716,7 +716,7 @@ project("spring-web") { exclude group: "javax.servlet", module: "javax.servlet-api" } optional("log4j:log4j:1.2.17") - optional("com.googlecode.protobuf-java-format:protobuf-java-format:1.2") + optional("com.googlecode.protobuf-java-format:protobuf-java-format:1.4") optional("com.google.protobuf:protobuf-java:${protobufVersion}") optional("javax.mail:javax.mail-api:${javamailVersion}") testCompile(project(":spring-context-support")) // for JafMediaTypeFactory From 1d39c97def2592e899025e978263d5266481a8e4 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 14 Apr 2016 15:54:52 +0200 Subject: [PATCH 127/344] Downgrade to Protobuf Java Format 1.2 Issue: SPR-14171 --- build.gradle | 5 +- .../ProtobufHttpMessageConverter.java | 126 +++++++++--------- .../ProtobufHttpMessageConverterTests.java | 18 ++- 3 files changed, 77 insertions(+), 72 deletions(-) diff --git a/build.gradle b/build.gradle index f97ec2274b..1c0c22508c 100644 --- a/build.gradle +++ b/build.gradle @@ -62,7 +62,6 @@ configure(allprojects) { project -> ext.okhttpVersion = "2.7.5" ext.openjpaVersion = "2.4.1" ext.poiVersion = "3.13" - ext.protobufVersion = "2.6.1" ext.reactorVersion = "2.0.7.RELEASE" ext.romeVersion = "1.5.1" ext.seleniumVersion = "2.48.2" @@ -716,8 +715,8 @@ project("spring-web") { exclude group: "javax.servlet", module: "javax.servlet-api" } optional("log4j:log4j:1.2.17") - optional("com.googlecode.protobuf-java-format:protobuf-java-format:1.4") - optional("com.google.protobuf:protobuf-java:${protobufVersion}") + optional("com.google.protobuf:protobuf-java:2.6.1") + optional("com.googlecode.protobuf-java-format:protobuf-java-format:1.2") optional("javax.mail:javax.mail-api:${javamailVersion}") testCompile(project(":spring-context-support")) // for JafMediaTypeFactory testCompile("xmlunit:xmlunit:${xmlunitVersion}") diff --git a/spring-web/src/main/java/org/springframework/http/converter/protobuf/ProtobufHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/protobuf/ProtobufHttpMessageConverter.java index 281d00a761..388b553301 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/protobuf/ProtobufHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/protobuf/ProtobufHttpMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,7 +30,6 @@ import com.googlecode.protobuf.format.JsonFormat; import com.googlecode.protobuf.format.XmlFormat; -import org.springframework.http.HttpHeaders; import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpOutputMessage; import org.springframework.http.MediaType; @@ -41,20 +40,20 @@ /** - * An {@code HttpMessageConverter} that can read and write Protobuf - * {@link com.google.protobuf.Message} using - * Google Protocol buffers. + * An {@code HttpMessageConverter} that reads and writes {@link com.google.protobuf.Message}s + * using Google Protocol Buffers. * - *

    By default it supports {@code "application/json"}, {@code "application/xml"}, - * {@code "text/plain"} and {@code "application/x-protobuf"} while writing also - * supports {@code "text/html"} + *

    By default, it supports {@code "application/x-protobuf"}, {@code "text/plain"}, + * {@code "application/json"}, {@code "application/xml"}, while also writing {@code "text/html"}. * - *

    To generate Message Java classes you need to install the protoc binary. + *

    To generate {@code Message} Java classes, you need to install the {@code protoc} binary. * - *

    Tested against Protobuf version 2.5.0. + *

    Requires Protobuf 2.5/2.6 and Protobuf Java Format 1.2. + * (Note: Does not work with later Protobuf Java Format versions in Spring 4.2 yet.) * * @author Alex Antonov * @author Brian Clozel + * @author Juergen Hoeller * @since 4.1 */ public class ProtobufHttpMessageConverter extends AbstractHttpMessageConverter { @@ -67,10 +66,10 @@ public class ProtobufHttpMessageConverter extends AbstractHttpMessageConverter, Method> methodCache = new ConcurrentHashMap, Method>(); + private static final ConcurrentHashMap, Method> methodCache = new ConcurrentHashMap, Method>(); - private ExtensionRegistry extensionRegistry = ExtensionRegistry.newInstance(); + private final ExtensionRegistry extensionRegistry = ExtensionRegistry.newInstance(); /** @@ -85,7 +84,7 @@ public ProtobufHttpMessageConverter() { * that allows the registration of message extensions. */ public ProtobufHttpMessageConverter(ExtensionRegistryInitializer registryInitializer) { - super(PROTOBUF, MediaType.TEXT_PLAIN, MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON); + super(PROTOBUF, MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML); if (registryInitializer != null) { registryInitializer.initializeExtensionRegistry(this.extensionRegistry); } @@ -97,26 +96,36 @@ protected boolean supports(Class clazz) { return Message.class.isAssignableFrom(clazz); } + @Override + protected MediaType getDefaultContentType(Message message) { + return PROTOBUF; + } + @Override protected Message readInternal(Class clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { MediaType contentType = inputMessage.getHeaders().getContentType(); - contentType = (contentType != null ? contentType : PROTOBUF); - - Charset charset = getCharset(inputMessage.getHeaders()); - InputStreamReader reader = new InputStreamReader(inputMessage.getBody(), charset); + if (contentType == null) { + contentType = PROTOBUF; + } + Charset charset = contentType.getCharSet(); + if (charset == null) { + charset = DEFAULT_CHARSET; + } try { Message.Builder builder = getMessageBuilder(clazz); - - if (MediaType.APPLICATION_JSON.isCompatibleWith(contentType)) { - JsonFormat.merge(reader, this.extensionRegistry, builder); - } - else if (MediaType.TEXT_PLAIN.isCompatibleWith(contentType)) { + if (MediaType.TEXT_PLAIN.isCompatibleWith(contentType)) { + InputStreamReader reader = new InputStreamReader(inputMessage.getBody(), charset); TextFormat.merge(reader, this.extensionRegistry, builder); } + else if (MediaType.APPLICATION_JSON.isCompatibleWith(contentType)) { + InputStreamReader reader = new InputStreamReader(inputMessage.getBody(), charset); + JsonFormat.merge(reader, this.extensionRegistry, builder); + } else if (MediaType.APPLICATION_XML.isCompatibleWith(contentType)) { + InputStreamReader reader = new InputStreamReader(inputMessage.getBody(), charset); XmlFormat.merge(reader, this.extensionRegistry, builder); } else { @@ -124,29 +133,9 @@ else if (MediaType.APPLICATION_XML.isCompatibleWith(contentType)) { } return builder.build(); } - catch (Exception e) { - throw new HttpMessageNotReadableException("Could not read Protobuf message: " + e.getMessage(), e); - } - } - - private Charset getCharset(HttpHeaders headers) { - if (headers == null || headers.getContentType() == null || headers.getContentType().getCharSet() == null) { - return DEFAULT_CHARSET; - } - return headers.getContentType().getCharSet(); - } - - /** - * Create a new {@code Message.Builder} instance for the given class. - *

    This method uses a ConcurrentHashMap for caching method lookups. - */ - private Message.Builder getMessageBuilder(Class clazz) throws Exception { - Method method = methodCache.get(clazz); - if (method == null) { - method = clazz.getMethod("newBuilder"); - methodCache.put(clazz, method); + catch (Exception ex) { + throw new HttpMessageNotReadableException("Could not read Protobuf message: " + ex.getMessage(), ex); } - return (Message.Builder) method.invoke(clazz); } /** @@ -155,7 +144,7 @@ private Message.Builder getMessageBuilder(Class clazz) throws */ @Override protected boolean canWrite(MediaType mediaType) { - return super.canWrite(mediaType) || MediaType.TEXT_HTML.isCompatibleWith(mediaType); + return (super.canWrite(mediaType) || MediaType.TEXT_HTML.isCompatibleWith(mediaType)); } @Override @@ -163,38 +152,40 @@ protected void writeInternal(Message message, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { MediaType contentType = outputMessage.getHeaders().getContentType(); - Charset charset = getCharset(contentType); + if (contentType == null) { + contentType = getDefaultContentType(message); + } + Charset charset = contentType.getCharSet(); + if (charset == null) { + charset = DEFAULT_CHARSET; + } - if (MediaType.TEXT_HTML.isCompatibleWith(contentType)) { - final OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputMessage.getBody(), charset); - HtmlFormat.print(message, outputStreamWriter); + if (MediaType.TEXT_PLAIN.isCompatibleWith(contentType)) { + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputMessage.getBody(), charset); + TextFormat.print(message, outputStreamWriter); outputStreamWriter.flush(); } else if (MediaType.APPLICATION_JSON.isCompatibleWith(contentType)) { - final OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputMessage.getBody(), charset); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputMessage.getBody(), charset); JsonFormat.print(message, outputStreamWriter); outputStreamWriter.flush(); } - else if (MediaType.TEXT_PLAIN.isCompatibleWith(contentType)) { - final OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputMessage.getBody(), charset); - TextFormat.print(message, outputStreamWriter); - outputStreamWriter.flush(); - } else if (MediaType.APPLICATION_XML.isCompatibleWith(contentType)) { - final OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputMessage.getBody(), charset); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputMessage.getBody(), charset); XmlFormat.print(message, outputStreamWriter); outputStreamWriter.flush(); } + else if (MediaType.TEXT_HTML.isCompatibleWith(contentType)) { + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputMessage.getBody(), charset); + HtmlFormat.print(message, outputStreamWriter); + outputStreamWriter.flush(); + } else if (PROTOBUF.isCompatibleWith(contentType)) { setProtoHeader(outputMessage, message); FileCopyUtils.copy(message.toByteArray(), outputMessage.getBody()); } } - private Charset getCharset(MediaType contentType) { - return contentType.getCharSet() != null ? contentType.getCharSet() : DEFAULT_CHARSET; - } - /** * Set the "X-Protobuf-*" HTTP headers when responding with a message of * content type "application/x-protobuf" @@ -206,9 +197,18 @@ private void setProtoHeader(HttpOutputMessage response, Message message) { response.getHeaders().set(X_PROTOBUF_MESSAGE_HEADER, message.getDescriptorForType().getFullName()); } - @Override - protected MediaType getDefaultContentType(Message message) { - return PROTOBUF; + + /** + * Create a new {@code Message.Builder} instance for the given class. + *

    This method uses a ConcurrentHashMap for caching method lookups. + */ + private static Message.Builder getMessageBuilder(Class clazz) throws Exception { + Method method = methodCache.get(clazz); + if (method == null) { + method = clazz.getMethod("newBuilder"); + methodCache.put(clazz, method); + } + return (Message.Builder) method.invoke(clazz); } } diff --git a/spring-web/src/test/java/org/springframework/http/converter/protobuf/ProtobufHttpMessageConverterTests.java b/spring-web/src/test/java/org/springframework/http/converter/protobuf/ProtobufHttpMessageConverterTests.java index 87751c6fe8..0a82b7d469 100644 --- a/spring-web/src/test/java/org/springframework/http/converter/protobuf/ProtobufHttpMessageConverterTests.java +++ b/spring-web/src/test/java/org/springframework/http/converter/protobuf/ProtobufHttpMessageConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,9 +31,9 @@ import static org.junit.Assert.*; import static org.mockito.Mockito.*; - /** - * Test suite for {@link ProtobufHttpMessageConverter} + * Test suite for {@link ProtobufHttpMessageConverter}. + * * @author Alex Antonov */ public class ProtobufHttpMessageConverterTests { @@ -52,6 +52,7 @@ public void setUp() { this.testMsg = Msg.newBuilder().setFoo("Foo").setBlah(SecondMsg.newBuilder().setBlah(123).build()).build(); } + @Test public void extensionRegistryInitialized() { verify(this.registryInitializer, times(1)).initializeExtensionRegistry(anyObject()); @@ -61,7 +62,8 @@ public void extensionRegistryInitialized() { public void extensionRegistryNull() { try { new ProtobufHttpMessageConverter(null); - } catch (Exception e) { + } + catch (Exception ex) { fail("Unable to create ProtobufHttpMessageConverter with null extensionRegistry"); } } @@ -73,6 +75,7 @@ public void canRead() { assertTrue(this.converter.canRead(Msg.class, MediaType.APPLICATION_JSON)); assertTrue(this.converter.canRead(Msg.class, MediaType.APPLICATION_XML)); assertTrue(this.converter.canRead(Msg.class, MediaType.TEXT_PLAIN)); + // only supported as an output format assertFalse(this.converter.canRead(Msg.class, MediaType.TEXT_HTML)); } @@ -114,9 +117,11 @@ public void write() throws IOException { Message result = Msg.parseFrom(outputMessage.getBodyAsBytes()); assertEquals(this.testMsg, result); - String messageHeader = outputMessage.getHeaders().getFirst(ProtobufHttpMessageConverter.X_PROTOBUF_MESSAGE_HEADER); + String messageHeader = + outputMessage.getHeaders().getFirst(ProtobufHttpMessageConverter.X_PROTOBUF_MESSAGE_HEADER); assertEquals("Msg", messageHeader); - String schemaHeader = outputMessage.getHeaders().getFirst(ProtobufHttpMessageConverter.X_PROTOBUF_SCHEMA_HEADER); + String schemaHeader = + outputMessage.getHeaders().getFirst(ProtobufHttpMessageConverter.X_PROTOBUF_SCHEMA_HEADER); assertEquals("sample.proto", schemaHeader); } @@ -132,4 +137,5 @@ public void getContentLength() throws Exception { this.converter.write(this.testMsg, contentType, outputMessage); assertEquals(-1, outputMessage.getHeaders().getContentLength()); } + } From 4c41b9d4b902f619665186e4427293e138aac3eb Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 14 Apr 2016 16:39:36 +0200 Subject: [PATCH 128/344] Polishing (cherry picked from commit 3b44c47) --- .../context/annotation/LoadTimeWeavingConfiguration.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/annotation/LoadTimeWeavingConfiguration.java b/spring-context/src/main/java/org/springframework/context/annotation/LoadTimeWeavingConfiguration.java index f4ebae873a..5cab2a7eee 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/LoadTimeWeavingConfiguration.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/LoadTimeWeavingConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -75,12 +75,12 @@ public LoadTimeWeaver loadTimeWeaver() { LoadTimeWeaver loadTimeWeaver = null; if (this.ltwConfigurer != null) { - // the user has provided a custom LTW instance - loadTimeWeaver = ltwConfigurer.getLoadTimeWeaver(); + // The user has provided a custom LoadTimeWeaver instance + loadTimeWeaver = this.ltwConfigurer.getLoadTimeWeaver(); } if (loadTimeWeaver == null) { - // no custom LTW provided -> fall back to the default + // No custom LoadTimeWeaver provided -> fall back to the default loadTimeWeaver = new DefaultContextLoadTimeWeaver(this.beanClassLoader); } From f83e149f8a1553f0eb03d65ff68e3c123f025d24 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 15 Apr 2016 12:07:31 +0200 Subject: [PATCH 129/344] Strong Hibernate 5.1 recommendation Issue: SPR-13480 Issue: SPR-14176 (cherry picked from commit 44a9c49) --- src/asciidoc/data-access.adoc | 53 +++++++++++++++++------------------ 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/src/asciidoc/data-access.adoc b/src/asciidoc/data-access.adoc index cab8fffaf3..0cef793963 100644 --- a/src/asciidoc/data-access.adoc +++ b/src/asciidoc/data-access.adoc @@ -334,8 +334,8 @@ the `HibernateTransactionManager` needs a reference to the `SessionFactory`. [source,xml,indent=0] [subs="verbatim,quotes"] ---- - - + + org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml @@ -348,8 +348,8 @@ the `HibernateTransactionManager` needs a reference to the `SessionFactory`. - - + + ---- @@ -1296,7 +1296,7 @@ These default settings can be changed; the various properties of the `@Transacti annotation are summarized in the following table: [[tx-attransactional-properties]] -.@ +.@Transactional Settings |=== | Property| Type| Description @@ -1778,7 +1778,7 @@ a transaction. You then pass an instance of your custom `TransactionCallback` to // use constructor-injection to supply the PlatformTransactionManager public SimpleService(PlatformTransactionManager transactionManager) { - Assert.notNull(transactionManager, "The ''transactionManager'' argument must not be null."); + Assert.notNull(transactionManager, "The 'transactionManager' argument must not be null."); this.transactionTemplate = new TransactionTemplate(transactionManager); } @@ -1845,7 +1845,7 @@ a specific `TransactionTemplate:` private final TransactionTemplate transactionTemplate; public SimpleService(PlatformTransactionManager transactionManager) { - Assert.notNull(transactionManager, "The ''transactionManager'' argument must not be null."); + Assert.notNull(transactionManager, "The 'transactionManager' argument must not be null."); this.transactionTemplate = new TransactionTemplate(transactionManager); // the transaction settings can be set here explicitly if so desired @@ -2074,12 +2074,11 @@ the root exception. These exceptions wrap the original exception so there is nev risk that one might lose any information as to what might have gone wrong. In addition to JDBC exceptions, Spring can also wrap Hibernate-specific exceptions, -converting them from proprietary, checked exceptions (in the case of versions of -Hibernate prior to Hibernate 3.0), to a set of focused runtime exceptions (the same is -true for JDO and JPA exceptions). This allows one to handle most persistence exceptions, -which are non-recoverable, only in the appropriate layers, without having annoying -boilerplate catch-and-throw blocks and exception declarations in one's DAOs. (One can -still trap and handle exceptions anywhere one needs to though.) As mentioned above, JDBC +converting them to a set of focused runtime exceptions (the same is true for JDO and +JPA exceptions). This allows one to handle most persistence exceptions, which are +non-recoverable, only in the appropriate layers, without having annoying boilerplate +catch-and-throw blocks and exception declarations in one's DAOs. (One can still trap +and handle exceptions anywhere one needs to though.) As mentioned above, JDBC exceptions (including database-specific dialects) are also converted to the same hierarchy, meaning that one can perform some operations with JDBC within a consistent programming model. @@ -2919,10 +2918,6 @@ query methods, one for an `int` and one that queries for a `String`. public String getName() { return this.jdbcTemplate.queryForObject("select name from mytable", String.class); } - - public void setDataSource(DataSource dataSource) { - this.dataSource = dataSource; - } } ---- @@ -5107,7 +5102,7 @@ exception hierarchies. [[orm-hibernate]] === Hibernate -We will start with a coverage of http://www.hibernate.org/[Hibernate 3] in a Spring +We will start with a coverage of http://www.hibernate.org/[Hibernate 5] in a Spring environment, using it to demonstrate the approach that Spring takes towards integrating O/R mappers. This section will cover many issues in detail and show different variations of DAO implementations and transaction demarcation. Most of these patterns can be @@ -5116,7 +5111,9 @@ chapter will then cover the other ORM technologies, showing briefer examples the [NOTE] ==== -As of Spring 4.0, Spring requires Hibernate 3.6 or later. +As of Spring 4.0, Spring requires Hibernate 3.6 or later. Note that the Hibernate team +stopped supporting Hibernate 3 years ago and even phased out support for Hibernate 4.x +in late 2015. We therefore recommend Hibernate 5.1 and higher from a 2016+ perspective. ==== @@ -5145,7 +5142,7 @@ JDBC `DataSource` and a Hibernate `SessionFactory` on top of it: - + @@ -5181,8 +5178,8 @@ is typically not common outside of an EJB context. [[orm-hibernate-straight]] -==== Implementing DAOs based on plain Hibernate 3 API -Hibernate 3 has a feature called contextual sessions, wherein Hibernate itself manages +==== Implementing DAOs based on plain Hibernate API +Hibernate has a feature called contextual sessions, wherein Hibernate itself manages one current `Session` per transaction. This is roughly equivalent to Spring's synchronization of one Hibernate `Session` per transaction. A corresponding DAO implementation resembles the following example, based on the plain Hibernate API: @@ -5251,7 +5248,7 @@ the return of the current `Session` associated with the ongoing JTA transaction, This behavior applies regardless of whether you are using Spring's `JtaTransactionManager`, EJB container managed transactions (CMTs), or JTA. -In summary: you can implement DAOs based on the plain Hibernate 3 API, while still being +In summary: you can implement DAOs based on the plain Hibernate API, while still being able to participate in Spring-managed transactions. @@ -5296,7 +5293,7 @@ XML, for a simple service class: + class="org.springframework.orm.hibernate5.HibernateTransactionManager"> @@ -5398,7 +5395,7 @@ provide is the TransactionManager implementation and a "" + class="org.springframework.orm.hibernate5.HibernateTransactionManager"> @@ -5429,7 +5426,7 @@ and an example for a business method implementation: ---- - + @@ -5509,7 +5506,7 @@ long as it is using `JtaTransactionManager` as the strategy. + class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"> @@ -5525,7 +5522,7 @@ long as it is using `JtaTransactionManager` as the strategy. + class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"> From 0075240d8f2b0a3290ff10f7c504809737db4dc7 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Fri, 15 Apr 2016 17:54:37 +0200 Subject: [PATCH 130/344] Change Eclipse Settings to allow forbidden references Spring uses lots of forbidden references. The current Eclipse settings set forbiddenReference=error This was not ideal because there were lots of errors in the workspace. This commit changes forbiddenReference=warning since we nee to allow forbidden references. (cherry picked from commit 6489a76) --- src/eclipse/org.eclipse.jdt.core.prefs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eclipse/org.eclipse.jdt.core.prefs b/src/eclipse/org.eclipse.jdt.core.prefs index 74687b5d18..3f1149f8da 100644 --- a/src/eclipse/org.eclipse.jdt.core.prefs +++ b/src/eclipse/org.eclipse.jdt.core.prefs @@ -42,7 +42,7 @@ org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning -org.eclipse.jdt.core.compiler.problem.forbiddenReference=error +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning From 9438c4185312ba9fbbeacd82bb0ac2706e723856 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Fri, 15 Apr 2016 18:49:46 +0200 Subject: [PATCH 131/344] Add --no-daemon to Gradle import Previously using the Gradle Daemon with import eclipse scripts would cause issues with the merge plugin. Specifically, projects would randomly not be associated to the classpath. This change explicitly adds --no-daemon in the import scripts to ensure that the problem does not occur. Issue SPR-14102 (cherry picked from commit 970dc4a) --- import-into-eclipse.bat | 6 +++--- import-into-eclipse.sh | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/import-into-eclipse.bat b/import-into-eclipse.bat index 8ba6e975fe..807cd4f07f 100644 --- a/import-into-eclipse.bat +++ b/import-into-eclipse.bat @@ -29,8 +29,8 @@ REM - generates OXM test classes to avoid errors on import into Eclipse REM - generates metadata for all subprojects REM - skips metadata gen for the root project (-x :eclipse) to work REM around Eclipse's inability to import hierarchical project structures -REM SET COMMAND="./gradlew cleanEclipse :spring-oxm:compileTestJava eclipse -x :eclipse" -SET COMMAND=gradlew cleanEclipse :spring-oxm:compileTestJava eclipse -x :eclipse +REM SET COMMAND="./gradlew --no-daemon cleanEclipse :spring-oxm:compileTestJava eclipse -x :eclipse" +SET COMMAND=gradlew --no-daemon cleanEclipse :spring-oxm:compileTestJava eclipse -x :eclipse echo. echo ----------------------------------------------------------------------- @@ -69,7 +69,7 @@ echo When the above is complete, return here and press the enter key. pause -set COMMAND=gradlew :eclipse +set COMMAND=gradlew --no-daemon :eclipse echo. echo ----------------------------------------------------------------------- diff --git a/import-into-eclipse.sh b/import-into-eclipse.sh index c682fbc2a4..b87c12b63c 100755 --- a/import-into-eclipse.sh +++ b/import-into-eclipse.sh @@ -42,7 +42,7 @@ read # - generates metadata for all subprojects # - skips metadata gen for the root project (-x :eclipse) to work # around Eclipse's inability to import hierarchical project structures -COMMAND="./gradlew cleanEclipse :spring-oxm:compileTestJava eclipse -x :eclipse" +COMMAND="./gradlew --no-daemon cleanEclipse :spring-oxm:compileTestJava eclipse -x :eclipse" cat < Date: Fri, 15 Apr 2016 22:26:58 +0200 Subject: [PATCH 132/344] Consistent public constructors in ast package Issue: SPR-14181 (cherry picked from commit e38bfb1) --- .../springframework/expression/spel/ast/BooleanLiteral.java | 5 +++-- .../springframework/expression/spel/ast/FloatLiteral.java | 6 ++++-- .../org/springframework/expression/spel/ast/IntLiteral.java | 6 ++++-- .../springframework/expression/spel/ast/LongLiteral.java | 5 +++-- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/BooleanLiteral.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/BooleanLiteral.java index 59afccb055..e7448f31ff 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/BooleanLiteral.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/BooleanLiteral.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.springframework.asm.MethodVisitor; @@ -20,7 +21,7 @@ import org.springframework.expression.spel.support.BooleanTypedValue; /** - * Represents the literal values TRUE and FALSE. + * Represents the literal values {@code TRUE} and {@code FALSE}. * * @author Andy Clement * @since 3.0 diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/FloatLiteral.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/FloatLiteral.java index 4e70ae2ac2..e2c652a2dd 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/FloatLiteral.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/FloatLiteral.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,7 +31,8 @@ public class FloatLiteral extends Literal { private final TypedValue value; - FloatLiteral(String payload, int pos, float value) { + + public FloatLiteral(String payload, int pos, float value) { super(payload, pos); this.value = new TypedValue(value); this.exitTypeDescriptor = "F"; @@ -53,4 +54,5 @@ public void generateCode(MethodVisitor mv, CodeFlow cf) { mv.visitLdcInsn(this.value.getValue()); cf.pushDescriptor(this.exitTypeDescriptor); } + } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/IntLiteral.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/IntLiteral.java index bc65136ccf..9d670d4cff 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/IntLiteral.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/IntLiteral.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel.ast; import org.springframework.asm.MethodVisitor; @@ -29,7 +30,8 @@ public class IntLiteral extends Literal { private final TypedValue value; - IntLiteral(String payload, int pos, int value) { + + public IntLiteral(String payload, int pos, int value) { super(payload, pos); this.value = new TypedValue(value); this.exitTypeDescriptor = "I"; diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/LongLiteral.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/LongLiteral.java index 28e3892fce..4f03d8d292 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/LongLiteral.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/LongLiteral.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,12 +31,13 @@ public class LongLiteral extends Literal { private final TypedValue value; - LongLiteral(String payload, int pos, long value) { + public LongLiteral(String payload, int pos, long value) { super(payload, pos); this.value = new TypedValue(value); this.exitTypeDescriptor = "J"; } + @Override public TypedValue getLiteralValue() { return this.value; From e205a1b30a5caf70751446f31120402df353c8ec Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 18 Apr 2016 14:43:05 +0200 Subject: [PATCH 133/344] Upgrade to EhCache 3.0 GA Issue: SPR-14185 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 1c0c22508c..15851a7478 100644 --- a/build.gradle +++ b/build.gradle @@ -31,7 +31,7 @@ configure(allprojects) { project -> ext.eclipselinkVersion = "2.4.2" ext.ehcacheVersion = "2.10.1" ext.ehcachejcacheVersion = "1.0.1" - ext.ehcache3Version = "3.0.0.rc3" + ext.ehcache3Version = "3.0.0" ext.ejbApiVersion = "3.0" ext.fileuploadVersion = "1.3.1" ext.freemarkerVersion = "2.3.23" From 2eab3df4d161c5577af4d3fc85a4e056054c07cf Mon Sep 17 00:00:00 2001 From: Venil Noronha Date: Thu, 14 Apr 2016 18:10:20 +0530 Subject: [PATCH 134/344] Delegate sendMessage Issue: SPR-14139 --- .../web/socket/handler/WebSocketSessionDecorator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/handler/WebSocketSessionDecorator.java b/spring-websocket/src/main/java/org/springframework/web/socket/handler/WebSocketSessionDecorator.java index d66a74de01..9bf3b685c9 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/handler/WebSocketSessionDecorator.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/handler/WebSocketSessionDecorator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -145,7 +145,7 @@ public boolean isOpen() { @Override public void sendMessage(WebSocketMessage message) throws IOException { - + this.delegate.sendMessage(message); } @Override From e67a274b32818b00299ae378b3f2eb57766d5757 Mon Sep 17 00:00:00 2001 From: Venil Noronha Date: Thu, 14 Apr 2016 18:32:50 +0530 Subject: [PATCH 135/344] Remove isOpen assertion when sending WebSocket message Issue: SPR-14138 --- .../web/socket/adapter/AbstractWebSocketSession.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/adapter/AbstractWebSocketSession.java b/spring-websocket/src/main/java/org/springframework/web/socket/adapter/AbstractWebSocketSession.java index 5b49d3bb78..7db88740f4 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/adapter/AbstractWebSocketSession.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/adapter/AbstractWebSocketSession.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -95,7 +95,6 @@ protected final void checkNativeSessionInitialized() { public final void sendMessage(WebSocketMessage message) throws IOException { checkNativeSessionInitialized(); - Assert.isTrue(isOpen(), "Cannot send message after connection closed."); if (logger.isTraceEnabled()) { logger.trace("Sending " + message + ", " + this); From 6314df367ca037dce2f54df95e0fd1c2dda2656e Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Thu, 21 Apr 2016 14:07:27 -0400 Subject: [PATCH 136/344] Update spring-mvc-3.2.xsd The backport in commit cf39078 changed the spring-mvc-3.2.xsd. This change updates the same in the 4.2.x branch. Issue: SPR-14186 --- .../web/servlet/config/spring-mvc-3.2.xsd | 57 ++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/spring-webmvc/src/main/resources/org/springframework/web/servlet/config/spring-mvc-3.2.xsd b/spring-webmvc/src/main/resources/org/springframework/web/servlet/config/spring-mvc-3.2.xsd index 9740c087aa..2649e87793 100644 --- a/spring-webmvc/src/main/resources/org/springframework/web/servlet/config/spring-mvc-3.2.xsd +++ b/spring-webmvc/src/main/resources/org/springframework/web/servlet/config/spring-mvc-3.2.xsd @@ -1,5 +1,4 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Date: Tue, 26 Apr 2016 17:22:26 -0400 Subject: [PATCH 137/344] XML config properly initialize WS message broker stats Issue: SPR-14190 --- .../socket/config/MessageBrokerBeanDefinitionParser.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/config/MessageBrokerBeanDefinitionParser.java b/spring-websocket/src/main/java/org/springframework/web/socket/config/MessageBrokerBeanDefinitionParser.java index 6e31e25dc8..12778c9d80 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/config/MessageBrokerBeanDefinitionParser.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/config/MessageBrokerBeanDefinitionParser.java @@ -590,10 +590,9 @@ private void registerWebSocketMessageBrokerStats(RootBeanDefinition broker, Runt if (context.getRegistry().containsBeanDefinition(name)) { beanDef.getPropertyValues().add("outboundChannelExecutor", context.getRegistry().getBeanDefinition(name)); } - name = SCHEDULER_BEAN_NAME; - if (context.getRegistry().containsBeanDefinition(name)) { - beanDef.getPropertyValues().add("sockJsTaskScheduler", context.getRegistry().getBeanDefinition(name)); - } + Object scheduler = WebSocketNamespaceUtils.registerScheduler(SCHEDULER_BEAN_NAME, context, source); + beanDef.getPropertyValues().add("sockJsTaskScheduler", scheduler); + registerBeanDefByName("webSocketMessageBrokerStats", beanDef, context, source); } From b9fa67e0e68468a715c514780eadc52a49855eeb Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 26 Apr 2016 17:00:59 +0200 Subject: [PATCH 138/344] FastByteArrayInputStream returns correct count from read(byte[]) Issue: SPR-14209 (cherry picked from commit 9bf5a5c) --- .../util/FastByteArrayOutputStream.java | 5 +- .../util/FastByteArrayOutputStreamTests.java | 89 +++++++++++-------- 2 files changed, 56 insertions(+), 38 deletions(-) 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 c54591f005..09cb9e8cc0 100644 --- a/spring-core/src/main/java/org/springframework/util/FastByteArrayOutputStream.java +++ b/spring-core/src/main/java/org/springframework/util/FastByteArrayOutputStream.java @@ -53,7 +53,7 @@ public class FastByteArrayOutputStream extends OutputStream { // The size, in bytes, to use when allocating the first byte[] private final int initialBlockSize; - // The size, in bytes, to use when allocating the next next byte[] + // The size, in bytes, to use when allocating the next byte[] private int nextBlockSize = 0; // The number of bytes in previous buffers. @@ -421,7 +421,8 @@ else if (off < 0) { System.arraycopy(this.currentBuffer, this.nextIndexInCurrentBuffer, b, off, bytesToCopy); this.totalBytesRead += bytesToCopy; this.nextIndexInCurrentBuffer += bytesToCopy; - return (bytesToCopy + read(b, off + bytesToCopy, len - bytesToCopy)); + int remaining = read(b, off + bytesToCopy, len - bytesToCopy); + return bytesToCopy + Math.max(remaining, 0); } else { if (this.buffersIterator.hasNext()) { 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 9eedeca8ee..15217c87b3 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-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,8 +16,6 @@ package org.springframework.util; -import static org.junit.Assert.*; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @@ -25,6 +23,8 @@ import org.junit.Before; import org.junit.Test; +import static org.junit.Assert.*; + /** * Test suite for {@link FastByteArrayOutputStream} * @author Craig Andrews @@ -37,21 +37,23 @@ public class FastByteArrayOutputStreamTests { private byte[] helloBytes; + @Before public void setUp() throws Exception { this.os = new FastByteArrayOutputStream(INITIAL_CAPACITY); this.helloBytes = "Hello World".getBytes("UTF-8"); } + @Test public void size() throws Exception { - this.os.write(helloBytes); - assertEquals(this.os.size(), helloBytes.length); + this.os.write(this.helloBytes); + assertEquals(this.os.size(), this.helloBytes.length); } @Test public void resize() throws Exception { - this.os.write(helloBytes); + this.os.write(this.helloBytes); int sizeBefore = this.os.size(); this.os.resize(64); assertByteArrayEqualsString(this.os); @@ -70,104 +72,118 @@ public void autoGrow() throws IOException { @Test public void write() throws Exception { - this.os.write(helloBytes); + this.os.write(this.helloBytes); assertByteArrayEqualsString(this.os); } @Test public void reset() throws Exception { - this.os.write(helloBytes); + this.os.write(this.helloBytes); assertByteArrayEqualsString(this.os); this.os.reset(); assertEquals(0, this.os.size()); - this.os.write(helloBytes); + this.os.write(this.helloBytes); assertByteArrayEqualsString(this.os); } @Test(expected = IOException.class) public void close() throws Exception { this.os.close(); - this.os.write(helloBytes); + this.os.write(this.helloBytes); } @Test public void toByteArrayUnsafe() throws Exception { - this.os.write(helloBytes); + this.os.write(this.helloBytes); assertByteArrayEqualsString(this.os); assertSame(this.os.toByteArrayUnsafe(), this.os.toByteArrayUnsafe()); - assertArrayEquals(this.os.toByteArray(), helloBytes); + assertArrayEquals(this.os.toByteArray(), this.helloBytes); } @Test public void writeTo() throws Exception { - this.os.write(helloBytes); + this.os.write(this.helloBytes); assertByteArrayEqualsString(this.os); ByteArrayOutputStream baos = new ByteArrayOutputStream(); this.os.writeTo(baos); - assertArrayEquals(baos.toByteArray(), helloBytes); + assertArrayEquals(baos.toByteArray(), this.helloBytes); } @Test(expected = IllegalArgumentException.class) public void failResize() throws Exception { - this.os.write(helloBytes); + this.os.write(this.helloBytes); this.os.resize(5); } @Test public void getInputStream() throws Exception { - this.os.write(helloBytes); + this.os.write(this.helloBytes); assertNotNull(this.os.getInputStream()); } @Test public void getInputStreamAvailable() throws Exception { - this.os.write(helloBytes); - assertEquals(this.os.getInputStream().available(), helloBytes.length); + this.os.write(this.helloBytes); + assertEquals(this.os.getInputStream().available(), this.helloBytes.length); } @Test public void getInputStreamRead() throws Exception { - this.os.write(helloBytes); + this.os.write(this.helloBytes); InputStream inputStream = this.os.getInputStream(); - assertEquals(inputStream.read(), helloBytes[0]); - assertEquals(inputStream.read(), helloBytes[1]); - assertEquals(inputStream.read(), helloBytes[2]); - assertEquals(inputStream.read(), helloBytes[3]); + assertEquals(inputStream.read(), this.helloBytes[0]); + assertEquals(inputStream.read(), this.helloBytes[1]); + assertEquals(inputStream.read(), this.helloBytes[2]); + assertEquals(inputStream.read(), this.helloBytes[3]); } @Test public void getInputStreamReadAll() throws Exception { - this.os.write(helloBytes); + this.os.write(this.helloBytes); InputStream inputStream = this.os.getInputStream(); byte[] actual = new byte[inputStream.available()]; int bytesRead = inputStream.read(actual); - assertEquals(bytesRead, helloBytes.length); - assertArrayEquals(actual, helloBytes); + assertEquals(this.helloBytes.length, bytesRead); + assertArrayEquals(this.helloBytes, actual); + assertEquals(0, inputStream.available()); + } + + @Test + public void getInputStreamReadBeyondEndOfStream() throws Exception { + this.os.write(this.helloBytes); + InputStream inputStream = os.getInputStream(); + byte[] actual = new byte[inputStream.available() + 1]; + int bytesRead = inputStream.read(actual); + assertEquals(this.helloBytes.length, bytesRead); + for (int i = 0; i < bytesRead; i++) { + assertEquals(this.helloBytes[i], actual[i]); + } + assertEquals(0, actual[this.helloBytes.length]); assertEquals(0, inputStream.available()); } @Test public void getInputStreamSkip() throws Exception { - this.os.write(helloBytes); + this.os.write(this.helloBytes); InputStream inputStream = this.os.getInputStream(); - assertEquals(inputStream.read(), helloBytes[0]); + assertEquals(inputStream.read(), this.helloBytes[0]); assertEquals(inputStream.skip(1), 1); - assertEquals(inputStream.read(), helloBytes[2]); - assertEquals(helloBytes.length - 3, inputStream.available()); + assertEquals(inputStream.read(), this.helloBytes[2]); + assertEquals(this.helloBytes.length - 3, inputStream.available()); } @Test public void getInputStreamSkipAll() throws Exception { - this.os.write(helloBytes); + this.os.write(this.helloBytes); InputStream inputStream = this.os.getInputStream(); - assertEquals(inputStream.skip(1000), helloBytes.length); + assertEquals(inputStream.skip(1000), this.helloBytes.length); assertEquals(0, inputStream.available()); } @Test public void updateMessageDigest() throws Exception { StringBuilder builder = new StringBuilder("\"0"); - this.os.write(helloBytes); + this.os.write(this.helloBytes); InputStream inputStream = this.os.getInputStream(); DigestUtils.appendMd5DigestAsHex(inputStream, builder); builder.append("\""); @@ -179,8 +195,8 @@ public void updateMessageDigest() throws Exception { public void updateMessageDigestManyBuffers() throws Exception { StringBuilder builder = new StringBuilder("\"0"); // filling at least one 256 buffer - for( int i=0; i < 30; i++) { - this.os.write(helloBytes); + for ( int i = 0; i < 30; i++) { + this.os.write(this.helloBytes); } InputStream inputStream = this.os.getInputStream(); DigestUtils.appendMd5DigestAsHex(inputStream, builder); @@ -189,8 +205,9 @@ public void updateMessageDigestManyBuffers() throws Exception { assertEquals("\"06225ca1e4533354c516e74512065331d\"", actual); } + private void assertByteArrayEqualsString(FastByteArrayOutputStream actual) { - assertArrayEquals(helloBytes, actual.toByteArray()); + assertArrayEquals(this.helloBytes, actual.toByteArray()); } } From 002d6d70a511b5cfb61d287e068fa4b4fcf0583e Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 26 Apr 2016 17:03:00 +0200 Subject: [PATCH 139/344] SocketUtils considers port range including maxPort Issue: SPR-14211 (cherry picked from commit bf7b475) --- .../org/springframework/util/SocketUtils.java | 26 ++++----- .../util/SocketUtilsTests.java | 58 ++++++++++--------- 2 files changed, 43 insertions(+), 41 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/util/SocketUtils.java b/spring-core/src/main/java/org/springframework/util/SocketUtils.java index 6754a30a8b..091a3febbc 100644 --- a/spring-core/src/main/java/org/springframework/util/SocketUtils.java +++ b/spring-core/src/main/java/org/springframework/util/SocketUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -192,14 +192,14 @@ public static SortedSet findAvailableUdpPorts(int numRequested, int min } - private static enum SocketType { + private enum SocketType { TCP { @Override protected boolean isPortAvailable(int port) { try { - ServerSocket serverSocket = ServerSocketFactory.getDefault().createServerSocket(port, 1, - InetAddress.getByName("localhost")); + ServerSocket serverSocket = ServerSocketFactory.getDefault().createServerSocket( + port, 1, InetAddress.getByName("localhost")); serverSocket.close(); return true; } @@ -238,7 +238,7 @@ protected boolean isPortAvailable(int port) { */ private int findRandomPort(int minPort, int maxPort) { int portRange = maxPort - minPort; - return minPort + random.nextInt(portRange); + return minPort + random.nextInt(portRange + 1); } /** @@ -251,7 +251,7 @@ private int findRandomPort(int minPort, int maxPort) { */ int findAvailablePort(int minPort, int maxPort) { Assert.isTrue(minPort > 0, "'minPort' must be greater than 0"); - Assert.isTrue(maxPort > minPort, "'maxPort' must be greater than 'minPort'"); + Assert.isTrue(maxPort >= minPort, "'maxPort' must be greater than or equals 'minPort'"); Assert.isTrue(maxPort <= PORT_RANGE_MAX, "'maxPort' must be less than or equal to " + PORT_RANGE_MAX); int portRange = maxPort - minPort; @@ -260,8 +260,8 @@ int findAvailablePort(int minPort, int maxPort) { do { if (++searchCounter > portRange) { throw new IllegalStateException(String.format( - "Could not find an available %s port in the range [%d, %d] after %d attempts", name(), minPort, - maxPort, searchCounter)); + "Could not find an available %s port in the range [%d, %d] after %d attempts", + name(), minPort, maxPort, searchCounter)); } candidatePort = findRandomPort(minPort, maxPort); } @@ -285,18 +285,18 @@ SortedSet findAvailablePorts(int numRequested, int minPort, int maxPort Assert.isTrue(maxPort <= PORT_RANGE_MAX, "'maxPort' must be less than or equal to " + PORT_RANGE_MAX); Assert.isTrue(numRequested > 0, "'numRequested' must be greater than 0"); Assert.isTrue((maxPort - minPort) >= numRequested, - "'numRequested' must not be greater than 'maxPort' - 'minPort'"); + "'numRequested' must not be greater than 'maxPort' - 'minPort'"); - final SortedSet availablePorts = new TreeSet(); + SortedSet availablePorts = new TreeSet(); int attemptCount = 0; - while ((++attemptCount <= numRequested + 100) && (availablePorts.size() < numRequested)) { + while ((++attemptCount <= numRequested + 100) && availablePorts.size() < numRequested) { availablePorts.add(findAvailablePort(minPort, maxPort)); } if (availablePorts.size() != numRequested) { throw new IllegalStateException(String.format( - "Could not find %d available %s ports in the range [%d, %d]", numRequested, name(), minPort, - maxPort)); + "Could not find %d available %s ports in the range [%d, %d]", + numRequested, name(), minPort, maxPort)); } return availablePorts; diff --git a/spring-core/src/test/java/org/springframework/util/SocketUtilsTests.java b/spring-core/src/test/java/org/springframework/util/SocketUtilsTests.java index 362ef70132..8abd6c8878 100644 --- a/spring-core/src/test/java/org/springframework/util/SocketUtilsTests.java +++ b/spring-core/src/test/java/org/springframework/util/SocketUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,19 +35,7 @@ */ public class SocketUtilsTests { - private void assertPortInRange(int port, int minPort, int maxPort) { - assertTrue("port [" + port + "] >= " + minPort, port >= minPort); - assertTrue("port [" + port + "] <= " + maxPort, port <= maxPort); - } - - private void assertAvailablePorts(SortedSet ports, int numRequested, int minPort, int maxPort) { - assertEquals("number of ports requested", numRequested, ports.size()); - for (int port : ports) { - assertPortInRange(port, minPort, maxPort); - } - } - - // --- TCP ----------------------------------------------------------------- + // TCP @Test(expected = IllegalArgumentException.class) public void findAvailableTcpPortWithZeroMinPort() { @@ -70,8 +58,7 @@ public void findAvailableTcpPortWhenPortOnLoopbackInterfaceIsNotAvailable() thro int port = SocketUtils.findAvailableTcpPort(); ServerSocket socket = ServerSocketFactory.getDefault().createServerSocket(port, 1, InetAddress.getByName("localhost")); try { - // will only look for the exact port, since random.nextInt(1) always returns 0 - SocketUtils.findAvailableTcpPort(port, port + 1); + SocketUtils.findAvailableTcpPort(port, port); } finally { socket.close(); @@ -117,17 +104,8 @@ public void findAvailableTcpPortsWithRequestedNumberGreaterThanSizeOfRange() { findAvailableTcpPorts(50, 45000, 45010); } - private void findAvailableTcpPorts(int numRequested) { - SortedSet ports = SocketUtils.findAvailableTcpPorts(numRequested); - assertAvailablePorts(ports, numRequested, PORT_RANGE_MIN, PORT_RANGE_MAX); - } - private void findAvailableTcpPorts(int numRequested, int minPort, int maxPort) { - SortedSet ports = SocketUtils.findAvailableTcpPorts(numRequested, minPort, maxPort); - assertAvailablePorts(ports, numRequested, minPort, maxPort); - } - - // --- UDP ----------------------------------------------------------------- + // UDP @Test(expected = IllegalArgumentException.class) public void findAvailableUdpPortWithZeroMinPort() { @@ -150,8 +128,8 @@ public void findAvailableUdpPortWhenPortOnLoopbackInterfaceIsNotAvailable() thro int port = SocketUtils.findAvailableUdpPort(); DatagramSocket socket = new DatagramSocket(port, InetAddress.getByName("localhost")); try { - // will only look for the exact port, since random.nextInt(1) always returns 0 - SocketUtils.findAvailableUdpPort(port, port + 1); + // will only look for the exact port + SocketUtils.findAvailableUdpPort(port, port); } finally { socket.close(); @@ -197,6 +175,19 @@ public void findAvailableUdpPortsWithRequestedNumberGreaterThanSizeOfRange() { findAvailableUdpPorts(50, 45000, 45010); } + + // Helpers + + private void findAvailableTcpPorts(int numRequested) { + SortedSet ports = SocketUtils.findAvailableTcpPorts(numRequested); + assertAvailablePorts(ports, numRequested, PORT_RANGE_MIN, PORT_RANGE_MAX); + } + + private void findAvailableTcpPorts(int numRequested, int minPort, int maxPort) { + SortedSet ports = SocketUtils.findAvailableTcpPorts(numRequested, minPort, maxPort); + assertAvailablePorts(ports, numRequested, minPort, maxPort); + } + private void findAvailableUdpPorts(int numRequested) { SortedSet ports = SocketUtils.findAvailableUdpPorts(numRequested); assertAvailablePorts(ports, numRequested, PORT_RANGE_MIN, PORT_RANGE_MAX); @@ -206,5 +197,16 @@ private void findAvailableUdpPorts(int numRequested, int minPort, int maxPort) { SortedSet ports = SocketUtils.findAvailableUdpPorts(numRequested, minPort, maxPort); assertAvailablePorts(ports, numRequested, minPort, maxPort); } + private void assertPortInRange(int port, int minPort, int maxPort) { + assertTrue("port [" + port + "] >= " + minPort, port >= minPort); + assertTrue("port [" + port + "] <= " + maxPort, port <= maxPort); + } + + private void assertAvailablePorts(SortedSet ports, int numRequested, int minPort, int maxPort) { + assertEquals("number of ports requested", numRequested, ports.size()); + for (int port : ports) { + assertPortInRange(port, minPort, maxPort); + } + } } From 6a3d4940b57ca54e0f4c955df828d92cc586bb6d Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 26 Apr 2016 17:03:57 +0200 Subject: [PATCH 140/344] SpringServletContainerInitializer does not log WebApplicationInitializer class names Issue: SPR-14213 (cherry picked from commit f73df2e) --- .../web/SpringServletContainerInitializer.java | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/SpringServletContainerInitializer.java b/spring-web/src/main/java/org/springframework/web/SpringServletContainerInitializer.java index 7b4124263b..b5a43b7ba5 100644 --- a/spring-web/src/main/java/org/springframework/web/SpringServletContainerInitializer.java +++ b/spring-web/src/main/java/org/springframework/web/SpringServletContainerInitializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -113,18 +113,14 @@ public class SpringServletContainerInitializer implements ServletContainerInitia /** * Delegate the {@code ServletContext} to any {@link WebApplicationInitializer} * implementations present on the application classpath. - * *

    Because this class declares @{@code HandlesTypes(WebApplicationInitializer.class)}, * Servlet 3.0+ containers will automatically scan the classpath for implementations * of Spring's {@code WebApplicationInitializer} interface and provide the set of all * such types to the {@code webAppInitializerClasses} parameter of this method. - * - *

    If no {@code WebApplicationInitializer} implementations are found on the - * classpath, this method is effectively a no-op. An INFO-level log message will be - * issued notifying the user that the {@code ServletContainerInitializer} has indeed - * been invoked but that no {@code WebApplicationInitializer} implementations were - * found. - * + *

    If no {@code WebApplicationInitializer} implementations are found on the classpath, + * this method is effectively a no-op. An INFO-level log message will be issued notifying + * the user that the {@code ServletContainerInitializer} has indeed been invoked but that + * no {@code WebApplicationInitializer} implementations were found. *

    Assuming that one or more {@code WebApplicationInitializer} types are detected, * they will be instantiated (and sorted if the @{@link * org.springframework.core.annotation.Order @Order} annotation is present or @@ -134,7 +130,6 @@ public class SpringServletContainerInitializer implements ServletContainerInitia * that each instance may register and configure servlets such as Spring's * {@code DispatcherServlet}, listeners such as Spring's {@code ContextLoaderListener}, * or any other Servlet API componentry such as filters. - * * @param webAppInitializerClasses all implementations of * {@link WebApplicationInitializer} found on the application classpath * @param servletContext the servlet context to be initialized @@ -168,9 +163,8 @@ public void onStartup(Set> webAppInitializerClasses, ServletContext ser return; } + servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath"); AnnotationAwareOrderComparator.sort(initializers); - servletContext.log("Spring WebApplicationInitializers detected on classpath: " + initializers); - for (WebApplicationInitializer initializer : initializers) { initializer.onStartup(servletContext); } From 75a8f5b3cacdc89d0ac0e60335274988dd36ea0d Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 27 Apr 2016 21:30:46 +0200 Subject: [PATCH 141/344] ApplicationListenerDetector explicitly prevents serialization of its ApplicationContext reference Issue: SPR-14214 (cherry picked from commit e0734ae) --- .../PostProcessorRegistrationDelegate.java | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/support/PostProcessorRegistrationDelegate.java b/spring-context/src/main/java/org/springframework/context/support/PostProcessorRegistrationDelegate.java index aac3383938..70fd2e833b 100644 --- a/spring-context/src/main/java/org/springframework/context/support/PostProcessorRegistrationDelegate.java +++ b/spring-context/src/main/java/org/springframework/context/support/PostProcessorRegistrationDelegate.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -344,16 +344,23 @@ private boolean isInfrastructureBean(String beanName) { /** - * BeanPostProcessor that detects beans which implement the ApplicationListener interface. - * This catches beans that can't reliably be detected by getBeanNamesForType. + * {@code BeanPostProcessor} that detects beans which implement the {@code ApplicationListener} + * interface. This catches beans that can't reliably be detected by {@code getBeanNamesForType} + * and related operations which only work against top-level beans. + * + *

    With standard Java serialization, this post-processor won't get serialized as part of + * {@code DisposableBeanAdapter} to begin with. However, with alternative serialization + * mechanisms, {@code DisposableBeanAdapter.writeReplace} might not get used at all, so we + * defensively mark this post-processor's field state as {@code transient}. */ - private static class ApplicationListenerDetector implements MergedBeanDefinitionPostProcessor, DestructionAwareBeanPostProcessor { + private static class ApplicationListenerDetector + implements DestructionAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor { private static final Log logger = LogFactory.getLog(ApplicationListenerDetector.class); - private final AbstractApplicationContext applicationContext; + private transient final AbstractApplicationContext applicationContext; - private final Map singletonNames = new ConcurrentHashMap(256); + private transient final Map singletonNames = new ConcurrentHashMap(256); public ApplicationListenerDetector(AbstractApplicationContext applicationContext) { this.applicationContext = applicationContext; @@ -361,7 +368,7 @@ public ApplicationListenerDetector(AbstractApplicationContext applicationContext @Override public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) { - if (beanDefinition.isSingleton()) { + if (this.applicationContext != null && beanDefinition.isSingleton()) { this.singletonNames.put(beanName, Boolean.TRUE); } } @@ -373,7 +380,7 @@ public Object postProcessBeforeInitialization(Object bean, String beanName) { @Override public Object postProcessAfterInitialization(Object bean, String beanName) { - if (bean instanceof ApplicationListener) { + if (this.applicationContext != null && bean instanceof ApplicationListener) { // potentially not detected as a listener by getBeanNamesForType retrieval Boolean flag = this.singletonNames.get(beanName); if (Boolean.TRUE.equals(flag)) { From d66186a90b0b55946a86727983a7f8cb6684dd04 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 27 Apr 2016 21:35:26 +0200 Subject: [PATCH 142/344] Polishing (cherry picked from commit 8ddb432) --- .../test/web/servlet/DefaultMvcResult.java | 49 ++++++++++--------- .../test/web/servlet/MvcResult.java | 10 ++-- 2 files changed, 28 insertions(+), 31 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/DefaultMvcResult.java b/spring-test/src/main/java/org/springframework/test/web/servlet/DefaultMvcResult.java index 35a686287f..4bf5f8dd10 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/DefaultMvcResult.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/DefaultMvcResult.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.test.web.servlet; import java.util.concurrent.atomic.AtomicReference; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.util.Assert; import org.springframework.web.servlet.FlashMap; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; @@ -60,19 +60,15 @@ public DefaultMvcResult(MockHttpServletRequest request, MockHttpServletResponse this.mockResponse = response; } - @Override - public MockHttpServletResponse getResponse() { - return mockResponse; - } @Override public MockHttpServletRequest getRequest() { - return mockRequest; + return this.mockRequest; } @Override - public Object getHandler() { - return this.handler; + public MockHttpServletResponse getResponse() { + return this.mockResponse; } public void setHandler(Object handler) { @@ -80,17 +76,17 @@ public void setHandler(Object handler) { } @Override - public HandlerInterceptor[] getInterceptors() { - return this.interceptors; + public Object getHandler() { + return this.handler; } - public void setInterceptors(HandlerInterceptor[] interceptors) { + public void setInterceptors(HandlerInterceptor... interceptors) { this.interceptors = interceptors; } @Override - public Exception getResolvedException() { - return this.resolvedException; + public HandlerInterceptor[] getInterceptors() { + return this.interceptors; } public void setResolvedException(Exception resolvedException) { @@ -98,17 +94,22 @@ public void setResolvedException(Exception resolvedException) { } @Override - public ModelAndView getModelAndView() { - return this.modelAndView; + public Exception getResolvedException() { + return this.resolvedException; } public void setModelAndView(ModelAndView mav) { this.modelAndView = mav; } + @Override + public ModelAndView getModelAndView() { + return this.modelAndView; + } + @Override public FlashMap getFlashMap() { - return RequestContextUtils.getOutputFlashMap(mockRequest); + return RequestContextUtils.getOutputFlashMap(this.mockRequest); } public void setAsyncResult(Object asyncResult) { @@ -122,7 +123,6 @@ public Object getAsyncResult() { @Override public Object getAsyncResult(long timeToWait) { - if (this.mockRequest.getAsyncContext() != null) { timeToWait = (timeToWait == -1 ? this.mockRequest.getAsyncContext().getTimeout() : timeToWait); } @@ -131,7 +131,7 @@ public Object getAsyncResult(long timeToWait) { long endTime = System.currentTimeMillis() + timeToWait; while (System.currentTimeMillis() < endTime && this.asyncResult.get() == RESULT_NONE) { try { - Thread.sleep(200); + Thread.sleep(100); } catch (InterruptedException ex) { throw new IllegalStateException("Interrupted while waiting for " + @@ -140,11 +140,12 @@ public Object getAsyncResult(long timeToWait) { } } - Assert.state(this.asyncResult.get() != RESULT_NONE, - "Async result for handler [" + this.handler + "] " + - "was not set during the specified timeToWait=" + timeToWait); - - return this.asyncResult.get(); + Object result = this.asyncResult.get(); + if (result == RESULT_NONE) { + throw new IllegalStateException("Async result for handler [" + this.handler + "] " + + "was not set during the specified timeToWait=" + timeToWait); + } + return result; } } diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/MvcResult.java b/spring-test/src/main/java/org/springframework/test/web/servlet/MvcResult.java index 0aabe54563..a0676a238e 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/MvcResult.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/MvcResult.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License; Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -64,7 +64,6 @@ public interface MvcResult { /** * Return any exception raised by a handler and successfully resolved * through a {@link HandlerExceptionResolver}. - * * @return an exception, possibly {@code null} */ Exception getResolvedException(); @@ -79,20 +78,17 @@ public interface MvcResult { * Get the result of async execution. This method will wait for the async result * to be set for up to the amount of time configured on the async request, * i.e. {@link org.springframework.mock.web.MockAsyncContext#getTimeout()}. - * - * @throws IllegalStateException if the async result was not set. + * @throws IllegalStateException if the async result was not set */ Object getAsyncResult(); /** * Get the result of async execution. This method will wait for the async result * to be set for up to the specified amount of time. - * * @param timeToWait how long to wait for the async result to be set, in * milliseconds; if -1, then the async request timeout value is used, * i.e.{@link org.springframework.mock.web.MockAsyncContext#getTimeout()}. - * - * @throws IllegalStateException if the async result was not set. + * @throws IllegalStateException if the async result was not set */ Object getAsyncResult(long timeToWait); From a3da6b186fec82d3a13912fe9b4f7ab4f9b6cc12 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Thu, 21 Apr 2016 15:21:52 +0200 Subject: [PATCH 143/344] Upgrade to Reactor 2.0.8 (cherry picked from commit 6e4e52b) --- build.gradle | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index 15851a7478..f8f8619943 100644 --- a/build.gradle +++ b/build.gradle @@ -62,7 +62,7 @@ configure(allprojects) { project -> ext.okhttpVersion = "2.7.5" ext.openjpaVersion = "2.4.1" ext.poiVersion = "3.13" - ext.reactorVersion = "2.0.7.RELEASE" + ext.reactorVersion = "2.0.8.RELEASE" ext.romeVersion = "1.5.1" ext.seleniumVersion = "2.48.2" ext.slf4jVersion = "1.7.21" @@ -383,7 +383,7 @@ project("spring-beans") { optional("org.yaml:snakeyaml:${snakeyamlVersion}") testCompile("log4j:log4j:1.2.17") testCompile("org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}") - } + } } project("spring-beans-groovy") { @@ -783,7 +783,7 @@ project("spring-orm-hibernate4") { optional("org.hibernate:hibernate-core:${hibernate4Version}") optional("org.hibernate:hibernate-entitymanager:${hibernate4Version}") optional("javax.servlet:javax.servlet-api:3.0.1") - optional("aopalliance:aopalliance:1.0") + optional("aopalliance:aopalliance:1.0") testCompile("javax.validation:validation-api:1.1.0.GA") testCompile("org.hibernate:hibernate-validator:${hibval5Version}") testCompile("javax.el:javax.el-api:2.2.5") @@ -1161,9 +1161,7 @@ configure(rootProject) { separateOutputDirs = false backends = ['docbook'] options doctype: 'book', eruby: 'erubis' - attributes 'spring-version': project.version, - 'revnumber' : project.version, - 'docinfo' : "" + attributes 'spring-version': project.version, 'revnumber': project.version, 'docinfo': "" } reference { @@ -1299,7 +1297,7 @@ configure(rootProject) { baseName = "spring-framework" classifier = "dist" description = "Builds -${classifier} archive, containing all jars and docs, " + - "suitable for community download page." + "suitable for community download page." ext.baseDir = "${baseName}-${project.version}"; From d48eeb2c84e1be78dc1d09f02ab7d0747506df86 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Thu, 28 Apr 2016 15:26:35 -0400 Subject: [PATCH 144/344] Reactor2TcpClient cleans up TcpClient instances Issue: SPR-14231 --- .../tcp/reactor/Reactor2TcpClient.java | 34 ++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/spring-messaging/src/main/java/org/springframework/messaging/tcp/reactor/Reactor2TcpClient.java b/spring-messaging/src/main/java/org/springframework/messaging/tcp/reactor/Reactor2TcpClient.java index 15ca95e974..65e0460d5e 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/tcp/reactor/Reactor2TcpClient.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/tcp/reactor/Reactor2TcpClient.java @@ -163,7 +163,8 @@ private static NioEventLoopGroup initEventLoopGroup() { public ListenableFuture connect(final TcpConnectionHandler

    connectionHandler) { Assert.notNull(connectionHandler, "TcpConnectionHandler must not be null"); - TcpClient, Message

    > tcpClient; + final TcpClient, Message

    > tcpClient; + Runnable cleanupTask; synchronized (this.tcpClients) { if (this.stopping) { IllegalStateException ex = new IllegalStateException("Shutting down."); @@ -172,9 +173,18 @@ public ListenableFuture connect(final TcpConnectionHandler

    connectionHa } tcpClient = NetStreams.tcpClient(REACTOR_TCP_CLIENT_TYPE, this.tcpClientSpecFactory); this.tcpClients.add(tcpClient); + cleanupTask = new Runnable() { + @Override + public void run() { + synchronized (tcpClients) { + tcpClients.remove(tcpClient); + } + } + }; } - Promise promise = tcpClient.start(new MessageChannelStreamHandler

    (connectionHandler)); + Promise promise = tcpClient.start( + new MessageChannelStreamHandler

    (connectionHandler, cleanupTask)); return new PassThroughPromiseToListenableFutureAdapter( promise.onError(new Consumer() { @@ -191,7 +201,8 @@ public ListenableFuture connect(TcpConnectionHandler

    connectionHandler, Assert.notNull(connectionHandler, "TcpConnectionHandler must not be null"); Assert.notNull(strategy, "ReconnectStrategy must not be null"); - TcpClient, Message

    > tcpClient; + final TcpClient, Message

    > tcpClient; + Runnable cleanupTask; synchronized (this.tcpClients) { if (this.stopping) { IllegalStateException ex = new IllegalStateException("Shutting down."); @@ -200,10 +211,18 @@ public ListenableFuture connect(TcpConnectionHandler

    connectionHandler, } tcpClient = NetStreams.tcpClient(REACTOR_TCP_CLIENT_TYPE, this.tcpClientSpecFactory); this.tcpClients.add(tcpClient); + cleanupTask = new Runnable() { + @Override + public void run() { + synchronized (tcpClients) { + tcpClients.remove(tcpClient); + } + } + }; } Stream> stream = tcpClient.start( - new MessageChannelStreamHandler

    (connectionHandler), + new MessageChannelStreamHandler

    (connectionHandler, cleanupTask), new ReactorReconnectAdapter(strategy)); return new PassThroughPromiseToListenableFutureAdapter(stream.next().after()); @@ -249,6 +268,7 @@ public void operationComplete(Future future) throws Exception { }); promise = eventLoopPromise; } + return new PassThroughPromiseToListenableFutureAdapter(promise); } @@ -278,8 +298,11 @@ private static class MessageChannelStreamHandler

    private final TcpConnectionHandler

    connectionHandler; - public MessageChannelStreamHandler(TcpConnectionHandler

    connectionHandler) { + private final Runnable cleanupTask; + + public MessageChannelStreamHandler(TcpConnectionHandler

    connectionHandler, Runnable cleanupTask) { this.connectionHandler = connectionHandler; + this.cleanupTask = cleanupTask; } @Override @@ -290,6 +313,7 @@ public Publisher apply(ChannelStream, Message

    > channelStream .finallyDo(new Consumer>>() { @Override public void accept(Signal> signal) { + cleanupTask.run(); if (signal.isOnError()) { connectionHandler.handleFailure(signal.getThrowable()); } From f7ace544884762e40d2179ca37a1f1aaf03a09b5 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Thu, 28 Apr 2016 17:46:46 -0400 Subject: [PATCH 145/344] Ensure Environment.shutdown() in Reactor2TcpClient Issue: SPR-14229 --- .../messaging/tcp/reactor/Reactor2TcpClient.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/spring-messaging/src/main/java/org/springframework/messaging/tcp/reactor/Reactor2TcpClient.java b/spring-messaging/src/main/java/org/springframework/messaging/tcp/reactor/Reactor2TcpClient.java index 65e0460d5e..664e23c219 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/tcp/reactor/Reactor2TcpClient.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/tcp/reactor/Reactor2TcpClient.java @@ -84,6 +84,8 @@ public class Reactor2TcpClient

    implements TcpOperations

    { private final EventLoopGroup eventLoopGroup; + private final Environment environment; + private final TcpClientFactory, Message

    > tcpClientSpecFactory; private final List, Message

    >> tcpClients = @@ -108,12 +110,13 @@ public Reactor2TcpClient(final String host, final int port, final Codec, Message

    >() { @Override public TcpClientSpec, Message

    > apply(TcpClientSpec, Message

    > spec) { return spec - .env(new Environment(new SynchronousDispatcherConfigReader())) + .env(environment) .codec(codec) .connect(host, port) .options(createClientSocketOptions()); @@ -139,6 +142,7 @@ public Reactor2TcpClient(TcpClientFactory, Message

    > tcpClientSpecF Assert.notNull(tcpClientSpecFactory, "'tcpClientClientFactory' must not be null"); this.tcpClientSpecFactory = tcpClientSpecFactory; this.eventLoopGroup = null; + this.environment = null; } @@ -269,6 +273,15 @@ public void operationComplete(Future future) throws Exception { promise = eventLoopPromise; } + if (this.environment != null) { + promise.onComplete(new Consumer>() { + @Override + public void accept(Promise voidPromise) { + environment.shutdown(); + } + }); + } + return new PassThroughPromiseToListenableFutureAdapter(promise); } From d49ecab89a55cabd816529e4cb6d87940ec785d1 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 29 Apr 2016 11:12:20 +0200 Subject: [PATCH 146/344] Polishing --- .../interceptor/CacheOperationInvoker.java | 13 ++++++------ .../cache/CacheReproTests.java | 18 +++++++++------- .../springframework/util/ReflectionUtils.java | 21 +++++++++++-------- 3 files changed, 29 insertions(+), 23 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationInvoker.java b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationInvoker.java index 199791e6a9..8901ba1945 100644 --- a/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationInvoker.java +++ b/spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationInvoker.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,8 +21,8 @@ * *

    Does not provide a way to transmit checked exceptions but * provide a special exception that should be used to wrap any - * exception that was thrown by the underlying invocation. Callers - * are expected to handle this issue type specifically. + * exception that was thrown by the underlying invocation. + * Callers are expected to handle this issue type specifically. * * @author Stephane Nicoll * @since 4.1 @@ -38,11 +38,12 @@ public interface CacheOperationInvoker { */ Object invoke() throws ThrowableWrapper; + /** - * Wrap any exception thrown while invoking {@link #invoke()} + * Wrap any exception thrown while invoking {@link #invoke()}. */ @SuppressWarnings("serial") - public static class ThrowableWrapper extends RuntimeException { + class ThrowableWrapper extends RuntimeException { private final Throwable original; @@ -52,7 +53,7 @@ public ThrowableWrapper(Throwable original) { } public Throwable getOriginal() { - return original; + return this.original; } } diff --git a/spring-context/src/test/java/org/springframework/cache/CacheReproTests.java b/spring-context/src/test/java/org/springframework/cache/CacheReproTests.java index 84d6bc7696..8781e82a4b 100644 --- a/spring-context/src/test/java/org/springframework/cache/CacheReproTests.java +++ b/spring-context/src/test/java/org/springframework/cache/CacheReproTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -83,11 +83,11 @@ public void spr11592GetSimple() { String key = "1"; Object result = bean.getSimple("1"); - verify(cache, times(1)).get(key); // first call: cache miss + verify(cache, times(1)).get(key); // first call: cache miss Object cachedResult = bean.getSimple("1"); assertSame(result, cachedResult); - verify(cache, times(2)).get(key); // second call: cache hit + verify(cache, times(2)).get(key); // second call: cache hit context.close(); } @@ -100,11 +100,11 @@ public void spr11592GetNeverCache() { String key = "1"; Object result = bean.getNeverCache("1"); - verify(cache, times(0)).get(key); // no cache hit at all, caching disabled + verify(cache, times(0)).get(key); // no cache hit at all, caching disabled Object cachedResult = bean.getNeverCache("1"); assertNotSame(result, cachedResult); - verify(cache, times(0)).get(key); // caching disabled + verify(cache, times(0)).get(key); // caching disabled context.close(); } @@ -114,8 +114,9 @@ public void spr13081ConfigNoCacheNameIsRequired() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Spr13081Config.class); MyCacheResolver cacheResolver = context.getBean(MyCacheResolver.class); Spr13081Service bean = context.getBean(Spr13081Service.class); + assertNull(cacheResolver.getCache("foo").get("foo")); - Object result = bean.getSimple("foo"); // cache name = id + Object result = bean.getSimple("foo"); // cache name = id assertEquals(result, cacheResolver.getCache("foo").get("foo").get()); } @@ -124,7 +125,6 @@ public void spr13081ConfigFailIfCacheResolverReturnsNullCacheName() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Spr13081Config.class); Spr13081Service bean = context.getBean(Spr13081Service.class); - thrown.expect(IllegalStateException.class); thrown.expectMessage(MyCacheResolver.class.getName()); bean.getSimple(null); @@ -245,6 +245,7 @@ public Object getNeverCache(String key) { } } + @Configuration @EnableCaching public static class Spr13081Config extends CachingConfigurerSupport { @@ -259,9 +260,9 @@ public CacheResolver cacheResolver() { public Spr13081Service service() { return new Spr13081Service(); } - } + public static class MyCacheResolver extends AbstractCacheResolver { public MyCacheResolver() { @@ -282,6 +283,7 @@ public Cache getCache(String name) { } } + public static class Spr13081Service { @Cacheable 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 acd36a2151..301ad5a71a 100644 --- a/spring-core/src/main/java/org/springframework/util/ReflectionUtils.java +++ b/spring-core/src/main/java/org/springframework/util/ReflectionUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -265,7 +265,8 @@ public static Object invokeJdbcMethod(Method method, Object target, Object... ar * checked exception is expected to be thrown by the target method. *

    Throws the underlying RuntimeException or Error in case of an * InvocationTargetException with such a root cause. Throws an - * IllegalStateException with an appropriate message else. + * IllegalStateException with an appropriate message or + * UndeclaredThrowableException otherwise. * @param ex the reflection exception to handle */ public static void handleReflectionException(Exception ex) { @@ -288,7 +289,7 @@ public static void handleReflectionException(Exception ex) { * Handle the given invocation target exception. Should only be called if no * checked exception is expected to be thrown by the target method. *

    Throws the underlying RuntimeException or Error in case of such a root - * cause. Throws an IllegalStateException else. + * cause. Throws an UndeclaredThrowableException otherwise. * @param ex the invocation target exception to handle */ public static void handleInvocationTargetException(InvocationTargetException ex) { @@ -300,8 +301,9 @@ public static void handleInvocationTargetException(InvocationTargetException ex) * target exception of an {@link InvocationTargetException}. * Should only be called if no checked exception is expected to be thrown * by the target method. - *

    Rethrows the underlying exception cast to an {@link RuntimeException} or - * {@link Error} if appropriate; otherwise, throws an {@link IllegalStateException}. + *

    Rethrows the underlying exception cast to a {@link RuntimeException} or + * {@link Error} if appropriate; otherwise, throws an + * {@link UndeclaredThrowableException}. * @param ex the exception to rethrow * @throws RuntimeException the rethrown exception */ @@ -321,7 +323,8 @@ public static void rethrowRuntimeException(Throwable ex) { * Should only be called if no checked exception is expected to be thrown * by the target method. *

    Rethrows the underlying exception cast to an {@link Exception} or - * {@link Error} if appropriate; otherwise, throws an {@link IllegalStateException}. + * {@link Error} if appropriate; otherwise, throws an + * {@link UndeclaredThrowableException}. * @param ex the exception to rethrow * @throws Exception the rethrown exception (in case of a checked exception) */ @@ -804,7 +807,7 @@ public interface FieldFilter { /** * Pre-built FieldFilter that matches all non-static, non-final fields. */ - public static FieldFilter COPYABLE_FIELDS = new FieldFilter() { + public static final FieldFilter COPYABLE_FIELDS = new FieldFilter() { @Override public boolean matches(Field field) { @@ -816,7 +819,7 @@ public boolean matches(Field field) { /** * Pre-built MethodFilter that matches all non-bridge methods. */ - public static MethodFilter NON_BRIDGED_METHODS = new MethodFilter() { + public static final MethodFilter NON_BRIDGED_METHODS = new MethodFilter() { @Override public boolean matches(Method method) { @@ -829,7 +832,7 @@ public boolean matches(Method method) { * Pre-built MethodFilter that matches all non-bridge methods * which are not declared on {@code java.lang.Object}. */ - public static MethodFilter USER_DECLARED_METHODS = new MethodFilter() { + public static final MethodFilter USER_DECLARED_METHODS = new MethodFilter() { @Override public boolean matches(Method method) { From 85faeef8168baa62beb471be8add5ec539de22f5 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 2 May 2016 12:57:30 +0200 Subject: [PATCH 147/344] DefaultMessageListenerContainer immediately invokes stop callback when not running Issue: SPR-14233 (cherry picked from commit e45d33f) --- .../DefaultMessageListenerContainer.java | 6 ++ .../DefaultMessageListenerContainerTests.java | 91 ++++++++++++++----- 2 files changed, 74 insertions(+), 23 deletions(-) diff --git a/spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java b/spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java index 06209132b3..5fe49eb452 100644 --- a/spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java +++ b/spring-jms/src/main/java/org/springframework/jms/listener/DefaultMessageListenerContainer.java @@ -614,6 +614,12 @@ public void start() throws JmsException { @Override public void stop(Runnable callback) throws JmsException { synchronized (this.lifecycleMonitor) { + if (!isRunning() || this.stopCallback != null) { + // Not started, already stopped, or previous stop attempt in progress + // -> return immediately, no stop process to control anymore. + callback.run(); + return; + } this.stopCallback = callback; } stop(); diff --git a/spring-jms/src/test/java/org/springframework/jms/listener/DefaultMessageListenerContainerTests.java b/spring-jms/src/test/java/org/springframework/jms/listener/DefaultMessageListenerContainerTests.java index e9a8e8d844..aa24fcd04f 100644 --- a/spring-jms/src/test/java/org/springframework/jms/listener/DefaultMessageListenerContainerTests.java +++ b/spring-jms/src/test/java/org/springframework/jms/listener/DefaultMessageListenerContainerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ package org.springframework.jms.listener; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.Destination; @@ -32,73 +34,90 @@ import static org.mockito.BDDMockito.*; /** - * * @author Stephane Nicoll */ public class DefaultMessageListenerContainerTests { @Test public void applyBackOff() { - BackOff mock = mock(BackOff.class); + BackOff backOff = mock(BackOff.class); BackOffExecution execution = mock(BackOffExecution.class); given(execution.nextBackOff()).willReturn(BackOffExecution.STOP); - given(mock.start()).willReturn(execution); + given(backOff.start()).willReturn(execution); - DefaultMessageListenerContainer container = createContainer(mock, createFailingContainerFactory()); + DefaultMessageListenerContainer container = createContainer(createFailingContainerFactory()); + container.setBackOff(backOff); container.start(); assertEquals(true, container.isRunning()); container.refreshConnectionUntilSuccessful(); assertEquals(false, container.isRunning()); - verify(mock).start(); + verify(backOff).start(); verify(execution).nextBackOff(); } @Test public void applyBackOffRetry() { - BackOff mock = mock(BackOff.class); + BackOff backOff = mock(BackOff.class); BackOffExecution execution = mock(BackOffExecution.class); given(execution.nextBackOff()).willReturn(50L, BackOffExecution.STOP); - given(mock.start()).willReturn(execution); + given(backOff.start()).willReturn(execution); - DefaultMessageListenerContainer container = createContainer(mock, createFailingContainerFactory()); + DefaultMessageListenerContainer container = createContainer(createFailingContainerFactory()); + container.setBackOff(backOff); container.start(); container.refreshConnectionUntilSuccessful(); assertEquals(false, container.isRunning()); - verify(mock).start(); + verify(backOff).start(); verify(execution, times(2)).nextBackOff(); } @Test public void recoverResetBackOff() { - BackOff mock = mock(BackOff.class); + BackOff backOff = mock(BackOff.class); BackOffExecution execution = mock(BackOffExecution.class); given(execution.nextBackOff()).willReturn(50L, 50L, 50L); // 3 attempts max - given(mock.start()).willReturn(execution); + given(backOff.start()).willReturn(execution); - DefaultMessageListenerContainer container = createContainer(mock, createRecoverableContainerFactory(1)); + DefaultMessageListenerContainer container = createContainer(createRecoverableContainerFactory(1)); + container.setBackOff(backOff); container.start(); container.refreshConnectionUntilSuccessful(); assertEquals(true, container.isRunning()); - verify(mock).start(); + verify(backOff).start(); verify(execution, times(1)).nextBackOff(); // only on attempt as the second one lead to a recovery } - private DefaultMessageListenerContainer createContainer(BackOff backOff, ConnectionFactory connectionFactory) { + @Test + public void runnableIsInvokedEvenIfContainerIsNotRunning() throws InterruptedException { + DefaultMessageListenerContainer container = createRunningContainer(); + container.stop(); + + // container is stopped but should nevertheless invoke the runnable argument + TestRunnable runnable2 = new TestRunnable(); + container.stop(runnable2); + runnable2.waitForCompletion(); + } - Destination destination = new Destination() {}; + private DefaultMessageListenerContainer createRunningContainer() { + DefaultMessageListenerContainer container = createContainer(createSuccessfulConnectionFactory()); + container.afterPropertiesSet(); + container.start(); + return container; + } + + private DefaultMessageListenerContainer createContainer(ConnectionFactory connectionFactory) { + Destination destination = new Destination() {}; DefaultMessageListenerContainer container = new DefaultMessageListenerContainer(); container.setConnectionFactory(connectionFactory); container.setCacheLevel(DefaultMessageListenerContainer.CACHE_NONE); container.setDestination(destination); - container.setBackOff(backOff); return container; - } private ConnectionFactory createFailingContainerFactory() { @@ -112,8 +131,8 @@ public Object answer(InvocationOnMock invocation) throws Throwable { }); return connectionFactory; } - catch (JMSException e) { - throw new IllegalStateException(); // never happen + catch (JMSException ex) { + throw new IllegalStateException(ex); // never happen } } @@ -122,7 +141,6 @@ private ConnectionFactory createRecoverableContainerFactory(final int failingAtt ConnectionFactory connectionFactory = mock(ConnectionFactory.class); given(connectionFactory.createConnection()).will(new Answer() { int currentAttempts = 0; - @Override public Object answer(InvocationOnMock invocation) throws Throwable { currentAttempts++; @@ -136,8 +154,35 @@ public Object answer(InvocationOnMock invocation) throws Throwable { }); return connectionFactory; } - catch (JMSException e) { - throw new IllegalStateException(); // never happen + catch (JMSException ex) { + throw new IllegalStateException(ex); // never happen + } + } + + private ConnectionFactory createSuccessfulConnectionFactory() { + try { + ConnectionFactory connectionFactory = mock(ConnectionFactory.class); + given(connectionFactory.createConnection()).willReturn(mock(Connection.class)); + return connectionFactory; + } + catch (JMSException ex) { + throw new IllegalStateException(ex); // never happen + } + } + + + private static class TestRunnable implements Runnable { + + private final CountDownLatch countDownLatch = new CountDownLatch(1); + + @Override + public void run() { + this.countDownLatch.countDown(); + } + + public void waitForCompletion() throws InterruptedException { + this.countDownLatch.await(2, TimeUnit.SECONDS); + assertEquals("callback was not invoked", 0, this.countDownLatch.getCount()); } } From 85675fbe2e8c3418d3f8eb6124a13fbdfb9553a2 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 2 May 2016 13:01:44 +0200 Subject: [PATCH 148/344] Consistent SmartLifecycle implementations Issue: SPR-14233 (cherry picked from commit f83cbff) --- .../AbstractJmsListeningContainer.java | 4 +- .../SimpAnnotationMethodMessageHandler.java | 16 ++--- .../user/UserDestinationMessageHandler.java | 33 +++++----- .../GenericMessageEndpointManager.java | 4 +- .../client/ConnectionManagerSupport.java | 60 ++++++++++--------- .../AnnotatedEndpointConnectionManager.java | 17 +++--- .../standard/EndpointConnectionManager.java | 19 +++--- .../SubProtocolWebSocketHandler.java | 16 ++--- .../messaging/WebSocketStompClient.java | 31 +++++----- .../support/WebSocketHandlerMapping.java | 12 ++-- .../messaging/WebSocketStompClientTests.java | 18 +++--- 11 files changed, 116 insertions(+), 114 deletions(-) diff --git a/spring-jms/src/main/java/org/springframework/jms/listener/AbstractJmsListeningContainer.java b/spring-jms/src/main/java/org/springframework/jms/listener/AbstractJmsListeningContainer.java index ab67e4bf4c..0de5f39a8d 100644 --- a/spring-jms/src/main/java/org/springframework/jms/listener/AbstractJmsListeningContainer.java +++ b/spring-jms/src/main/java/org/springframework/jms/listener/AbstractJmsListeningContainer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -315,7 +315,7 @@ public void stop() throws JmsException { @Override public void stop(Runnable callback) { - this.stop(); + stop(); callback.run(); } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandler.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandler.java index 6b886aa517..ccadb980e7 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandler.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -272,13 +272,6 @@ public int getPhase() { return Integer.MAX_VALUE; } - @Override - public final boolean isRunning() { - synchronized (this.lifecycleMonitor) { - return this.running; - } - } - @Override public final void start() { synchronized (this.lifecycleMonitor) { @@ -303,6 +296,13 @@ public final void stop(Runnable callback) { } } + @Override + public final boolean isRunning() { + synchronized (this.lifecycleMonitor) { + return this.running; + } + } + protected List initArgumentResolvers() { ConfigurableBeanFactory beanFactory = (getApplicationContext() instanceof ConfigurableApplicationContext ? diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/user/UserDestinationMessageHandler.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/user/UserDestinationMessageHandler.java index 372c504aa5..77a3528983 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/user/UserDestinationMessageHandler.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/user/UserDestinationMessageHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,8 +16,6 @@ package org.springframework.messaging.simp.user; -import static org.springframework.messaging.simp.SimpMessageHeaderAccessor.*; - import java.util.Arrays; import java.util.List; @@ -154,13 +152,6 @@ public boolean isAutoStartup() { return true; } - @Override - public final boolean isRunning() { - synchronized (this.lifecycleMonitor) { - return this.running; - } - } - @Override public final void start() { synchronized (this.lifecycleMonitor) { @@ -187,6 +178,13 @@ public final void stop(Runnable callback) { } } + @Override + public final boolean isRunning() { + synchronized (this.lifecycleMonitor) { + return this.running; + } + } + @Override public void handleMessage(Message message) throws MessagingException { @@ -211,7 +209,7 @@ public void handleMessage(Message message) throws MessagingException { } SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.wrap(message); initHeaders(accessor); - accessor.setNativeHeader(ORIGINAL_DESTINATION, result.getSubscribeDestination()); + accessor.setNativeHeader(SimpMessageHeaderAccessor.ORIGINAL_DESTINATION, result.getSubscribeDestination()); accessor.setLeaveMutable(true); message = MessageBuilder.createMessage(message.getPayload(), accessor.getMessageHeaders()); if (logger.isTraceEnabled()) { @@ -242,18 +240,15 @@ private static class BroadcastHandler { private static final List NO_COPY_LIST = Arrays.asList("subscription", "message-id"); - private final MessageSendingOperations messagingTemplate; private final String broadcastDestination; - public BroadcastHandler(MessageSendingOperations template, String destination) { this.messagingTemplate = template; this.broadcastDestination = destination; } - public String getBroadcastDestination() { return this.broadcastDestination; } @@ -263,12 +258,13 @@ public Message preHandle(Message message) throws MessagingException { if (!getBroadcastDestination().equals(destination)) { return message; } - SimpMessageHeaderAccessor accessor = getAccessor(message, SimpMessageHeaderAccessor.class); + SimpMessageHeaderAccessor accessor = + SimpMessageHeaderAccessor.getAccessor(message, SimpMessageHeaderAccessor.class); if (accessor.getSessionId() == null) { // Our own broadcast return null; } - destination = accessor.getFirstNativeHeader(ORIGINAL_DESTINATION); + destination = accessor.getFirstNativeHeader(SimpMessageHeaderAccessor.ORIGINAL_DESTINATION); if (logger.isTraceEnabled()) { logger.trace("Checking unresolved user destination: " + destination); } @@ -286,13 +282,14 @@ public Message preHandle(Message message) throws MessagingException { public void handleUnresolved(Message message) { MessageHeaders headers = message.getHeaders(); - if (SimpMessageHeaderAccessor.getFirstNativeHeader(ORIGINAL_DESTINATION, headers) != null) { + if (SimpMessageHeaderAccessor.getFirstNativeHeader( + SimpMessageHeaderAccessor.ORIGINAL_DESTINATION, headers) != null) { // Re-broadcast return; } SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.wrap(message); String destination = accessor.getDestination(); - accessor.setNativeHeader(ORIGINAL_DESTINATION, destination); + accessor.setNativeHeader(SimpMessageHeaderAccessor.ORIGINAL_DESTINATION, destination); accessor.setLeaveMutable(true); message = MessageBuilder.createMessage(message.getPayload(), accessor.getMessageHeaders()); if (logger.isTraceEnabled()) { diff --git a/spring-tx/src/main/java/org/springframework/jca/endpoint/GenericMessageEndpointManager.java b/spring-tx/src/main/java/org/springframework/jca/endpoint/GenericMessageEndpointManager.java index c05c613253..57c356aeba 100644 --- a/spring-tx/src/main/java/org/springframework/jca/endpoint/GenericMessageEndpointManager.java +++ b/spring-tx/src/main/java/org/springframework/jca/endpoint/GenericMessageEndpointManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -308,7 +308,7 @@ public void stop() { @Override public void stop(Runnable callback) { synchronized (this.lifecycleMonitor) { - this.stop(); + stop(); callback.run(); } } diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/client/ConnectionManagerSupport.java b/spring-websocket/src/main/java/org/springframework/web/socket/client/ConnectionManagerSupport.java index 7fca9edb34..7146a59395 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/client/ConnectionManagerSupport.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/client/ConnectionManagerSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -56,6 +56,10 @@ public ConnectionManagerSupport(String uriTemplate, Object... uriVariables) { } + protected URI getUri() { + return this.uri; + } + /** * Set whether to auto-connect to the remote endpoint after this connection manager * has been initialized and the Spring context has been refreshed. @@ -95,22 +99,9 @@ public int getPhase() { return this.phase; } - protected URI getUri() { - return this.uri; - } /** - * Return whether this ConnectionManager has been started. - */ - @Override - public boolean isRunning() { - synchronized (this.lifecycleMonitor) { - return this.running; - } - } - - /** - * Start the websocket connection. If already connected, the method has no impact. + * Start the WebSocket connection. If already connected, the method has no impact. */ @Override public final void start() { @@ -122,29 +113,27 @@ public final void start() { } protected void startInternal() { - synchronized (lifecycleMonitor) { + synchronized (this.lifecycleMonitor) { if (logger.isInfoEnabled()) { - logger.info("Starting " + this.getClass().getSimpleName()); + logger.info("Starting " + getClass().getSimpleName()); } this.running = true; openConnection(); } } - protected abstract void openConnection(); - @Override public final void stop() { synchronized (this.lifecycleMonitor) { if (isRunning()) { if (logger.isInfoEnabled()) { - logger.info("Stopping " + this.getClass().getSimpleName()); + logger.info("Stopping " + getClass().getSimpleName()); } try { stopInternal(); } - catch (Throwable e) { - logger.error("Failed to stop WebSocket connection", e); + catch (Throwable ex) { + logger.error("Failed to stop WebSocket connection", ex); } finally { this.running = false; @@ -153,22 +142,35 @@ public final void stop() { } } + @Override + public final void stop(Runnable callback) { + synchronized (this.lifecycleMonitor) { + stop(); + callback.run(); + } + } + protected void stopInternal() throws Exception { if (isConnected()) { closeConnection(); } } - protected abstract boolean isConnected(); - - protected abstract void closeConnection() throws Exception; - + /** + * Return whether this ConnectionManager has been started. + */ @Override - public final void stop(Runnable callback) { + public boolean isRunning() { synchronized (this.lifecycleMonitor) { - this.stop(); - callback.run(); + return this.running; } } + + protected abstract void openConnection(); + + protected abstract void closeConnection() throws Exception; + + protected abstract boolean isConnected(); + } diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/client/standard/AnnotatedEndpointConnectionManager.java b/spring-websocket/src/main/java/org/springframework/web/socket/client/standard/AnnotatedEndpointConnectionManager.java index aa4c16910a..77de7def22 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/client/standard/AnnotatedEndpointConnectionManager.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/client/standard/AnnotatedEndpointConnectionManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,10 +47,10 @@ public class AnnotatedEndpointConnectionManager extends ConnectionManagerSupport private WebSocketContainer webSocketContainer = ContainerProvider.getWebSocketContainer(); - private Session session; - private TaskExecutor taskExecutor = new SimpleAsyncTaskExecutor("AnnotatedEndpointConnectionManager-"); + private volatile Session session; + public AnnotatedEndpointConnectionManager(Object endpoint, String uriTemplate, Object... uriVariables) { super(uriTemplate, uriVariables); @@ -96,19 +96,22 @@ public TaskExecutor getTaskExecutor() { return this.taskExecutor; } + @Override protected void openConnection() { this.taskExecutor.execute(new Runnable() { @Override public void run() { try { - logger.info("Connecting to WebSocket at " + getUri()); + if (logger.isInfoEnabled()) { + logger.info("Connecting to WebSocket at " + getUri()); + } Object endpointToUse = (endpoint != null) ? endpoint : endpointProvider.getHandler(); session = webSocketContainer.connectToServer(endpointToUse, getUri()); - logger.info("Successfully connected"); + logger.info("Successfully connected to WebSocket"); } catch (Throwable ex) { - logger.error("Failed to connect", ex); + logger.error("Failed to connect to WebSocket", ex); } } }); @@ -128,7 +131,7 @@ protected void closeConnection() throws Exception { @Override protected boolean isConnected() { - return ((this.session != null) && this.session.isOpen()); + return (this.session != null && this.session.isOpen()); } } diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/client/standard/EndpointConnectionManager.java b/spring-websocket/src/main/java/org/springframework/web/socket/client/standard/EndpointConnectionManager.java index fd7fef157e..82bc4817e4 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/client/standard/EndpointConnectionManager.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/client/standard/EndpointConnectionManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,6 @@ import javax.websocket.Session; import javax.websocket.WebSocketContainer; -import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.core.task.SimpleAsyncTaskExecutor; @@ -57,10 +56,10 @@ public class EndpointConnectionManager extends ConnectionManagerSupport implemen private WebSocketContainer webSocketContainer = ContainerProvider.getWebSocketContainer(); - private Session session; - private TaskExecutor taskExecutor = new SimpleAsyncTaskExecutor("EndpointConnectionManager-"); + private volatile Session session; + public EndpointConnectionManager(Endpoint endpoint, String uriTemplate, Object... uriVariables) { super(uriTemplate, uriVariables); @@ -106,7 +105,7 @@ public WebSocketContainer getWebSocketContainer() { } @Override - public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + public void setBeanFactory(BeanFactory beanFactory) { if (this.endpointProvider != null) { this.endpointProvider.setBeanFactory(beanFactory); } @@ -135,14 +134,16 @@ protected void openConnection() { @Override public void run() { try { - logger.info("Connecting to WebSocket at " + getUri()); + if (logger.isInfoEnabled()) { + logger.info("Connecting to WebSocket at " + getUri()); + } Endpoint endpointToUse = (endpoint != null) ? endpoint : endpointProvider.getHandler(); ClientEndpointConfig endpointConfig = configBuilder.build(); session = getWebSocketContainer().connectToServer(endpointToUse, endpointConfig, getUri()); - logger.info("Successfully connected"); + logger.info("Successfully connected to WebSocket"); } catch (Throwable ex) { - logger.error("Failed to connect", ex); + logger.error("Failed to connect to WebSocket", ex); } } }); @@ -162,7 +163,7 @@ protected void closeConnection() throws Exception { @Override protected boolean isConnected() { - return ((this.session != null) && this.session.isOpen()); + return (this.session != null && this.session.isOpen()); } } diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/messaging/SubProtocolWebSocketHandler.java b/spring-websocket/src/main/java/org/springframework/web/socket/messaging/SubProtocolWebSocketHandler.java index 2a8f28a585..11d81cc308 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/messaging/SubProtocolWebSocketHandler.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/messaging/SubProtocolWebSocketHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -239,13 +239,6 @@ public int getPhase() { return Integer.MAX_VALUE; } - @Override - public final boolean isRunning() { - synchronized (this.lifecycleMonitor) { - return this.running; - } - } - @Override public final void start() { Assert.isTrue(this.defaultProtocolHandler != null || !this.protocolHandlers.isEmpty(), "No handlers"); @@ -281,6 +274,13 @@ public final void stop(Runnable callback) { } } + @Override + public final boolean isRunning() { + synchronized (this.lifecycleMonitor) { + return this.running; + } + } + @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/messaging/WebSocketStompClient.java b/spring-websocket/src/main/java/org/springframework/web/socket/messaging/WebSocketStompClient.java index 1e648d62c9..3623508253 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/messaging/WebSocketStompClient.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/messaging/WebSocketStompClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -91,7 +91,7 @@ public class WebSocketStompClient extends StompClientSupport implements SmartLif * @param webSocketClient the WebSocket client to connect with */ public WebSocketStompClient(WebSocketClient webSocketClient) { - Assert.notNull(webSocketClient, "'webSocketClient' is required."); + Assert.notNull(webSocketClient, "WebSocketClient is required"); this.webSocketClient = webSocketClient; setDefaultHeartbeat(new long[] {0, 0}); } @@ -153,11 +153,6 @@ public boolean isAutoStartup() { return this.autoStartup; } - @Override - public boolean isRunning() { - return this.running; - } - /** * Specify the phase in which the WebSocket client should be started and * subsequently closed. The startup order proceeds from lowest to highest, @@ -201,10 +196,16 @@ public void stop() { @Override public void stop(Runnable callback) { - this.stop(); + stop(); callback.run(); } + @Override + public boolean isRunning() { + return this.running; + } + + /** * Connect to the given WebSocket URL and notify the given * {@link org.springframework.messaging.simp.stomp.StompSessionHandler} @@ -249,7 +250,7 @@ public ListenableFuture connect(String url, WebSocketHttpHeaders h public ListenableFuture connect(String url, WebSocketHttpHeaders handshakeHeaders, StompHeaders connectHeaders, StompSessionHandler handler, Object... uriVariables) { - Assert.notNull(url, "uriTemplate must not be null"); + Assert.notNull(url, "'url' must not be null"); URI uri = UriComponentsBuilder.fromUriString(url).buildAndExpand(uriVariables).encode().toUri(); return connect(uri, handshakeHeaders, connectHeaders, handler); } @@ -267,7 +268,7 @@ public ListenableFuture connect(String url, WebSocketHttpHeaders h public ListenableFuture connect(URI url, WebSocketHttpHeaders handshakeHeaders, StompHeaders connectHeaders, StompSessionHandler sessionHandler) { - Assert.notNull(url, "'uri' must not be null"); + Assert.notNull(url, "'url' must not be null"); ConnectionHandlingStompSession session = createSession(connectHeaders, sessionHandler); WebSocketTcpConnectionHandlerAdapter adapter = new WebSocketTcpConnectionHandlerAdapter(session); getWebSocketClient().doHandshake(adapter, handshakeHeaders, url).addCallback(adapter); @@ -278,7 +279,7 @@ public ListenableFuture connect(URI url, WebSocketHttpHeaders hand protected StompHeaders processConnectHeaders(StompHeaders connectHeaders) { connectHeaders = super.processConnectHeaders(connectHeaders); if (connectHeaders.isHeartbeatEnabled()) { - Assert.notNull(getTaskScheduler(), "TaskScheduler cannot be null if heartbeats are enabled."); + Assert.state(getTaskScheduler() != null, "TaskScheduler must be set if heartbeats are enabled"); } return connectHeaders; } @@ -303,7 +304,7 @@ private class WebSocketTcpConnectionHandlerAdapter implements ListenableFutureCa private final List> inactivityTasks = new ArrayList>(2); public WebSocketTcpConnectionHandlerAdapter(TcpConnectionHandler connectionHandler) { - Assert.notNull(connectionHandler); + Assert.notNull(connectionHandler, "TcpConnectionHandler must not be null"); this.connectionHandler = connectionHandler; } @@ -397,7 +398,7 @@ private void updateLastWriteTime() { @Override public void onReadInactivity(final Runnable runnable, final long duration) { - Assert.notNull(getTaskScheduler(), "No scheduler configured."); + Assert.state(getTaskScheduler() != null, "No TaskScheduler configured"); this.lastReadTime = System.currentTimeMillis(); this.inactivityTasks.add(getTaskScheduler().scheduleWithFixedDelay(new Runnable() { @Override @@ -418,7 +419,7 @@ public void run() { @Override public void onWriteInactivity(final Runnable runnable, final long duration) { - Assert.notNull(getTaskScheduler(), "No scheduler configured."); + Assert.state(getTaskScheduler() != null, "No TaskScheduler configured"); this.lastWriteTime = System.currentTimeMillis(); this.inactivityTasks.add(getTaskScheduler().scheduleWithFixedDelay(new Runnable() { @Override @@ -491,7 +492,7 @@ else if (webSocketMessage instanceof BinaryMessage) { public WebSocketMessage encode(Message message, Class sessionType) { StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class); - Assert.notNull(accessor); + Assert.notNull(accessor, "No StompHeaderAccessor available"); byte[] payload = message.getPayload(); byte[] bytes = ENCODER.encode(accessor.getMessageHeaders(), payload); diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/support/WebSocketHandlerMapping.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/support/WebSocketHandlerMapping.java index 90def82bcf..2cd4eb9d53 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/server/support/WebSocketHandlerMapping.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/support/WebSocketHandlerMapping.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -51,11 +51,6 @@ public boolean isAutoStartup() { return true; } - @Override - public boolean isRunning() { - return this.running; - } - @Override public int getPhase() { return Integer.MAX_VALUE; @@ -91,4 +86,9 @@ public void stop(Runnable callback) { callback.run(); } + @Override + public boolean isRunning() { + return this.running; + } + } diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/messaging/WebSocketStompClientTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/messaging/WebSocketStompClientTests.java index 04b8836a2c..cd519aad0a 100644 --- a/spring-websocket/src/test/java/org/springframework/web/socket/messaging/WebSocketStompClientTests.java +++ b/spring-websocket/src/test/java/org/springframework/web/socket/messaging/WebSocketStompClientTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,16 +16,12 @@ package org.springframework.web.socket.messaging; -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; - import java.net.URI; import java.nio.charset.Charset; import java.util.concurrent.ScheduledFuture; import org.junit.Before; import org.junit.Test; - import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -50,6 +46,9 @@ import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.client.WebSocketClient; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + /** * Unit tests for {@link WebSocketStompClient}. * @@ -91,6 +90,7 @@ public void setUp() throws Exception { .thenReturn(this.handshakeFuture); } + @Test public void webSocketHandshakeFailure() throws Exception { connect(); @@ -246,9 +246,9 @@ public void heartbeatDefaultValueSetWithoutScheduler() throws Exception { stompClient.setDefaultHeartbeat(new long[] {5, 5}); try { stompClient.processConnectHeaders(null); - fail("Expected exception"); + fail("Expected IllegalStateException"); } - catch (IllegalArgumentException ex) { + catch (IllegalStateException ex) { // Ignore } } @@ -308,7 +308,6 @@ public void cancelInactivityTasks() throws Exception { private WebSocketHandler connect() { - this.stompClient.connect("/foo", mock(StompSessionHandler.class)); verify(this.stompSession).getSessionFuture(); @@ -354,7 +353,6 @@ private static class TestWebSocketStompClient extends WebSocketStompClient { private ConnectionHandlingStompSession stompSession; - public TestWebSocketStompClient(WebSocketClient webSocketClient) { super(webSocketClient); } @@ -369,4 +367,4 @@ protected ConnectionHandlingStompSession createSession(StompHeaders headers, Sto } } -} \ No newline at end of file +} From 0589c1b4b5c4ca1fc07a658cf0dc378504ff4a65 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 2 May 2016 15:19:05 +0200 Subject: [PATCH 149/344] Warn about non-static BeanDefinitionRegistryPostProcessor declarations on @Configuration classes Issue: SPR-14234 (cherry picked from commit 7737c3c) --- .../context/annotation/ConfigurationClassEnhancer.java | 5 +++-- .../annotation/ConfigurationClassPostProcessor.java | 8 +++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java index 5d7e801cc4..4aea07f9a9 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java @@ -159,7 +159,7 @@ public interface EnhancedConfiguration extends BeanFactoryAware { * Conditional {@link Callback}. * @see ConditionalCallbackFilter */ - private static interface ConditionalCallback extends Callback { + private interface ConditionalCallback extends Callback { boolean isMatch(Method candidateMethod); } @@ -343,7 +343,8 @@ public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object // The factory is calling the bean method in order to instantiate and register the bean // (i.e. via a getBean() call) -> invoke the super implementation of the method to actually // create the bean instance. - if (BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType())) { + if (logger.isWarnEnabled() && + BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType())) { logger.warn(String.format("@Bean method %s.%s is non-static and returns an object " + "assignable to Spring's BeanFactoryPostProcessor interface. This will " + "result in a failure to process annotations such as @Autowired, " + diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java index 78f3b04ed5..301256a6a8 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -382,6 +382,12 @@ public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFact throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" + beanName + "' since it is not stored in an AbstractBeanDefinition subclass"); } + else if (logger.isWarnEnabled() && beanFactory.containsSingleton(beanName)) { + logger.warn("Cannot enhance @Configuration bean definition '" + beanName + + "' since its singleton instance has been created too early. The typical cause " + + "is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " + + "return type: Consider declaring such methods as 'static'."); + } configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef); } } From a6563a32637a2c188021613189238d4255766f0f Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 3 May 2016 18:44:37 +0200 Subject: [PATCH 150/344] Explicit note on Java deserialization (cherry picked from commit 76964e1) --- .../remoting/httpinvoker/HttpInvokerProxyFactoryBean.java | 7 ++++++- .../remoting/httpinvoker/HttpInvokerServiceExporter.java | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/remoting/httpinvoker/HttpInvokerProxyFactoryBean.java b/spring-web/src/main/java/org/springframework/remoting/httpinvoker/HttpInvokerProxyFactoryBean.java index 3514f5e2e5..1386ea1a91 100644 --- a/spring-web/src/main/java/org/springframework/remoting/httpinvoker/HttpInvokerProxyFactoryBean.java +++ b/spring-web/src/main/java/org/springframework/remoting/httpinvoker/HttpInvokerProxyFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,6 +36,11 @@ * expense of being tied to Java. Nevertheless, it is as easy to set up as * Hessian and Burlap, which is its main advantage compared to RMI. * + *

    WARNING: Be aware of vulnerabilities due to unsafe Java deserialization: + * Manipulated input streams could lead to unwanted code execution on the server + * during the deserialization step. As a consequence, do not expose HTTP invoker + * endpoints to untrusted clients but rather just between your own services. + * * @author Juergen Hoeller * @since 1.1 * @see #setServiceInterface diff --git a/spring-web/src/main/java/org/springframework/remoting/httpinvoker/HttpInvokerServiceExporter.java b/spring-web/src/main/java/org/springframework/remoting/httpinvoker/HttpInvokerServiceExporter.java index 0e85a7bcc4..bcbc09f9f7 100644 --- a/spring-web/src/main/java/org/springframework/remoting/httpinvoker/HttpInvokerServiceExporter.java +++ b/spring-web/src/main/java/org/springframework/remoting/httpinvoker/HttpInvokerServiceExporter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,6 +47,11 @@ * expense of being tied to Java. Nevertheless, it is as easy to set up as * Hessian and Burlap, which is its main advantage compared to RMI. * + *

    WARNING: Be aware of vulnerabilities due to unsafe Java deserialization: + * Manipulated input streams could lead to unwanted code execution on the server + * during the deserialization step. As a consequence, do not expose HTTP invoker + * endpoints to untrusted clients but rather just between your own services. + * * @author Juergen Hoeller * @since 1.1 * @see HttpInvokerClientInterceptor From bf3dee94925cb01e5e513599f06e2b7321b340bb Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 3 May 2016 18:47:33 +0200 Subject: [PATCH 151/344] Polishing --- .../transaction/jta/JtaTransactionManager.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/spring-tx/src/main/java/org/springframework/transaction/jta/JtaTransactionManager.java b/spring-tx/src/main/java/org/springframework/transaction/jta/JtaTransactionManager.java index 9f136ba1e7..453453aa4c 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/jta/JtaTransactionManager.java +++ b/spring-tx/src/main/java/org/springframework/transaction/jta/JtaTransactionManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -748,8 +748,7 @@ protected TransactionSynchronizationRegistry findTransactionSynchronizationRegis } catch (NamingException ex) { if (logger.isDebugEnabled()) { - logger.debug( - "No JTA TransactionSynchronizationRegistry found at default JNDI location [" + jndiName + "]", ex); + logger.debug("No JTA TransactionSynchronizationRegistry found at default JNDI location [" + jndiName + "]", ex); } } } @@ -834,12 +833,12 @@ protected void doBegin(Object transaction, TransactionDefinition definition) { catch (NotSupportedException ex) { // assume nested transaction not supported throw new NestedTransactionNotSupportedException( - "JTA implementation does not support nested transactions", ex); + "JTA implementation does not support nested transactions", ex); } catch (UnsupportedOperationException ex) { // assume nested transaction not supported throw new NestedTransactionNotSupportedException( - "JTA implementation does not support nested transactions", ex); + "JTA implementation does not support nested transactions", ex); } catch (SystemException ex) { throw new CannotCreateTransactionException("JTA failure on begin", ex); @@ -894,8 +893,8 @@ protected void applyIsolationLevel(JtaTransactionObject txObject, int isolationL if (!this.allowCustomIsolationLevels && isolationLevel != TransactionDefinition.ISOLATION_DEFAULT) { throw new InvalidIsolationLevelException( - "JtaTransactionManager does not support custom isolation levels by default - " + - "switch 'allowCustomIsolationLevels' to 'true'"); + "JtaTransactionManager does not support custom isolation levels by default - " + + "switch 'allowCustomIsolationLevels' to 'true'"); } } From 68cdd5d3589a1ae3b423c6383433dce0589853f9 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 4 May 2016 09:13:09 +0200 Subject: [PATCH 152/344] LazySingletonAspectInstanceFactoryDecorator uses shared singleton mutex Issue: SPR-14241 --- .../BeanFactoryAspectInstanceFactory.java | 19 ++++++++++++------- ...ngletonAspectInstanceFactoryDecorator.java | 10 +++++++--- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/BeanFactoryAspectInstanceFactory.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/BeanFactoryAspectInstanceFactory.java index 4ac2b0282c..713fb44fce 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/BeanFactoryAspectInstanceFactory.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/BeanFactoryAspectInstanceFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.core.Ordered; import org.springframework.core.annotation.OrderUtils; +import org.springframework.util.Assert; import org.springframework.util.ClassUtils; /** @@ -66,6 +67,8 @@ public BeanFactoryAspectInstanceFactory(BeanFactory beanFactory, String name) { * @param type the type that should be introspected by AspectJ */ public BeanFactoryAspectInstanceFactory(BeanFactory beanFactory, String name, Class type) { + Assert.notNull(beanFactory, "BeanFactory must not be null"); + Assert.notNull(name, "Bean name must not be null"); this.beanFactory = beanFactory; this.name = name; this.aspectMetadata = new AspectMetadata(type, name); @@ -79,12 +82,9 @@ public Object getAspectInstance() { @Override public ClassLoader getAspectClassLoader() { - if (this.beanFactory instanceof ConfigurableBeanFactory) { - return ((ConfigurableBeanFactory) this.beanFactory).getBeanClassLoader(); - } - else { - return ClassUtils.getDefaultClassLoader(); - } + return (this.beanFactory instanceof ConfigurableBeanFactory ? + ((ConfigurableBeanFactory) this.beanFactory).getBeanClassLoader() : + ClassUtils.getDefaultClassLoader()); } @Override @@ -92,6 +92,11 @@ public AspectMetadata getAspectMetadata() { return this.aspectMetadata; } + public Object getAspectCreationMutex() { + return (this.beanFactory instanceof ConfigurableBeanFactory ? + ((ConfigurableBeanFactory) this.beanFactory).getSingletonMutex() : this); + } + /** * Determine the order for this factory's target aspect, either * an instance-specific order expressed through implementing the diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/LazySingletonAspectInstanceFactoryDecorator.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/LazySingletonAspectInstanceFactoryDecorator.java index 36bb4eeb42..78df54658c 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/LazySingletonAspectInstanceFactoryDecorator.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/LazySingletonAspectInstanceFactoryDecorator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,9 +43,13 @@ public LazySingletonAspectInstanceFactoryDecorator(MetadataAwareAspectInstanceFa @Override - public synchronized Object getAspectInstance() { + public Object getAspectInstance() { if (this.materialized == null) { - synchronized (this) { + Object mutex = this; + if (this.maaif instanceof BeanFactoryAspectInstanceFactory) { + mutex = ((BeanFactoryAspectInstanceFactory) this.maaif).getAspectCreationMutex(); + } + synchronized (mutex) { if (this.materialized == null) { this.materialized = this.maaif.getAspectInstance(); } From 1e491f11d7e192fe68c1742b813c6211af5dc692 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 4 May 2016 17:37:30 +0200 Subject: [PATCH 153/344] Polishing (cherry picked from commit 8c139ef) --- .../scheduling/config/TriggerTask.java | 9 +++--- ...msListenerAnnotationBeanPostProcessor.java | 16 ++++------ .../config/JmsListenerEndpointRegistrar.java | 11 ++++--- ...ansactionalAnnotationIntegrationTests.java | 30 ++++++++++++++----- 4 files changed, 40 insertions(+), 26 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/scheduling/config/TriggerTask.java b/spring-context/src/main/java/org/springframework/scheduling/config/TriggerTask.java index 6d0aa6c813..0766d6580e 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/config/TriggerTask.java +++ b/spring-context/src/main/java/org/springframework/scheduling/config/TriggerTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,8 +19,8 @@ import org.springframework.scheduling.Trigger; /** - * {@link Task} implementation defining a {@code Runnable} to be executed according to a - * given {@link Trigger}. + * {@link Task} implementation defining a {@code Runnable} to be executed + * according to a given {@link Trigger}. * * @author Chris Beams * @since 3.2 @@ -45,6 +45,7 @@ public TriggerTask(Runnable runnable, Trigger trigger) { public Trigger getTrigger() { - return trigger; + return this.trigger; } + } diff --git a/spring-jms/src/main/java/org/springframework/jms/annotation/JmsListenerAnnotationBeanPostProcessor.java b/spring-jms/src/main/java/org/springframework/jms/annotation/JmsListenerAnnotationBeanPostProcessor.java index bda5965ed6..10b35b7ea1 100644 --- a/spring-jms/src/main/java/org/springframework/jms/annotation/JmsListenerAnnotationBeanPostProcessor.java +++ b/spring-jms/src/main/java/org/springframework/jms/annotation/JmsListenerAnnotationBeanPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -198,8 +198,8 @@ public Object postProcessAfterInitialization(final Object bean, String beanName) new MethodIntrospector.MetadataLookup>() { @Override public Set inspect(Method method) { - Set listenerMethods = - AnnotationUtils.getRepeatableAnnotations(method, JmsListener.class, JmsListeners.class); + Set listenerMethods = AnnotationUtils.getRepeatableAnnotations( + method, JmsListener.class, JmsListeners.class); return (!listenerMethods.isEmpty() ? listenerMethods : null); } }); @@ -293,15 +293,9 @@ private String getEndpointId(JmsListener jmsListener) { } } - /** - * Resolve the specified value if possible. - * @see ConfigurableBeanFactory#resolveEmbeddedValue - */ private String resolve(String value) { - if (this.beanFactory instanceof ConfigurableBeanFactory) { - return ((ConfigurableBeanFactory) this.beanFactory).resolveEmbeddedValue(value); - } - return value; + return (this.beanFactory instanceof ConfigurableBeanFactory ? + ((ConfigurableBeanFactory) this.beanFactory).resolveEmbeddedValue(value) : value); } diff --git a/spring-jms/src/main/java/org/springframework/jms/config/JmsListenerEndpointRegistrar.java b/spring-jms/src/main/java/org/springframework/jms/config/JmsListenerEndpointRegistrar.java index f965a64d1f..af540e59a7 100644 --- a/spring-jms/src/main/java/org/springframework/jms/config/JmsListenerEndpointRegistrar.java +++ b/spring-jms/src/main/java/org/springframework/jms/config/JmsListenerEndpointRegistrar.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -136,7 +136,7 @@ protected void registerAllEndpoints() { this.endpointRegistry.registerListenerContainer( descriptor.endpoint, resolveContainerFactory(descriptor)); } - startImmediately = true; // trigger immediate startup + this.startImmediately = true; // trigger immediate startup } } @@ -149,9 +149,10 @@ else if (this.containerFactory != null) { } else if (this.containerFactoryBeanName != null) { Assert.state(this.beanFactory != null, "BeanFactory must be set to obtain container factory by bean name"); + // Consider changing this if live change of the factory is required... this.containerFactory = this.beanFactory.getBean( this.containerFactoryBeanName, JmsListenerContainerFactory.class); - return this.containerFactory; // Consider changing this if live change of the factory is required + return this.containerFactory; } else { throw new IllegalStateException("Could not resolve the " + @@ -169,10 +170,12 @@ else if (this.containerFactoryBeanName != null) { public void registerEndpoint(JmsListenerEndpoint endpoint, JmsListenerContainerFactory factory) { Assert.notNull(endpoint, "Endpoint must be set"); Assert.hasText(endpoint.getId(), "Endpoint id must be set"); + // Factory may be null, we defer the resolution right before actually creating the container JmsListenerEndpointDescriptor descriptor = new JmsListenerEndpointDescriptor(endpoint, factory); + synchronized (this.mutex) { - if (startImmediately) { // Register and start immediately + if (this.startImmediately) { // register and start immediately this.endpointRegistry.registerListenerContainer(descriptor.endpoint, resolveContainerFactory(descriptor), true); } diff --git a/src/test/java/org/springframework/scheduling/annotation/ScheduledAndTransactionalAnnotationIntegrationTests.java b/src/test/java/org/springframework/scheduling/annotation/ScheduledAndTransactionalAnnotationIntegrationTests.java index 318fa211b7..1a0f250d86 100644 --- a/src/test/java/org/springframework/scheduling/annotation/ScheduledAndTransactionalAnnotationIntegrationTests.java +++ b/src/test/java/org/springframework/scheduling/annotation/ScheduledAndTransactionalAnnotationIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import org.junit.Before; import org.junit.Test; + import org.springframework.aop.support.AopUtils; import org.springframework.beans.factory.BeanCreationException; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -55,13 +56,14 @@ public void setUp() { Assume.group(TestGroup.PERFORMANCE); } + @Test public void failsWhenJdkProxyAndScheduledMethodNotPresentOnInterface() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(Config.class, JdkProxyTxConfig.class, RepoConfigA.class); try { ctx.refresh(); - fail("expected exception"); + fail("Should have thrown BeanCreationException"); } catch (BeanCreationException ex) { assertTrue(ex.getRootCause().getMessage().startsWith("@Scheduled method 'scheduled' found")); @@ -101,28 +103,36 @@ public void succeedsWhenJdkProxyAndScheduledMethodIsPresentOnInterface() throws @Configuration @EnableTransactionManagement - static class JdkProxyTxConfig { } + static class JdkProxyTxConfig { + } + @Configuration @EnableTransactionManagement(proxyTargetClass=true) - static class SubclassProxyTxConfig { } + static class SubclassProxyTxConfig { + } + @Configuration static class RepoConfigA { + @Bean public MyRepository repository() { return new MyRepositoryImpl(); } } + @Configuration static class RepoConfigB { + @Bean public MyRepositoryWithScheduledMethod repository() { return new MyRepositoryWithScheduledMethodImpl(); } } + @Configuration @EnableScheduling static class Config { @@ -139,15 +149,17 @@ public PlatformTransactionManager txManager() { @Bean public PersistenceExceptionTranslator peTranslator() { - PersistenceExceptionTranslator txlator = mock(PersistenceExceptionTranslator.class); - return txlator; + return mock(PersistenceExceptionTranslator.class); } } + public interface MyRepository { + int getInvocationCount(); } + @Repository static class MyRepositoryImpl implements MyRepository { @@ -165,11 +177,15 @@ public int getInvocationCount() { } } + public interface MyRepositoryWithScheduledMethod { + int getInvocationCount(); - public void scheduled(); + + void scheduled(); } + @Repository static class MyRepositoryWithScheduledMethodImpl implements MyRepositoryWithScheduledMethod { From 0f2bc3f6bfecdbcff28cd94397f01b7247a8a5d2 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 4 May 2016 18:17:06 +0200 Subject: [PATCH 154/344] AbstractRequestLoggingFilter ignores non-available query string Issue: SPR-14244 (cherry picked from commit 08ddd1b) --- .../filter/AbstractRequestLoggingFilter.java | 7 ++- .../web/filter/RequestLoggingFilterTests.java | 44 ++++++++++++------- 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/filter/AbstractRequestLoggingFilter.java b/spring-web/src/main/java/org/springframework/web/filter/AbstractRequestLoggingFilter.java index 0fbdbe855f..607b8bc076 100644 --- a/spring-web/src/main/java/org/springframework/web/filter/AbstractRequestLoggingFilter.java +++ b/spring-web/src/main/java/org/springframework/web/filter/AbstractRequestLoggingFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -255,7 +255,10 @@ protected String createMessage(HttpServletRequest request, String prefix, String msg.append(prefix); msg.append("uri=").append(request.getRequestURI()); if (isIncludeQueryString()) { - msg.append('?').append(request.getQueryString()); + String queryString = request.getQueryString(); + if (queryString != null) { + msg.append('?').append(queryString); + } } if (isIncludeClientInfo()) { String client = request.getRemoteAddr(); diff --git a/spring-web/src/test/java/org/springframework/web/filter/RequestLoggingFilterTests.java b/spring-web/src/test/java/org/springframework/web/filter/RequestLoggingFilterTests.java index ea9a334402..5a179e726f 100644 --- a/spring-web/src/test/java/org/springframework/web/filter/RequestLoggingFilterTests.java +++ b/spring-web/src/test/java/org/springframework/web/filter/RequestLoggingFilterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.junit.Before; import org.junit.Test; import org.springframework.mock.web.test.MockHttpServletRequest; @@ -34,19 +33,14 @@ import static org.junit.Assert.*; /** - * Test for {@link AbstractRequestLoggingFilter} and sub classes. + * Test for {@link AbstractRequestLoggingFilter} and subclasses. * * @author Arjen Poutsma + * @author Juergen Hoeller */ public class RequestLoggingFilterTests { - private MyRequestLoggingFilter filter; - - - @Before - public void createFilter() throws Exception { - filter = new MyRequestLoggingFilter(); - } + private final MyRequestLoggingFilter filter = new MyRequestLoggingFilter(); @Test @@ -70,23 +64,39 @@ public void uri() throws Exception { } @Test - public void queryString() throws Exception { + public void queryStringIncluded() throws Exception { filter.setIncludeQueryString(true); - final MockHttpServletRequest request = new MockHttpServletRequest("POST", "/hotels"); + MockHttpServletRequest request = new MockHttpServletRequest("POST", "/hotels"); MockHttpServletResponse response = new MockHttpServletResponse(); request.setQueryString("booking=42"); FilterChain filterChain = new NoOpFilterChain(); + filter.doFilter(request, response, filterChain); + + assertNotNull(filter.beforeRequestMessage); + assertTrue(filter.beforeRequestMessage.contains("[uri=/hotels?booking=42]")); + + assertNotNull(filter.afterRequestMessage); + assertTrue(filter.afterRequestMessage.contains("[uri=/hotels?booking=42]")); + } + + @Test + public void noQueryStringAvailable() throws Exception { + filter.setIncludeQueryString(true); + + MockHttpServletRequest request = new MockHttpServletRequest("POST", "/hotels"); + MockHttpServletResponse response = new MockHttpServletResponse(); + FilterChain filterChain = new NoOpFilterChain(); filter.doFilter(request, response, filterChain); assertNotNull(filter.beforeRequestMessage); - assertTrue(filter.beforeRequestMessage.contains("uri=/hotels?booking=42")); + assertTrue(filter.beforeRequestMessage.contains("[uri=/hotels]")); assertNotNull(filter.afterRequestMessage); - assertTrue(filter.afterRequestMessage.contains("uri=/hotels?booking=42")); + assertTrue(filter.afterRequestMessage.contains("[uri=/hotels]")); } @Test @@ -98,8 +108,8 @@ public void payloadInputStream() throws Exception { final byte[] requestBody = "Hello World".getBytes("UTF-8"); request.setContent(requestBody); - FilterChain filterChain = new FilterChain() { + FilterChain filterChain = new FilterChain() { @Override public void doFilter(ServletRequest filterRequest, ServletResponse filterResponse) throws IOException, ServletException { @@ -124,8 +134,8 @@ public void payloadReader() throws Exception { final String requestBody = "Hello World"; request.setContent(requestBody.getBytes("UTF-8")); - FilterChain filterChain = new FilterChain() { + FilterChain filterChain = new FilterChain() { @Override public void doFilter(ServletRequest filterRequest, ServletResponse filterResponse) throws IOException, ServletException { @@ -151,8 +161,8 @@ public void payloadMaxLength() throws Exception { final byte[] requestBody = "Hello World".getBytes("UTF-8"); request.setContent(requestBody); - FilterChain filterChain = new FilterChain() { + FilterChain filterChain = new FilterChain() { @Override public void doFilter(ServletRequest filterRequest, ServletResponse filterResponse) throws IOException, ServletException { From 2dbc8b0381524a632c102f7915aab742fe8731da Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 4 May 2016 18:44:11 +0200 Subject: [PATCH 155/344] Latest applicable dependency updates (EhCache 2.10.2, JRuby 1.7.25, Quartz 2.2.3, Undertow 1.3.22) --- build.gradle | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/build.gradle b/build.gradle index f8f8619943..7dd448283a 100644 --- a/build.gradle +++ b/build.gradle @@ -29,10 +29,10 @@ configure(allprojects) { project -> ext.aspectjVersion = "1.8.9" ext.eclipselinkVersion = "2.4.2" - ext.ehcacheVersion = "2.10.1" + ext.ehcacheVersion = "2.10.2" ext.ehcachejcacheVersion = "1.0.1" ext.ehcache3Version = "3.0.0" - ext.ejbApiVersion = "3.0" + ext.ejbVersion = "3.0" ext.fileuploadVersion = "1.3.1" ext.freemarkerVersion = "2.3.23" ext.groovyVersion = "2.4.6" @@ -45,7 +45,6 @@ configure(allprojects) { project -> ext.hibval4Version = "4.3.2.Final" ext.hibval5Version = "5.2.4.Final" ext.hsqldbVersion = "2.3.3" - ext.htmlunitVersion = "2.19" ext.httpasyncVersion = "4.1.1" ext.httpclientVersion = "4.5.2" ext.jackson2Version = "2.6.6" @@ -53,9 +52,7 @@ configure(allprojects) { project -> ext.javamailVersion = "1.5.5" ext.jettyVersion = "9.3.8.v20160314" ext.jodaVersion = "2.9.3" - ext.jrubyVersion = "1.7.24" // JRuby 9000 only supported through JSR-223 (StandardScriptFactory) - ext.jsonassertVersion = "1.2.3" - ext.jsonpathVersion = "2.1.0" + ext.jrubyVersion = "1.7.25" // JRuby 9000 only supported through JSR-223 (StandardScriptFactory) ext.jtaVersion = "1.2" ext.junitVersion = "4.12" ext.nettyVersion = "4.0.36.Final" @@ -64,7 +61,6 @@ configure(allprojects) { project -> ext.poiVersion = "3.13" ext.reactorVersion = "2.0.8.RELEASE" ext.romeVersion = "1.5.1" - ext.seleniumVersion = "2.48.2" ext.slf4jVersion = "1.7.21" ext.snakeyamlVersion = "1.17" ext.snifferVersion = "1.14" @@ -73,8 +69,7 @@ configure(allprojects) { project -> ext.tiles3Version = "3.0.5" ext.tomcatVersion = "8.0.33" ext.tyrusVersion = "1.3.5" // constrained by WebLogic 12.1.3 support - ext.undertowVersion = "1.3.21.Final" - ext.woodstoxVersion = "5.0.2" + ext.undertowVersion = "1.3.22.Final" ext.xmlunitVersion = "1.6" ext.xstreamVersion = "1.4.9" @@ -353,7 +348,7 @@ project("spring-core") { optional("log4j:log4j:1.2.17") testCompile("org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}") testCompile("xmlunit:xmlunit:${xmlunitVersion}") - testCompile("com.fasterxml.woodstox:woodstox-core:${woodstoxVersion}") { + testCompile("com.fasterxml.woodstox:woodstox-core:5.0.2") { exclude group: "stax", module: "stax-api" } } @@ -474,7 +469,7 @@ project("spring-context") { compile(files(project(":spring-core").cglibRepackJar)) optional(project(":spring-instrument")) optional("javax.inject:javax.inject:1") - optional("javax.ejb:ejb-api:${ejbApiVersion}") + optional("javax.ejb:ejb-api:${ejbVersion}") optional("javax.enterprise.concurrent:javax.enterprise.concurrent-api:1.0") optional("javax.money:money-api:1.0") optional("org.eclipse.persistence:javax.persistence:2.0.0") @@ -545,7 +540,7 @@ project("spring-tx") { optional("aopalliance:aopalliance:1.0") optional("javax.transaction:javax.transaction-api:${jtaVersion}") optional("javax.resource:connector-api:1.5") - optional("javax.ejb:ejb-api:${ejbApiVersion}") + optional("javax.ejb:ejb-api:${ejbVersion}") optional("com.ibm.websphere:uow:6.0.2.17") testCompile("org.aspectj:aspectjweaver:${aspectjVersion}") testCompile("org.eclipse.persistence:javax.persistence:2.0.0") @@ -641,7 +636,7 @@ project("spring-context-support") { optional("javax.cache:cache-api:1.0.0") optional("com.google.guava:guava:${guavaVersion}") optional("net.sf.ehcache:ehcache:${ehcacheVersion}") - optional("org.quartz-scheduler:quartz:2.2.2") + optional("org.quartz-scheduler:quartz:2.2.3") optional("org.codehaus.fabric3.api:commonj:1.1.0") optional("org.apache.velocity:velocity:1.7") optional("org.freemarker:freemarker:${freemarkerVersion}") @@ -1010,15 +1005,15 @@ project("spring-test") { optional("org.aspectj:aspectjweaver:${aspectjVersion}") optional("org.codehaus.groovy:groovy-all:${groovyVersion}") optional("org.hamcrest:hamcrest-core:${hamcrestVersion}") - optional("org.seleniumhq.selenium:selenium-htmlunit-driver:${seleniumVersion}") - optional("net.sourceforge.htmlunit:htmlunit:${htmlunitVersion}") optional("xmlunit:xmlunit:${xmlunitVersion}") - optional("org.skyscreamer:jsonassert:${jsonassertVersion}") - optional("com.jayway.jsonpath:json-path:${jsonpathVersion}") + optional("net.sourceforge.htmlunit:htmlunit:2.19") + optional("org.seleniumhq.selenium:selenium-htmlunit-driver:2.48.2") + optional("org.skyscreamer:jsonassert:1.2.3") + optional("com.jayway.jsonpath:json-path:2.1.0") testCompile(project(":spring-context-support")) testCompile(project(":spring-oxm")) testCompile("javax.mail:javax.mail-api:${javamailVersion}") - testCompile("javax.ejb:ejb-api:${ejbApiVersion}") + testCompile("javax.ejb:ejb-api:${ejbVersion}") testCompile("org.hibernate:hibernate-core:${hibernate4Version}") testCompile("org.hibernate:hibernate-entitymanager:${hibernate4Version}") testCompile("org.hibernate:hibernate-validator:${hibval5Version}") From 5fc0d43852d22a488ce37e7dcdc1112ab2758933 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 5 May 2016 20:40:30 +0200 Subject: [PATCH 156/344] AsyncResult properly propagates execution exception Issue: SPR-14249 (cherry picked from commit 1b1aac9) --- .../scheduling/annotation/AsyncResult.java | 17 ++-- .../annotation/AsyncResultTests.java | 89 +++++++++++++++++++ 2 files changed, 101 insertions(+), 5 deletions(-) create mode 100644 spring-context/src/test/java/org/springframework/scheduling/annotation/AsyncResultTests.java diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncResult.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncResult.java index ae6ecc4271..6d67ac3ebc 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncResult.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/AsyncResult.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,6 +37,7 @@ * to the caller. * * @author Juergen Hoeller + * @author Rossen Stoyanchev * @since 3.0 * @see Async * @see #forValue(Object) @@ -102,11 +103,17 @@ public void addCallback(ListenableFutureCallback callback) { @Override public void addCallback(SuccessCallback successCallback, FailureCallback failureCallback) { - try { - successCallback.onSuccess(this.value); + if (this.executionException != null) { + Throwable cause = this.executionException.getCause(); + failureCallback.onFailure(cause != null ? cause : this.executionException); } - catch (Throwable ex) { - failureCallback.onFailure(ex); + else { + try { + successCallback.onSuccess(this.value); + } + catch (Throwable ex) { + failureCallback.onFailure(ex); + } } } diff --git a/spring-context/src/test/java/org/springframework/scheduling/annotation/AsyncResultTests.java b/spring-context/src/test/java/org/springframework/scheduling/annotation/AsyncResultTests.java new file mode 100644 index 0000000000..e37e0d8bf0 --- /dev/null +++ b/spring-context/src/test/java/org/springframework/scheduling/annotation/AsyncResultTests.java @@ -0,0 +1,89 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.scheduling.annotation; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; + +import org.junit.Test; + +import org.springframework.util.concurrent.ListenableFuture; +import org.springframework.util.concurrent.ListenableFutureCallback; + +import static org.junit.Assert.*; + +/** + * @author Juergen Hoeller + */ +public class AsyncResultTests { + + @Test + public void asyncResultWithCallbackAndValue() { + String value = "val"; + final Set values = new HashSet<>(1); + ListenableFuture future = AsyncResult.forValue(value); + future.addCallback(new ListenableFutureCallback() { + @Override + public void onSuccess(String result) { + values.add(result); + } + @Override + public void onFailure(Throwable ex) { + fail("Failure callback not expected: " + ex); + } + }); + assertSame(value, values.iterator().next()); + } + + @Test + public void asyncResultWithCallbackAndException() { + IOException ex = new IOException(); + final Set values = new HashSet<>(1); + ListenableFuture future = AsyncResult.forExecutionException(ex); + future.addCallback(new ListenableFutureCallback() { + @Override + public void onSuccess(String result) { + fail("Success callback not expected: " + result); + } + @Override + public void onFailure(Throwable ex) { + values.add(ex); + } + }); + assertSame(ex, values.iterator().next()); + } + + @Test + public void asyncResultWithSeparateCallbacksAndValue() { + String value = "val"; + final Set values = new HashSet<>(1); + ListenableFuture future = AsyncResult.forValue(value); + future.addCallback(values::add, (ex) -> fail("Failure callback not expected: " + ex)); + assertSame(value, values.iterator().next()); + } + + @Test + public void asyncResultWithSeparateCallbacksAndException() { + IOException ex = new IOException(); + final Set values = new HashSet<>(1); + ListenableFuture future = AsyncResult.forExecutionException(ex); + future.addCallback((result) -> fail("Success callback not expected: " + result), values::add); + assertSame(ex, values.iterator().next()); + } + +} From 6eba220d1bbec1f36a28e426dec4ffcdb4ae2b50 Mon Sep 17 00:00:00 2001 From: Spring Buildmaster Date: Fri, 6 May 2016 07:56:13 +0000 Subject: [PATCH 157/344] Next Development Version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index dc7301f5c3..3edeafeecf 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=4.2.6.BUILD-SNAPSHOT +version=4.2.7.BUILD-SNAPSHOT From b5022108c74763d0695f5655a3a685c364477afd Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Thu, 12 May 2016 11:21:41 -0400 Subject: [PATCH 158/344] Fix STOMP connect failure related memory leak Normally heartbeats keep connections from hanging. However in some cases a connection may hang before a CONNECTED frame is received and heartbeats are put in place. This commit adds a change to enforce a 60 limit on receiving the CONNECTED frame. Issue: SPR-14266 --- .../stomp/StompBrokerRelayMessageHandler.java | 17 +++++++++++++++++ .../tcp/reactor/Reactor2TcpClient.java | 3 ++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompBrokerRelayMessageHandler.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompBrokerRelayMessageHandler.java index 1eb2e49669..18ab77f67f 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompBrokerRelayMessageHandler.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/StompBrokerRelayMessageHandler.java @@ -88,6 +88,14 @@ public class StompBrokerRelayMessageHandler extends AbstractBrokerMessageHandler private static final Message HEARTBEAT_MESSAGE; + /** + * A heartbeat is setup once a CONNECTED frame is received which contains + * the heartbeat settings we need. If we don't receive CONNECTED within + * a minute, the connection is closed proactively. + */ + private static final int MAX_TIME_TO_CONNECTED_FRAME = 60 * 1000; + + static { EMPTY_TASK.run(); @@ -567,6 +575,15 @@ public void afterConnected(TcpConnection connection) { logger.debug("TCP connection opened in session=" + getSessionId()); } this.tcpConnection = connection; + this.tcpConnection.onReadInactivity(new Runnable() { + @Override + public void run() { + if (tcpConnection != null && !isStompConnected) { + handleTcpConnectionFailure("No CONNECTED frame received in " + + MAX_TIME_TO_CONNECTED_FRAME + " ms.", null); + } + } + }, MAX_TIME_TO_CONNECTED_FRAME); connection.send(MessageBuilder.createMessage(EMPTY_PAYLOAD, this.connectHeaders.getMessageHeaders())); } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/tcp/reactor/Reactor2TcpClient.java b/spring-messaging/src/main/java/org/springframework/messaging/tcp/reactor/Reactor2TcpClient.java index 664e23c219..71541dcd79 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/tcp/reactor/Reactor2TcpClient.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/tcp/reactor/Reactor2TcpClient.java @@ -168,7 +168,7 @@ public ListenableFuture connect(final TcpConnectionHandler

    connectionHa Assert.notNull(connectionHandler, "TcpConnectionHandler must not be null"); final TcpClient, Message

    > tcpClient; - Runnable cleanupTask; + final Runnable cleanupTask; synchronized (this.tcpClients) { if (this.stopping) { IllegalStateException ex = new IllegalStateException("Shutting down."); @@ -194,6 +194,7 @@ public void run() { promise.onError(new Consumer() { @Override public void accept(Throwable ex) { + cleanupTask.run(); connectionHandler.afterConnectFailure(ex); } }) From 28321946bfa6ccab66a110e590adef0ce9323644 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Wed, 18 May 2016 10:57:54 -0400 Subject: [PATCH 159/344] Remove unnecessary assertion Issue: SPR-14279 --- .../messaging/simp/broker/SimpleBrokerMessageHandler.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/broker/SimpleBrokerMessageHandler.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/broker/SimpleBrokerMessageHandler.java index b0a2c2481f..b78ab54a0f 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/broker/SimpleBrokerMessageHandler.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/broker/SimpleBrokerMessageHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -223,12 +223,6 @@ protected void handleMessageInternal(Message message) { return; } - SimpMessageHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, SimpMessageHeaderAccessor.class); - if (accessor == null) { - throw new IllegalStateException( - "No header accessor (not using the SimpMessagingTemplate?): " + message); - } - if (SimpMessageType.MESSAGE.equals(messageType)) { logMessage(message); sendMessageToSubscribers(destination, message); From a2088c38adc2b66cc3832d0cb3623f329a881e6f Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 31 May 2016 13:19:52 +0200 Subject: [PATCH 160/344] Latest applicable dependency updates (Joda-Time 2.9.4, Jetty 9.3.9, EhCache 3.0.1) --- build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 7dd448283a..3880c2e80c 100644 --- a/build.gradle +++ b/build.gradle @@ -31,7 +31,7 @@ configure(allprojects) { project -> ext.eclipselinkVersion = "2.4.2" ext.ehcacheVersion = "2.10.2" ext.ehcachejcacheVersion = "1.0.1" - ext.ehcache3Version = "3.0.0" + ext.ehcache3Version = "3.0.1" ext.ejbVersion = "3.0" ext.fileuploadVersion = "1.3.1" ext.freemarkerVersion = "2.3.23" @@ -50,8 +50,8 @@ configure(allprojects) { project -> ext.jackson2Version = "2.6.6" ext.jasperreportsVersion = "6.2.1" ext.javamailVersion = "1.5.5" - ext.jettyVersion = "9.3.8.v20160314" - ext.jodaVersion = "2.9.3" + ext.jettyVersion = "9.3.9.v20160517" + ext.jodaVersion = "2.9.4" ext.jrubyVersion = "1.7.25" // JRuby 9000 only supported through JSR-223 (StandardScriptFactory) ext.jtaVersion = "1.2" ext.junitVersion = "4.12" From ad29db86755e190a6352ca21ad27c5a74bb56209 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 30 May 2016 15:04:57 +0200 Subject: [PATCH 161/344] Reference documentation updates Issue: SPR-14087 Issue: SPR-14272 Issue: SPR-13535 Issue: SPR-13843 Issue: SPR-14164 Issue: SPR-14319 --- src/asciidoc/core-aop.adoc | 11 +++++----- src/asciidoc/core-beans.adoc | 32 +++++++++++++++++++++-------- src/asciidoc/integration.adoc | 38 +++++++++++++++++++++-------------- 3 files changed, 52 insertions(+), 29 deletions(-) diff --git a/src/asciidoc/core-aop.adoc b/src/asciidoc/core-aop.adoc index 5fc62fb460..57dffd371a 100644 --- a/src/asciidoc/core-aop.adoc +++ b/src/asciidoc/core-aop.adoc @@ -438,7 +438,7 @@ be &&'ed, ||'ed, and ! (negated) too. ==== Please note that the '++bean++' PCD is __only__ supported in Spring AOP - and __not__ in native AspectJ weaving. It is a Spring-specific extension to the standard PCDs that -AspectJ defines. +AspectJ defines and therefore not available for aspects declared in the `@Aspect` model. The '++bean++' PCD operates at the __instance__ level (building on the Spring bean name concept) rather than at the type level only (which is what weaving-based AOP is limited @@ -1991,7 +1991,7 @@ proceed with the method call: the presence of this parameter is an indication th public class SimpleProfiler { public Object profile(ProceedingJoinPoint call, String name, int age) throws Throwable { - StopWatch clock = new StopWatch("Profiling for ''" + name + "'' and ''" + age + "''"); + StopWatch clock = new StopWatch("Profiling for '" + name + "' and '" + age + "'"); try { clock.start(call.toShortString()); return call.proceed(); @@ -2884,10 +2884,9 @@ A `@Transactional` annotation on a class specifies the default transaction seman the execution of any __public__ operation in the class. A `@Transactional` annotation on a method within the class overrides the default -transaction semantics given by the class annotation (if present). Methods with `public`, -`protected`, and default visibility may all be annotated. Annotating `protected` and -default visibility methods directly is the only way to get transaction demarcation for -the execution of such methods. +transaction semantics given by the class annotation (if present). Methods of any +visibility may be annotated, including private methods. Annotating non-public methods +directly is the only way to get transaction demarcation for the execution of such methods. [TIP] ==== diff --git a/src/asciidoc/core-beans.adoc b/src/asciidoc/core-beans.adoc index 2214a26d6b..3a78701b82 100644 --- a/src/asciidoc/core-beans.adoc +++ b/src/asciidoc/core-beans.adoc @@ -437,6 +437,16 @@ you are using Spring AOP it helps a lot when applying advice to a set of beans r by name. **** +[NOTE] +==== +With component scanning in the classpath, Spring generates bean names for unnamed +components, following the rules above: essentially, taking the simple class name +and turning its initial character to lower-case. However, in the (unusual) special +case when there is more than one character and both the first and second characters +are upper case, the original casing gets preserved. These are the same rules as +defined by `java.beans.Introspector.decapitalize` (which Spring is using here). +==== + [[beans-beanname-alias]] ==== Aliasing a bean outside the bean definition @@ -1719,7 +1729,7 @@ then nested `constructor-arg` elements. Let's review the examples from <> with the `c:` namespace: -[source,java,indent=0] +[source,xml,indent=0] [subs="verbatim,quotes"] ---- @@ -2561,7 +2571,7 @@ The Spring container creates a new instance of the `AppPreferences` bean by usin `appPreferences` bean is scoped at the `ServletContext` level, stored as a regular `ServletContext` attribute. This is somewhat similar to a Spring singleton bean but differs in two important ways: It is a singleton per `ServletContext`, not per Spring -'ApplicationContext' (or which there may be several in any given web application), +'ApplicationContext' (for which there may be several in any given web application), and it is actually exposed and therefore visible as a `ServletContext` attribute. @@ -3707,7 +3717,7 @@ Find below the custom `BeanPostProcessor` implementation class definition: public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - System.out.println("Bean ''" + beanName + "'' created : " + bean.toString()); + System.out.println("Bean '" + beanName + "' created : " + bean.toString()); return bean; } @@ -5414,7 +5424,7 @@ resolving expression text. The `@Bean` methods in a Spring component are processed differently than their counterparts inside a Spring `@Configuration` class. The difference is that `@Component` classes are not enhanced with CGLIB to intercept the invocation of methods and fields. -CGLIB proxying is the means by which invoking methods or fields within `@Bean` methods +CGLIB proxying is the means by which invoking methods or fields within `@Bean` methods in `@Configuration` classes creates bean metadata references to collaborating objects; such methods are __not__ invoked with normal Java semantics but rather go through the container in order to provide the usual lifecycle management and proxying of Spring @@ -5798,8 +5808,8 @@ It is very common to use `@Component` without specifying a name for the componen } ---- -When using `@Named`, it is possible to use -component-scanning in the exact same way as when using Spring annotations: +When using `@Named`, it is possible to use component scanning in the exact same way +as when using Spring annotations: [source,java,indent=0] [subs="verbatim,quotes"] @@ -5811,6 +5821,12 @@ component-scanning in the exact same way as when using Spring annotations: } ---- +[NOTE] +==== +In contrast to `@Component`, the JSR-330 `@Named` annotation is not composable. +Please use Spring's stereotype model for building custom component annotations. +==== + [[beans-standard-annotations-limitations]] @@ -7460,7 +7476,7 @@ hierarchy of property sources. To explain fully, consider the following: ApplicationContext ctx = new GenericApplicationContext(); Environment env = ctx.getEnvironment(); boolean containsFoo = env.containsProperty("foo"); -System.out.println("Does my environment contain the ''foo'' property? " + containsFoo); +System.out.println("Does my environment contain the 'foo' property? " + containsFoo); ---- In the snippet above, we see a high-level way of asking Spring whether the `foo` property is diff --git a/src/asciidoc/integration.adoc b/src/asciidoc/integration.adoc index febfb656b7..532852bcee 100644 --- a/src/asciidoc/integration.adoc +++ b/src/asciidoc/integration.adoc @@ -6001,7 +6001,7 @@ along with an inline image. helper.setTo("test@host.com"); // use the true flag to indicate the text included is HTML - helper.setText("", true); + helper.setText("", true); // let's include the infamous windows Sample file (this time copied to c:/) FileSystemResource res = new FileSystemResource(new File("c:/Sample.jpg")); @@ -6492,7 +6492,7 @@ reference is provided for managing those methods annotated with `@Scheduled`. [[scheduling-annotation-support-scheduled]] ==== The @Scheduled Annotation -The @Scheduled annotation can be added to a method along with trigger metadata. For +The `@Scheduled` annotation can be added to a method along with trigger metadata. For example, the following method would be invoked every 5 seconds with a fixed delay, meaning that the period will be measured from the completion time of each preceding invocation. @@ -6556,13 +6556,13 @@ Context, then those would typically have been provided through dependency inject [NOTE] ==== -Make sure that you are not initializing multiple instances of the same @Scheduled +Make sure that you are not initializing multiple instances of the same `@Scheduled` annotation class at runtime, unless you do want to schedule callbacks to each such -instance. Related to this, make sure that you do not use @Configurable on bean classes -which are annotated with @Scheduled and registered as regular Spring beans with the -container: You would get double initialization otherwise, once through the container and -once through the @Configurable aspect, with the consequence of each @Scheduled method -being invoked twice. +instance. Related to this, make sure that you do not use `@Configurable` on bean +classes which are annotated with `@Scheduled` and registered as regular Spring beans +with the container: You would get double initialization otherwise, once through the +container and once through the `@Configurable` aspect, with the consequence of each +`@Scheduled` method being invoked twice. ==== @@ -6613,8 +6613,8 @@ asynchronous execution so that the caller can perform other tasks prior to calli ---- `@Async` can not be used in conjunction with lifecycle callbacks such as -`@PostConstruct`. To asynchronously initialize Spring beans you currently have to use a -separate initializing Spring bean that invokes the `@Async` annotated method on the +`@PostConstruct`. To asynchronously initialize Spring beans you currently have to use +a separate initializing Spring bean that invokes the `@Async` annotated method on the target then. [source,java,indent=0] @@ -6629,7 +6629,7 @@ target then. } - public class SampleBeanInititalizer { + public class SampleBeanInitializer { private final SampleBean bean; @@ -6645,6 +6645,14 @@ target then. } ---- +[NOTE] +==== +There is no direct XML equivalent for `@Async` since such methods should be designed +for asynchronous execution in the first place, not externally re-declared to be async. +However, you may manually set up Spring's `AsyncExecutionInterceptor` with Spring AOP, +in combination with a custom pointcut. +==== + [[scheduling-annotation-support-qualification]] @@ -7360,7 +7368,7 @@ message is surrounded by quotes. Below are the changes that I (the author) make public String getMessage() { // change the implementation to surround the message in quotes - return "''" + this.message + "''" + return "'" + this.message + "'" } public void setMessage(String message) { @@ -7767,7 +7775,7 @@ will want to do with this callback, and you can see an example of doing that bel DelegatingMetaClass metaClass = new DelegatingMetaClass(goo.getMetaClass()) { public Object invokeMethod(Object object, String methodName, Object[] arguments) { - System.out.println("Invoking ''" + methodName + "''."); + System.out.println("Invoking '" + methodName + "'."); return super.invokeMethod(object, methodName, arguments); } }; @@ -8814,7 +8822,7 @@ up its declaration at runtime and understands its meaning. Note that as mentione === JCache (JSR-107) annotations Since the Spring Framework 4.1, the caching abstraction fully supports the JCache -standard annotations: these are `@CacheResult`, `@CacheEvict`, `@CacheRemove` and +standard annotations: these are `@CacheResult`, `@CachePut`, `@CacheRemove` and `@CacheRemoveAll` as well as the `@CacheDefaults`, `@CacheKey` and `@CacheValue` companions. These annotations can be used right the way without migrating your cache store to JSR-107: the internal implementation uses Spring's caching abstraction @@ -8996,7 +9004,7 @@ we did in the example above by defining the target cache through the `cache:defi [[cache-store-configuration]] === Configuring the cache storage -Out of the box, the cache abstraction provides several storages integration. To use +Out of the box, the cache abstraction provides several storage integration. To use them, one needs to simply declare an appropriate `CacheManager` - an entity that controls and manages ++Cache++s and can be used to retrieve these for storage. From a0a2a3337a3c9c806b80130f6c4cd7e82db4395d Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sun, 29 May 2016 13:19:09 +0200 Subject: [PATCH 162/344] @Bean's "autowire" attribute does not affect annotation-driven autowiring Issue: SPR-14282 (cherry picked from commit 98eaf05) --- .../java/org/springframework/context/annotation/Bean.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/spring-context/src/main/java/org/springframework/context/annotation/Bean.java b/spring-context/src/main/java/org/springframework/context/annotation/Bean.java index 2763560088..8cbaa0da3b 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/Bean.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/Bean.java @@ -198,6 +198,13 @@ /** * Are dependencies to be injected via convention-based autowiring by name or type? + *

    Note that this autowire mode is just about externally driven autowiring based + * on bean property setter methods by convention, analogous to XML bean definitions. + *

    The default mode does allow for annotation-driven autowiring. "no" refers to + * externally driven autowiring only, not affecting any autowiring demands that the + * bean class itself expresses through annotations. + * @see Autowire#BY_NAME + * @see Autowire#BY_TYPE */ Autowire autowire() default Autowire.NO; From 7b73f23f43ca1c9a75b0fcacb2db3aa974d669df Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 26 May 2016 19:18:30 +0200 Subject: [PATCH 163/344] For attribute is not actually required Issue: SPR-14287 (cherry picked from commit dacc31e) --- .../org/springframework/web/servlet/tags/form/LabelTag.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/LabelTag.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/LabelTag.java index f8dd7f28e8..cf1e2e6c90 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/LabelTag.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/LabelTag.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,8 +25,6 @@ * Databinding-aware JSP tag for rendering an HTML '{@code label}' element * that defines text that is associated with a single form element. * - *

    The {@link #setFor(String) 'for'} attribute is required. - * *

    See the "formTags" showcase application that ships with the * full Spring distribution for an example of this class in action. * From 07b5763324b0887455c560506f51110b8fc2a71a Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 27 May 2016 18:24:49 +0200 Subject: [PATCH 164/344] SpringTilesContainerFactory uses Tiles 3's createDecoratedContainer Issue: SPR-14311 (cherry picked from commit 2d85acc) --- .../web/servlet/view/tiles3/TilesConfigurer.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/tiles3/TilesConfigurer.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/tiles3/TilesConfigurer.java index 8e77b275b9..919f83d399 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/view/tiles3/TilesConfigurer.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/view/tiles3/TilesConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -293,9 +293,8 @@ protected AbstractTilesContainerFactory createContainerFactory(ApplicationContex private class SpringTilesContainerFactory extends BasicTilesContainerFactory { @Override - public TilesContainer createContainer(ApplicationContext context) { - TilesContainer container = super.createContainer(context); - return (useMutableTilesContainer ? new CachingTilesContainer(container) : container); + protected TilesContainer createDecoratedContainer(TilesContainer originalContainer, ApplicationContext context) { + return (useMutableTilesContainer ? new CachingTilesContainer(originalContainer) : originalContainer); } @Override From f64ed692800d82840e2842476ffd3201245fb254 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sun, 29 May 2016 13:22:00 +0200 Subject: [PATCH 165/344] ServletServerHttpRequest.getHeaders() ignores invalid content type Issue: SPR-14309 (cherry picked from commit f7f2327) --- .../http/server/ServletServerHttpRequest.java | 42 ++++++++++++------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java b/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java index 6fda91d29b..4a8f342aa3 100644 --- a/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java @@ -37,6 +37,7 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; +import org.springframework.http.InvalidMediaTypeException; import org.springframework.http.MediaType; import org.springframework.util.Assert; import org.springframework.util.LinkedCaseInsensitiveMap; @@ -104,6 +105,7 @@ public URI getURI() { public HttpHeaders getHeaders() { if (this.headers == null) { this.headers = new HttpHeaders(); + for (Enumeration headerNames = this.servletRequest.getHeaderNames(); headerNames.hasMoreElements();) { String headerName = (String) headerNames.nextElement(); for (Enumeration headerValues = this.servletRequest.getHeaders(headerName); @@ -112,26 +114,33 @@ public HttpHeaders getHeaders() { this.headers.add(headerName, headerValue); } } + // HttpServletRequest exposes some headers as properties: we should include those if not already present - MediaType contentType = this.headers.getContentType(); - if (contentType == null) { - String requestContentType = this.servletRequest.getContentType(); - if (StringUtils.hasLength(requestContentType)) { - contentType = MediaType.parseMediaType(requestContentType); - this.headers.setContentType(contentType); + try { + MediaType contentType = this.headers.getContentType(); + if (contentType == null) { + String requestContentType = this.servletRequest.getContentType(); + if (StringUtils.hasLength(requestContentType)) { + contentType = MediaType.parseMediaType(requestContentType); + this.headers.setContentType(contentType); + } } - } - if (contentType != null && contentType.getCharSet() == null) { - String requestEncoding = this.servletRequest.getCharacterEncoding(); - if (StringUtils.hasLength(requestEncoding)) { - Charset charSet = Charset.forName(requestEncoding); - Map params = new LinkedCaseInsensitiveMap(); - params.putAll(contentType.getParameters()); - params.put("charset", charSet.toString()); - MediaType newContentType = new MediaType(contentType.getType(), contentType.getSubtype(), params); - this.headers.setContentType(newContentType); + if (contentType != null && contentType.getCharSet() == null) { + String requestEncoding = this.servletRequest.getCharacterEncoding(); + if (StringUtils.hasLength(requestEncoding)) { + Charset charSet = Charset.forName(requestEncoding); + Map params = new LinkedCaseInsensitiveMap(); + params.putAll(contentType.getParameters()); + params.put("charset", charSet.toString()); + MediaType newContentType = new MediaType(contentType.getType(), contentType.getSubtype(), params); + this.headers.setContentType(newContentType); + } } } + catch (InvalidMediaTypeException ex) { + // Ignore: simply not exposing an invalid content type in HttpHeaders... + } + if (this.headers.getContentLength() < 0) { int requestContentLength = this.servletRequest.getContentLength(); if (requestContentLength != -1) { @@ -139,6 +148,7 @@ public HttpHeaders getHeaders() { } } } + return this.headers; } From cc7758869c01eed1d69fa98398344acc11866a9f Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 26 May 2016 19:39:22 +0200 Subject: [PATCH 166/344] SettableListenableFuture properly rethrows Error Issue: SPR-14298 (cherry picked from commit a979885) --- .../concurrent/SettableListenableFuture.java | 34 +++++++------- .../SettableListenableFutureTests.java | 44 ++++++++++++------- 2 files changed, 46 insertions(+), 32 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/util/concurrent/SettableListenableFuture.java b/spring-core/src/main/java/org/springframework/util/concurrent/SettableListenableFuture.java index cc2b079e87..3a61db2b5b 100644 --- a/spring-core/src/main/java/org/springframework/util/concurrent/SettableListenableFuture.java +++ b/spring-core/src/main/java/org/springframework/util/concurrent/SettableListenableFuture.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ import java.util.concurrent.atomic.AtomicReference; import org.springframework.util.Assert; +import org.springframework.util.ReflectionUtils; /** * A {@link org.springframework.util.concurrent.ListenableFuture ListenableFuture} @@ -49,11 +50,11 @@ public SettableListenableFuture() { /** - * Set the value of this future. This method will return {@code true} if - * the value was set successfully, or {@code false} if the future has already - * been set or cancelled. - * @param value the value that will be set. - * @return {@code true} if the value was successfully set, else {@code false}. + * Set the value of this future. This method will return {@code true} if the + * value was set successfully, or {@code false} if the future has already been + * set or cancelled. + * @param value the value that will be set + * @return {@code true} if the value was successfully set, else {@code false} */ public boolean set(T value) { boolean success = this.settableTask.setValue(value); @@ -64,14 +65,14 @@ public boolean set(T value) { } /** - * Set the exception of this future. This method will return {@code true} if - * the exception was set successfully, or {@code false} if the future has already - * been set or cancelled. - * @param exception the value that will be set. - * @return {@code true} if the exception was successfully set, else {@code false}. + * Set the exception of this future. This method will return {@code true} if the + * exception was set successfully, or {@code false} if the future has already been + * set or cancelled. + * @param exception the value that will be set + * @return {@code true} if the exception was successfully set, else {@code false} */ public boolean setException(Throwable exception) { - Assert.notNull(exception, "'exception' must not be null"); + Assert.notNull(exception, "Exception must not be null"); boolean success = this.settableTask.setException(exception); if (success) { this.listenableFuture.run(); @@ -149,7 +150,7 @@ protected void interruptTask() { private static class SettableTask implements Callable { - private static final String NO_VALUE = SettableListenableFuture.class.getName() + ".NO_VALUE"; + private static final Object NO_VALUE = new Object(); private final AtomicReference value = new AtomicReference(NO_VALUE); @@ -176,10 +177,11 @@ public void setCancelled() { @SuppressWarnings("unchecked") @Override public T call() throws Exception { - if (value.get() instanceof Exception) { - throw (Exception) value.get(); + Object val = this.value.get(); + if (val instanceof Throwable) { + ReflectionUtils.rethrowException((Throwable) val); } - return (T) value.get(); + return (T) val; } } diff --git a/spring-core/src/test/java/org/springframework/util/concurrent/SettableListenableFutureTests.java b/spring-core/src/test/java/org/springframework/util/concurrent/SettableListenableFutureTests.java index 0e87d4e156..c8037cac86 100644 --- a/spring-core/src/test/java/org/springframework/util/concurrent/SettableListenableFutureTests.java +++ b/spring-core/src/test/java/org/springframework/util/concurrent/SettableListenableFutureTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,7 +21,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import org.junit.Before; import org.junit.Test; import static org.hamcrest.Matchers.*; @@ -35,12 +34,8 @@ @SuppressWarnings({ "rawtypes", "unchecked" }) public class SettableListenableFutureTests { - private SettableListenableFuture settableListenableFuture; + private final SettableListenableFuture settableListenableFuture = new SettableListenableFuture(); - @Before - public void setUp() { - settableListenableFuture = new SettableListenableFuture(); - } @Test public void validateInitialValues() { @@ -76,6 +71,20 @@ public void throwsSetExceptionWrappedInExecutionException() throws ExecutionExce } } + @Test + public void throwsSetErrorWrappedInExecutionException() throws ExecutionException, InterruptedException { + Throwable exception = new OutOfMemoryError(); + boolean wasSet = settableListenableFuture.setException(exception); + assertTrue(wasSet); + try { + settableListenableFuture.get(); + fail("Expected ExecutionException"); + } + catch (ExecutionException ex) { + assertThat(ex.getCause(), equalTo(exception)); + } + } + @Test public void setValueTriggersCallback() { String string = "hello"; @@ -85,7 +94,6 @@ public void setValueTriggersCallback() { public void onSuccess(String result) { callbackHolder[0] = result; } - @Override public void onFailure(Throwable ex) { fail("Expected onSuccess() to be called"); @@ -104,7 +112,6 @@ public void setValueTriggersCallbackOnlyOnce() { public void onSuccess(String result) { callbackHolder[0] = result; } - @Override public void onFailure(Throwable ex) { fail("Expected onSuccess() to be called"); @@ -124,7 +131,6 @@ public void setExceptionTriggersCallback() { public void onSuccess(String result) { fail("Expected onFailure() to be called"); } - @Override public void onFailure(Throwable ex) { callbackHolder[0] = ex; @@ -143,7 +149,6 @@ public void setExceptionTriggersCallbackOnlyOnce() { public void onSuccess(String result) { fail("Expected onFailure() to be called"); } - @Override public void onFailure(Throwable ex) { callbackHolder[0] = ex; @@ -169,7 +174,8 @@ public void run() { try { Thread.sleep(20L); settableListenableFuture.set(string); - } catch (InterruptedException ex) { + } + catch (InterruptedException ex) { throw new RuntimeException(ex); } } @@ -183,7 +189,8 @@ public void getWithTimeoutThrowsTimeoutException() throws ExecutionException, In try { settableListenableFuture.get(1L, TimeUnit.MILLISECONDS); fail("Expected TimeoutException"); - } catch (TimeoutException ex) { + } + catch (TimeoutException ex) { // expected } } @@ -197,7 +204,8 @@ public void run() { try { Thread.sleep(20L); settableListenableFuture.set(string); - } catch (InterruptedException ex) { + } + catch (InterruptedException ex) { throw new RuntimeException(ex); } } @@ -278,7 +286,8 @@ public void run() { try { Thread.sleep(20L); settableListenableFuture.cancel(true); - } catch (InterruptedException ex) { + } + catch (InterruptedException ex) { throw new RuntimeException(ex); } } @@ -286,7 +295,8 @@ public void run() { try { settableListenableFuture.get(100L, TimeUnit.MILLISECONDS); fail("Expected CancellationException"); - } catch (CancellationException ex) { + } + catch (CancellationException ex) { // expected } } @@ -317,6 +327,7 @@ public void cancelDoesNotNotifyCallbacksOnSetException() { verifyNoMoreInteractions(callback); } + private static class InterruptableSettableListenableFuture extends SettableListenableFuture { private boolean interrupted = false; @@ -330,4 +341,5 @@ boolean calledInterruptTask() { return interrupted; } } + } From 7de2976c1e619e19819a404237b06a44091c9ad6 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 30 May 2016 22:39:27 +0200 Subject: [PATCH 167/344] Consistent meta-annotation attributes lookup through ASM Issue: SPR-14257 (cherry picked from commit 24f5f36) --- .../ConfigurationClassWithConditionTests.java | 64 ++++++++++++++----- .../AnnotationAttributesReadingVisitor.java | 33 ++++++---- .../AnnotationReadingVisitorUtils.java | 7 +- .../RecursiveAnnotationAttributesVisitor.java | 4 +- 4 files changed, 76 insertions(+), 32 deletions(-) diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassWithConditionTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassWithConditionTests.java index 312baf1cde..8d55a9f03f 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassWithConditionTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassWithConditionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,12 +22,10 @@ import java.lang.annotation.Target; import java.util.Map; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.core.type.AnnotationMetadata; @@ -45,9 +43,6 @@ @SuppressWarnings("resource") public class ConfigurationClassWithConditionTests { - @Rule - public ExpectedException thrown = ExpectedException.none(); - @Test public void conditionalOnMissingBeanMatch() throws Exception { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); @@ -94,13 +89,28 @@ public void metaConditional() throws Exception { assertTrue(ctx.containsBean("bean")); } + @Test + public void metaConditionalWithAsm() throws Exception { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.registerBeanDefinition("config", new RootBeanDefinition(ConfigurationWithMetaCondition.class.getName())); + ctx.refresh(); + assertTrue(ctx.containsBean("bean")); + } + @Test public void nonConfigurationClass() throws Exception { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(NonConfigurationClass.class); ctx.refresh(); - thrown.expect(NoSuchBeanDefinitionException.class); - assertNull(ctx.getBean(NonConfigurationClass.class)); + assertFalse(ctx.containsBean("bean1")); + } + + @Test + public void nonConfigurationClassWithAsm() throws Exception { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.registerBeanDefinition("config", new RootBeanDefinition(NonConfigurationClass.class.getName())); + ctx.refresh(); + assertFalse(ctx.containsBean("bean1")); } @Test @@ -108,8 +118,15 @@ public void methodConditional() throws Exception { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(ConditionOnMethodConfiguration.class); ctx.refresh(); - thrown.expect(NoSuchBeanDefinitionException.class); - assertNull(ctx.getBean(ExampleBean.class)); + assertFalse(ctx.containsBean("bean1")); + } + + @Test + public void methodConditionalWithAsm() throws Exception { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.registerBeanDefinition("config", new RootBeanDefinition(ConditionOnMethodConfiguration.class.getName())); + ctx.refresh(); + assertFalse(ctx.containsBean("bean1")); } @Test @@ -144,6 +161,7 @@ public void configWithAlternativeBeans() { @Configuration static class BeanOneConfiguration { + @Bean public ExampleBean bean1() { return new ExampleBean(); @@ -153,6 +171,7 @@ public ExampleBean bean1() { @Configuration @Conditional(NoBeanOneCondition.class) static class BeanTwoConfiguration { + @Bean public ExampleBean bean2() { return new ExampleBean(); @@ -162,6 +181,7 @@ public ExampleBean bean2() { @Configuration @Conditional(HasBeanOneCondition.class) static class BeanThreeConfiguration { + @Bean public ExampleBean bean3() { return new ExampleBean(); @@ -171,6 +191,7 @@ public ExampleBean bean3() { @Configuration @MetaConditional("test") static class ConfigurationWithMetaCondition { + @Bean public ExampleBean bean() { return new ExampleBean(); @@ -180,14 +201,22 @@ public ExampleBean bean() { @Conditional(MetaConditionalFilter.class) @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) - public static @interface MetaConditional { + public @interface MetaConditional { + String value(); } @Conditional(NeverCondition.class) @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) - public static @interface Never { + public @interface Never { + } + + @Conditional(AlwaysCondition.class) + @Never + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.TYPE, ElementType.METHOD}) + public @interface MetaNever { } static class NoBeanOneCondition implements Condition { @@ -238,8 +267,13 @@ public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) } @Component - @Never + @MetaNever static class NonConfigurationClass { + + @Bean + public ExampleBean bean1() { + return new ExampleBean(); + } } @Configuration @@ -254,7 +288,7 @@ public ExampleBean bean1() { @Configuration @Never - @Import({ ConfigurationNotCreated.class, RegistrarNotCreated.class, ImportSelectorNotCreated.class }) + @Import({ConfigurationNotCreated.class, RegistrarNotCreated.class, ImportSelectorNotCreated.class}) static class ImportsNotCreated { static { if (true) throw new RuntimeException(); diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationAttributesReadingVisitor.java b/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationAttributesReadingVisitor.java index 6937e811e4..ab001b50ec 100644 --- a/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationAttributesReadingVisitor.java +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationAttributesReadingVisitor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -72,32 +72,43 @@ public void doVisitEnd(Class annotationClass) { else { attributes.add(0, this.attributes); } - Set metaAnnotationTypeNames = new LinkedHashSet(); + Set visited = new LinkedHashSet(); Annotation[] metaAnnotations = AnnotationUtils.getAnnotations(annotationClass); if (!ObjectUtils.isEmpty(metaAnnotations)) { for (Annotation metaAnnotation : metaAnnotations) { if (!AnnotationUtils.isInJavaLangAnnotationPackage(metaAnnotation)) { - recursivelyCollectMetaAnnotations(metaAnnotationTypeNames, metaAnnotation); + recursivelyCollectMetaAnnotations(visited, metaAnnotation); } } } if (this.metaAnnotationMap != null) { + Set metaAnnotationTypeNames = new LinkedHashSet(visited.size()); + for (Annotation ann : visited) { + metaAnnotationTypeNames.add(ann.annotationType().getName()); + } this.metaAnnotationMap.put(annotationClass.getName(), metaAnnotationTypeNames); } } - private void recursivelyCollectMetaAnnotations(Set visited, Annotation annotation) { - String annotationName = annotation.annotationType().getName(); - if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation) && visited.add(annotationName)) { - // Only do further scanning for public annotations; we'd run into - // IllegalAccessExceptions otherwise, and we don't want to mess with - // accessibility in a SecurityManager environment. - if (Modifier.isPublic(annotation.annotationType().getModifiers())) { - this.attributesMap.add(annotationName, AnnotationUtils.getAnnotationAttributes(annotation, false, true)); + private void recursivelyCollectMetaAnnotations(Set visited, Annotation annotation) { + if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation) && visited.add(annotation)) { + try { + // Only do attribute scanning for public annotations; we'd run into + // IllegalAccessExceptions otherwise, and we don't want to mess with + // accessibility in a SecurityManager environment. + if (Modifier.isPublic(annotation.annotationType().getModifiers())) { + String annotationName = annotation.annotationType().getName(); + this.attributesMap.add(annotationName, AnnotationUtils.getAnnotationAttributes(annotation, false, true)); + } for (Annotation metaMetaAnnotation : annotation.annotationType().getAnnotations()) { recursivelyCollectMetaAnnotations(visited, metaMetaAnnotation); } } + catch (Exception ex) { + if (logger.isDebugEnabled()) { + logger.debug("Failed to introspect meta-annotations on [" + annotation + "]: " + ex); + } + } } } diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationReadingVisitorUtils.java b/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationReadingVisitorUtils.java index d63c72ffa3..0d7ba35f25 100644 --- a/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationReadingVisitorUtils.java +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationReadingVisitorUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -150,9 +150,8 @@ public static AnnotationAttributes getMergedAnnotationAttributes( for (String overridableAttributeName : overridableAttributeNames) { Object value = currentAttributes.get(overridableAttributeName); if (value != null) { - // Store the value, potentially overriding a value from an - // attribute of the same name found higher in the annotation - // hierarchy. + // Store the value, potentially overriding a value from an attribute + // of the same name found higher in the annotation hierarchy. results.put(overridableAttributeName, value); } } diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/RecursiveAnnotationAttributesVisitor.java b/spring-core/src/main/java/org/springframework/core/type/classreading/RecursiveAnnotationAttributesVisitor.java index 2f1be81db2..0d2176f015 100644 --- a/spring-core/src/main/java/org/springframework/core/type/classreading/RecursiveAnnotationAttributesVisitor.java +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/RecursiveAnnotationAttributesVisitor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -58,7 +58,7 @@ protected void doVisitEnd(Class annotationClass) { } private void registerDefaultValues(Class annotationClass) { - // Only do further scanning for public annotations; we'd run into + // Only do defaults scanning for public annotations; we'd run into // IllegalAccessExceptions otherwise, and we don't want to mess with // accessibility in a SecurityManager environment. if (Modifier.isPublic(annotationClass.getModifiers())) { From 62199e82efb99de16a9b547b24262da0f0025f90 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 27 May 2016 22:34:09 +0200 Subject: [PATCH 168/344] ConfigurationClassParser detects @Bean methods in interface hierarchies as well Issue: SPR-14288 (cherry picked from commit 03affa0) --- .../annotation/ConfigurationClassParser.java | 28 +++++++++++-------- .../ConfigurationClassPostProcessorTests.java | 20 +++++++++---- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java index c0541652b2..023371b4d3 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java @@ -295,15 +295,7 @@ protected final SourceClass doProcessConfigurationClass(ConfigurationClass confi } // Process default methods on interfaces - for (SourceClass ifc : sourceClass.getInterfaces()) { - beanMethods = ifc.getMetadata().getAnnotatedMethods(Bean.class.getName()); - for (MethodMetadata methodMetadata : beanMethods) { - if (!methodMetadata.isAbstract()) { - // A default method or other concrete method on a Java 8+ interface... - configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); - } - } - } + processInterfaces(configClass, sourceClass); // Process superclass, if any if (sourceClass.getMetadata().hasSuperClass()) { @@ -321,8 +313,6 @@ protected final SourceClass doProcessConfigurationClass(ConfigurationClass confi /** * Register member (nested) classes that happen to be configuration classes themselves. - * @param sourceClass the source class to process - * @throws IOException if there is any problem reading metadata from a member class */ private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { for (SourceClass memberClass : sourceClass.getMemberClasses()) { @@ -344,6 +334,22 @@ private void processMemberClasses(ConfigurationClass configClass, SourceClass so } } + /** + * Register default methods on interfaces implemented by the configuration class. + */ + private void processInterfaces(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { + for (SourceClass ifc : sourceClass.getInterfaces()) { + Set beanMethods = ifc.getMetadata().getAnnotatedMethods(Bean.class.getName()); + for (MethodMetadata methodMetadata : beanMethods) { + if (!methodMetadata.isAbstract()) { + // A default method or other concrete method on a Java 8+ interface... + configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); + } + } + processInterfaces(configClass, ifc); + } + } + /** * Process the given @PropertySource annotation metadata. * @param propertySource metadata for the @PropertySource annotation found 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 a1feb216c7..5bebb953b6 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 @@ -1063,17 +1063,27 @@ public void validate() { } } - public interface DefaultMethodsConfig { + public interface BaseInterface { - @Bean - default ServiceBean serviceBean() { - return provider().getServiceBean(); - } + ServiceBean serviceBean(); + } + + public interface BaseDefaultMethods extends BaseInterface { @Bean default ServiceBeanProvider provider() { return new ServiceBeanProvider(); } + + @Bean + @Override + default ServiceBean serviceBean() { + return provider().getServiceBean(); + } + } + + public interface DefaultMethodsConfig extends BaseDefaultMethods { + } @Configuration From 1d0c305a25d8140898ebb145bf0fa5ddbb88d749 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 30 May 2016 15:15:27 +0200 Subject: [PATCH 169/344] BeanWrapper avoids StringIndexOutOfBoundsException for incompletely quoted keys Issue: SPR-14293 (cherry picked from commit cf0a0cd) --- .../beans/AbstractNestablePropertyAccessor.java | 5 +++-- .../springframework/beans/BeanWrapperTests.java | 16 +++++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java b/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java index b987fb56c8..cde668d48c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java @@ -800,7 +800,7 @@ protected String getFinalPath(AbstractNestablePropertyAccessor pa, String nested /** * Recursively navigate to return a property accessor for the nested property path. - * @param propertyPath property property path, which may be nested + * @param propertyPath property path, which may be nested * @return a property accessor for the target bean */ @SuppressWarnings("unchecked") // avoid nested generic @@ -942,7 +942,8 @@ private PropertyTokenHolder getPropertyNameTokens(String propertyName) { actualName = propertyName.substring(0, keyStart); } String key = propertyName.substring(keyStart + PROPERTY_KEY_PREFIX.length(), keyEnd); - if ((key.startsWith("'") && key.endsWith("'")) || (key.startsWith("\"") && key.endsWith("\""))) { + if (key.length() > 1 && (key.startsWith("'") && key.endsWith("'")) || + (key.startsWith("\"") && key.endsWith("\""))) { key = key.substring(1, key.length() - 1); } keys.add(key); diff --git a/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java b/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java index 7a3232b1b1..a99bc92d89 100644 --- a/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -194,6 +194,19 @@ public void getPropertyWithOptionalAndAutoGrow() { assertEquals("x", accessor.getPropertyValue("object.name")); } + @Test + public void incompletelyQuotedKeyLeadsToPropertyException() { + TestBean target = new TestBean(); + try { + BeanWrapper accessor = createAccessor(target); + accessor.setPropertyValue("[']", "foobar"); + fail("Should throw exception on invalid property"); + } + catch (NotWritablePropertyException ex) { + assertNull(ex.getPossibleMatches()); + } + } + @SuppressWarnings("unused") private static class GetterBean { @@ -212,6 +225,7 @@ public String getName() { } } + @SuppressWarnings("unused") private static class IntelliBean { From 933bbf2de90c720e6ada08089c3401317230d3b0 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 31 May 2016 11:05:29 +0200 Subject: [PATCH 170/344] AbstractBeanFactory.markBeanAsCreated() reliably clears merged bean definition only once Issue: SPR-14269 (cherry picked from commit 9064d38) --- .../factory/support/AbstractBeanFactory.java | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) 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 e33f085ec7..80e91528cf 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -1498,11 +1498,14 @@ else if (mbd.isLazyInit()) { */ protected void markBeanAsCreated(String beanName) { if (!this.alreadyCreated.contains(beanName)) { - this.alreadyCreated.add(beanName); - - // Let the bean definition get re-merged now that we're actually creating - // the bean... just in case some of its metadata changed in the meantime. - clearMergedBeanDefinition(beanName); + synchronized (this.mergedBeanDefinitions) { + if (!this.alreadyCreated.contains(beanName)) { + // Let the bean definition get re-merged now that we're actually creating + // the bean... just in case some of its metadata changed in the meantime. + clearMergedBeanDefinition(beanName); + this.alreadyCreated.add(beanName); + } + } } } @@ -1511,7 +1514,9 @@ protected void markBeanAsCreated(String beanName) { * @param beanName the name of the bean */ protected void cleanupAfterBeanCreationFailure(String beanName) { - this.alreadyCreated.remove(beanName); + synchronized (this.mergedBeanDefinitions) { + this.alreadyCreated.remove(beanName); + } } /** From a27200e753ed7d6eff66d43065fcc8c4b498ddb3 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 31 May 2016 13:27:34 +0200 Subject: [PATCH 171/344] Polishing --- .../springframework/core/annotation/AnnotationUtils.java | 4 ++-- .../test/web/client/match/MockRestRequestMatchers.java | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java index c47c801863..09eb167eac 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -1721,7 +1721,7 @@ static boolean isAnnotationTypeMethod(Method method) { } /** - *

    If the supplied throwable is an {@link AnnotationConfigurationException}, + * If the supplied throwable is an {@link AnnotationConfigurationException}, * it will be cast to an {@code AnnotationConfigurationException} and thrown, * allowing it to propagate to the caller. *

    Otherwise, this method does nothing. diff --git a/spring-test/src/main/java/org/springframework/test/web/client/match/MockRestRequestMatchers.java b/spring-test/src/main/java/org/springframework/test/web/client/match/MockRestRequestMatchers.java index 9637bfc6f1..318e9d8800 100644 --- a/spring-test/src/main/java/org/springframework/test/web/client/match/MockRestRequestMatchers.java +++ b/spring-test/src/main/java/org/springframework/test/web/client/match/MockRestRequestMatchers.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -155,8 +155,8 @@ public void match(ClientHttpRequest request) { private static void assertHeaderValueCount(final String name, HttpHeaders headers, int expectedCount) { List actualValues = headers.get(name); AssertionErrors.assertTrue("Expected header <" + name + ">", actualValues != null); - AssertionErrors.assertTrue("Expected header <" + name + "> to have at least <" + expectedCount - + "> values but found " + actualValues, expectedCount <= actualValues.size()); + AssertionErrors.assertTrue("Expected header <" + name + "> to have at least <" + expectedCount + + "> values but found " + actualValues, expectedCount <= actualValues.size()); } /** From 899131997ea620fc2add7a3f9cb9f56f2185c86e Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 24 Mar 2016 15:33:44 +0100 Subject: [PATCH 172/344] Less restrictive CORS origin comparison UriComponentsBuilder allows more efficient (and less restrictive) CORS origin comparison. Also introduces a specifically narrowed cloneBuilder() method for UriComponentsBuilders. Cherry-picked from 9a52c8. Issue: SPR-14080 --- .../web/util/UriComponentsBuilder.java | 178 ++++++++++-------- .../springframework/web/util/WebUtils.java | 24 ++- .../annotation/MvcUriComponentsBuilder.java | 4 +- .../support/ServletUriComponentsBuilder.java | 4 +- 4 files changed, 122 insertions(+), 88 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java b/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java index 31c0162e10..7dea422f5a 100644 --- a/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java +++ b/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +24,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpRequest; import org.springframework.util.Assert; import org.springframework.util.LinkedMultiValueMap; @@ -130,7 +131,7 @@ protected UriComponentsBuilder(UriComponentsBuilder other) { this.userInfo = other.userInfo; this.host = other.host; this.port = other.port; - this.pathBuilder = (CompositePathComponentBuilder) other.pathBuilder.clone(); + this.pathBuilder = other.pathBuilder.cloneBuilder(); this.queryParams.putAll(other.queryParams); this.fragment = other.fragment; } @@ -271,72 +272,17 @@ public static UriComponentsBuilder fromHttpUrl(String httpUrl) { /** * Create a new {@code UriComponents} object from the URI associated with * the given HttpRequest while also overlaying with values from the headers - * "Forwarded" (RFC 7239, or - * "X-Forwarded-Host", "X-Forwarded-Port", and "X-Forwarded-Proto" if "Forwarded" is - * not found. + * "Forwarded" (RFC 7239, + * or "X-Forwarded-Host", "X-Forwarded-Port", and "X-Forwarded-Proto" if + * "Forwarded" is not found. * @param request the source request * @return the URI components of the URI * @since 4.1.5 */ public static UriComponentsBuilder fromHttpRequest(HttpRequest request) { - URI uri = request.getURI(); - UriComponentsBuilder builder = UriComponentsBuilder.fromUri(uri); - - String scheme = uri.getScheme(); - String host = uri.getHost(); - int port = uri.getPort(); - - String forwardedHeader = request.getHeaders().getFirst("Forwarded"); - if (StringUtils.hasText(forwardedHeader)) { - String forwardedToUse = StringUtils.commaDelimitedListToStringArray(forwardedHeader)[0]; - Matcher m = FORWARDED_HOST_PATTERN.matcher(forwardedToUse); - if (m.find()) { - host = m.group(1).trim(); - } - m = FORWARDED_PROTO_PATTERN.matcher(forwardedToUse); - if (m.find()) { - scheme = m.group(1).trim(); - } - } - else { - String hostHeader = request.getHeaders().getFirst("X-Forwarded-Host"); - if (StringUtils.hasText(hostHeader)) { - String[] hosts = StringUtils.commaDelimitedListToStringArray(hostHeader); - String hostToUse = hosts[0]; - if (hostToUse.contains(":")) { - String[] hostAndPort = StringUtils.split(hostToUse, ":"); - host = hostAndPort[0]; - port = Integer.parseInt(hostAndPort[1]); - } - else { - host = hostToUse; - port = -1; - } - } - - String portHeader = request.getHeaders().getFirst("X-Forwarded-Port"); - if (StringUtils.hasText(portHeader)) { - String[] ports = StringUtils.commaDelimitedListToStringArray(portHeader); - port = Integer.parseInt(ports[0]); - } - - String protocolHeader = request.getHeaders().getFirst("X-Forwarded-Proto"); - if (StringUtils.hasText(protocolHeader)) { - String[] protocols = StringUtils.commaDelimitedListToStringArray(protocolHeader); - scheme = protocols[0]; - } - } - - builder.scheme(scheme); - builder.host(host); - builder.port(null); - if (scheme.equals("http") && port != 80 || scheme.equals("https") && port != 443) { - builder.port(port); - } - return builder; + return fromUri(request.getURI()).adaptFromForwardedHeaders(request.getHeaders()); } - /** * Create an instance by parsing the "Origin" header of an HTTP request. * @see RFC 6454 @@ -463,18 +409,6 @@ public UriComponentsBuilder uri(URI uri) { return this; } - private void resetHierarchicalComponents() { - this.userInfo = null; - this.host = null; - this.port = null; - this.pathBuilder = new CompositePathComponentBuilder(); - this.queryParams.clear(); - } - - private void resetSchemeSpecificPart() { - this.ssp = null; - } - /** * Set the URI scheme. The given scheme may contain URI template variables, * and may also be {@code null} to clear the scheme of this builder. @@ -724,17 +658,103 @@ public UriComponentsBuilder fragment(String fragment) { return this; } + /** + * Adapt this builder's scheme+host+port from the given headers, specifically + * "Forwarded" (RFC 7239, + * or "X-Forwarded-Host", "X-Forwarded-Port", and "X-Forwarded-Proto" if + * "Forwarded" is not found. + * @param headers the HTTP headers to consider + * @return this UriComponentsBuilder + * @since 4.3 + */ + UriComponentsBuilder adaptFromForwardedHeaders(HttpHeaders headers) { + String forwardedHeader = headers.getFirst("Forwarded"); + if (StringUtils.hasText(forwardedHeader)) { + String forwardedToUse = StringUtils.commaDelimitedListToStringArray(forwardedHeader)[0]; + Matcher matcher = FORWARDED_HOST_PATTERN.matcher(forwardedToUse); + if (matcher.find()) { + host(matcher.group(1).trim()); + } + matcher = FORWARDED_PROTO_PATTERN.matcher(forwardedToUse); + if (matcher.find()) { + scheme(matcher.group(1).trim()); + } + } + else { + String hostHeader = headers.getFirst("X-Forwarded-Host"); + if (StringUtils.hasText(hostHeader)) { + String[] hosts = StringUtils.commaDelimitedListToStringArray(hostHeader); + String hostToUse = hosts[0]; + if (hostToUse.contains(":")) { + String[] hostAndPort = StringUtils.split(hostToUse, ":"); + host(hostAndPort[0]); + port(Integer.parseInt(hostAndPort[1])); + } + else { + host(hostToUse); + port(null); + } + } + + String portHeader = headers.getFirst("X-Forwarded-Port"); + if (StringUtils.hasText(portHeader)) { + String[] ports = StringUtils.commaDelimitedListToStringArray(portHeader); + port(Integer.parseInt(ports[0])); + } + + String protocolHeader = headers.getFirst("X-Forwarded-Proto"); + if (StringUtils.hasText(protocolHeader)) { + String[] protocols = StringUtils.commaDelimitedListToStringArray(protocolHeader); + scheme(protocols[0]); + } + } + + if ((this.scheme.equals("http") && "80".equals(this.port)) || + (this.scheme.equals("https") && "443".equals(this.port))) { + this.port = null; + } + + return this; + } + + private void resetHierarchicalComponents() { + this.userInfo = null; + this.host = null; + this.port = null; + this.pathBuilder = new CompositePathComponentBuilder(); + this.queryParams.clear(); + } + + private void resetSchemeSpecificPart() { + this.ssp = null; + } + + + /** + * Public declaration of Object's {@code clone()} method. + * Delegates to {@link #cloneBuilder()}. + * @see Object#clone() + */ @Override public Object clone() { + return cloneBuilder(); + } + + /** + * Clone this {@code UriComponentsBuilder}. + * @return the cloned {@code UriComponentsBuilder} object + * @since 4.3 + */ + public UriComponentsBuilder cloneBuilder() { return new UriComponentsBuilder(this); } - private interface PathComponentBuilder extends Cloneable { + private interface PathComponentBuilder { PathComponent build(); - Object clone(); + PathComponentBuilder cloneBuilder(); } @@ -810,10 +830,10 @@ public PathComponent build() { } @Override - public Object clone() { + public CompositePathComponentBuilder cloneBuilder() { CompositePathComponentBuilder compositeBuilder = new CompositePathComponentBuilder(); for (PathComponentBuilder builder : this.builders) { - compositeBuilder.builders.add((PathComponentBuilder) builder.clone()); + compositeBuilder.builders.add(builder.cloneBuilder()); } return compositeBuilder; } @@ -852,7 +872,7 @@ public void removeTrailingSlash() { } @Override - public Object clone() { + public FullPathComponentBuilder cloneBuilder() { FullPathComponentBuilder builder = new FullPathComponentBuilder(); builder.append(this.path.toString()); return builder; @@ -879,7 +899,7 @@ public PathComponent build() { } @Override - public Object clone() { + public PathSegmentComponentBuilder cloneBuilder() { PathSegmentComponentBuilder builder = new PathSegmentComponentBuilder(); builder.pathSegments.addAll(this.pathSegments); return builder; diff --git a/spring-web/src/main/java/org/springframework/web/util/WebUtils.java b/spring-web/src/main/java/org/springframework/web/util/WebUtils.java index 76c5dd042e..6d8062fbdc 100644 --- a/spring-web/src/main/java/org/springframework/web/util/WebUtils.java +++ b/spring-web/src/main/java/org/springframework/web/util/WebUtils.java @@ -34,6 +34,7 @@ import javax.servlet.http.HttpSession; import org.springframework.http.HttpRequest; +import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.LinkedMultiValueMap; @@ -811,18 +812,31 @@ public static boolean isSameOrigin(HttpRequest request) { if (origin == null) { return true; } - UriComponents actualUrl = UriComponentsBuilder.fromHttpRequest(request).build(); + UriComponentsBuilder urlBuilder; + if (request instanceof ServletServerHttpRequest) { + // Build more efficiently if we can: we only need scheme, host, port for origin comparison + HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest(); + urlBuilder = new UriComponentsBuilder(). + scheme(servletRequest.getScheme()). + host(servletRequest.getServerName()). + port(servletRequest.getServerPort()). + adaptFromForwardedHeaders(request.getHeaders()); + } + else { + urlBuilder = UriComponentsBuilder.fromHttpRequest(request); + } + UriComponents actualUrl = urlBuilder.build(); UriComponents originUrl = UriComponentsBuilder.fromOriginHeader(origin).build(); return (actualUrl.getHost().equals(originUrl.getHost()) && getPort(actualUrl) == getPort(originUrl)); } - private static int getPort(UriComponents component) { - int port = component.getPort(); + private static int getPort(UriComponents uri) { + int port = uri.getPort(); if (port == -1) { - if ("http".equals(component.getScheme()) || "ws".equals(component.getScheme())) { + if ("http".equals(uri.getScheme()) || "ws".equals(uri.getScheme())) { port = 80; } - else if ("https".equals(component.getScheme()) || "wss".equals(component.getScheme())) { + else if ("https".equals(uri.getScheme()) || "wss".equals(uri.getScheme())) { port = 443; } } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java index e8ff7ee93a..25018ff9e6 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -406,7 +406,7 @@ private static UriComponentsBuilder fromMethodInternal(UriComponentsBuilder base private static UriComponentsBuilder getBaseUrlToUse(UriComponentsBuilder baseUrl) { if (baseUrl != null) { - return (UriComponentsBuilder) baseUrl.clone(); + return baseUrl.cloneBuilder(); } else { return ServletUriComponentsBuilder.fromCurrentServletMapping(); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/ServletUriComponentsBuilder.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/ServletUriComponentsBuilder.java index 74c804f40f..07abc64b45 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/support/ServletUriComponentsBuilder.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/support/ServletUriComponentsBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -223,7 +223,7 @@ public String removePathExtension() { } @Override - public Object clone() { + public ServletUriComponentsBuilder cloneBuilder() { return new ServletUriComponentsBuilder(this); } From f36070b5607389c630b67fada12efc3073f59f8b Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 2 Jun 2016 17:02:44 +0200 Subject: [PATCH 173/344] Polishing (cherry picked from commit 521c41d) --- .../init/CompositeDatabasePopulator.java | 12 +++--- ...ractMappingContentNegotiationStrategy.java | 7 ++-- .../web/accept/ContentNegotiationManager.java | 14 +++---- .../ContentNegotiationManagerFactoryBean.java | 38 +++++++++---------- .../accept/ContentNegotiationStrategy.java | 6 +-- .../FixedContentNegotiationStrategy.java | 5 +-- .../HeaderContentNegotiationStrategy.java | 6 +-- ...MappingMediaTypeFileExtensionResolver.java | 4 +- .../MediaTypeFileExtensionResolver.java | 7 ++-- .../ParameterContentNegotiationStrategy.java | 22 +++++------ ...thExtensionContentNegotiationStrategy.java | 5 +-- ...thExtensionContentNegotiationStrategy.java | 14 +++---- .../support/WebApplicationContextUtils.java | 4 +- .../filter/AbstractRequestLoggingFilter.java | 7 +++- .../PortletApplicationContextUtils.java | 4 +- 15 files changed, 71 insertions(+), 84 deletions(-) diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/CompositeDatabasePopulator.java b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/CompositeDatabasePopulator.java index 1b74ac0001..b8924624fb 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/CompositeDatabasePopulator.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/CompositeDatabasePopulator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,7 @@ import java.util.List; /** - * {@link DatabasePopulator} implementation that delegates to a list of other + * Composite {@link DatabasePopulator} that delegates to a list of given * {@code DatabasePopulator} implementations, executing all scripts. * * @author Dave Syer @@ -33,11 +33,11 @@ */ public class CompositeDatabasePopulator implements DatabasePopulator { - private List populators = new ArrayList(); + private final List populators = new ArrayList(4); /** - * Specify a list of populators to delegate to. + * Specify one or more populators to delegate to. */ public void setPopulators(DatabasePopulator... populators) { this.populators.clear(); @@ -51,9 +51,7 @@ public void addPopulators(DatabasePopulator... populators) { this.populators.addAll(Arrays.asList(populators)); } - /** - * {@inheritDoc} - */ + @Override public void populate(Connection connection) throws SQLException, ScriptException { for (DatabasePopulator populator : this.populators) { diff --git a/spring-web/src/main/java/org/springframework/web/accept/AbstractMappingContentNegotiationStrategy.java b/spring-web/src/main/java/org/springframework/web/accept/AbstractMappingContentNegotiationStrategy.java index 0568b90465..ee83d31f77 100644 --- a/spring-web/src/main/java/org/springframework/web/accept/AbstractMappingContentNegotiationStrategy.java +++ b/spring-web/src/main/java/org/springframework/web/accept/AbstractMappingContentNegotiationStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,11 +43,9 @@ * @author Rossen Stoyanchev * @since 3.2 */ -public abstract class AbstractMappingContentNegotiationStrategy - extends MappingMediaTypeFileExtensionResolver +public abstract class AbstractMappingContentNegotiationStrategy extends MappingMediaTypeFileExtensionResolver implements ContentNegotiationStrategy { - /** * Create an instance with the given map of file extensions and media types. */ @@ -86,6 +84,7 @@ public List resolveMediaTypeKey(NativeWebRequest webRequest, String k return Collections.emptyList(); } + /** * Extract a key from the request to use to look up media types. * @return the lookup key or {@code null}. diff --git a/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManager.java b/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManager.java index 4450ad1064..3f6efab393 100644 --- a/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManager.java +++ b/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,18 +41,14 @@ * @author Rossen Stoyanchev * @since 3.2 */ -public class ContentNegotiationManager implements ContentNegotiationStrategy, - MediaTypeFileExtensionResolver { +public class ContentNegotiationManager implements ContentNegotiationStrategy, MediaTypeFileExtensionResolver { - private static final List MEDIA_TYPE_ALL = - Collections.singletonList(MediaType.ALL); + private static final List MEDIA_TYPE_ALL = Collections.singletonList(MediaType.ALL); - private final List strategies = - new ArrayList(); + private final List strategies = new ArrayList(); - private final Set resolvers = - new LinkedHashSet(); + private final Set resolvers = new LinkedHashSet(); /** diff --git a/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBean.java b/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBean.java index b035b079ac..dfcd84990e 100644 --- a/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBean.java +++ b/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,34 +40,34 @@ * * * - * - * - * + * + * + * * * - * - * - * + * + * + * * * - * - * - * + * + * + * * * - * - * - * + * + * + * * * - * - * - * + * + * + * * * - * - * - * + * + * + * * *
    Property SetterUnderlying StrategyDefault SettingProperty SetterUnderlying StrategyDefault Setting
    {@link #setFavorPathExtension}{@link PathExtensionContentNegotiationStrategy Path Extension strategy}On{@link #setFavorPathExtension}{@link PathExtensionContentNegotiationStrategy Path Extension strategy}On
    {@link #setFavorParameter favorParameter}{@link ParameterContentNegotiationStrategy Parameter strategy}Off{@link #setFavorParameter favorParameter}{@link ParameterContentNegotiationStrategy Parameter strategy}Off
    {@link #setIgnoreAcceptHeader ignoreAcceptHeader}{@link HeaderContentNegotiationStrategy Header strategy}On{@link #setIgnoreAcceptHeader ignoreAcceptHeader}{@link HeaderContentNegotiationStrategy Header strategy}On
    {@link #setDefaultContentType defaultContentType}{@link FixedContentNegotiationStrategy Fixed content strategy}Not set{@link #setDefaultContentType defaultContentType}{@link FixedContentNegotiationStrategy Fixed content strategy}Not set
    {@link #setDefaultContentTypeStrategy defaultContentTypeStrategy}{@link ContentNegotiationStrategy}Not set{@link #setDefaultContentTypeStrategy defaultContentTypeStrategy}{@link ContentNegotiationStrategy}Not set
    * diff --git a/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationStrategy.java b/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationStrategy.java index 127f90cef4..586485a72a 100644 --- a/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationStrategy.java +++ b/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,10 +33,8 @@ public interface ContentNegotiationStrategy { /** * Resolve the given request to a list of media types. The returned list is * ordered by specificity first and by quality parameter second. - * * @param webRequest the current request - * @return the requested media types or an empty list, never {@code null} - * + * @return the requested media types or an empty list (never {@code null}) * @throws HttpMediaTypeNotAcceptableException if the requested media * types cannot be parsed */ diff --git a/spring-web/src/main/java/org/springframework/web/accept/FixedContentNegotiationStrategy.java b/spring-web/src/main/java/org/springframework/web/accept/FixedContentNegotiationStrategy.java index 547ccef791..50478683b1 100644 --- a/spring-web/src/main/java/org/springframework/web/accept/FixedContentNegotiationStrategy.java +++ b/spring-web/src/main/java/org/springframework/web/accept/FixedContentNegotiationStrategy.java @@ -33,8 +33,7 @@ */ public class FixedContentNegotiationStrategy implements ContentNegotiationStrategy { - private static final Log logger = LogFactory.getLog( - FixedContentNegotiationStrategy.class); + private static final Log logger = LogFactory.getLog(FixedContentNegotiationStrategy.class); private final List contentType; @@ -50,7 +49,7 @@ public FixedContentNegotiationStrategy(MediaType contentType) { @Override public List resolveMediaTypes(NativeWebRequest request) { if (logger.isDebugEnabled()) { - logger.debug("Requested media types is " + this.contentType + "."); + logger.debug("Requested media types: " + this.contentType); } return this.contentType; } diff --git a/spring-web/src/main/java/org/springframework/web/accept/HeaderContentNegotiationStrategy.java b/spring-web/src/main/java/org/springframework/web/accept/HeaderContentNegotiationStrategy.java index bcefd7279e..2642853d9d 100644 --- a/spring-web/src/main/java/org/springframework/web/accept/HeaderContentNegotiationStrategy.java +++ b/spring-web/src/main/java/org/springframework/web/accept/HeaderContentNegotiationStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,11 +34,9 @@ */ public class HeaderContentNegotiationStrategy implements ContentNegotiationStrategy { - /** * {@inheritDoc} - * @throws HttpMediaTypeNotAcceptableException if the 'Accept' header - * cannot be parsed. + * @throws HttpMediaTypeNotAcceptableException if the 'Accept' header cannot be parsed */ @Override public List resolveMediaTypes(NativeWebRequest request) diff --git a/spring-web/src/main/java/org/springframework/web/accept/MappingMediaTypeFileExtensionResolver.java b/spring-web/src/main/java/org/springframework/web/accept/MappingMediaTypeFileExtensionResolver.java index 6c2c8c7d6b..49c38d6dd3 100644 --- a/spring-web/src/main/java/org/springframework/web/accept/MappingMediaTypeFileExtensionResolver.java +++ b/spring-web/src/main/java/org/springframework/web/accept/MappingMediaTypeFileExtensionResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,7 +35,7 @@ * lookups between file extensions and MediaTypes in both directions. * *

    Initially created with a map of file extensions and media types. - * Subsequently sub-classes can use {@link #addMapping} to add more mappings. + * Subsequently subclasses can use {@link #addMapping} to add more mappings. * * @author Rossen Stoyanchev * @since 3.2 diff --git a/spring-web/src/main/java/org/springframework/web/accept/MediaTypeFileExtensionResolver.java b/spring-web/src/main/java/org/springframework/web/accept/MediaTypeFileExtensionResolver.java index c063e5c50b..49a7fcd986 100644 --- a/spring-web/src/main/java/org/springframework/web/accept/MediaTypeFileExtensionResolver.java +++ b/spring-web/src/main/java/org/springframework/web/accept/MediaTypeFileExtensionResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,15 +31,14 @@ public interface MediaTypeFileExtensionResolver { /** * Resolve the given media type to a list of path extensions. - * * @param mediaType the media type to resolve - * @return a list of extensions or an empty list, never {@code null} + * @return a list of extensions or an empty list (never {@code null}) */ List resolveFileExtensions(MediaType mediaType); /** * Return all registered file extensions. - * @return a list of extensions or an empty list, never {@code null} + * @return a list of extensions or an empty list (never {@code null}) */ List getAllFileExtensions(); diff --git a/spring-web/src/main/java/org/springframework/web/accept/ParameterContentNegotiationStrategy.java b/spring-web/src/main/java/org/springframework/web/accept/ParameterContentNegotiationStrategy.java index a2520d27fd..f6a9cb4eaa 100644 --- a/spring-web/src/main/java/org/springframework/web/accept/ParameterContentNegotiationStrategy.java +++ b/spring-web/src/main/java/org/springframework/web/accept/ParameterContentNegotiationStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,18 +27,15 @@ import org.springframework.web.context.request.NativeWebRequest; /** - * A {@code ContentNegotiationStrategy} that resolves a query parameter to a - * key to be used to look up a media type. The default parameter name is - * {@code format}. - *s + * A {@code ContentNegotiationStrategy} that resolves a query parameter to a key + * to be used to look up a media type. The default parameter name is {@code format}. + * * @author Rossen Stoyanchev * @since 3.2 */ -public class ParameterContentNegotiationStrategy - extends AbstractMappingContentNegotiationStrategy { +public class ParameterContentNegotiationStrategy extends AbstractMappingContentNegotiationStrategy { - private static final Log logger = LogFactory.getLog( - ParameterContentNegotiationStrategy.class); + private static final Log logger = LogFactory.getLog(ParameterContentNegotiationStrategy.class); private String parameterName = "format"; @@ -56,7 +53,7 @@ public ParameterContentNegotiationStrategy(Map mediaTypes) { *

    By default this is set to {@code "format"}. */ public void setParameterName(String parameterName) { - Assert.notNull(parameterName, "parameterName is required"); + Assert.notNull(parameterName, "'parameterName' is required"); this.parameterName = parameterName; } @@ -64,6 +61,7 @@ public String getParameterName() { return this.parameterName; } + @Override protected String getMediaTypeKey(NativeWebRequest request) { return request.getParameter(getParameterName()); @@ -72,8 +70,8 @@ protected String getMediaTypeKey(NativeWebRequest request) { @Override protected void handleMatch(String mediaTypeKey, MediaType mediaType) { if (logger.isDebugEnabled()) { - logger.debug("Requested media type is '" + mediaType + - "' based on '" + getParameterName() + "'='" + mediaTypeKey + "'."); + logger.debug("Requested media type: '" + mediaType + "' based on '" + + getParameterName() + "'='" + mediaTypeKey + "'"); } } diff --git a/spring-web/src/main/java/org/springframework/web/accept/PathExtensionContentNegotiationStrategy.java b/spring-web/src/main/java/org/springframework/web/accept/PathExtensionContentNegotiationStrategy.java index 9b19c27154..9f0c26727a 100644 --- a/spring-web/src/main/java/org/springframework/web/accept/PathExtensionContentNegotiationStrategy.java +++ b/spring-web/src/main/java/org/springframework/web/accept/PathExtensionContentNegotiationStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -56,8 +56,7 @@ public class PathExtensionContentNegotiationStrategy private static final Log logger = LogFactory.getLog(PathExtensionContentNegotiationStrategy.class); - private static final boolean JAF_PRESENT = ClassUtils.isPresent( - "javax.activation.FileTypeMap", + private static final boolean JAF_PRESENT = ClassUtils.isPresent("javax.activation.FileTypeMap", PathExtensionContentNegotiationStrategy.class.getClassLoader()); private static final UrlPathHelper PATH_HELPER = new UrlPathHelper(); diff --git a/spring-web/src/main/java/org/springframework/web/accept/ServletPathExtensionContentNegotiationStrategy.java b/spring-web/src/main/java/org/springframework/web/accept/ServletPathExtensionContentNegotiationStrategy.java index 203387105c..615568b46d 100644 --- a/spring-web/src/main/java/org/springframework/web/accept/ServletPathExtensionContentNegotiationStrategy.java +++ b/spring-web/src/main/java/org/springframework/web/accept/ServletPathExtensionContentNegotiationStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.web.accept; import java.util.Map; @@ -31,8 +32,7 @@ * @author Rossen Stoyanchev * @since 3.2 */ -public class ServletPathExtensionContentNegotiationStrategy - extends PathExtensionContentNegotiationStrategy { +public class ServletPathExtensionContentNegotiationStrategy extends PathExtensionContentNegotiationStrategy { private final ServletContext servletContext; @@ -40,12 +40,12 @@ public class ServletPathExtensionContentNegotiationStrategy /** * Create an instance with the given extension-to-MediaType lookup. */ - public ServletPathExtensionContentNegotiationStrategy(ServletContext context, - Map mediaTypes) { + public ServletPathExtensionContentNegotiationStrategy( + ServletContext servletContext, Map mediaTypes) { super(mediaTypes); - Assert.notNull(context, "ServletContext is required!"); - this.servletContext = context; + Assert.notNull(servletContext, "ServletContext is required"); + this.servletContext = servletContext; } /** diff --git a/spring-web/src/main/java/org/springframework/web/context/support/WebApplicationContextUtils.java b/spring-web/src/main/java/org/springframework/web/context/support/WebApplicationContextUtils.java index ae75fbe4e8..26838d3907 100644 --- a/spring-web/src/main/java/org/springframework/web/context/support/WebApplicationContextUtils.java +++ b/spring-web/src/main/java/org/springframework/web/context/support/WebApplicationContextUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -288,7 +288,7 @@ public static void initServletPropertySources(MutablePropertySources propertySou public static void initServletPropertySources( MutablePropertySources propertySources, ServletContext servletContext, ServletConfig servletConfig) { - Assert.notNull(propertySources, "propertySources must not be null"); + Assert.notNull(propertySources, "'propertySources' must not be null"); if (servletContext != null && propertySources.contains(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME) && propertySources.get(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME) instanceof StubPropertySource) { propertySources.replace(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME, diff --git a/spring-web/src/main/java/org/springframework/web/filter/AbstractRequestLoggingFilter.java b/spring-web/src/main/java/org/springframework/web/filter/AbstractRequestLoggingFilter.java index 607b8bc076..4062bf6db3 100644 --- a/spring-web/src/main/java/org/springframework/web/filter/AbstractRequestLoggingFilter.java +++ b/spring-web/src/main/java/org/springframework/web/filter/AbstractRequestLoggingFilter.java @@ -46,7 +46,7 @@ * *

    Prefixes and suffixes for the before and after messages can be configured using the * {@code beforeMessagePrefix}, {@code afterMessagePrefix}, {@code beforeMessageSuffix} and - * {@code afterMessageSuffix} properties, + * {@code afterMessageSuffix} properties. * * @author Rob Harrop * @author Juergen Hoeller @@ -124,7 +124,6 @@ protected boolean isIncludeClientInfo() { *

    Should be configured using an {@code } for parameter name * "includePayload" in the filter definition in {@code web.xml}. */ - public void setIncludePayload(boolean includePayload) { this.includePayload = includePayload; } @@ -254,12 +253,14 @@ protected String createMessage(HttpServletRequest request, String prefix, String StringBuilder msg = new StringBuilder(); msg.append(prefix); msg.append("uri=").append(request.getRequestURI()); + if (isIncludeQueryString()) { String queryString = request.getQueryString(); if (queryString != null) { msg.append('?').append(queryString); } } + if (isIncludeClientInfo()) { String client = request.getRemoteAddr(); if (StringUtils.hasLength(client)) { @@ -274,6 +275,7 @@ protected String createMessage(HttpServletRequest request, String prefix, String msg.append(";user=").append(user); } } + if (isIncludePayload()) { ContentCachingRequestWrapper wrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class); @@ -292,6 +294,7 @@ protected String createMessage(HttpServletRequest request, String prefix, String } } } + msg.append(suffix); return msg.toString(); } diff --git a/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/context/PortletApplicationContextUtils.java b/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/context/PortletApplicationContextUtils.java index 4e27a129bf..ce29022a04 100644 --- a/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/context/PortletApplicationContextUtils.java +++ b/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/context/PortletApplicationContextUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -211,7 +211,7 @@ static void registerEnvironmentBeans(ConfigurableListableBeanFactory bf, Servlet public static void initPortletPropertySources(MutablePropertySources propertySources, ServletContext servletContext, PortletContext portletContext, PortletConfig portletConfig) { - Assert.notNull(propertySources, "propertySources must not be null"); + Assert.notNull(propertySources, "'propertySources' must not be null"); WebApplicationContextUtils.initServletPropertySources(propertySources, servletContext); if (portletContext != null && propertySources.contains(StandardPortletEnvironment.PORTLET_CONTEXT_PROPERTY_SOURCE_NAME)) { From 24b9e3a0be101c0ccc9d4a4063fd24a66529cf16 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 2 Jun 2016 17:14:29 +0200 Subject: [PATCH 174/344] Latest applicable dependency updates (Commons FileUpload 1.3.2, Tomcat 8.0.35, HSQLDB 2.3.4, H2 1.4.192) --- build.gradle | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 3880c2e80c..59100af590 100644 --- a/build.gradle +++ b/build.gradle @@ -33,7 +33,7 @@ configure(allprojects) { project -> ext.ehcachejcacheVersion = "1.0.1" ext.ehcache3Version = "3.0.1" ext.ejbVersion = "3.0" - ext.fileuploadVersion = "1.3.1" + ext.fileuploadVersion = "1.3.2" ext.freemarkerVersion = "2.3.23" ext.groovyVersion = "2.4.6" ext.gsonVersion = "2.6.2" @@ -44,7 +44,7 @@ configure(allprojects) { project -> ext.hibernate5Version = "5.0.9.Final" ext.hibval4Version = "4.3.2.Final" ext.hibval5Version = "5.2.4.Final" - ext.hsqldbVersion = "2.3.3" + ext.hsqldbVersion = "2.3.4" ext.httpasyncVersion = "4.1.1" ext.httpclientVersion = "4.5.2" ext.jackson2Version = "2.6.6" @@ -67,7 +67,7 @@ configure(allprojects) { project -> ext.testngVersion = "6.9.10" ext.tiles2Version = "2.2.2" ext.tiles3Version = "3.0.5" - ext.tomcatVersion = "8.0.33" + ext.tomcatVersion = "8.0.35" ext.tyrusVersion = "1.3.5" // constrained by WebLogic 12.1.3 support ext.undertowVersion = "1.3.22.Final" ext.xmlunitVersion = "1.6" @@ -617,7 +617,7 @@ project("spring-jdbc") { optional("javax.transaction:javax.transaction-api:${jtaVersion}") optional("com.mchange:c3p0:0.9.5.2") optional("org.hsqldb:hsqldb:${hsqldbVersion}") - optional("com.h2database:h2:1.4.191") + optional("com.h2database:h2:1.4.192") optional("org.apache.derby:derby:10.12.1.1") optional("org.apache.derby:derbyclient:10.12.1.1") } From d467dc2a052531fb5d6fdecd155b0fc8af2d2097 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sat, 4 Jun 2016 00:20:46 +0200 Subject: [PATCH 175/344] Avoid double getFlushMode call --- .../orm/hibernate4/HibernateTransactionManager.java | 4 ++-- .../orm/hibernate5/HibernateTransactionManager.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/HibernateTransactionManager.java b/spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/HibernateTransactionManager.java index ad527e3867..c9ac33332c 100644 --- a/spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/HibernateTransactionManager.java +++ b/spring-orm-hibernate4/src/main/java/org/springframework/orm/hibernate4/HibernateTransactionManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -480,7 +480,7 @@ protected void doBegin(Object transaction, TransactionDefinition definition) { if (!definition.isReadOnly() && !txObject.isNewSession()) { // We need AUTO or COMMIT for a non-read-only transaction. FlushMode flushMode = session.getFlushMode(); - if (session.getFlushMode().equals(FlushMode.MANUAL)) { + if (FlushMode.MANUAL.equals(flushMode)) { session.setFlushMode(FlushMode.AUTO); txObject.getSessionHolder().setPreviousFlushMode(flushMode); } diff --git a/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateTransactionManager.java b/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateTransactionManager.java index d26f18b805..4067c176e1 100644 --- a/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateTransactionManager.java +++ b/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateTransactionManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -477,7 +477,7 @@ protected void doBegin(Object transaction, TransactionDefinition definition) { if (!definition.isReadOnly() && !txObject.isNewSession()) { // We need AUTO or COMMIT for a non-read-only transaction. FlushMode flushMode = session.getFlushMode(); - if (session.getFlushMode().equals(FlushMode.MANUAL)) { + if (FlushMode.MANUAL.equals(flushMode)) { session.setFlushMode(FlushMode.AUTO); txObject.getSessionHolder().setPreviousFlushMode(flushMode); } From 2fd691b3402a495cd857dc55767b9a81fc0ad2fe Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sat, 4 Jun 2016 00:15:52 +0200 Subject: [PATCH 176/344] EventListenerMethodProcessor defensively handles unresolvable method signatures Issue: SPR-14330 (cherry picked from commit f657952) --- .../event/EventListenerMethodProcessor.java | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/event/EventListenerMethodProcessor.java b/spring-context/src/main/java/org/springframework/context/event/EventListenerMethodProcessor.java index d551d509e5..e24cac8d91 100644 --- a/spring-context/src/main/java/org/springframework/context/event/EventListenerMethodProcessor.java +++ b/spring-context/src/main/java/org/springframework/context/event/EventListenerMethodProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,6 +41,7 @@ import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; /** * Register {@link EventListener} annotated method as individual {@link ApplicationListener} @@ -124,14 +125,23 @@ protected List getEventListenerFactories() { protected void processBean(final List factories, final String beanName, final Class targetType) { if (!this.nonAnnotatedClasses.contains(targetType)) { - Map annotatedMethods = MethodIntrospector.selectMethods(targetType, - new MethodIntrospector.MetadataLookup() { - @Override - public EventListener inspect(Method method) { - return AnnotationUtils.findAnnotation(method, EventListener.class); - } - }); - if (annotatedMethods.isEmpty()) { + Map annotatedMethods = null; + try { + annotatedMethods = MethodIntrospector.selectMethods(targetType, + new MethodIntrospector.MetadataLookup() { + @Override + public EventListener inspect(Method method) { + return AnnotationUtils.findAnnotation(method, EventListener.class); + } + }); + } + catch (Throwable ex) { + // An unresolvable type in a method signature, probably from a lazy bean - let's ignore it. + if (logger.isDebugEnabled()) { + logger.debug("Could not resolve methods for bean with name '" + beanName + "'", ex); + } + } + if (CollectionUtils.isEmpty(annotatedMethods)) { this.nonAnnotatedClasses.add(targetType); if (logger.isTraceEnabled()) { logger.trace("No @EventListener annotations found on bean class: " + targetType); From e2195764149ac94c95e8d3346c80973da0f5cb7d Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sat, 4 Jun 2016 00:17:20 +0200 Subject: [PATCH 177/344] Defensive catching of any Throwable subclasses instead of just Error Issue: SPR-14329 Issue: SPR-14323 (cherry picked from commit a9fda3e) --- .../messaging/support/AbstractMessageChannel.java | 6 +++--- .../support/ExecutorSubscribableChannel.java | 11 ++++++----- .../transaction/support/TransactionTemplate.java | 4 ++-- .../web/portlet/DispatcherPortlet.java | 10 +++++----- .../web/servlet/DispatcherServlet.java | 4 ++-- 5 files changed, 18 insertions(+), 17 deletions(-) diff --git a/spring-messaging/src/main/java/org/springframework/messaging/support/AbstractMessageChannel.java b/spring-messaging/src/main/java/org/springframework/messaging/support/AbstractMessageChannel.java index 56e663ca28..37825c7674 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/support/AbstractMessageChannel.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/support/AbstractMessageChannel.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -126,9 +126,9 @@ public final boolean send(Message message, long timeout) { } throw new MessageDeliveryException(message,"Failed to send message to " + this, ex); } - catch (Error ex) { + catch (Throwable err) { MessageDeliveryException ex2 = - new MessageDeliveryException(message, "Failed to send message to " + this, ex); + new MessageDeliveryException(message, "Failed to send message to " + this, err); chain.triggerAfterSendCompletion(message, this, sent, ex2); throw ex2; } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/support/ExecutorSubscribableChannel.java b/spring-messaging/src/main/java/org/springframework/messaging/support/ExecutorSubscribableChannel.java index 0e939d6ddc..45acbbf03f 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/support/ExecutorSubscribableChannel.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/support/ExecutorSubscribableChannel.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -99,7 +99,7 @@ public boolean sendInternal(Message message, long timeout) { /** - * Invoke a MessageHandler with ExecutorChannelInterceptor's. + * Invoke a MessageHandler with ExecutorChannelInterceptors. */ private class SendTask implements MessageHandlingRunnable { @@ -143,10 +143,11 @@ public void run() { String description = "Failed to handle " + message + " to " + this + " in " + this.messageHandler; throw new MessageDeliveryException(message, description, ex); } - catch (Error ex) { + catch (Throwable err) { String description = "Failed to handle " + message + " to " + this + " in " + this.messageHandler; - triggerAfterMessageHandled(message, new MessageDeliveryException(message, description, ex)); - throw ex; + MessageDeliveryException ex2 = new MessageDeliveryException(message, description, err); + triggerAfterMessageHandled(message, ex2); + throw ex2; } } diff --git a/spring-tx/src/main/java/org/springframework/transaction/support/TransactionTemplate.java b/spring-tx/src/main/java/org/springframework/transaction/support/TransactionTemplate.java index 7515148694..b2fd8ce019 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/support/TransactionTemplate.java +++ b/spring-tx/src/main/java/org/springframework/transaction/support/TransactionTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -142,7 +142,7 @@ public T execute(TransactionCallback action) throws TransactionException rollbackOnException(status, err); throw err; } - catch (Exception ex) { + catch (Throwable ex) { // Transactional code threw unexpected exception -> rollback rollbackOnException(status, ex); throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception"); diff --git a/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/DispatcherPortlet.java b/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/DispatcherPortlet.java index 9c5add7fc6..35300634ec 100644 --- a/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/DispatcherPortlet.java +++ b/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/DispatcherPortlet.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -687,7 +687,7 @@ protected void doActionService(ActionRequest request, ActionResponse response) t throw ex; } } - catch (Error err) { + catch (Throwable err) { PortletException ex = new PortletException("Error occured during request processing: " + err.getMessage(), err); // Trigger after-completion for thrown exception. @@ -800,7 +800,7 @@ protected void doRenderService(RenderRequest request, RenderResponse response) t triggerAfterRenderCompletion(mappedHandler, interceptorIndex, request, response, ex); throw ex; } - catch (Error err) { + catch (Throwable err) { PortletException ex = new PortletException("Error occured during request processing: " + err.getMessage(), err); // Trigger after-completion for thrown exception. @@ -891,7 +891,7 @@ protected void doResourceService(ResourceRequest request, ResourceResponse respo triggerAfterResourceCompletion(mappedHandler, interceptorIndex, request, response, ex); throw ex; } - catch (Error err) { + catch (Throwable err) { PortletException ex = new PortletException("Error occured during request processing: " + err.getMessage(), err); // Trigger after-completion for thrown exception. @@ -965,7 +965,7 @@ protected void doEventService(EventRequest request, EventResponse response) thro throw ex; } } - catch (Error err) { + catch (Throwable err) { PortletException ex = new PortletException("Error occured during request processing: " + err.getMessage(), err); // Trigger after-completion for thrown exception. diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java index 7fd9424807..38d16f9eea 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java @@ -975,7 +975,7 @@ protected void doDispatch(HttpServletRequest request, HttpServletResponse respon catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } - catch (Error err) { + catch (Throwable err) { triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err); } finally { @@ -1300,7 +1300,7 @@ private void triggerAfterCompletion(HttpServletRequest request, HttpServletRespo } private void triggerAfterCompletionWithError(HttpServletRequest request, HttpServletResponse response, - HandlerExecutionChain mappedHandler, Error error) throws Exception { + HandlerExecutionChain mappedHandler, Throwable error) throws Exception { ServletException ex = new NestedServletException("Handler processing failed", error); if (mappedHandler != null) { From 24b3525f8de9a88abf8b8fe3bbfe9f093239b797 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sat, 4 Jun 2016 11:20:21 +0200 Subject: [PATCH 178/344] Upgrade to Undertow 1.3.23 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 59100af590..ca2d008b4f 100644 --- a/build.gradle +++ b/build.gradle @@ -69,7 +69,7 @@ configure(allprojects) { project -> ext.tiles3Version = "3.0.5" ext.tomcatVersion = "8.0.35" ext.tyrusVersion = "1.3.5" // constrained by WebLogic 12.1.3 support - ext.undertowVersion = "1.3.22.Final" + ext.undertowVersion = "1.3.23.Final" ext.xmlunitVersion = "1.6" ext.xstreamVersion = "1.4.9" From 47c62fd1732c88102e589e8ca62903194707debf Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 6 Jun 2016 17:15:56 +0200 Subject: [PATCH 179/344] Synchronized access to method overrides (in particular for @Lookup) Issue: SPR-14333 (cherry picked from commit 9131ebb) --- .../support/AbstractBeanDefinition.java | 11 ++++--- .../factory/support/MethodOverrides.java | 30 +++++++++++++------ 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java index 85b5de4a50..a0d7dc21dd 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -731,7 +731,7 @@ public void setMethodOverrides(MethodOverrides methodOverrides) { /** * Return information about methods to be overridden by the IoC * container. This will be empty if there are no method overrides. - * Never returns null. + * Never returns {@code null}. */ public MethodOverrides getMethodOverrides() { return this.methodOverrides; @@ -934,8 +934,11 @@ public void prepareMethodOverrides() throws BeanDefinitionValidationException { // Check that lookup methods exists. MethodOverrides methodOverrides = getMethodOverrides(); if (!methodOverrides.isEmpty()) { - for (MethodOverride mo : methodOverrides.getOverrides()) { - prepareMethodOverride(mo); + Set overrides = methodOverrides.getOverrides(); + synchronized (overrides) { + for (MethodOverride mo : overrides) { + prepareMethodOverride(mo); + } } } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/MethodOverrides.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/MethodOverrides.java index 05159bd78d..11b98753e9 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/MethodOverrides.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/MethodOverrides.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package org.springframework.beans.factory.support; import java.lang.reflect.Method; +import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; @@ -34,7 +35,10 @@ */ public class MethodOverrides { - private final Set overrides = new LinkedHashSet(0); + private final Set overrides = + Collections.synchronizedSet(new LinkedHashSet(0)); + + private volatile boolean modified = false; /** @@ -56,7 +60,8 @@ public MethodOverrides(MethodOverrides other) { */ public void addOverrides(MethodOverrides other) { if (other != null) { - this.overrides.addAll(other.getOverrides()); + this.modified = true; + this.overrides.addAll(other.overrides); } } @@ -64,6 +69,7 @@ public void addOverrides(MethodOverrides other) { * Add the given method override. */ public void addOverride(MethodOverride override) { + this.modified = true; this.overrides.add(override); } @@ -73,6 +79,7 @@ public void addOverride(MethodOverride override) { * @see MethodOverride */ public Set getOverrides() { + this.modified = true; return this.overrides; } @@ -80,7 +87,7 @@ public Set getOverrides() { * Return whether the set of method overrides is empty. */ public boolean isEmpty() { - return this.overrides.isEmpty(); + return (!this.modified || this.overrides.isEmpty()); } /** @@ -89,13 +96,18 @@ public boolean isEmpty() { * @return the method override, or {@code null} if none */ public MethodOverride getOverride(Method method) { - MethodOverride match = null; - for (MethodOverride candidate : this.overrides) { - if (candidate.matches(method)) { - match = candidate; + if (!this.modified) { + return null; + } + synchronized (this.overrides) { + MethodOverride match = null; + for (MethodOverride candidate : this.overrides) { + if (candidate.matches(method)) { + match = candidate; + } } + return match; } - return match; } From e9ba64ae49ff2f42f5fd3c7abb6980ce68b6a400 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 6 Jun 2016 22:22:23 +0200 Subject: [PATCH 180/344] Upgrade to EhCache 3.0.2 and Castor 1.4.1 (cherry picked from commit 3c987b1) --- build.gradle | 4 ++-- spring-oxm/oxm.gradle | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index ca2d008b4f..5ad7ecb665 100644 --- a/build.gradle +++ b/build.gradle @@ -31,7 +31,7 @@ configure(allprojects) { project -> ext.eclipselinkVersion = "2.4.2" ext.ehcacheVersion = "2.10.2" ext.ehcachejcacheVersion = "1.0.1" - ext.ehcache3Version = "3.0.1" + ext.ehcache3Version = "3.0.2" ext.ejbVersion = "3.0" ext.fileuploadVersion = "1.3.2" ext.freemarkerVersion = "2.3.23" @@ -561,7 +561,7 @@ project("spring-oxm") { dependencies { compile(project(":spring-beans")) compile(project(":spring-core")) - optional("org.codehaus.castor:castor-xml:1.4.0") { + optional("org.codehaus.castor:castor-xml:1.4.1") { exclude group: 'stax', module: 'stax-api' exclude group: "org.springframework", module: "spring-context" } diff --git a/spring-oxm/oxm.gradle b/spring-oxm/oxm.gradle index 142ef1c30a..140b35130e 100644 --- a/spring-oxm/oxm.gradle +++ b/spring-oxm/oxm.gradle @@ -5,7 +5,7 @@ configurations { jibx } dependencies { - castor "org.codehaus.castor:castor-anttasks:1.4.0" + castor "org.codehaus.castor:castor-anttasks:1.4.1" castor "org.apache.velocity:velocity:1.7" xjc "com.sun.xml.bind:jaxb-xjc:2.1.17" xmlbeans "org.apache.xmlbeans:xmlbeans:2.6.0" From d0b2fe5e8c8e8d1a51f57f15ffbcbb8c2180b835 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 7 Jun 2016 12:36:17 +0200 Subject: [PATCH 181/344] Allow JAXB to be registered next to plain Jackson Issue: SPR-14336 (cherry picked from commit abcfffd) --- .../support/AllEncompassingFormHttpMessageConverter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/converter/support/AllEncompassingFormHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/support/AllEncompassingFormHttpMessageConverter.java index d93e6ff1e7..2edcbb8f99 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/support/AllEncompassingFormHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/support/AllEncompassingFormHttpMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -53,7 +53,7 @@ public class AllEncompassingFormHttpMessageConverter extends FormHttpMessageConv public AllEncompassingFormHttpMessageConverter() { addPartConverter(new SourceHttpMessageConverter()); - if (jaxb2Present && !jackson2Present) { + if (jaxb2Present && !jackson2XmlPresent) { addPartConverter(new Jaxb2RootElementHttpMessageConverter()); } From 9a4177436464368b10a89fa5d61453cae0d9d29b Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 7 Jun 2016 13:35:19 +0200 Subject: [PATCH 182/344] Fixed @since references after SPR-14080 backport Issue: SPR-14305 --- .../org/springframework/web/util/UriComponentsBuilder.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java b/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java index 7dea422f5a..74d0105e1b 100644 --- a/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java +++ b/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java @@ -665,7 +665,7 @@ public UriComponentsBuilder fragment(String fragment) { * "Forwarded" is not found. * @param headers the HTTP headers to consider * @return this UriComponentsBuilder - * @since 4.3 + * @since 4.2.7 */ UriComponentsBuilder adaptFromForwardedHeaders(HttpHeaders headers) { String forwardedHeader = headers.getFirst("Forwarded"); @@ -743,7 +743,7 @@ public Object clone() { /** * Clone this {@code UriComponentsBuilder}. * @return the cloned {@code UriComponentsBuilder} object - * @since 4.3 + * @since 4.2.7 */ public UriComponentsBuilder cloneBuilder() { return new UriComponentsBuilder(this); From 70017070286e96706040ff92c2b6dab382254f7c Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 7 Jun 2016 16:49:23 +0200 Subject: [PATCH 183/344] CorsFilter asserts presence of CorsConfigurationSource (cherry picked from commit 31aed61) --- .../web/cors/CorsConfigurationSource.java | 3 ++- .../web/filter/CorsFilter.java | 22 +++++++++++-------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/cors/CorsConfigurationSource.java b/spring-web/src/main/java/org/springframework/web/cors/CorsConfigurationSource.java index 2fc6d9ce41..48166caa19 100644 --- a/spring-web/src/main/java/org/springframework/web/cors/CorsConfigurationSource.java +++ b/spring-web/src/main/java/org/springframework/web/cors/CorsConfigurationSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,6 +29,7 @@ public interface CorsConfigurationSource { /** * Return a {@link CorsConfiguration} based on the incoming request. + * @return the associated {@link CorsConfiguration}, or {@code null} if none */ CorsConfiguration getCorsConfiguration(HttpServletRequest request); diff --git a/spring-web/src/main/java/org/springframework/web/filter/CorsFilter.java b/spring-web/src/main/java/org/springframework/web/filter/CorsFilter.java index 8079532e33..812deb0a20 100644 --- a/spring-web/src/main/java/org/springframework/web/filter/CorsFilter.java +++ b/spring-web/src/main/java/org/springframework/web/filter/CorsFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,10 +31,10 @@ import org.springframework.web.cors.UrlBasedCorsConfigurationSource; /** - * {@link javax.servlet.Filter} that handles CORS preflight requests and intercepts CORS - * simple and actual requests thanks to a {@link CorsProcessor} implementation - * ({@link DefaultCorsProcessor} by default) in order to add the relevant CORS response - * headers (like {@code Access-Control-Allow-Origin}) using the provided + * {@link javax.servlet.Filter} that handles CORS preflight requests and intercepts + * CORS simple and actual requests thanks to a {@link CorsProcessor} implementation + * ({@link DefaultCorsProcessor} by default) in order to add the relevant CORS + * response headers (like {@code Access-Control-Allow-Origin}) using the provided * {@link CorsConfigurationSource} (for example an {@link UrlBasedCorsConfigurationSource} * instance. * @@ -52,20 +52,22 @@ */ public class CorsFilter extends OncePerRequestFilter { - private CorsProcessor processor = new DefaultCorsProcessor(); - private final CorsConfigurationSource configSource; + private CorsProcessor processor = new DefaultCorsProcessor(); + /** - * Constructor accepting a {@link CorsConfigurationSource} used by the filter to find - * the {@link CorsConfiguration} to use for each incoming request. + * Constructor accepting a {@link CorsConfigurationSource} used by the filter + * to find the {@link CorsConfiguration} to use for each incoming request. * @see UrlBasedCorsConfigurationSource */ public CorsFilter(CorsConfigurationSource configSource) { + Assert.notNull(configSource, "CorsConfigurationSource must not be null"); this.configSource = configSource; } + /** * Configure a custom {@link CorsProcessor} to use to apply the matched * {@link CorsConfiguration} for a request. @@ -76,6 +78,7 @@ public void setCorsProcessor(CorsProcessor processor) { this.processor = processor; } + @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { @@ -89,6 +92,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse } } } + filterChain.doFilter(request, response); } From c349ac9d420bfbdfcfa8b77581418a1ee4d2886b Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 7 Jun 2016 18:09:02 +0200 Subject: [PATCH 184/344] Upgrade to Groovy 2.4.7 (cherry picked from commit f5a9d5e) --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 5ad7ecb665..aeff4ae3ae 100644 --- a/build.gradle +++ b/build.gradle @@ -35,7 +35,7 @@ configure(allprojects) { project -> ext.ejbVersion = "3.0" ext.fileuploadVersion = "1.3.2" ext.freemarkerVersion = "2.3.23" - ext.groovyVersion = "2.4.6" + ext.groovyVersion = "2.4.7" ext.gsonVersion = "2.6.2" ext.guavaVersion = "19.0" ext.hamcrestVersion = "1.3" From 57aa2afaa04b054b54dd7727c63e549f48a9ff35 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 8 Jun 2016 10:06:26 +0200 Subject: [PATCH 185/344] Upgrade to Jackson 2.6.7 and Netty 4.0.37 --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index aeff4ae3ae..0a2176ee7d 100644 --- a/build.gradle +++ b/build.gradle @@ -47,7 +47,7 @@ configure(allprojects) { project -> ext.hsqldbVersion = "2.3.4" ext.httpasyncVersion = "4.1.1" ext.httpclientVersion = "4.5.2" - ext.jackson2Version = "2.6.6" + ext.jackson2Version = "2.6.7" ext.jasperreportsVersion = "6.2.1" ext.javamailVersion = "1.5.5" ext.jettyVersion = "9.3.9.v20160517" @@ -55,7 +55,7 @@ configure(allprojects) { project -> ext.jrubyVersion = "1.7.25" // JRuby 9000 only supported through JSR-223 (StandardScriptFactory) ext.jtaVersion = "1.2" ext.junitVersion = "4.12" - ext.nettyVersion = "4.0.36.Final" + ext.nettyVersion = "4.0.37.Final" ext.okhttpVersion = "2.7.5" ext.openjpaVersion = "2.4.1" ext.poiVersion = "3.13" From 9ac661df82ce7273e4fe93d0e9e4630dbfac0163 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 8 Jun 2016 14:13:22 +0200 Subject: [PATCH 186/344] Polishing --- .../AbstractHttpMessageConverter.java | 2 +- .../ByteArrayHttpMessageConverter.java | 8 +++- .../converter/FormHttpMessageConverter.java | 4 +- .../ObjectToStringHttpMessageConverter.java | 37 +++++++++---------- 4 files changed, 27 insertions(+), 24 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/converter/AbstractHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/AbstractHttpMessageConverter.java index 42a9119282..d63d2870ec 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/AbstractHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/AbstractHttpMessageConverter.java @@ -200,7 +200,7 @@ public HttpHeaders getHeaders() { /** * Add default headers to the output message. *

    This implementation delegates to {@link #getDefaultContentType(Object)} if a content - * type was not provided, calls {@link #getContentLength}, and sets the corresponding headers + * type was not provided, calls {@link #getContentLength}, and sets the corresponding headers. * @since 4.2 */ protected void addDefaultHeaders(HttpHeaders headers, T t, MediaType contentType) throws IOException{ diff --git a/spring-web/src/main/java/org/springframework/http/converter/ByteArrayHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/ByteArrayHttpMessageConverter.java index 87c003d7a5..12c95e141b 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/ByteArrayHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/ByteArrayHttpMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,15 +32,19 @@ * overridden by setting the {@link #setSupportedMediaTypes supportedMediaTypes} property. * * @author Arjen Poutsma + * @author Juergen Hoeller * @since 3.0 */ public class ByteArrayHttpMessageConverter extends AbstractHttpMessageConverter { - /** Creates a new instance of the {@code ByteArrayHttpMessageConverter}. */ + /** + * Create a new instance of the {@code ByteArrayHttpMessageConverter}. + */ public ByteArrayHttpMessageConverter() { super(new MediaType("application", "octet-stream"), MediaType.ALL); } + @Override public boolean supports(Class clazz) { return byte[].class == clazz; diff --git a/spring-web/src/main/java/org/springframework/http/converter/FormHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/FormHttpMessageConverter.java index 44491be00f..fa2d0e4a22 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/FormHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/FormHttpMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -255,7 +255,7 @@ private void writeForm(MultiValueMap form, MediaType contentType Charset charset; if (contentType != null) { outputMessage.getHeaders().setContentType(contentType); - charset = contentType.getCharSet() != null ? contentType.getCharSet() : this.charset; + charset = (contentType.getCharSet() != null ? contentType.getCharSet() : this.charset); } else { outputMessage.getHeaders().setContentType(MediaType.APPLICATION_FORM_URLENCODED); diff --git a/spring-web/src/main/java/org/springframework/http/converter/ObjectToStringHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/ObjectToStringHttpMessageConverter.java index c1ef5c26dc..5fab4fec5a 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/ObjectToStringHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/ObjectToStringHttpMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,11 +29,12 @@ * An {@code HttpMessageConverter} that uses {@link StringHttpMessageConverter} * for reading and writing content and a {@link ConversionService} for converting * the String content to and from the target object type. - *

    - * By default, this converter supports the media type {@code text/plain} only. - * This can be overridden by setting the - * {@link #setSupportedMediaTypes supportedMediaTypes} property. - * Example of usage: + * + *

    By default, this converter supports the media type {@code text/plain} only. + * This can be overridden through the {@link #setSupportedMediaTypes supportedMediaTypes} + * property. + * + *

    A usage example: * *

      * <bean class="org.springframework.http.converter.ObjectToStringHttpMessageConverter">
    @@ -49,17 +50,15 @@
      */
     public class ObjectToStringHttpMessageConverter extends AbstractHttpMessageConverter {
     
    -	private ConversionService conversionService;
    +	private final ConversionService conversionService;
     
    -	private StringHttpMessageConverter stringHttpMessageConverter;
    +	private final StringHttpMessageConverter stringHttpMessageConverter;
     
     
     	/**
     	 * A constructor accepting a {@code ConversionService} to use to convert the
    -	 * (String) message body to/from the target class type. This constructor
    -	 * uses {@link StringHttpMessageConverter#DEFAULT_CHARSET} as the default
    -	 * charset.
    -	 *
    +	 * (String) message body to/from the target class type. This constructor uses
    +	 * {@link StringHttpMessageConverter#DEFAULT_CHARSET} as the default charset.
     	 * @param conversionService the conversion service
     	 */
     	public ObjectToStringHttpMessageConverter(ConversionService conversionService) {
    @@ -67,20 +66,19 @@ public ObjectToStringHttpMessageConverter(ConversionService conversionService) {
     	}
     
     	/**
    -	 * A constructor accepting a {@code ConversionService} as well as a default
    -	 * charset.
    -	 *
    +	 * A constructor accepting a {@code ConversionService} as well as a default charset.
     	 * @param conversionService the conversion service
     	 * @param defaultCharset the default charset
     	 */
     	public ObjectToStringHttpMessageConverter(ConversionService conversionService, Charset defaultCharset) {
     		super(new MediaType("text", "plain", defaultCharset));
     
    -		Assert.notNull(conversionService, "conversionService is required");
    +		Assert.notNull(conversionService, "ConversionService is required");
     		this.conversionService = conversionService;
     		this.stringHttpMessageConverter = new StringHttpMessageConverter(defaultCharset);
     	}
     
    +
     	/**
     	 * Indicates whether the {@code Accept-Charset} should be written to any outgoing request.
     	 * 

    Default is {@code true}. @@ -89,6 +87,7 @@ public void setWriteAcceptCharset(boolean writeAcceptCharset) { this.stringHttpMessageConverter.setWriteAcceptCharset(writeAcceptCharset); } + @Override public boolean canRead(Class clazz, MediaType mediaType) { return this.conversionService.canConvert(String.class, clazz) && canRead(mediaType); @@ -106,15 +105,15 @@ protected boolean supports(Class clazz) { } @Override - protected Object readInternal(Class clazz, HttpInputMessage inputMessage) throws IOException { + protected Object readInternal(Class clazz, HttpInputMessage inputMessage) throws IOException { String value = this.stringHttpMessageConverter.readInternal(String.class, inputMessage); return this.conversionService.convert(value, clazz); } @Override protected void writeInternal(Object obj, HttpOutputMessage outputMessage) throws IOException { - String s = this.conversionService.convert(obj, String.class); - this.stringHttpMessageConverter.writeInternal(s, outputMessage); + String value = this.conversionService.convert(obj, String.class); + this.stringHttpMessageConverter.writeInternal(value, outputMessage); } @Override From 367e663730229e645acf2e656e91dac9b8fa1e43 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 8 Jun 2016 15:54:00 +0200 Subject: [PATCH 187/344] Assertions for error codes in case of formatting failures Issue: SPR-14345 (cherry picked from commit c6f63bd) --- .../springframework/validation/DataBinderTests.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/spring-context/src/test/java/org/springframework/validation/DataBinderTests.java b/spring-context/src/test/java/org/springframework/validation/DataBinderTests.java index aecc61995a..352b24c6f0 100644 --- a/spring-context/src/test/java/org/springframework/validation/DataBinderTests.java +++ b/spring-context/src/test/java/org/springframework/validation/DataBinderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -226,12 +226,14 @@ public void testBindingWithErrors() throws Exception { assertTrue("Has age errors", br.hasFieldErrors("age")); assertTrue("Correct number of age errors", br.getFieldErrorCount("age") == 1); + assertEquals("typeMismatch", binder.getBindingResult().getFieldError("age").getCode()); assertEquals("32x", binder.getBindingResult().getFieldValue("age")); assertEquals("32x", binder.getBindingResult().getFieldError("age").getRejectedValue()); assertEquals(0, tb.getAge()); assertTrue("Has touchy errors", br.hasFieldErrors("touchy")); assertTrue("Correct number of touchy errors", br.getFieldErrorCount("touchy") == 1); + assertEquals("methodInvocation", binder.getBindingResult().getFieldError("touchy").getCode()); assertEquals("m.y", binder.getBindingResult().getFieldValue("touchy")); assertEquals("m.y", binder.getBindingResult().getFieldError("touchy").getRejectedValue()); assertNull(tb.getTouchy()); @@ -315,12 +317,14 @@ public String getAsText() { assertTrue("Has age errors", br.hasFieldErrors("age")); assertTrue("Correct number of age errors", br.getFieldErrorCount("age") == 1); + assertEquals("typeMismatch", binder.getBindingResult().getFieldError("age").getCode()); assertEquals("32x", binder.getBindingResult().getFieldValue("age")); assertEquals("32x", binder.getBindingResult().getFieldError("age").getRejectedValue()); assertEquals(0, tb.getAge()); assertTrue("Has touchy errors", br.hasFieldErrors("touchy")); assertTrue("Correct number of touchy errors", br.getFieldErrorCount("touchy") == 1); + assertEquals("methodInvocation", binder.getBindingResult().getFieldError("touchy").getCode()); assertEquals("m.y", binder.getBindingResult().getFieldValue("touchy")); assertEquals("m.y", binder.getBindingResult().getFieldError("touchy").getRejectedValue()); assertNull(tb.getTouchy()); @@ -419,6 +423,7 @@ public String print(String object, Locale locale) { binder.bind(pvs); assertTrue(binder.getBindingResult().hasFieldErrors("name")); + assertEquals("typeMismatch", binder.getBindingResult().getFieldError("name").getCode()); assertEquals("test", binder.getBindingResult().getFieldValue("name")); } @@ -567,6 +572,7 @@ public void testBindingErrorWithCustomFormatter() { assertEquals(new Float(0.0), tb.getMyFloat()); assertEquals("1x2", binder.getBindingResult().getFieldValue("myFloat")); assertTrue(binder.getBindingResult().hasFieldErrors("myFloat")); + assertEquals("typeMismatch", binder.getBindingResult().getFieldError("myFloat").getCode()); } finally { LocaleContextHolder.resetLocaleContext(); @@ -881,7 +887,6 @@ public void testCustomEditorForPrimitiveProperty() { public void setAsText(String text) throws IllegalArgumentException { setValue(new Integer(99)); } - @Override public String getAsText() { return "argh"; @@ -906,7 +911,6 @@ public void testCustomEditorForAllStringProperties() { public void setAsText(String text) throws IllegalArgumentException { setValue("prefix" + text); } - @Override public String getAsText() { return ((String) getValue()).substring(6); @@ -979,7 +983,6 @@ public void testCustomFormatterForPrimitiveProperty() { public Integer parse(String text, Locale locale) throws ParseException { return 99; } - @Override public String print(Integer object, Locale locale) { return "argh"; From f9e8924b3dd9dc883a503ed59ee88b17434aaeef Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 9 Jun 2016 10:49:15 +0200 Subject: [PATCH 188/344] Consistent processing of empty values and catching of RuntimeExceptions for Formatters Issue: SPR-14345 (cherry picked from commit d51c22a) --- .../AbstractNestablePropertyAccessor.java | 2 +- .../beans/TypeConverterSupport.java | 4 +- .../FormatterPropertyEditorAdapter.java | 19 ++++-- .../support/FormattingConversionService.java | 4 +- .../validation/DataBinderTests.java | 64 ++++++++++++++++++- .../convert/ConversionFailedException.java | 4 +- .../convert/ConverterNotFoundException.java | 6 +- .../core/convert/support/ConversionUtils.java | 2 +- 8 files changed, 85 insertions(+), 20 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java b/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java index cde668d48c..cf3ebd9a64 100644 --- a/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java @@ -590,7 +590,7 @@ private Object convertIfNecessary(String propertyName, Object oldValue, Object n new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, newValue); throw new ConversionNotSupportedException(pce, requiredType, ex); } - catch (IllegalArgumentException ex) { + catch (Throwable ex) { PropertyChangeEvent pce = new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, newValue); throw new TypeMismatchException(pce, requiredType, ex); diff --git a/spring-beans/src/main/java/org/springframework/beans/TypeConverterSupport.java b/spring-beans/src/main/java/org/springframework/beans/TypeConverterSupport.java index ec1f5a2b4c..ba9fc09cca 100644 --- a/spring-beans/src/main/java/org/springframework/beans/TypeConverterSupport.java +++ b/spring-beans/src/main/java/org/springframework/beans/TypeConverterSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -73,7 +73,7 @@ private T doConvert(Object value, Class requiredType, MethodParameter met catch (IllegalStateException ex) { throw new ConversionNotSupportedException(value, requiredType, ex); } - catch (IllegalArgumentException ex) { + catch (Throwable ex) { throw new TypeMismatchException(value, requiredType, ex); } } diff --git a/spring-context/src/main/java/org/springframework/format/support/FormatterPropertyEditorAdapter.java b/spring-context/src/main/java/org/springframework/format/support/FormatterPropertyEditorAdapter.java index c5db9ecdd2..7d490f5d32 100644 --- a/spring-context/src/main/java/org/springframework/format/support/FormatterPropertyEditorAdapter.java +++ b/spring-context/src/main/java/org/springframework/format/support/FormatterPropertyEditorAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.format.Formatter; import org.springframework.util.Assert; +import org.springframework.util.StringUtils; /** * Adapter that bridges between {@link Formatter} and {@link PropertyEditor}. @@ -60,17 +61,23 @@ public Class getFieldType() { @Override public void setAsText(String text) throws IllegalArgumentException { - try { - setValue(this.formatter.parse(text, LocaleContextHolder.getLocale())); + if (StringUtils.hasText(text)) { + try { + setValue(this.formatter.parse(text, LocaleContextHolder.getLocale())); + } + catch (ParseException ex) { + throw new IllegalArgumentException("Parse attempt failed for value [" + text + "]", ex); + } } - catch (ParseException ex) { - throw new IllegalArgumentException("Parse attempt failed for value [" + text + "]", ex); + else { + setValue(null); } } @Override public String getAsText() { - return this.formatter.print(getValue(), LocaleContextHolder.getLocale()); + Object value = getValue(); + return (value != null ? this.formatter.print(value, LocaleContextHolder.getLocale()) : ""); } } diff --git a/spring-context/src/main/java/org/springframework/format/support/FormattingConversionService.java b/spring-context/src/main/java/org/springframework/format/support/FormattingConversionService.java index 276f87066f..e65e4fbda9 100644 --- a/spring-context/src/main/java/org/springframework/format/support/FormattingConversionService.java +++ b/spring-context/src/main/java/org/springframework/format/support/FormattingConversionService.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -194,7 +194,7 @@ public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor t result = this.parser.parse(text, LocaleContextHolder.getLocale()); } catch (ParseException ex) { - throw new IllegalArgumentException("Unable to parse '" + text + "'", ex); + throw new IllegalArgumentException("Parse attempt failed for value [" + text + "]", ex); } if (result == null) { throw new IllegalStateException("Parsers are not allowed to return null"); diff --git a/spring-context/src/test/java/org/springframework/validation/DataBinderTests.java b/spring-context/src/test/java/org/springframework/validation/DataBinderTests.java index 352b24c6f0..b9090e8b04 100644 --- a/spring-context/src/test/java/org/springframework/validation/DataBinderTests.java +++ b/spring-context/src/test/java/org/springframework/validation/DataBinderTests.java @@ -402,11 +402,12 @@ public void testBindingErrorWithFormatter() { } @Test - public void testBindingErrorWithStringFormatter() { + public void testBindingErrorWithParseExceptionFromFormatter() { TestBean tb = new TestBean(); DataBinder binder = new DataBinder(tb); FormattingConversionService conversionService = new FormattingConversionService(); DefaultConversionService.addDefaultConverters(conversionService); + conversionService.addFormatter(new Formatter() { @Override public String parse(String text, Locale locale) throws ParseException { @@ -417,6 +418,35 @@ public String print(String object, Locale locale) { return object; } }); + + binder.setConversionService(conversionService); + MutablePropertyValues pvs = new MutablePropertyValues(); + pvs.add("name", "test"); + + binder.bind(pvs); + assertTrue(binder.getBindingResult().hasFieldErrors("name")); + assertEquals("typeMismatch", binder.getBindingResult().getFieldError("name").getCode()); + assertEquals("test", binder.getBindingResult().getFieldValue("name")); + } + + @Test + public void testBindingErrorWithRuntimeExceptionFromFormatter() { + TestBean tb = new TestBean(); + DataBinder binder = new DataBinder(tb); + FormattingConversionService conversionService = new FormattingConversionService(); + DefaultConversionService.addDefaultConverters(conversionService); + + conversionService.addFormatter(new Formatter() { + @Override + public String parse(String text, Locale locale) throws ParseException { + throw new RuntimeException(text); + } + @Override + public String print(String object, Locale locale) { + return object; + } + }); + binder.setConversionService(conversionService); MutablePropertyValues pvs = new MutablePropertyValues(); pvs.add("name", "test"); @@ -580,9 +610,10 @@ public void testBindingErrorWithCustomFormatter() { } @Test - public void testBindingErrorWithCustomStringFormatter() { + public void testBindingErrorWithParseExceptionFromCustomFormatter() { TestBean tb = new TestBean(); DataBinder binder = new DataBinder(tb); + binder.addCustomFormatter(new Formatter() { @Override public String parse(String text, Locale locale) throws ParseException { @@ -593,12 +624,39 @@ public String print(String object, Locale locale) { return object; } }); + MutablePropertyValues pvs = new MutablePropertyValues(); pvs.add("name", "test"); binder.bind(pvs); assertTrue(binder.getBindingResult().hasFieldErrors("name")); assertEquals("test", binder.getBindingResult().getFieldValue("name")); + assertEquals("typeMismatch", binder.getBindingResult().getFieldError("name").getCode()); + } + + @Test + public void testBindingErrorWithRuntimeExceptionFromCustomFormatter() { + TestBean tb = new TestBean(); + DataBinder binder = new DataBinder(tb); + + binder.addCustomFormatter(new Formatter() { + @Override + public String parse(String text, Locale locale) throws ParseException { + throw new RuntimeException(text); + } + @Override + public String print(String object, Locale locale) { + return object; + } + }); + + MutablePropertyValues pvs = new MutablePropertyValues(); + pvs.add("name", "test"); + + binder.bind(pvs); + assertTrue(binder.getBindingResult().hasFieldErrors("name")); + assertEquals("test", binder.getBindingResult().getFieldValue("name")); + assertEquals("typeMismatch", binder.getBindingResult().getFieldError("name").getCode()); } @Test @@ -990,7 +1048,7 @@ public String print(Integer object, Locale locale) { }, "age"); MutablePropertyValues pvs = new MutablePropertyValues(); - pvs.add("age", ""); + pvs.add("age", "x"); binder.bind(pvs); assertEquals("argh", binder.getBindingResult().getFieldValue("age")); diff --git a/spring-core/src/main/java/org/springframework/core/convert/ConversionFailedException.java b/spring-core/src/main/java/org/springframework/core/convert/ConversionFailedException.java index 2d2ddd2a4e..fcf456b8ce 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/ConversionFailedException.java +++ b/spring-core/src/main/java/org/springframework/core/convert/ConversionFailedException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ * @since 3.0 */ @SuppressWarnings("serial") -public final class ConversionFailedException extends ConversionException { +public class ConversionFailedException extends ConversionException { private final TypeDescriptor sourceType; diff --git a/spring-core/src/main/java/org/springframework/core/convert/ConverterNotFoundException.java b/spring-core/src/main/java/org/springframework/core/convert/ConverterNotFoundException.java index 4a918bccad..25e854749d 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/ConverterNotFoundException.java +++ b/spring-core/src/main/java/org/springframework/core/convert/ConverterNotFoundException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,7 +25,7 @@ * @since 3.0 */ @SuppressWarnings("serial") -public final class ConverterNotFoundException extends ConversionException { +public class ConverterNotFoundException extends ConversionException { private final TypeDescriptor sourceType; @@ -33,7 +33,7 @@ public final class ConverterNotFoundException extends ConversionException { /** - * Creates a new conversion executor not found exception. + * Create a new conversion executor not found exception. * @param sourceType the source type requested to convert from * @param targetType the target type requested to convert to */ diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/ConversionUtils.java b/spring-core/src/main/java/org/springframework/core/convert/support/ConversionUtils.java index c4d41ec351..e93eabe90e 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/support/ConversionUtils.java +++ b/spring-core/src/main/java/org/springframework/core/convert/support/ConversionUtils.java @@ -37,7 +37,7 @@ public static Object invokeConverter(GenericConverter converter, Object source, catch (ConversionFailedException ex) { throw ex; } - catch (Exception ex) { + catch (Throwable ex) { throw new ConversionFailedException(sourceType, targetType, source, ex); } } From 3680ec5a0da3e732ab0e682b2501b1d5e165e007 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 9 Jun 2016 17:21:23 +0200 Subject: [PATCH 189/344] Polishing --- .../json/SpringHandlerInstantiator.java | 40 +++++++++---------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/converter/json/SpringHandlerInstantiator.java b/spring-web/src/main/java/org/springframework/http/converter/json/SpringHandlerInstantiator.java index ef7446aede..f208c32a19 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/json/SpringHandlerInstantiator.java +++ b/spring-web/src/main/java/org/springframework/http/converter/json/SpringHandlerInstantiator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,14 +32,15 @@ import org.springframework.util.Assert; /** - * Eventually get Jackson handler ({@link JsonSerializer}, {@link JsonDeserializer}, - * {@link KeyDeserializer}, {@link TypeResolverBuilder}, {@link TypeIdResolver}) beans by - * type from Spring {@link ApplicationContext}. If no bean is found, the default behavior - * happen (calling no-argument constructor via reflection). + * Allows for creating Jackson ({@link JsonSerializer}, {@link JsonDeserializer}, + * {@link KeyDeserializer}, {@link TypeResolverBuilder}, {@link TypeIdResolver}) + * beans with autowiring against a Spring {@link ApplicationContext}. * - * @since 4.1.3 * @author Sebastien Deleuze + * @author Juergen Hoeller + * @since 4.1.3 * @see Jackson2ObjectMapperBuilder#handlerInstantiator(HandlerInstantiator) + * @see ApplicationContext#getAutowireCapableBeanFactory() * @see HandlerInstantiator */ public class SpringHandlerInstantiator extends HandlerInstantiator { @@ -56,33 +57,30 @@ public SpringHandlerInstantiator(AutowireCapableBeanFactory beanFactory) { this.beanFactory = beanFactory; } + @Override - public JsonSerializer serializerInstance(SerializationConfig config, - Annotated annotated, Class keyDeserClass) { - return (JsonSerializer) this.beanFactory.createBean(keyDeserClass); + public JsonDeserializer deserializerInstance(DeserializationConfig config, Annotated annotated, Class implClass) { + return (JsonDeserializer) this.beanFactory.createBean(implClass); } @Override - public JsonDeserializer deserializerInstance(DeserializationConfig config, - Annotated annotated, Class deserClass) { - return (JsonDeserializer) this.beanFactory.createBean(deserClass); + public KeyDeserializer keyDeserializerInstance(DeserializationConfig config, Annotated annotated, Class implClass) { + return (KeyDeserializer) this.beanFactory.createBean(implClass); } @Override - public KeyDeserializer keyDeserializerInstance(DeserializationConfig config, - Annotated annotated, Class serClass) { - return (KeyDeserializer) this.beanFactory.createBean(serClass); + public JsonSerializer serializerInstance(SerializationConfig config, Annotated annotated, Class implClass) { + return (JsonSerializer) this.beanFactory.createBean(implClass); } @Override - public TypeResolverBuilder typeResolverBuilderInstance(MapperConfig config, - Annotated annotated, Class resolverClass) { - return (TypeResolverBuilder) this.beanFactory.createBean(resolverClass); + public TypeResolverBuilder typeResolverBuilderInstance(MapperConfig config, Annotated annotated, Class implClass) { + return (TypeResolverBuilder) this.beanFactory.createBean(implClass); } @Override - public TypeIdResolver typeIdResolverInstance(MapperConfig config, - Annotated annotated, Class resolverClass) { - return (TypeIdResolver) this.beanFactory.createBean(resolverClass); + public TypeIdResolver typeIdResolverInstance(MapperConfig config, Annotated annotated, Class implClass) { + return (TypeIdResolver) this.beanFactory.createBean(implClass); } + } From 71df9cef804b74495ceeb7f1f5bc58741599c3dd Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 9 Jun 2016 22:40:08 +0200 Subject: [PATCH 190/344] ObjectUtils.nullSafeEquals allows for JVM method inlining (through reducing its bytecode size) Issue: SPR-14349 (cherry picked from commit ca12e13) --- .../org/springframework/util/ObjectUtils.java | 77 +++++++++++-------- 1 file changed, 46 insertions(+), 31 deletions(-) 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 00d5916e4a..624d6d3567 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-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -277,14 +277,14 @@ public static Object[] toObjectArray(Object source) { //--------------------------------------------------------------------- /** - * Determine if the given objects are equal, returning {@code true} - * if both are {@code null} or {@code false} if only one is - * {@code null}. + * Determine if the given objects are equal, returning {@code true} if + * both are {@code null} or {@code false} if only one is {@code null}. *

    Compares arrays with {@code Arrays.equals}, performing an equality * check based on the array elements rather than the array reference. * @param o1 first Object to compare * @param o2 second Object to compare * @return whether the given objects are equal + * @see Object#equals(Object) * @see java.util.Arrays#equals */ public static boolean nullSafeEquals(Object o1, Object o2) { @@ -298,33 +298,47 @@ public static boolean nullSafeEquals(Object o1, Object o2) { return true; } if (o1.getClass().isArray() && o2.getClass().isArray()) { - if (o1 instanceof Object[] && o2 instanceof Object[]) { - return Arrays.equals((Object[]) o1, (Object[]) o2); - } - if (o1 instanceof boolean[] && o2 instanceof boolean[]) { - return Arrays.equals((boolean[]) o1, (boolean[]) o2); - } - if (o1 instanceof byte[] && o2 instanceof byte[]) { - return Arrays.equals((byte[]) o1, (byte[]) o2); - } - if (o1 instanceof char[] && o2 instanceof char[]) { - return Arrays.equals((char[]) o1, (char[]) o2); - } - if (o1 instanceof double[] && o2 instanceof double[]) { - return Arrays.equals((double[]) o1, (double[]) o2); - } - if (o1 instanceof float[] && o2 instanceof float[]) { - return Arrays.equals((float[]) o1, (float[]) o2); - } - if (o1 instanceof int[] && o2 instanceof int[]) { - return Arrays.equals((int[]) o1, (int[]) o2); - } - if (o1 instanceof long[] && o2 instanceof long[]) { - return Arrays.equals((long[]) o1, (long[]) o2); - } - if (o1 instanceof short[] && o2 instanceof short[]) { - return Arrays.equals((short[]) o1, (short[]) o2); - } + return arrayEquals(o1, o2); + } + return false; + } + + /** + * Compare the given arrays with {@code Arrays.equals}, performing an equality + * check based on the array elements rather than the array reference. + * @param o1 first array to compare + * @param o2 second array to compare + * @return whether the given objects are equal + * @see #nullSafeEquals(Object, Object) + * @see java.util.Arrays#equals + */ + private static boolean arrayEquals(Object o1, Object o2) { + if (o1 instanceof Object[] && o2 instanceof Object[]) { + return Arrays.equals((Object[]) o1, (Object[]) o2); + } + if (o1 instanceof boolean[] && o2 instanceof boolean[]) { + return Arrays.equals((boolean[]) o1, (boolean[]) o2); + } + if (o1 instanceof byte[] && o2 instanceof byte[]) { + return Arrays.equals((byte[]) o1, (byte[]) o2); + } + if (o1 instanceof char[] && o2 instanceof char[]) { + return Arrays.equals((char[]) o1, (char[]) o2); + } + if (o1 instanceof double[] && o2 instanceof double[]) { + return Arrays.equals((double[]) o1, (double[]) o2); + } + if (o1 instanceof float[] && o2 instanceof float[]) { + return Arrays.equals((float[]) o1, (float[]) o2); + } + if (o1 instanceof int[] && o2 instanceof int[]) { + return Arrays.equals((int[]) o1, (int[]) o2); + } + if (o1 instanceof long[] && o2 instanceof long[]) { + return Arrays.equals((long[]) o1, (long[]) o2); + } + if (o1 instanceof short[] && o2 instanceof short[]) { + return Arrays.equals((short[]) o1, (short[]) o2); } return false; } @@ -335,6 +349,7 @@ public static boolean nullSafeEquals(Object o1, Object o2) { * this method will delegate to any of the {@code nullSafeHashCode} * methods for arrays in this class. If the object is {@code null}, * this method returns 0. + * @see Object#hashCode() * @see #nullSafeHashCode(Object[]) * @see #nullSafeHashCode(boolean[]) * @see #nullSafeHashCode(byte[]) From 4c87167c9852fc9213c4d9d59d2d8c5e024dcc95 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Fri, 17 Jun 2016 16:54:54 -0400 Subject: [PATCH 191/344] Add heartbeat lock to SockJS server sessions Even before this change SockJS sessions always cancelled the heartbeat task first prior to sending messages. However when the heartbeat task is already in progress, cancellation of it is not enough and we must wait until the heartbeat is sent. This commit adds a heartbeat write lock which is obtained and held during the sending of a heartbeat. Now when sessions send a message they still cancel the heartbeat task but if that fails they also wait for the heartbeat write lock. Issue: SPR-14356 --- .../session/AbstractSockJsSession.java | 40 +++++++++++++++---- .../transport/session/SockJsSessionTests.java | 7 ++-- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractSockJsSession.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractSockJsSession.java index 90351ba51b..f2fbadeef5 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractSockJsSession.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractSockJsSession.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,6 +27,8 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -106,6 +108,8 @@ private enum State {NEW, OPEN, CLOSED} private volatile ScheduledFuture heartbeatTask; + private final Lock heartbeatLock = new ReentrantLock(); + private volatile boolean heartbeatDisabled; @@ -246,8 +250,15 @@ public void disableHeartbeat() { public void sendHeartbeat() throws SockJsTransportFailureException { if (isActive()) { - writeFrame(SockJsFrame.heartbeatFrame()); - scheduleHeartbeat(); + if (heartbeatLock.tryLock()) { + try { + writeFrame(SockJsFrame.heartbeatFrame()); + scheduleHeartbeat(); + } + finally { + heartbeatLock.unlock(); + } + } } } @@ -282,13 +293,26 @@ protected void cancelHeartbeat() { try { ScheduledFuture task = this.heartbeatTask; this.heartbeatTask = null; + if (task == null || task.isCancelled()) { + return; + } - if ((task != null) && !task.isDone()) { - if (logger.isTraceEnabled()) { - logger.trace("Cancelling heartbeat in session " + getId()); - } - task.cancel(false); + if (logger.isTraceEnabled()) { + logger.trace("Cancelling heartbeat in session " + getId()); + } + if (task.cancel(false)) { + return; + } + + if (logger.isTraceEnabled()) { + logger.trace("Failed to cancel heartbeat, acquiring heartbeat write lock."); + } + this.heartbeatLock.lock(); + + if (logger.isTraceEnabled()) { + logger.trace("Releasing heartbeat lock."); } + this.heartbeatLock.unlock(); } catch (Throwable ex) { logger.debug("Failure while cancelling heartbeat in session " + getId(), ex); diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/session/SockJsSessionTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/session/SockJsSessionTests.java index 9b699b4089..8741ef315e 100644 --- a/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/session/SockJsSessionTests.java +++ b/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/session/SockJsSessionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -287,11 +287,12 @@ public void scheduleAndCancelHeartbeat() throws Exception { verify(this.taskScheduler).schedule(any(Runnable.class), any(Date.class)); verifyNoMoreInteractions(this.taskScheduler); - given(task.isDone()).willReturn(false); + given(task.isCancelled()).willReturn(false); + given(task.cancel(false)).willReturn(true); this.session.cancelHeartbeat(); - verify(task).isDone(); + verify(task).isCancelled(); verify(task).cancel(false); verifyNoMoreInteractions(task); } From 269742589ba9ee123223c519482e51cb03956744 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 28 Jun 2016 15:39:19 -0400 Subject: [PATCH 192/344] Improve static resource path check --- .../web/servlet/resource/ResourceHttpRequestHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 f531989375..fd25c30687 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 @@ -371,7 +371,7 @@ protected boolean isInvalidPath(String path) { return true; } } - if (path.contains("../")) { + if (path.contains("..")) { path = StringUtils.cleanPath(path); if (path.contains("../")) { if (logger.isTraceEnabled()) { From f60c6c90c18f2d78ba4c55bb06bde65bc632665b Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 29 Jun 2016 14:46:15 +0200 Subject: [PATCH 193/344] Upgrade to CGLIB 3.2.4 Issue: SPR-14385 --- build.gradle | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index 0a2176ee7d..c32fcbcf10 100644 --- a/build.gradle +++ b/build.gradle @@ -55,6 +55,7 @@ configure(allprojects) { project -> ext.jrubyVersion = "1.7.25" // JRuby 9000 only supported through JSR-223 (StandardScriptFactory) ext.jtaVersion = "1.2" ext.junitVersion = "4.12" + ext.log4jVersion = "1.2.17" ext.nettyVersion = "4.0.37.Final" ext.okhttpVersion = "2.7.5" ext.openjpaVersion = "2.4.1" @@ -67,7 +68,7 @@ configure(allprojects) { project -> ext.testngVersion = "6.9.10" ext.tiles2Version = "2.2.2" ext.tiles3Version = "3.0.5" - ext.tomcatVersion = "8.0.35" + ext.tomcatVersion = "8.0.36" ext.tyrusVersion = "1.3.5" // constrained by WebLogic 12.1.3 support ext.undertowVersion = "1.3.23.Final" ext.xmlunitVersion = "1.6" @@ -283,7 +284,7 @@ project("spring-core") { // both into the spring-core jar. cglib 3.2 itself depends on asm 5.0 and is therefore // further transformed by the JarJar task to depend on org.springframework.asm; this // avoids including two different copies of asm unnecessarily. - def cglibVersion = "3.2.2" + def cglibVersion = "3.2.4" def objenesisVersion = "2.2" configurations { @@ -345,7 +346,7 @@ project("spring-core") { optional("commons-codec:commons-codec:1.10") optional("org.aspectj:aspectjweaver:${aspectjVersion}") optional("net.sf.jopt-simple:jopt-simple:4.9") - optional("log4j:log4j:1.2.17") + optional("log4j:log4j:${log4jVersion}") testCompile("org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}") testCompile("xmlunit:xmlunit:${xmlunitVersion}") testCompile("com.fasterxml.woodstox:woodstox-core:5.0.2") { @@ -376,7 +377,7 @@ project("spring-beans") { optional("javax.inject:javax.inject:1") optional("javax.el:javax.el-api:2.2.5") optional("org.yaml:snakeyaml:${snakeyamlVersion}") - testCompile("log4j:log4j:1.2.17") + testCompile("log4j:log4j:${log4jVersion}") testCompile("org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}") } } @@ -523,7 +524,7 @@ project("spring-messaging") { testCompile("org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}") testCompile("io.netty:netty-all:${nettyVersion}") testCompile("commons-dbcp:commons-dbcp:1.4") - testCompile("log4j:log4j:1.2.17") + testCompile("log4j:log4j:${log4jVersion}") testCompile("org.slf4j:slf4j-jcl:${slf4jVersion}") testCompile("xmlunit:xmlunit:${xmlunitVersion}") } @@ -709,7 +710,7 @@ project("spring-web") { optional("org.eclipse.jetty:jetty-server:${jettyVersion}") { exclude group: "javax.servlet", module: "javax.servlet-api" } - optional("log4j:log4j:1.2.17") + optional("log4j:log4j:${log4jVersion}") optional("com.google.protobuf:protobuf-java:2.6.1") optional("com.googlecode.protobuf-java-format:protobuf-java-format:1.2") optional("javax.mail:javax.mail-api:${javamailVersion}") @@ -973,7 +974,7 @@ project("spring-websocket") { testCompile("org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}") testCompile("io.projectreactor:reactor-net:${reactorVersion}") testCompile("io.netty:netty-all:${nettyVersion}") - testCompile("log4j:log4j:1.2.17") + testCompile("log4j:log4j:${log4jVersion}") testCompile("org.slf4j:slf4j-jcl:${slf4jVersion}") } } @@ -1031,6 +1032,7 @@ project("spring-test") { testCompile("org.slf4j:slf4j-jcl:${slf4jVersion}") testCompile("org.apache.httpcomponents:httpclient:${httpclientVersion}") testCompile("javax.cache:cache-api:1.0.0") + testRuntime("log4j:log4j:${log4jVersion}") testRuntime("org.ehcache:ehcache:${ehcache3Version}") testRuntime("org.terracotta:management-model:2.0.0") } From f19599884dfb6668157c80be815fbfe13d552d20 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sun, 26 Jun 2016 00:13:53 +0200 Subject: [PATCH 194/344] Fixed ambiguous sentence in conditional cache documentation Issue: SPR-14399 (cherry picked from commit 4102c62) --- src/asciidoc/integration.adoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/asciidoc/integration.adoc b/src/asciidoc/integration.adoc index 532852bcee..43903b45aa 100644 --- a/src/asciidoc/integration.adoc +++ b/src/asciidoc/integration.adoc @@ -8400,9 +8400,9 @@ Sometimes, a method might not be suitable for caching all the time (for example, might depend on the given arguments). The cache annotations support such functionality through the `condition` parameter which takes a `SpEL` expression that is evaluated to either `true` or `false`. If `true`, the method is cached - if not, it behaves as if the -method is not cached, that is executed every since time no matter what values are in the -cache or what arguments are used. A quick example - the following method will be cached -only if the argument `name` has a length shorter than 32: +method is not cached, that is executed every time no matter what values are in the cache +or what arguments are used. A quick example - the following method will be cached only +if the argument `name` has a length shorter than 32: [source,java,indent=0] [subs="verbatim,quotes"] From 0d3a22c4877f189b49bcf87b7121d514efd6e885 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 22 Jun 2016 14:41:20 +0200 Subject: [PATCH 195/344] Avoid potential deadlock in AbstractBeanFactoryPointcutAdvisor Issue: SPR-14388 (cherry picked from commit fd9d518) --- .../AbstractBeanFactoryPointcutAdvisor.java | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/spring-aop/src/main/java/org/springframework/aop/support/AbstractBeanFactoryPointcutAdvisor.java b/spring-aop/src/main/java/org/springframework/aop/support/AbstractBeanFactoryPointcutAdvisor.java index e46ee28c26..2c2eff5feb 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/AbstractBeanFactoryPointcutAdvisor.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/AbstractBeanFactoryPointcutAdvisor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.util.Assert; /** @@ -72,8 +73,23 @@ public String getAdviceBeanName() { @Override public void setBeanFactory(BeanFactory beanFactory) { this.beanFactory = beanFactory; + resetAdviceMonitor(); } + private void resetAdviceMonitor() { + if (this.beanFactory instanceof ConfigurableBeanFactory) { + this.adviceMonitor = ((ConfigurableBeanFactory) this.beanFactory).getSingletonMutex(); + } + else { + this.adviceMonitor = new Object(); + } + } + + /** + * Specify a particular instance of the target advice directly, + * avoiding lazy resolution in {@link #getAdvice()}. + * @since 3.1 + */ public void setAdvice(Advice advice) { synchronized (this.adviceMonitor) { this.advice = advice; @@ -93,7 +109,15 @@ public Advice getAdvice() { @Override public String toString() { - return getClass().getName() + ": advice bean '" + getAdviceBeanName() + "'"; + StringBuilder sb = new StringBuilder(getClass().getName()); + sb.append(": advice "); + if (this.adviceBeanName != null) { + sb.append("bean '").append(this.adviceBeanName).append("'"); + } + else { + sb.append(this.advice); + } + return sb.toString(); } @@ -106,7 +130,7 @@ private void readObject(ObjectInputStream ois) throws IOException, ClassNotFound ois.defaultReadObject(); // Initialize transient fields. - this.adviceMonitor = new Object(); + resetAdviceMonitor(); } } From 07c9c55fd394e6996fab65f417ef61072b78b8cb Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 23 Jun 2016 17:32:30 +0200 Subject: [PATCH 196/344] PayloadArgumentResolver's MessageConversionException includes original payload type Issue: SPR-14394 (cherry picked from commit f5282bc) --- .../support/PayloadArgumentResolver.java | 19 ++++++++++++------- .../support/PayloadArgumentResolverTests.java | 4 ++-- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/PayloadArgumentResolver.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/PayloadArgumentResolver.java index 099fa1ae6f..d14a4878e8 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/PayloadArgumentResolver.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/PayloadArgumentResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -106,17 +106,22 @@ public Object resolveArgument(MethodParameter parameter, Message message) thr } Class targetClass = parameter.getParameterType(); - if (ClassUtils.isAssignable(targetClass, payload.getClass())) { + Class payloadClass = payload.getClass(); + if (ClassUtils.isAssignable(targetClass, payloadClass)) { validate(message, parameter, payload); return payload; } else { - payload = (this.converter instanceof SmartMessageConverter ? - ((SmartMessageConverter) this.converter).fromMessage(message, targetClass, parameter) : - this.converter.fromMessage(message, targetClass)); + if (this.converter instanceof SmartMessageConverter) { + SmartMessageConverter smartConverter = (SmartMessageConverter) this.converter; + payload = smartConverter.fromMessage(message, targetClass, parameter); + } + else { + payload = this.converter.fromMessage(message, targetClass); + } if (payload == null) { - throw new MessageConversionException(message, - "No converter found to convert to " + targetClass + ", message=" + message); + throw new MessageConversionException(message, "Cannot convert from [" + + payloadClass.getName() + "] to [" + targetClass.getName() + "] for " + message); } validate(message, parameter, payload); return payload; diff --git a/spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/PayloadArgumentResolverTests.java b/spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/PayloadArgumentResolverTests.java index ffc0954109..33a098221e 100644 --- a/spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/PayloadArgumentResolverTests.java +++ b/spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/PayloadArgumentResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -132,7 +132,7 @@ public void resolveNonConvertibleParam() throws Exception { Message notEmptyMessage = MessageBuilder.withPayload(123).build(); thrown.expect(MessageConversionException.class); - thrown.expectMessage("No converter found"); + thrown.expectMessage("Cannot convert"); this.resolver.resolveArgument(this.paramAnnotatedRequired, notEmptyMessage); } From 3564616b7f36c5a59f2175a5a1b834e53bf9880f Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Wed, 29 Jun 2016 09:21:59 +0200 Subject: [PATCH 197/344] Align caching AspectJ configuration The `CacheResolver` and `ErrorHandler` features introduced in 4.1 were not properly enabled in AspectJ mode. This commit adds more tests from the regular proxy-based mode and align the AspectJ caching configuration. Issue: SPR-14413 (cherry picked from commit 6cd85dd) --- .../aspectj/AspectJCachingConfiguration.java | 15 +- .../AspectJEnableCachingIsolatedTests.java | 285 ++++++++++++++++++ .../aspectj/AspectJEnableCachingTests.java | 92 ++++++ .../AnnotatedClassCacheableService.java | 6 +- .../cache/config/DefaultCacheableService.java | 5 +- .../cache/config/SomeCustomKeyGenerator.java | 4 +- 6 files changed, 395 insertions(+), 12 deletions(-) create mode 100644 spring-aspects/src/test/java/org/springframework/cache/aspectj/AspectJEnableCachingIsolatedTests.java create mode 100644 spring-aspects/src/test/java/org/springframework/cache/aspectj/AspectJEnableCachingTests.java diff --git a/spring-aspects/src/main/java/org/springframework/cache/aspectj/AspectJCachingConfiguration.java b/spring-aspects/src/main/java/org/springframework/cache/aspectj/AspectJCachingConfiguration.java index b1aa1791d1..9932c79d12 100644 --- a/spring-aspects/src/main/java/org/springframework/cache/aspectj/AspectJCachingConfiguration.java +++ b/spring-aspects/src/main/java/org/springframework/cache/aspectj/AspectJCachingConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,10 +24,11 @@ import org.springframework.context.annotation.Role; /** - * {@code @Configuration} class that registers the Spring infrastructure beans necessary - * to enable AspectJ-based annotation-driven cache management. + * {@code @Configuration} class that registers the Spring infrastructure beans + * necessary to enable AspectJ-based annotation-driven cache management. * * @author Chris Beams + * @author Stephane Nicoll * @since 3.1 * @see org.springframework.cache.annotation.EnableCaching * @see org.springframework.cache.annotation.CachingConfigurationSelector @@ -39,12 +40,18 @@ public class AspectJCachingConfiguration extends AbstractCachingConfiguration { @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public AnnotationCacheAspect cacheAspect() { AnnotationCacheAspect cacheAspect = AnnotationCacheAspect.aspectOf(); - if (this.cacheManager != null) { + if (this.cacheResolver != null) { + cacheAspect.setCacheResolver(this.cacheResolver); + } + else if (this.cacheManager != null) { cacheAspect.setCacheManager(this.cacheManager); } if (this.keyGenerator != null) { cacheAspect.setKeyGenerator(this.keyGenerator); } + if (this.errorHandler != null) { + cacheAspect.setErrorHandler(this.errorHandler); + } return cacheAspect; } diff --git a/spring-aspects/src/test/java/org/springframework/cache/aspectj/AspectJEnableCachingIsolatedTests.java b/spring-aspects/src/test/java/org/springframework/cache/aspectj/AspectJEnableCachingIsolatedTests.java new file mode 100644 index 0000000000..15680c9ac6 --- /dev/null +++ b/spring-aspects/src/test/java/org/springframework/cache/aspectj/AspectJEnableCachingIsolatedTests.java @@ -0,0 +1,285 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cache.aspectj; + +import org.junit.After; +import org.junit.Ignore; +import org.junit.Test; + +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.cache.CacheManager; +import org.springframework.cache.CacheTestUtils; +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cache.config.AnnotatedClassCacheableService; +import org.springframework.cache.config.CacheableService; +import org.springframework.cache.config.DefaultCacheableService; +import org.springframework.cache.config.SomeCustomKeyGenerator; +import org.springframework.cache.config.SomeKeyGenerator; +import org.springframework.cache.interceptor.CacheErrorHandler; +import org.springframework.cache.interceptor.CacheResolver; +import org.springframework.cache.interceptor.KeyGenerator; +import org.springframework.cache.interceptor.NamedCacheResolver; +import org.springframework.cache.interceptor.SimpleCacheErrorHandler; +import org.springframework.cache.interceptor.SimpleCacheResolver; +import org.springframework.cache.support.NoOpCacheManager; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.AdviceMode; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import static org.junit.Assert.*; + +/** + * @author Stephane Nicoll + */ +public class AspectJEnableCachingIsolatedTests { + + private ConfigurableApplicationContext ctx; + + + private void load(Class... config) { + this.ctx = new AnnotationConfigApplicationContext(config); + } + + @After + public void closeContext() { + if (this.ctx != null) { + this.ctx.close(); + } + } + + + @Test + public void testKeyStrategy() { + load(EnableCachingConfig.class); + AnnotationCacheAspect aspect = this.ctx.getBean(AnnotationCacheAspect.class); + assertSame(this.ctx.getBean("keyGenerator", KeyGenerator.class), aspect.getKeyGenerator()); + } + + @Test + public void testCacheErrorHandler() { + load(EnableCachingConfig.class); + AnnotationCacheAspect aspect = this.ctx.getBean(AnnotationCacheAspect.class); + assertSame(this.ctx.getBean("errorHandler", CacheErrorHandler.class), aspect.getErrorHandler()); + } + + + // --- local tests ------- + + @Test + public void singleCacheManagerBean() { + load(SingleCacheManagerConfig.class); + } + + @Test + public void multipleCacheManagerBeans() { + try { + load(MultiCacheManagerConfig.class); + } + catch (IllegalStateException ex) { + assertTrue(ex.getMessage().contains("bean of type CacheManager")); + } + } + + @Test + public void multipleCacheManagerBeans_implementsCachingConfigurer() { + load(MultiCacheManagerConfigurer.class); // does not throw + } + + @Test + public void multipleCachingConfigurers() { + try { + load(MultiCacheManagerConfigurer.class, EnableCachingConfig.class); + } + catch (BeanCreationException ex) { + Throwable root = ex.getRootCause(); + assertTrue(root instanceof IllegalStateException); + assertTrue(ex.getMessage().contains("implementations of CachingConfigurer")); + } + } + + @Test + public void noCacheManagerBeans() { + try { + load(EmptyConfig.class); + } + catch (IllegalStateException ex) { + assertTrue(ex.getMessage().contains("No bean of type CacheManager")); + } + } + + @Test + @Ignore("AspectJ has some sort of caching that makes this one fail") + public void emptyConfigSupport() { + load(EmptyConfigSupportConfig.class); + AnnotationCacheAspect aspect = this.ctx.getBean(AnnotationCacheAspect.class); + assertNotNull(aspect.getCacheResolver()); + assertEquals(SimpleCacheResolver.class, aspect.getCacheResolver().getClass()); + assertSame(this.ctx.getBean(CacheManager.class), + ((SimpleCacheResolver) aspect.getCacheResolver()).getCacheManager()); + } + + @Test + public void bothSetOnlyResolverIsUsed() { + load(FullCachingConfig.class); + + AnnotationCacheAspect aspect = this.ctx.getBean(AnnotationCacheAspect.class); + assertSame(this.ctx.getBean("cacheResolver"), aspect.getCacheResolver()); + assertSame(this.ctx.getBean("keyGenerator"), aspect.getKeyGenerator()); + } + + + @Configuration + @EnableCaching(mode = AdviceMode.ASPECTJ) + static class EnableCachingConfig extends CachingConfigurerSupport { + + @Override + @Bean + public CacheManager cacheManager() { + return CacheTestUtils.createSimpleCacheManager("testCache", "primary", "secondary"); + } + + @Bean + public CacheableService service() { + return new DefaultCacheableService(); + } + + @Bean + public CacheableService classService() { + return new AnnotatedClassCacheableService(); + } + + @Override + @Bean + public KeyGenerator keyGenerator() { + return new SomeKeyGenerator(); + } + + @Override + @Bean + public CacheErrorHandler errorHandler() { + return new SimpleCacheErrorHandler(); + } + + @Bean + public KeyGenerator customKeyGenerator() { + return new SomeCustomKeyGenerator(); + } + + @Bean + public CacheManager customCacheManager() { + return CacheTestUtils.createSimpleCacheManager("testCache"); + } + } + + + @Configuration + @EnableCaching(mode = AdviceMode.ASPECTJ) + static class EmptyConfig { + } + + + @Configuration + @EnableCaching(mode = AdviceMode.ASPECTJ) + static class SingleCacheManagerConfig { + + @Bean + public CacheManager cm1() { + return new NoOpCacheManager(); + } + } + + + @Configuration + @EnableCaching(mode = AdviceMode.ASPECTJ) + static class MultiCacheManagerConfig { + + @Bean + public CacheManager cm1() { + return new NoOpCacheManager(); + } + + @Bean + public CacheManager cm2() { + return new NoOpCacheManager(); + } + } + + + @Configuration + @EnableCaching(mode = AdviceMode.ASPECTJ) + static class MultiCacheManagerConfigurer extends CachingConfigurerSupport { + + @Bean + public CacheManager cm1() { + return new NoOpCacheManager(); + } + + @Bean + public CacheManager cm2() { + return new NoOpCacheManager(); + } + + @Override + public CacheManager cacheManager() { + return cm1(); + } + + @Override + public KeyGenerator keyGenerator() { + return null; + } + } + + + @Configuration + @EnableCaching(mode = AdviceMode.ASPECTJ) + static class EmptyConfigSupportConfig extends CachingConfigurerSupport { + + @Bean + public CacheManager cm() { + return new NoOpCacheManager(); + } + + } + + + @Configuration + @EnableCaching(mode = AdviceMode.ASPECTJ) + static class FullCachingConfig extends CachingConfigurerSupport { + + @Override + @Bean + public CacheManager cacheManager() { + return new NoOpCacheManager(); + } + + @Override + @Bean + public KeyGenerator keyGenerator() { + return new SomeKeyGenerator(); + } + + @Override + @Bean + public CacheResolver cacheResolver() { + return new NamedCacheResolver(cacheManager(), "foo"); + } + } +} diff --git a/spring-aspects/src/test/java/org/springframework/cache/aspectj/AspectJEnableCachingTests.java b/spring-aspects/src/test/java/org/springframework/cache/aspectj/AspectJEnableCachingTests.java new file mode 100644 index 0000000000..a8ae57d20a --- /dev/null +++ b/spring-aspects/src/test/java/org/springframework/cache/aspectj/AspectJEnableCachingTests.java @@ -0,0 +1,92 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cache.aspectj; + +import org.springframework.cache.CacheManager; +import org.springframework.cache.CacheTestUtils; +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cache.config.AbstractCacheAnnotationTests; +import org.springframework.cache.config.AnnotatedClassCacheableService; +import org.springframework.cache.config.CacheableService; +import org.springframework.cache.config.DefaultCacheableService; +import org.springframework.cache.config.SomeCustomKeyGenerator; +import org.springframework.cache.config.SomeKeyGenerator; +import org.springframework.cache.interceptor.CacheErrorHandler; +import org.springframework.cache.interceptor.KeyGenerator; +import org.springframework.cache.interceptor.SimpleCacheErrorHandler; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.AdviceMode; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author Stephane Nicoll + */ +public class AspectJEnableCachingTests extends AbstractCacheAnnotationTests { + + @Override + protected ConfigurableApplicationContext getApplicationContext() { + return new AnnotationConfigApplicationContext(EnableCachingConfig.class); + } + + + @Configuration + @EnableCaching(mode = AdviceMode.ASPECTJ) + static class EnableCachingConfig extends CachingConfigurerSupport { + + @Override + @Bean + public CacheManager cacheManager() { + return CacheTestUtils.createSimpleCacheManager("testCache", "primary", "secondary"); + } + + @Bean + public CacheableService service() { + return new DefaultCacheableService(); + } + + @Bean + public CacheableService classService() { + return new AnnotatedClassCacheableService(); + } + + @Override + @Bean + public KeyGenerator keyGenerator() { + return new SomeKeyGenerator(); + } + + @Override + @Bean + public CacheErrorHandler errorHandler() { + return new SimpleCacheErrorHandler(); + } + + @Bean + public KeyGenerator customKeyGenerator() { + return new SomeCustomKeyGenerator(); + } + + @Bean + public CacheManager customCacheManager() { + return CacheTestUtils.createSimpleCacheManager("testCache"); + } + } + +} diff --git a/spring-aspects/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java b/spring-aspects/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java index 224f7ffa95..cebb67883b 100644 --- a/spring-aspects/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java +++ b/spring-aspects/src/test/java/org/springframework/cache/config/AnnotatedClassCacheableService.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -177,7 +177,7 @@ public Object multiCache(Object arg1) { } @Override - @Caching(evict = { @CacheEvict("primary"), @CacheEvict(cacheNames = "secondary", key = "#p0") }) + @Caching(evict = { @CacheEvict("primary"), @CacheEvict(cacheNames = "secondary", key = "#a0"), @CacheEvict(cacheNames = "primary", key = "#p0 + 'A'") }) public Object multiEvict(Object arg1) { return counter.getAndIncrement(); } @@ -189,7 +189,7 @@ public Object multiCacheAndEvict(Object arg1) { } @Override - @Caching(cacheable = { @Cacheable(cacheNames = "primary", condition = "#p0 == 3") }, evict = { @CacheEvict("secondary") }) + @Caching(cacheable = { @Cacheable(cacheNames = "primary", condition = "#a0 == 3") }, evict = { @CacheEvict("secondary") }) public Object multiConditionalCacheAndEvict(Object arg1) { return counter.getAndIncrement(); } diff --git a/spring-aspects/src/test/java/org/springframework/cache/config/DefaultCacheableService.java b/spring-aspects/src/test/java/org/springframework/cache/config/DefaultCacheableService.java index c2c923f531..d539075e0d 100644 --- a/spring-aspects/src/test/java/org/springframework/cache/config/DefaultCacheableService.java +++ b/spring-aspects/src/test/java/org/springframework/cache/config/DefaultCacheableService.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -185,8 +185,7 @@ public Long multiCache(Object arg1) { } @Override -//FIXME @Caching(evict = { @CacheEvict("primary"), @CacheEvict(cacheNames = "secondary", key = "#p0"), @CacheEvict(cacheNames = "primary", key = "#p0 + 'A'") }) - @Caching(evict = { @CacheEvict("primary"), @CacheEvict(cacheNames = "secondary", key = "#p0") }) + @Caching(evict = { @CacheEvict("primary"), @CacheEvict(cacheNames = "secondary", key = "#p0"), @CacheEvict(cacheNames = "primary", key = "#p0 + 'A'") }) public Long multiEvict(Object arg1) { return counter.getAndIncrement(); } diff --git a/spring-aspects/src/test/java/org/springframework/cache/config/SomeCustomKeyGenerator.java b/spring-aspects/src/test/java/org/springframework/cache/config/SomeCustomKeyGenerator.java index 8db104f0cc..d817b205d3 100644 --- a/spring-aspects/src/test/java/org/springframework/cache/config/SomeCustomKeyGenerator.java +++ b/spring-aspects/src/test/java/org/springframework/cache/config/SomeCustomKeyGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ * * @author Stephane Nicoll */ -final class SomeCustomKeyGenerator implements KeyGenerator { +public final class SomeCustomKeyGenerator implements KeyGenerator { @Override public Object generate(Object target, Method method, Object... params) { From dd7ddc08ffd359e86cc9234057a7b93d3e12f4e7 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 29 Jun 2016 14:48:09 +0200 Subject: [PATCH 198/344] Polishing --- .../weaving/AspectJWeavingEnabler.java | 19 ++++-- .../springframework/core/MethodParameter.java | 66 ++++++++++--------- .../core/convert/ConversionService.java | 8 +-- .../HibernateTransactionManager.java | 6 +- .../web/servlet/HandlerInterceptor.java | 20 ++---- ...bstractHandlerMethodExceptionResolver.java | 10 ++- .../ExceptionHandlerExceptionResolver.java | 12 ++-- 7 files changed, 71 insertions(+), 70 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/weaving/AspectJWeavingEnabler.java b/spring-context/src/main/java/org/springframework/context/weaving/AspectJWeavingEnabler.java index ab78f5c35b..e93e500baf 100644 --- a/spring-context/src/main/java/org/springframework/context/weaving/AspectJWeavingEnabler.java +++ b/spring-context/src/main/java/org/springframework/context/weaving/AspectJWeavingEnabler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,6 @@ package org.springframework.context.weaving; - import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; import java.security.ProtectionDomain; @@ -44,12 +43,13 @@ public class AspectJWeavingEnabler implements BeanFactoryPostProcessor, BeanClassLoaderAware, LoadTimeWeaverAware, Ordered { + public static final String ASPECTJ_AOP_XML_RESOURCE = "META-INF/aop.xml"; + + private ClassLoader beanClassLoader; private LoadTimeWeaver loadTimeWeaver; - public static final String ASPECTJ_AOP_XML_RESOURCE = "META-INF/aop.xml"; - @Override public void setBeanClassLoader(ClassLoader classLoader) { @@ -71,6 +71,12 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) enableAspectJWeaving(this.loadTimeWeaver, this.beanClassLoader); } + + /** + * Enable AspectJ weaving with the given {@link LoadTimeWeaver}. + * @param weaverToUse the LoadTimeWeaver to apply to (or {@code null} for a default weaver) + * @param beanClassLoader the class loader to create a default weaver for (if necessary) + */ public static void enableAspectJWeaving(LoadTimeWeaver weaverToUse, ClassLoader beanClassLoader) { if (weaverToUse == null) { if (InstrumentationLoadTimeWeaver.isInstrumentationAvailable()) { @@ -80,8 +86,8 @@ public static void enableAspectJWeaving(LoadTimeWeaver weaverToUse, ClassLoader throw new IllegalStateException("No LoadTimeWeaver available"); } } - weaverToUse.addTransformer(new AspectJClassBypassingClassFileTransformer( - new ClassPreProcessorAgentAdapter())); + weaverToUse.addTransformer( + new AspectJClassBypassingClassFileTransformer(new ClassPreProcessorAgentAdapter())); } @@ -108,4 +114,5 @@ public byte[] transform(ClassLoader loader, String className, Class classBein return this.delegate.transform(loader, className, classBeingRedefined, protectionDomain, classfileBuffer); } } + } diff --git a/spring-core/src/main/java/org/springframework/core/MethodParameter.java b/spring-core/src/main/java/org/springframework/core/MethodParameter.java index 171575693a..e34b8d00b9 100644 --- a/spring-core/src/main/java/org/springframework/core/MethodParameter.java +++ b/spring-core/src/main/java/org/springframework/core/MethodParameter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,15 +29,13 @@ import org.springframework.util.Assert; /** - * Helper class that encapsulates the specification of a method parameter, i.e. - * a {@link Method} or {@link Constructor} plus a parameter index and a nested - * type index for a declared generic type. Useful as a specification object to - * pass along. + * Helper class that encapsulates the specification of a method parameter, i.e. a {@link Method} + * or {@link Constructor} plus a parameter index and a nested type index for a declared generic + * type. Useful as a specification object to pass along. * - *

    As of 4.2, there is a {@link org.springframework.core.annotation.SynthesizingMethodParameter - * SynthesizingMethodParameter} subclass available which synthesizes annotations - * with attribute aliases. That subclass is used for web and message endpoint - * processing, in particular. + *

    As of 4.2, there is a {@link org.springframework.core.annotation.SynthesizingMethodParameter} + * subclass available which synthesizes annotations with attribute aliases. That subclass is used + * for web and message endpoint processing, in particular. * * @author Juergen Hoeller * @author Rob Harrop @@ -167,7 +165,14 @@ public Constructor getConstructor() { } /** - * Returns the wrapped member. + * Return the class that declares the underlying Method or Constructor. + */ + public Class getDeclaringClass() { + return getMember().getDeclaringClass(); + } + + /** + * Return the wrapped member. * @return the Method or Constructor as Member */ public Member getMember() { @@ -183,7 +188,9 @@ public Member getMember() { } /** - * Returns the wrapped annotated element. + * Return the wrapped annotated element. + *

    Note: This method exposes the annotations declared on the method/constructor + * itself (i.e. at the method/constructor level, not at the parameter level). * @return the Method or Constructor as AnnotatedElement */ public AnnotatedElement getAnnotatedElement() { @@ -198,13 +205,6 @@ public AnnotatedElement getAnnotatedElement() { } } - /** - * Return the class that declares the underlying Method or Constructor. - */ - public Class getDeclaringClass() { - return getMember().getDeclaringClass(); - } - /** * Return the index of the method/constructor parameter. * @return the parameter index (-1 in case of the return type) @@ -338,8 +338,8 @@ public Type getGenericParameterType() { /** * Return the nested type of the method/constructor parameter. * @return the parameter type (never {@code null}) - * @see #getNestingLevel() * @since 3.1 + * @see #getNestingLevel() */ public Class getNestedParameterType() { if (this.nestingLevel > 1) { @@ -370,8 +370,8 @@ else if (type instanceof ParameterizedType) { /** * Return the nested generic type of the method/constructor parameter. * @return the parameter type (never {@code null}) - * @see #getNestingLevel() * @since 4.2 + * @see #getNestingLevel() */ public Type getNestedGenericParameterType() { if (this.nestingLevel > 1) { @@ -423,33 +423,37 @@ public Annotation[] getParameterAnnotations() { return this.parameterAnnotations; } + /** + * Return {@code true} if the parameter has at least one annotation, + * {@code false} if it has none. + * @see #getParameterAnnotations() + */ + public boolean hasParameterAnnotations() { + return (getParameterAnnotations().length != 0); + } + /** * Return the parameter annotation of the given type, if available. * @param annotationType the annotation type to look for * @return the annotation object, or {@code null} if not found */ @SuppressWarnings("unchecked") - public T getParameterAnnotation(Class annotationType) { + public A getParameterAnnotation(Class annotationType) { Annotation[] anns = getParameterAnnotations(); for (Annotation ann : anns) { if (annotationType.isInstance(ann)) { - return (T) ann; + return (A) ann; } } return null; } /** - * Return true if the parameter has at least one annotation, false if it has none. - */ - public boolean hasParameterAnnotations() { - return (getParameterAnnotations().length != 0); - } - - /** - * Return true if the parameter has the given annotation type, and false if it doesn't. + * Return whether the parameter is declared with the given annotation type. + * @param annotationType the annotation type to look for + * @see #getParameterAnnotation(Class) */ - public boolean hasParameterAnnotation(Class annotationType) { + public boolean hasParameterAnnotation(Class annotationType) { return (getParameterAnnotation(annotationType) != null); } diff --git a/spring-core/src/main/java/org/springframework/core/convert/ConversionService.java b/spring-core/src/main/java/org/springframework/core/convert/ConversionService.java index 75f9a276c7..382e5ad353 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/ConversionService.java +++ b/spring-core/src/main/java/org/springframework/core/convert/ConversionService.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -64,11 +64,11 @@ public interface ConversionService { /** * Convert the given {@code source} to the specified {@code targetType}. - * @param source the source object to convert (may be null) + * @param source the source object to convert (may be {@code null}) * @param targetType the target type to convert to (required) * @return the converted object, an instance of targetType * @throws ConversionException if a conversion exception occurred - * @throws IllegalArgumentException if targetType is null + * @throws IllegalArgumentException if targetType is {@code null} */ T convert(Object source, Class targetType); @@ -76,7 +76,7 @@ public interface ConversionService { * Convert the given {@code source} to the specified {@code targetType}. * The TypeDescriptors provide additional context about the source and target locations * where conversion will occur, often object fields or property locations. - * @param source the source object to convert (may be null) + * @param source the source object to convert (may be {@code null}) * @param sourceType context about the source type to convert from * (may be {@code null} if source is {@code null}) * @param targetType context about the target type to convert to (required) diff --git a/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateTransactionManager.java b/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateTransactionManager.java index 4067c176e1..53f6b01a0e 100644 --- a/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateTransactionManager.java +++ b/spring-orm-hibernate5/src/main/java/org/springframework/orm/hibernate5/HibernateTransactionManager.java @@ -69,9 +69,9 @@ * and services which use plain JDBC (without being aware of Hibernate)! * Application code needs to stick to the same simple Connection lookup pattern as * with {@link org.springframework.jdbc.datasource.DataSourceTransactionManager} - * (i.e. {@link DataSourceUtils#getConnection} + * (i.e. {@link org.springframework.jdbc.datasource.DataSourceUtils#getConnection} * or going through a - * {@link TransactionAwareDataSourceProxy}). + * {@link org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy}). * *

    Note: To be able to register a DataSource's Connection for plain JDBC code, * this instance needs to be aware of the DataSource ({@link #setDataSource}). @@ -235,7 +235,6 @@ public void setPrepareConnection(boolean prepareConnection) { *

    Default is "false". Turning this flag on enforces over-commit holdability on the * underlying JDBC Connection (if {@link #prepareConnection "prepareConnection"} is on) * and skips the disconnect-on-completion step. - * @since 4.2 * @see Connection#setHoldability * @see ResultSet#HOLD_CURSORS_OVER_COMMIT * @see #disconnectOnCompletion(Session) @@ -687,7 +686,6 @@ protected void doCleanupAfterCompletion(Object transaction) { *

    The default implementation simply calls {@link Session#disconnect()}. * Subclasses may override this with a no-op or with fine-tuned disconnection logic. * @param session the Hibernate Session to disconnect - * @since 4.2 * @see Session#disconnect() */ protected void disconnectOnCompletion(Session session) { diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/HandlerInterceptor.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/HandlerInterceptor.java index 2aa28d5dfb..6f8e803674 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/HandlerInterceptor.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/HandlerInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,8 +33,8 @@ * or common handler behavior like locale or theme changes. Its main purpose * is to allow for factoring out repetitive handler code. * - *

    In an async processing scenario, the handler may be executed in a separate - * thread while the main thread exits without rendering or invoking the + *

    In an asynchronous processing scenario, the handler may be executed in a + * separate thread while the main thread exits without rendering or invoking the * {@code postHandle} and {@code afterCompletion} callbacks. When concurrent * handler execution completes, the request is dispatched back in order to * proceed with rendering the model and all methods of this contract are invoked @@ -77,16 +77,13 @@ public interface HandlerInterceptor { /** * Intercept the execution of a handler. Called after HandlerMapping determined * an appropriate handler object, but before HandlerAdapter invokes the handler. - * *

    DispatcherServlet processes a handler in an execution chain, consisting * of any number of interceptors, with the handler itself at the end. * With this method, each interceptor can decide to abort the execution chain, * typically sending a HTTP error or writing a custom response. - * *

    Note: special considerations apply for asynchronous * request processing. For more details see * {@link org.springframework.web.servlet.AsyncHandlerInterceptor}. - * * @param request current HTTP request * @param response current HTTP response * @param handler chosen handler to execute, for type and/or instance evaluation @@ -102,19 +99,16 @@ boolean preHandle(HttpServletRequest request, HttpServletResponse response, Obje * Intercept the execution of a handler. Called after HandlerAdapter actually * invoked the handler, but before the DispatcherServlet renders the view. * Can expose additional model objects to the view via the given ModelAndView. - * *

    DispatcherServlet processes a handler in an execution chain, consisting * of any number of interceptors, with the handler itself at the end. * With this method, each interceptor can post-process an execution, * getting applied in inverse order of the execution chain. - * *

    Note: special considerations apply for asynchronous * request processing. For more details see * {@link org.springframework.web.servlet.AsyncHandlerInterceptor}. - * * @param request current HTTP request * @param response current HTTP response - * @param handler handler (or {@link HandlerMethod}) that started async + * @param handler handler (or {@link HandlerMethod}) that started asynchronous * execution, for type and/or instance examination * @param modelAndView the {@code ModelAndView} that the handler returned * (can also be {@code null}) @@ -127,21 +121,17 @@ void postHandle(HttpServletRequest request, HttpServletResponse response, Object * Callback after completion of request processing, that is, after rendering * the view. Will be called on any outcome of handler execution, thus allows * for proper resource cleanup. - * *

    Note: Will only be called if this interceptor's {@code preHandle} * method has successfully completed and returned {@code true}! - * *

    As with the {@code postHandle} method, the method will be invoked on each * interceptor in the chain in reverse order, so the first interceptor will be * the last to be invoked. - * *

    Note: special considerations apply for asynchronous * request processing. For more details see * {@link org.springframework.web.servlet.AsyncHandlerInterceptor}. - * * @param request current HTTP request * @param response current HTTP response - * @param handler handler (or {@link HandlerMethod}) that started async + * @param handler handler (or {@link HandlerMethod}) that started asynchronous * execution, for type and/or instance examination * @param ex exception thrown on handler execution, if any * @throws Exception in case of errors diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodExceptionResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodExceptionResolver.java index 5a4c7d9248..7f9bc06665 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodExceptionResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodExceptionResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,7 +34,7 @@ public abstract class AbstractHandlerMethodExceptionResolver extends AbstractHan /** * Checks if the handler is a {@link HandlerMethod} and then delegates to the - * base class implementation of {@link #shouldApplyTo(HttpServletRequest, Object)} + * base class implementation of {@code #shouldApplyTo(HttpServletRequest, Object)} * passing the bean of the {@code HandlerMethod}. Otherwise returns {@code false}. */ @Override @@ -54,8 +54,7 @@ else if (handler instanceof HandlerMethod) { @Override protected final ModelAndView doResolveException( - HttpServletRequest request, HttpServletResponse response, - Object handler, Exception ex) { + HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { return doResolveHandlerMethodException(request, response, (HandlerMethod) handler, ex); } @@ -75,7 +74,6 @@ protected final ModelAndView doResolveException( * @return a corresponding ModelAndView to forward to, or {@code null} for default processing */ protected abstract ModelAndView doResolveHandlerMethodException( - HttpServletRequest request, HttpServletResponse response, - HandlerMethod handlerMethod, Exception ex); + HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod, Exception ex); } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.java index c897a4196a..572f0b7c62 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -95,7 +95,7 @@ public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExce public ExceptionHandlerExceptionResolver() { StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(); - stringHttpMessageConverter.setWriteAcceptCharset(false); // See SPR-7316 + stringHttpMessageConverter.setWriteAcceptCharset(false); // see SPR-7316 this.messageConverters = new ArrayList>(); this.messageConverters.add(new ByteArrayHttpMessageConverter()); @@ -264,11 +264,15 @@ private void initExceptionHandlerAdviceCache() { ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(adviceBean.getBeanType()); if (resolver.hasExceptionMappings()) { this.exceptionHandlerAdviceCache.put(adviceBean, resolver); - logger.info("Detected @ExceptionHandler methods in " + adviceBean); + if (logger.isInfoEnabled()) { + logger.info("Detected @ExceptionHandler methods in " + adviceBean); + } } if (ResponseBodyAdvice.class.isAssignableFrom(adviceBean.getBeanType())) { this.responseBodyAdvice.add(adviceBean); - logger.info("Detected ResponseBodyAdvice implementation in " + adviceBean); + if (logger.isInfoEnabled()) { + logger.info("Detected ResponseBodyAdvice implementation in " + adviceBean); + } } } } From a63247b281a107085fc08a1b52ae52da8d0ffd7e Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 29 Jun 2016 16:07:03 +0200 Subject: [PATCH 199/344] Polishing --- .../web/servlet/AsyncHandlerInterceptor.java | 7 ++---- ...xceptionHandlerExceptionResolverTests.java | 24 +++++++++---------- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/AsyncHandlerInterceptor.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/AsyncHandlerInterceptor.java index 5284a2d172..22abd51f96 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/AsyncHandlerInterceptor.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/AsyncHandlerInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -53,7 +53,6 @@ * * @author Rossen Stoyanchev * @since 3.2 - * * @see org.springframework.web.context.request.async.WebAsyncManager * @see org.springframework.web.context.request.async.CallableProcessingInterceptor * @see org.springframework.web.context.request.async.DeferredResultProcessingInterceptor @@ -67,15 +66,13 @@ public interface AsyncHandlerInterceptor extends HandlerInterceptor { * avoid modifying them in ways that would conflict with the concurrent * execution of the handler. A typical use of this method would be to * clean up thread-local variables. - * * @param request the current request * @param response the current response * @param handler the handler (or {@link HandlerMethod}) that started async * execution, for type and/or instance examination * @throws Exception in case of errors */ - void afterConcurrentHandlingStarted( - HttpServletRequest request, HttpServletResponse response, Object handler) + void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolverTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolverTests.java index 8675990ae1..d64e82205f 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolverTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,7 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; import java.io.Writer; -import java.util.Arrays; +import java.util.Collections; import org.junit.Before; import org.junit.BeforeClass; @@ -68,10 +68,10 @@ public class ExceptionHandlerExceptionResolverTests { @BeforeClass public static void setupOnce() { - ExceptionHandlerExceptionResolver r = new ExceptionHandlerExceptionResolver(); - r.afterPropertiesSet(); - RESOLVER_COUNT = r.getArgumentResolvers().getResolvers().size(); - HANDLER_COUNT = r.getReturnValueHandlers().getHandlers().size(); + ExceptionHandlerExceptionResolver resolver = new ExceptionHandlerExceptionResolver(); + resolver.afterPropertiesSet(); + RESOLVER_COUNT = resolver.getArgumentResolvers().getResolvers().size(); + HANDLER_COUNT = resolver.getReturnValueHandlers().getHandlers().size(); } @Before @@ -93,7 +93,7 @@ public void nullHandler() { @Test public void setCustomArgumentResolvers() throws Exception { HandlerMethodArgumentResolver resolver = new ServletRequestMethodArgumentResolver(); - this.resolver.setCustomArgumentResolvers(Arrays.asList(resolver)); + this.resolver.setCustomArgumentResolvers(Collections.singletonList(resolver)); this.resolver.afterPropertiesSet(); assertTrue(this.resolver.getArgumentResolvers().getResolvers().contains(resolver)); @@ -103,7 +103,7 @@ public void setCustomArgumentResolvers() throws Exception { @Test public void setArgumentResolvers() throws Exception { HandlerMethodArgumentResolver resolver = new ServletRequestMethodArgumentResolver(); - this.resolver.setArgumentResolvers(Arrays.asList(resolver)); + this.resolver.setArgumentResolvers(Collections.singletonList(resolver)); this.resolver.afterPropertiesSet(); assertMethodProcessorCount(1, HANDLER_COUNT); @@ -112,7 +112,7 @@ public void setArgumentResolvers() throws Exception { @Test public void setCustomReturnValueHandlers() { HandlerMethodReturnValueHandler handler = new ViewNameMethodReturnValueHandler(); - this.resolver.setCustomReturnValueHandlers(Arrays.asList(handler)); + this.resolver.setCustomReturnValueHandlers(Collections.singletonList(handler)); this.resolver.afterPropertiesSet(); assertTrue(this.resolver.getReturnValueHandlers().getHandlers().contains(handler)); @@ -122,7 +122,7 @@ public void setCustomReturnValueHandlers() { @Test public void setReturnValueHandlers() { HandlerMethodReturnValueHandler handler = new ModelMethodProcessor(); - this.resolver.setReturnValueHandlers(Arrays.asList(handler)); + this.resolver.setReturnValueHandlers(Collections.singletonList(handler)); this.resolver.afterPropertiesSet(); assertMethodProcessorCount(RESOLVER_COUNT, 1); @@ -217,9 +217,7 @@ public void resolveExceptionGlobalHandlerOrdered() throws Exception { assertEquals("TestExceptionResolver: IllegalStateException", this.response.getContentAsString()); } - // SPR-12605 - - @Test + @Test // SPR-12605 public void resolveExceptionWithHandlerMethodArg() throws Exception { AnnotationConfigApplicationContext cxt = new AnnotationConfigApplicationContext(MyConfig.class); this.resolver.setApplicationContext(cxt); From 09c7194c781f0c2bdf4007c5ceafa4f93e877ed2 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 30 Jun 2016 14:03:28 +0200 Subject: [PATCH 200/344] Polishing (cherry picked from commit 232cfe5) --- .../org/springframework/beans/TypeConverterDelegate.java | 9 +++++---- .../org/springframework/core/convert/TypeDescriptor.java | 9 ++++----- .../springframework/util/DefaultPropertiesPersister.java | 6 +++--- .../org/springframework/util/PropertiesPersister.java | 8 +------- 4 files changed, 13 insertions(+), 19 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java b/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java index 9b1eda75ee..dd63e8e9c6 100644 --- a/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java +++ b/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -183,9 +183,10 @@ public T convertIfNecessary(String propertyName, Object oldValue, Object new // Value not of required type? if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) { - if (requiredType != null && Collection.class.isAssignableFrom(requiredType) && convertedValue instanceof String) { - TypeDescriptor elementType = typeDescriptor.getElementTypeDescriptor(); - if (elementType != null && Enum.class.isAssignableFrom(elementType.getType())) { + if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType) && + convertedValue instanceof String) { + TypeDescriptor elementTypeDesc = typeDescriptor.getElementTypeDescriptor(); + if (elementTypeDesc != null && Enum.class.isAssignableFrom(elementTypeDesc.getType())) { convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue); } } diff --git a/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java b/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java index b55df08308..dde18ce805 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java +++ b/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -145,10 +145,9 @@ public Class getObjectType() { /** * The type of the backing class, method parameter, field, or property * described by this TypeDescriptor. - *

    Returns primitive types as-is. - *

    See {@link #getObjectType()} for a variation of this operation that - * resolves primitive types to their corresponding Object types if necessary. - * @return the type, or {@code null} if it cannot be determined + *

    Returns primitive types as-is. See {@link #getObjectType()} for a + * variation of this operation that resolves primitive types to their + * corresponding Object types if necessary. * @see #getObjectType() */ public Class getType() { diff --git a/spring-core/src/main/java/org/springframework/util/DefaultPropertiesPersister.java b/spring-core/src/main/java/org/springframework/util/DefaultPropertiesPersister.java index b4be1bb2bb..10e0bb23e7 100644 --- a/spring-core/src/main/java/org/springframework/util/DefaultPropertiesPersister.java +++ b/spring-core/src/main/java/org/springframework/util/DefaultPropertiesPersister.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,12 +29,12 @@ * *

    Allows for reading from any Reader and writing to any Writer, for example * to specify a charset for a properties file. This is a capability that standard - * {@code java.util.Properties} unfortunately lacked up until JDK 1.5: + * {@code java.util.Properties} unfortunately lacked up until JDK 5: * You were only able to load files using the ISO-8859-1 charset there. * *

    Loading from and storing to a stream delegates to {@code Properties.load} * and {@code Properties.store}, respectively, to be fully compatible with - * the Unicode conversion as implemented by the JDK Properties class. As of JDK 1.6, + * the Unicode conversion as implemented by the JDK Properties class. As of JDK 6, * {@code Properties.load/store} will also be used for readers/writers, * effectively turning this class into a plain backwards compatibility adapter. * diff --git a/spring-core/src/main/java/org/springframework/util/PropertiesPersister.java b/spring-core/src/main/java/org/springframework/util/PropertiesPersister.java index 1e4c3c9711..c2b765404d 100644 --- a/spring-core/src/main/java/org/springframework/util/PropertiesPersister.java +++ b/spring-core/src/main/java/org/springframework/util/PropertiesPersister.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,10 +32,6 @@ * but allowing for reading from any Reader and writing to any Writer * (which allows to specify an encoding for a properties file). * - *

    As of Spring 1.2.2, this interface also supports properties XML files, - * through the {@code loadFromXml} and {@code storeToXml} methods. - * The default implementations delegate to JDK 1.5's corresponding methods. - * * @author Juergen Hoeller * @since 10.03.2004 * @see DefaultPropertiesPersister @@ -62,7 +58,6 @@ public interface PropertiesPersister { */ void load(Properties props, Reader reader) throws IOException; - /** * Write the contents of the given Properties object to the * given OutputStream. @@ -84,7 +79,6 @@ public interface PropertiesPersister { */ void store(Properties props, Writer writer, String header) throws IOException; - /** * Load properties from the given XML InputStream into the * given Properties object. From 833ed7168e04149486fcc31d3948b7376ba8ced1 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 30 Jun 2016 21:39:06 +0200 Subject: [PATCH 201/344] Add missing package-info files for common packages Issue: SPR-14420 (cherry picked from commit 66ec1c1) --- .../validation/beanvalidation/package-info.java | 2 +- .../main/java/org/springframework/cglib/package-info.java | 4 ++-- .../org/springframework/expression/common/package-info.java | 4 ++++ .../java/org/springframework/expression/package-info.java | 4 ++++ .../springframework/expression/spel/ast/package-info.java | 4 ++++ .../org/springframework/expression/spel/package-info.java | 4 ++++ .../expression/spel/standard/package-info.java | 4 ++++ .../expression/spel/support/package-info.java | 4 ++++ .../org/springframework/test/context/cache/package-info.java | 4 ++++ .../springframework/transaction/annotation/package-info.java | 2 +- .../org/springframework/transaction/event/package-info.java | 4 ++++ .../springframework/http/converter/json/package-info.java | 2 +- .../http/converter/protobuf/package-info.java | 5 +++++ .../springframework/http/converter/support/package-info.java | 4 ++++ .../org/springframework/http/converter/xml/package-info.java | 2 +- 15 files changed, 47 insertions(+), 6 deletions(-) create mode 100644 spring-expression/src/main/java/org/springframework/expression/common/package-info.java create mode 100644 spring-expression/src/main/java/org/springframework/expression/package-info.java create mode 100644 spring-expression/src/main/java/org/springframework/expression/spel/ast/package-info.java create mode 100644 spring-expression/src/main/java/org/springframework/expression/spel/package-info.java create mode 100644 spring-expression/src/main/java/org/springframework/expression/spel/standard/package-info.java create mode 100644 spring-expression/src/main/java/org/springframework/expression/spel/support/package-info.java create mode 100644 spring-test/src/main/java/org/springframework/test/context/cache/package-info.java create mode 100644 spring-tx/src/main/java/org/springframework/transaction/event/package-info.java create mode 100644 spring-web/src/main/java/org/springframework/http/converter/protobuf/package-info.java create mode 100644 spring-web/src/main/java/org/springframework/http/converter/support/package-info.java diff --git a/spring-context/src/main/java/org/springframework/validation/beanvalidation/package-info.java b/spring-context/src/main/java/org/springframework/validation/beanvalidation/package-info.java index 7961d3ab94..a8bea39a03 100644 --- a/spring-context/src/main/java/org/springframework/validation/beanvalidation/package-info.java +++ b/spring-context/src/main/java/org/springframework/validation/beanvalidation/package-info.java @@ -1,6 +1,6 @@ /** * Support classes for integrating a JSR-303 Bean Validation provider - * (such as Hibernate Validator 4.0) into a Spring ApplicationContext + * (such as Hibernate Validator) into a Spring ApplicationContext * and in particular with Spring's data binding and validation APIs. * *

    The central class is {@link 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 d799a84f32..78ef762bee 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,6 +1,6 @@ /** * Spring's repackaging of - * net.sf.cglib 3.1 + * net.sf.cglib 3.2 * (for internal use only). * *

    This repackaging technique avoids any potential conflicts with @@ -9,7 +9,7 @@ * *

    As this repackaging happens at the class file level, sources * and javadocs are not available here. See the original - * CGLIB 3.1 javadocs + * CGLIB 3.2 javadocs * for details when working with these classes. */ package org.springframework.cglib; diff --git a/spring-expression/src/main/java/org/springframework/expression/common/package-info.java b/spring-expression/src/main/java/org/springframework/expression/common/package-info.java new file mode 100644 index 0000000000..7cf633a44b --- /dev/null +++ b/spring-expression/src/main/java/org/springframework/expression/common/package-info.java @@ -0,0 +1,4 @@ +/** + * Common utility classes behind the Spring Expression Language. + */ +package org.springframework.expression.common; diff --git a/spring-expression/src/main/java/org/springframework/expression/package-info.java b/spring-expression/src/main/java/org/springframework/expression/package-info.java new file mode 100644 index 0000000000..e7631cc36b --- /dev/null +++ b/spring-expression/src/main/java/org/springframework/expression/package-info.java @@ -0,0 +1,4 @@ +/** + * Core abstractions behind the Spring Expression Language. + */ +package org.springframework.expression; diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/package-info.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/package-info.java new file mode 100644 index 0000000000..d601d80eee --- /dev/null +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/package-info.java @@ -0,0 +1,4 @@ +/** + * SpEL's abstract syntax tree. + */ +package org.springframework.expression.spel.ast; diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/package-info.java b/spring-expression/src/main/java/org/springframework/expression/spel/package-info.java new file mode 100644 index 0000000000..89c07e63b1 --- /dev/null +++ b/spring-expression/src/main/java/org/springframework/expression/spel/package-info.java @@ -0,0 +1,4 @@ +/** + * SpEL's central implementation package. + */ +package org.springframework.expression.spel; diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/standard/package-info.java b/spring-expression/src/main/java/org/springframework/expression/spel/standard/package-info.java new file mode 100644 index 0000000000..a7f7fd14f5 --- /dev/null +++ b/spring-expression/src/main/java/org/springframework/expression/spel/standard/package-info.java @@ -0,0 +1,4 @@ +/** + * SpEL's standard parser implementation. + */ +package org.springframework.expression.spel.standard; diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/package-info.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/package-info.java new file mode 100644 index 0000000000..d5503bc870 --- /dev/null +++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/package-info.java @@ -0,0 +1,4 @@ +/** + * SpEL's default implementations for various core abstractions. + */ +package org.springframework.expression.spel.support; diff --git a/spring-test/src/main/java/org/springframework/test/context/cache/package-info.java b/spring-test/src/main/java/org/springframework/test/context/cache/package-info.java new file mode 100644 index 0000000000..85fa178450 --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/context/cache/package-info.java @@ -0,0 +1,4 @@ +/** + * Support for context caching within the Spring TestContext Framework. + */ +package org.springframework.test.context.cache; diff --git a/spring-tx/src/main/java/org/springframework/transaction/annotation/package-info.java b/spring-tx/src/main/java/org/springframework/transaction/annotation/package-info.java index 7388259a91..152819a3b8 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/annotation/package-info.java +++ b/spring-tx/src/main/java/org/springframework/transaction/annotation/package-info.java @@ -1,5 +1,5 @@ /** - * Java 5 annotation for transaction demarcation. + * Spring's support for annotation-based transaction demarcation. * Hooked into Spring's transaction interception infrastructure * via a special TransactionAttributeSource implementation. */ diff --git a/spring-tx/src/main/java/org/springframework/transaction/event/package-info.java b/spring-tx/src/main/java/org/springframework/transaction/event/package-info.java new file mode 100644 index 0000000000..037c8e0324 --- /dev/null +++ b/spring-tx/src/main/java/org/springframework/transaction/event/package-info.java @@ -0,0 +1,4 @@ +/** + * Spring's support for listening to transaction events. + */ +package org.springframework.transaction.event; diff --git a/spring-web/src/main/java/org/springframework/http/converter/json/package-info.java b/spring-web/src/main/java/org/springframework/http/converter/json/package-info.java index c786a6d244..886aa94408 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/json/package-info.java +++ b/spring-web/src/main/java/org/springframework/http/converter/json/package-info.java @@ -1,4 +1,4 @@ /** - * Provides an HttpMessageConverter implementations for handling JSON. + * Provides HttpMessageConverter implementations for handling JSON. */ package org.springframework.http.converter.json; diff --git a/spring-web/src/main/java/org/springframework/http/converter/protobuf/package-info.java b/spring-web/src/main/java/org/springframework/http/converter/protobuf/package-info.java new file mode 100644 index 0000000000..3c73a272c5 --- /dev/null +++ b/spring-web/src/main/java/org/springframework/http/converter/protobuf/package-info.java @@ -0,0 +1,5 @@ +/** + * Provides an HttpMessageConverter implementation for handling + * Google Protocol Buffers. + */ +package org.springframework.http.converter.protobuf; diff --git a/spring-web/src/main/java/org/springframework/http/converter/support/package-info.java b/spring-web/src/main/java/org/springframework/http/converter/support/package-info.java new file mode 100644 index 0000000000..eaaf8204b4 --- /dev/null +++ b/spring-web/src/main/java/org/springframework/http/converter/support/package-info.java @@ -0,0 +1,4 @@ +/** + * Provides a comprehensive HttpMessageConverter variant for form handling. + */ +package org.springframework.http.converter.support; diff --git a/spring-web/src/main/java/org/springframework/http/converter/xml/package-info.java b/spring-web/src/main/java/org/springframework/http/converter/xml/package-info.java index ef2c3c95ba..a889b35119 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/xml/package-info.java +++ b/spring-web/src/main/java/org/springframework/http/converter/xml/package-info.java @@ -1,4 +1,4 @@ /** - * Provides an HttpMessageConverter implementations for handling XML. + * Provides HttpMessageConverter implementations for handling XML. */ package org.springframework.http.converter.xml; From db4882cea44c8df37a688b3855c199d25e65dd09 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 30 Jun 2016 21:45:55 +0200 Subject: [PATCH 202/344] Polishing --- .../test/context/cache/ContextCache.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/context/cache/ContextCache.java b/spring-test/src/main/java/org/springframework/test/context/cache/ContextCache.java index 895109fa59..27902a6d66 100644 --- a/spring-test/src/main/java/org/springframework/test/context/cache/ContextCache.java +++ b/spring-test/src/main/java/org/springframework/test/context/cache/ContextCache.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,8 +22,8 @@ /** * {@code ContextCache} defines the SPI for caching Spring - * {@link ApplicationContext ApplicationContexts} within the Spring - * TestContext Framework. + * {@link ApplicationContext ApplicationContexts} within the + * Spring TestContext Framework. * *

    A {@code ContextCache} maintains a cache of {@code ApplicationContexts} * keyed by {@link MergedContextConfiguration} instances. @@ -47,7 +47,7 @@ public interface ContextCache { * The name of the logging category used for reporting {@code ContextCache} * statistics. */ - public static final String CONTEXT_CACHE_LOGGING_CATEGORY = "org.springframework.test.context.cache"; + String CONTEXT_CACHE_LOGGING_CATEGORY = "org.springframework.test.context.cache"; /** @@ -59,8 +59,8 @@ public interface ContextCache { /** * Obtain a cached {@code ApplicationContext} for the given key. - *

    The {@link #getHitCount() hit} and {@link #getMissCount() miss} counts - * must be updated accordingly. + *

    The {@linkplain #getHitCount() hit} and {@linkplain #getMissCount() miss} + * counts must be updated accordingly. * @param key the context key (never {@code null}) * @return the corresponding {@code ApplicationContext} instance, or {@code null} * if not found in the cache @@ -70,7 +70,7 @@ public interface ContextCache { /** * Explicitly add an {@code ApplicationContext} instance to the cache - * under the given key. + * under the given key, potentially honoring a custom eviction policy. * @param key the context key (never {@code null}) * @param context the {@code ApplicationContext} instance (never {@code null}) */ @@ -80,9 +80,10 @@ public interface ContextCache { * Remove the context with the given key from the cache and explicitly * {@linkplain org.springframework.context.ConfigurableApplicationContext#close() close} * it if it is an instance of {@code ConfigurableApplicationContext}. - *

    Generally speaking, this method should be called if the state of - * a singleton bean has been modified, potentially affecting future - * interaction with the context. + *

    Generally speaking, this method should be called to properly evict + * a context from the cache (e.g., due to a custom eviction policy) or if + * the state of a singleton bean has been modified, potentially affecting + * future interaction with the context. *

    In addition, the semantics of the supplied {@code HierarchyMode} must * be honored. See the Javadoc for {@link HierarchyMode} for details. * @param key the context key; never {@code null} From d73f91eaf07474ed76f538ecf74c38d93d46da30 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Fri, 1 Jul 2016 14:26:48 +0200 Subject: [PATCH 203/344] Polish Closes gh-1097 (cherry picked from commit 037746d) --- .../annotation/aspectj/SpringConfiguredConfiguration.java | 4 ++-- .../java/org/springframework/context/annotation/Import.java | 4 ++-- .../annotation/ScheduledAnnotationBeanPostProcessor.java | 2 +- .../scheduling/annotation/SchedulingConfiguration.java | 6 +++--- .../scripting/groovy/GroovyScriptFactory.java | 2 +- .../transaction/annotation/EnableTransactionManagement.java | 4 ++-- .../annotation/TransactionManagementConfigurer.java | 4 ++-- .../annotation/RequestParamMethodArgumentResolver.java | 4 ++-- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/spring-aspects/src/main/java/org/springframework/context/annotation/aspectj/SpringConfiguredConfiguration.java b/spring-aspects/src/main/java/org/springframework/context/annotation/aspectj/SpringConfiguredConfiguration.java index 747f280f27..1d141b2ee9 100644 --- a/spring-aspects/src/main/java/org/springframework/context/annotation/aspectj/SpringConfiguredConfiguration.java +++ b/spring-aspects/src/main/java/org/springframework/context/annotation/aspectj/SpringConfiguredConfiguration.java @@ -29,8 +29,8 @@ * Configurable}. * *

    This configuration class is automatically imported when using the - * @{@link EnableSpringConfigured} annotation. See {@code @EnableSpringConfigured}'s - * javadoc for complete usage details. + * {@link EnableSpringConfigured @EnableSpringConfigured} annotation. See + * {@code @EnableSpringConfigured}'s javadoc for complete usage details. * * @author Chris Beams * @since 3.1 diff --git a/spring-context/src/main/java/org/springframework/context/annotation/Import.java b/spring-context/src/main/java/org/springframework/context/annotation/Import.java index e70c68ef42..6c9fa7cd91 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/Import.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/Import.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -54,7 +54,7 @@ public @interface Import { /** - * @{@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar} + * {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar} * or regular component classes to import. */ Class[] value(); diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java index a0ac5d481b..bacd01b500 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java @@ -64,7 +64,7 @@ * *

    This post-processor is automatically registered by Spring's * {@code } XML element, and also by the - * @{@link EnableScheduling} annotation. + * {@link EnableScheduling @EnableScheduling} annotation. * *

    Autodetects any {@link SchedulingConfigurer} instances in the container, * allowing for customization of the scheduler to be used or for fine-grained diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/SchedulingConfiguration.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/SchedulingConfiguration.java index b7c40b1dc9..78d840499c 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/SchedulingConfiguration.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/SchedulingConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,8 +27,8 @@ * bean capable of processing Spring's @{@link Scheduled} annotation. * *

    This configuration class is automatically imported when using the - * @{@link EnableScheduling} annotation. See {@code @EnableScheduling}'s javadoc - * for complete usage details. + * {@link EnableScheduling @EnableScheduling} annotation. See + * {@code @EnableScheduling}'s javadoc for complete usage details. * * @author Chris Beams * @since 3.1 diff --git a/spring-context/src/main/java/org/springframework/scripting/groovy/GroovyScriptFactory.java b/spring-context/src/main/java/org/springframework/scripting/groovy/GroovyScriptFactory.java index c082ca4ce1..439f3c1e0e 100644 --- a/spring-context/src/main/java/org/springframework/scripting/groovy/GroovyScriptFactory.java +++ b/spring-context/src/main/java/org/springframework/scripting/groovy/GroovyScriptFactory.java @@ -40,7 +40,7 @@ * *

    Typically used in combination with a * {@link org.springframework.scripting.support.ScriptFactoryPostProcessor}; - * see the latter's javadoc} for a configuration example. + * see the latter's javadoc for a configuration example. * *

    Note: Spring 4.0 supports Groovy 1.8 and higher. * diff --git a/spring-tx/src/main/java/org/springframework/transaction/annotation/EnableTransactionManagement.java b/spring-tx/src/main/java/org/springframework/transaction/annotation/EnableTransactionManagement.java index 5e3e229463..f0d8d733f7 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/annotation/EnableTransactionManagement.java +++ b/spring-tx/src/main/java/org/springframework/transaction/annotation/EnableTransactionManagement.java @@ -29,8 +29,8 @@ /** * Enables Spring's annotation-driven transaction management capability, similar to * the support found in Spring's {@code } XML namespace. To be used on - * @{@link org.springframework.context.annotation.Configuration Configuration} classes - * as follows: + * {@link org.springframework.context.annotation.Configuration @Configuration} + * classes as follows: * *

      * @Configuration
    diff --git a/spring-tx/src/main/java/org/springframework/transaction/annotation/TransactionManagementConfigurer.java b/spring-tx/src/main/java/org/springframework/transaction/annotation/TransactionManagementConfigurer.java
    index 1c7d432960..ac95aed8f5 100644
    --- a/spring-tx/src/main/java/org/springframework/transaction/annotation/TransactionManagementConfigurer.java
    +++ b/spring-tx/src/main/java/org/springframework/transaction/annotation/TransactionManagementConfigurer.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright 2002-2015 the original author or authors.
    + * Copyright 2002-2016 the original author or authors.
      *
      * Licensed under the Apache License, Version 2.0 (the "License");
      * you may not use this file except in compliance with the License.
    @@ -32,7 +32,7 @@
      * 

    Note that in by-type lookup disambiguation cases, an alternative approach to * implementing this interface is to simply mark one of the offending * {@code PlatformTransactionManager} {@code @Bean} methods as - * @{@link org.springframework.context.annotation.Primary Primary}. + * {@link org.springframework.context.annotation.Primary @Primary}. * This is even generally preferred since it doesn't lead to early initialization * of the {@code PlatformTransactionManager} bean. * 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 aba8999a18..c481b152f6 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 @@ -54,8 +54,8 @@ * abstraction, and arguments of type {@code javax.servlet.http.Part} in conjunction * with Servlet 3.0 multipart requests. This resolver can also be created in default * resolution mode in which simple types (int, long, etc.) not annotated with - * @{@link RequestParam} are also treated as request parameters with the - * parameter name derived from the argument name. + * {@link RequestParam @RequestParam} are also treated as request parameters with + * the parameter name derived from the argument name. * *

    If the method parameter type is {@link Map}, the name specified in the * annotation is used to resolve the request parameter String value. The value is From 3dad61fbbf926945056b4091600be70f242bdc87 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 1 Jul 2016 15:40:45 +0200 Subject: [PATCH 204/344] Backported applicable beans chapter revisions --- src/asciidoc/core-beans.adoc | 86 +++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 41 deletions(-) diff --git a/src/asciidoc/core-beans.adoc b/src/asciidoc/core-beans.adoc index 3a78701b82..89d85349d2 100644 --- a/src/asciidoc/core-beans.adoc +++ b/src/asciidoc/core-beans.adoc @@ -2274,7 +2274,7 @@ the __scope__ of the objects created from a particular bean definition. This app powerful and flexible in that you can __choose__ the scope of the objects you create through configuration instead of having to bake in the scope of an object at the Java class level. Beans can be defined to be deployed in one of a number of scopes: out of -the box, the Spring Framework supports five scopes, three of which are available only if +the box, the Spring Framework supports seven scopes, five of which are available only if you use a web-aware `ApplicationContext`. The following scopes are supported out of the box. You can also create @@ -2301,14 +2301,18 @@ The following scopes are supported out of the box. You can also create | Scopes a single bean definition to the lifecycle of an HTTP `Session`. Only valid in the context of a web-aware Spring `ApplicationContext`. -| <> +| <> | Scopes a single bean definition to the lifecycle of a global HTTP `Session`. Typically - only valid when used in a portlet context. Only valid in the context of a web-aware + only valid when used in a Portlet context. Only valid in the context of a web-aware Spring `ApplicationContext`. | <> | Scopes a single bean definition to the lifecycle of a `ServletContext`. Only valid in the context of a web-aware Spring `ApplicationContext`. + +| <> +| Scopes a single bean definition to the lifecycle of a `WebSocket`. Only valid in + the context of a web-aware Spring `ApplicationContext`. |=== [NOTE] @@ -2418,22 +2422,22 @@ runtime more than once, see <> [[beans-factory-scopes-other]] -=== Request, session, and global session scopes +=== Request, session, global session, application, and WebSocket scopes -The `request`, `session`, and `global session` scopes are __only__ available if you use -a web-aware Spring `ApplicationContext` implementation (such as -`XmlWebApplicationContext`). If you use these scopes with regular Spring IoC containers -such as the `ClassPathXmlApplicationContext`, you get an `IllegalStateException` -complaining about an unknown bean scope. +The `request`, `session`, `globalSession`, `application`, and `websocket` scopes are +__only__ available if you use a web-aware Spring `ApplicationContext` implementation +(such as `XmlWebApplicationContext`). If you use these scopes with regular Spring IoC +containers such as the `ClassPathXmlApplicationContext`, an `IllegalStateException` will +be thrown complaining about an unknown bean scope. [[beans-factory-scopes-other-web-configuration]] ==== Initial web configuration -To support the scoping of beans at the `request`, `session`, and `global session` levels -(web-scoped beans), some minor initial configuration is required before you define your -beans. (This initial setup is __not__ required for the standard scopes, `singleton` and -`prototype`.) +To support the scoping of beans at the `request`, `session`, `globalSession`, +`application`, and `websocket` levels (web-scoped beans), some minor initial +configuration is required before you define your beans. (This initial setup is __not__ +required for the standard scopes, `singleton` and `prototype`.) How you accomplish this initial setup depends on your particular Servlet environment. @@ -2493,7 +2497,7 @@ down the call chain. [[beans-factory-scopes-request]] ==== Request scope -Consider the following bean definition: +Consider the following XML configuration for a bean definition: [source,xml,indent=0] [subs="verbatim,quotes"] @@ -2513,7 +2517,7 @@ bean that is scoped to the request is discarded. [[beans-factory-scopes-session]] ==== Session scope -Consider the following bean definition: +Consider the following XML configuration for a bean definition: [source,xml,indent=0] [subs="verbatim,quotes"] @@ -2543,22 +2547,22 @@ Consider the following bean definition: ---- -The `global session` scope is similar to the standard HTTP `Session` scope +The `globalSession` scope is similar to the standard HTTP `Session` scope (<>), and applies only in the context of portlet-based web applications. The portlet specification defines the notion of a global `Session` that is shared among all portlets that make up a single portlet web -application. Beans defined at the `global session` scope are scoped (or bound) to the +application. Beans defined at the `globalSession` scope are scoped (or bound) to the lifetime of the global portlet `Session`. If you write a standard Servlet-based web application and you define one or more beans -as having `global session` scope, the standard HTTP `Session` scope is used, and no +as having `globalSession` scope, the standard HTTP `Session` scope is used, and no error is raised. [[beans-factory-scopes-application]] ==== Application scope -Consider the following bean definition: +Consider the following XML configuration for a bean definition: [source,xml,indent=0] [subs="verbatim,quotes"] @@ -2637,12 +2641,12 @@ understand the "why" as well as the "how" behind it. ---- To create such a proxy, you insert a child `` element into a scoped -bean definition. See <> and -<>.) Why do definitions of beans scoped at the `request`, `session`, -`globalSession` and custom-scope levels require the `` element ? +bean definition (see <> and +<>). Why do definitions of beans scoped at the `request`, `session`, +`globalSession` and custom-scope levels require the `` element? Let's examine the following singleton bean definition and contrast it with what you need -to define for the aforementioned scopes. (The following `userPreferences` bean -definition as it stands is __incomplete.)__ +to define for the aforementioned scopes (note that the following `userPreferences` bean +definition as it stands is __incomplete__). [source,xml,indent=0] [subs="verbatim,quotes"] @@ -3126,7 +3130,7 @@ You configure destroy method callbacks similarly (in XML, that is) by using the Where existing bean classes already have callback methods that are named at variance with the convention, you can override the default by specifying (in XML, that is) the -method name using the `init-method` and `destroy-method` attributes of the +method name using the `init-method` and `destroy-method` attributes of the `` itself. The Spring container guarantees that a configured initialization callback is called @@ -3395,7 +3399,7 @@ a reference to the name defined in its associated object definition. ---- public interface BeanNameAware { - void setBeanName(string name) throws BeansException; + void setBeanName(String name) throws BeansException; } ---- @@ -4227,18 +4231,18 @@ arguments: } ---- -You can apply `@Autowired` to constructors and fields: +You can apply `@Autowired` to fields as well and even mix it with constructors: [source,java,indent=0] [subs="verbatim,quotes"] ---- public class MovieRecommender { + private final CustomerPreferenceDao customerPreferenceDao; + @Autowired private MovieCatalog movieCatalog; - private CustomerPreferenceDao customerPreferenceDao; - @Autowired public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) { this.customerPreferenceDao = customerPreferenceDao; @@ -4373,7 +4377,7 @@ automatically resolved, with no special setup necessary. [NOTE] ==== -`@Autowired`, `@Inject`, `@Resource`, and `@Value` annotations are handled by a Spring +`@Autowired`, `@Inject`, `@Resource`, and `@Value` annotations are handled by Spring `BeanPostProcessor` implementations which in turn means that you __cannot__ apply these annotations within your own `BeanPostProcessor` or `BeanFactoryPostProcessor` types (if any). These types must be 'wired up' explicitly via XML or using a Spring `@Bean` method. @@ -4573,8 +4577,8 @@ to the specific collection or map bean by unique name. `@Autowired` applies to fields, constructors, and multi-argument methods, allowing for narrowing through qualifier annotations at the parameter level. By contrast, `@Resource` -is supported only for fields and bean property setter methods with a single argument. As -a consequence, stick with qualifiers if your injection target is a constructor or a +is supported only for fields and bean property setter methods with a single argument. +As a consequence, stick with qualifiers if your injection target is a constructor or a multi-argument method. ==== @@ -5324,9 +5328,9 @@ and the equivalent using XML [NOTE] ==== You can also disable the default filters by setting `useDefaultFilters=false` on the annotation or -providing `use-default-filters="false"` as an attribute of the element. This +providing `use-default-filters="false"` as an attribute of the `` element. This will in effect disable automatic detection of classes annotated with `@Component`, `@Repository`, -`@Service`, or `@Controller`. +`@Service`, `@Controller`, or `@Configuration`. ==== @@ -5474,14 +5478,14 @@ analogous to how the container selects between multiple `@Autowired` constructor When a component is autodetected as part of the scanning process, its bean name is generated by the `BeanNameGenerator` strategy known to that scanner. By default, any -Spring stereotype annotation ( `@Component`, `@Repository`, `@Service`, and -`@Controller`) that contains a `name` value will thereby provide that name to the +Spring stereotype annotation (`@Component`, `@Repository`, `@Service`, and +`@Controller`) that contains a _name_ `value` will thereby provide that name to the corresponding bean definition. -If such an annotation contains no `name` value or for any other detected component (such +If such an annotation contains no _name_ `value` or for any other detected component (such as those discovered by custom filters), the default bean name generator returns the uncapitalized non-qualified class name. For example, if the following two components -were detected, the names would be myMovieLister and movieFinderImpl: +were detected, the names would be `myMovieLister` and `movieFinderImpl`: [source,java,indent=0] [subs="verbatim,quotes"] @@ -5539,8 +5543,8 @@ auto-generated names are adequate whenever the container is responsible for wiri === Providing a scope for autodetected components As with Spring-managed components in general, the default and most common scope for -autodetected components is singleton. However, sometimes you need other scopes, which -Spring 2.5 provides with a new `@Scope` annotation. Simply provide the name of the scope +autodetected components is `singleton`. However, sometimes you need a different scope +which can be specified via the `@Scope` annotation. Simply provide the name of the scope within the annotation: [source,java,indent=0] @@ -5830,7 +5834,7 @@ Please use Spring's stereotype model for building custom component annotations. [[beans-standard-annotations-limitations]] -=== Limitations of the standard approach +=== Limitations of JSR-330 standard annotations When working with standard annotations, it is important to know that some significant features are not available as shown in the table below: From 3d106eb655a8e7883436b2fc209354b834d6fd74 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 1 Jul 2016 15:51:40 +0200 Subject: [PATCH 205/344] Upgrade to WebJars Locator 0.32 --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index c32fcbcf10..84067b7a82 100644 --- a/build.gradle +++ b/build.gradle @@ -855,9 +855,8 @@ project("spring-webmvc") { exclude group: "org.slf4j", module: "jcl-over-slf4j" exclude group: "org.springframework", module: "spring-web" } - optional('org.webjars:webjars-locator:0.30') + optional('org.webjars:webjars-locator:0.32') testCompile(project(":spring-aop")) - testCompile("org.mozilla:rhino:1.7.7.1") testCompile("xmlunit:xmlunit:${xmlunitVersion}") testCompile("dom4j:dom4j:1.6.1") { exclude group: "xml-apis", module: "xml-apis" @@ -882,6 +881,7 @@ project("spring-webmvc") { testCompile("org.slf4j:slf4j-jcl:${slf4jVersion}") testCompile("org.jruby:jruby:${jrubyVersion}") testCompile("org.python:jython-standalone:2.5.3") + testCompile("org.mozilla:rhino:1.7.7.1") testCompile("org.webjars:underscorejs:1.8.3") } } From 4ddd9572f70490bd8e1bc86bc89a74ea8391e942 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sat, 2 Jul 2016 12:55:30 +0200 Subject: [PATCH 206/344] Leniently accept custom DeferredResult etc subclasses for null values Issue: SPR-14423 (cherry picked from commit cfc560c) --- .../invocation/InvocableHandlerMethod.java | 6 +- .../ServletInvocableHandlerMethod.java | 14 +-- .../ServletInvocableHandlerMethodTests.java | 99 ++++++++++++++----- 3 files changed, 81 insertions(+), 38 deletions(-) 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 182e6d9224..99fb8592a8 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 @@ -277,10 +277,10 @@ public Class getParameterType() { if (this.returnValue != null) { return this.returnValue.getClass(); } - if (ResolvableType.NONE.equals(this.returnType)) { - throw new IllegalArgumentException("Expected Future-like type with generic parameter"); + if (!ResolvableType.NONE.equals(this.returnType)) { + return this.returnType.getRawClass(); } - return this.returnType.getRawClass(); + return super.getParameterType(); } @Override diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethod.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethod.java index 05cbba3c09..f843a58113 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethod.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethod.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -263,16 +263,10 @@ public Class getParameterType() { if (this.returnValue != null) { return this.returnValue.getClass(); } - Class parameterType = super.getParameterType(); - if (ResponseBodyEmitter.class.isAssignableFrom(parameterType) || - StreamingResponseBody.class.isAssignableFrom(parameterType)) { - return parameterType; + if (!ResolvableType.NONE.equals(this.returnType)) { + return this.returnType.getRawClass(); } - if (ResolvableType.NONE.equals(this.returnType)) { - throw new IllegalArgumentException("Expected one of Callable, DeferredResult, or ListenableFuture: " + - super.getParameterType()); - } - return this.returnType.getRawClass(); + return super.getParameterType(); } @Override diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethodTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethodTests.java index 431e4d5ccf..a1ba924624 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethodTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethodTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -56,6 +56,7 @@ * * @author Rossen Stoyanchev * @author Sam Brannen + * @author Juergen Hoeller */ public class ServletInvocableHandlerMethodTests { @@ -117,19 +118,17 @@ public void invokeAndHandle_VoidRequestNotModified() throws Exception { this.mavContainer.isRequestHandled()); } - // SPR-9159 - - @Test + @Test // SPR-9159 public void invokeAndHandle_NotVoidWithResponseStatusAndReason() throws Exception { ServletInvocableHandlerMethod handlerMethod = getHandlerMethod(new Handler(), "responseStatusWithReason"); handlerMethod.invokeAndHandle(this.webRequest, this.mavContainer); - assertTrue("When a status reason w/ used, the the request is handled", this.mavContainer.isRequestHandled()); + assertTrue("When a status reason w/ used, the request is handled", this.mavContainer.isRequestHandled()); assertEquals(HttpStatus.BAD_REQUEST.value(), this.response.getStatus()); assertEquals("400 Bad Request", this.response.getErrorMessage()); } - @Test(expected=HttpMessageNotWritableException.class) + @Test(expected = HttpMessageNotWritableException.class) public void invokeAndHandle_Exception() throws Exception { this.returnValueHandlers.addHandler(new ExceptionRaisingReturnValueHandler()); @@ -160,28 +159,52 @@ public void invokeAndHandle_DynamicReturnValue() throws Exception { @Test public void wrapConcurrentResult_MethodLevelResponseBody() throws Exception { - wrapConcurrentResult_ResponseBody(new MethodLevelResponseBodyHandler()); + wrapConcurrentResult_ResponseBody(new MethodLevelResponseBodyHandler(), "bar", String.class); + } + + @Test + public void wrapConcurrentResult_MethodLevelResponseBodyEmpty() throws Exception { + wrapConcurrentResult_ResponseBody(new MethodLevelResponseBodyHandler(), null, String.class); } @Test public void wrapConcurrentResult_TypeLevelResponseBody() throws Exception { - wrapConcurrentResult_ResponseBody(new TypeLevelResponseBodyHandler()); + wrapConcurrentResult_ResponseBody(new TypeLevelResponseBodyHandler(), "bar", String.class); + } + + @Test + public void wrapConcurrentResult_TypeLevelResponseBodyEmpty() throws Exception { + wrapConcurrentResult_ResponseBody(new TypeLevelResponseBodyHandler(), null, String.class); + } + + @Test + public void wrapConcurrentResult_DeferredResultSubclass() throws Exception { + wrapConcurrentResult_ResponseBody(new DeferredResultSubclassHandler(), "bar", String.class); } - private void wrapConcurrentResult_ResponseBody(Object handler) throws Exception { - List> converters = new ArrayList>(); + @Test + public void wrapConcurrentResult_DeferredResultSubclassEmpty() throws Exception { + wrapConcurrentResult_ResponseBody(new DeferredResultSubclassHandler(), null, CustomDeferredResult.class); + } + + private void wrapConcurrentResult_ResponseBody(Object handler, Object result, Class expectedReturnType) + throws Exception { + + List> converters = new ArrayList<>(); converters.add(new StringHttpMessageConverter()); + this.returnValueHandlers.addHandler(new ModelAndViewMethodReturnValueHandler()); this.returnValueHandlers.addHandler(new RequestResponseBodyMethodProcessor(converters)); ServletInvocableHandlerMethod handlerMethod = getHandlerMethod(handler, "handle"); - handlerMethod = handlerMethod.wrapConcurrentResult("bar"); - handlerMethod.invokeAndHandle(this.webRequest, this.mavContainer); - assertEquals("bar", this.response.getContentAsString()); + handlerMethod = handlerMethod.wrapConcurrentResult(result); + handlerMethod.invokeAndHandle(this.webRequest, this.mavContainer); + assertEquals((result != null ? result.toString() : ""), this.response.getContentAsString()); + assertEquals(expectedReturnType, handlerMethod.getReturnValueType(result).getParameterType()); } @Test public void wrapConcurrentResult_ResponseEntity() throws Exception { - List> converters = new ArrayList>(); + List> converters = new ArrayList<>(); converters.add(new StringHttpMessageConverter()); this.returnValueHandlers.addHandler(new HttpEntityMethodProcessor(converters)); ServletInvocableHandlerMethod handlerMethod = getHandlerMethod(new ResponseEntityHandler(), "handleDeferred"); @@ -191,11 +214,9 @@ public void wrapConcurrentResult_ResponseEntity() throws Exception { assertEquals("bar", this.response.getContentAsString()); } - // SPR-12287 - - @Test + @Test // SPR-12287 public void wrapConcurrentResult_ResponseEntityNullBody() throws Exception { - List> converters = new ArrayList>(); + List> converters = new ArrayList<>(); converters.add(new StringHttpMessageConverter()); List advice = Collections.singletonList(mock(ResponseBodyAdvice.class)); HttpEntityMethodProcessor processor = new HttpEntityMethodProcessor(converters, null, advice); @@ -210,7 +231,7 @@ public void wrapConcurrentResult_ResponseEntityNullBody() throws Exception { @Test public void wrapConcurrentResult_ResponseEntityNullReturnValue() throws Exception { - List> converters = new ArrayList>(); + List> converters = new ArrayList<>(); converters.add(new StringHttpMessageConverter()); List advice = Collections.singletonList(mock(ResponseBodyAdvice.class)); HttpEntityMethodProcessor processor = new HttpEntityMethodProcessor(converters, null, advice); @@ -225,7 +246,7 @@ public void wrapConcurrentResult_ResponseEntityNullReturnValue() throws Exceptio @Test public void wrapConcurrentResult_ResponseBodyEmitter() throws Exception { - List> converters = new ArrayList>(); + List> converters = new ArrayList<>(); converters.add(new StringHttpMessageConverter()); this.returnValueHandlers.addHandler(new ResponseBodyEmitterReturnValueHandler(converters)); ServletInvocableHandlerMethod handlerMethod = getHandlerMethod(new AsyncHandler(), "handleWithEmitter"); @@ -247,9 +268,7 @@ public void wrapConcurrentResult_StreamingResponseBody() throws Exception { assertEquals("", this.response.getContentAsString()); } - // SPR-12287 (16/Oct/14 comments) - - @Test + @Test // SPR-12287 (16/Oct/14 comments) public void responseEntityRawTypeWithNullBody() throws Exception { List> converters = Collections.singletonList(new StringHttpMessageConverter()); List advice = Collections.singletonList(mock(ResponseBodyAdvice.class)); @@ -272,6 +291,8 @@ private ServletInvocableHandlerMethod getHandlerMethod(Object controller, return handlerMethod; } + + @SuppressWarnings("unused") @ResponseStatus @Retention(RetentionPolicy.RUNTIME) @interface ComposedResponseStatus { @@ -280,6 +301,7 @@ private ServletInvocableHandlerMethod getHandlerMethod(Object controller, HttpStatus responseStatus() default HttpStatus.INTERNAL_SERVER_ERROR; } + @SuppressWarnings("unused") private static class Handler { @@ -311,6 +333,16 @@ public Object dynamicReturnValue(@RequestParam(required=false) String param) { } } + + @SuppressWarnings("unused") + @ResponseStatus(HttpStatus.BAD_REQUEST) + private static class ResponseStatusHandler { + + public void handle() { + } + } + + private static class MethodLevelResponseBodyHandler { @ResponseBody @@ -319,15 +351,30 @@ public DeferredResult handle() { } } + @SuppressWarnings("unused") @ResponseBody private static class TypeLevelResponseBodyHandler { public DeferredResult handle() { - return new DeferredResult(); + return new DeferredResult<>(); } } + + private static class DeferredResultSubclassHandler { + + @ResponseBody + public CustomDeferredResult handle() { + return new CustomDeferredResult(); + } + } + + + private static class CustomDeferredResult extends DeferredResult { + } + + @SuppressWarnings("unused") private static class ResponseEntityHandler { @@ -340,6 +387,7 @@ public ResponseEntity handleRawType() { } } + private static class ExceptionRaisingReturnValueHandler implements HandlerMethodReturnValueHandler { @Override @@ -354,6 +402,7 @@ public void handleReturnValue(Object returnValue, MethodParameter returnType, } } + @SuppressWarnings("unused") private static class AsyncHandler { @@ -366,4 +415,4 @@ public StreamingResponseBody handleWithStreaming() { } } -} \ No newline at end of file +} From cd3787397112e061c584c6046b9507ccaf2a0c86 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sat, 2 Jul 2016 13:02:22 +0200 Subject: [PATCH 207/344] Avoid stateful MethodParameter nesting level changes in MVC processing (cherry picked from commit e7a53e3) --- ...stractMessageConverterMethodProcessor.java | 117 +++++++++--------- 1 file changed, 57 insertions(+), 60 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodProcessor.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodProcessor.java index f403499504..296caaf8ce 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodProcessor.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,6 +30,7 @@ import javax.servlet.http.HttpServletResponse; import org.springframework.core.MethodParameter; +import org.springframework.core.ResolvableType; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpOutputMessage; @@ -62,17 +63,6 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolver implements HandlerMethodReturnValueHandler { - private static final MediaType MEDIA_TYPE_APPLICATION = new MediaType("application"); - - private static final UrlPathHelper RAW_URL_PATH_HELPER = new UrlPathHelper(); - - private static final UrlPathHelper DECODING_URL_PATH_HELPER = new UrlPathHelper(); - - static { - RAW_URL_PATH_HELPER.setRemoveSemicolonContent(false); - RAW_URL_PATH_HELPER.setUrlDecode(false); - } - /* Extensions associated with the built-in message converters */ private static final Set WHITELISTED_EXTENSIONS = new HashSet(Arrays.asList( "txt", "text", "yml", "properties", "csv", @@ -82,6 +72,17 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe private static final Set WHITELISTED_MEDIA_BASE_TYPES = new HashSet( Arrays.asList("audio", "image", "video")); + private static final MediaType MEDIA_TYPE_APPLICATION = new MediaType("application"); + + private static final UrlPathHelper DECODING_URL_PATH_HELPER = new UrlPathHelper(); + + private static final UrlPathHelper RAW_URL_PATH_HELPER = new UrlPathHelper(); + + static { + RAW_URL_PATH_HELPER.setRemoveSemicolonContent(false); + RAW_URL_PATH_HELPER.setUrlDecode(false); + } + private final ContentNegotiationManager contentNegotiationManager; @@ -145,17 +146,17 @@ protected ServletServerHttpResponse createOutputMessage(NativeWebRequest webRequ * Writes the given return value to the given web request. Delegates to * {@link #writeWithMessageConverters(Object, MethodParameter, ServletServerHttpRequest, ServletServerHttpResponse)} */ - protected void writeWithMessageConverters(T returnValue, MethodParameter returnType, NativeWebRequest webRequest) + protected void writeWithMessageConverters(T value, MethodParameter returnType, NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException { ServletServerHttpRequest inputMessage = createInputMessage(webRequest); ServletServerHttpResponse outputMessage = createOutputMessage(webRequest); - writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage); + writeWithMessageConverters(value, returnType, inputMessage, outputMessage); } /** * Writes the given return type to the given output message. - * @param returnValue the value to write to the output message + * @param value the value to write to the output message * @param returnType the type of the value * @param inputMessage the input messages. Used to inspect the {@code Accept} header. * @param outputMessage the output message to write to @@ -164,18 +165,18 @@ protected void writeWithMessageConverters(T returnValue, MethodParameter ret * the request cannot be met by the message converters */ @SuppressWarnings("unchecked") - protected void writeWithMessageConverters(T returnValue, MethodParameter returnType, + protected void writeWithMessageConverters(T value, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException { - Class returnValueClass = getReturnValueType(returnValue, returnType); - Type returnValueType = getGenericType(returnType); - HttpServletRequest servletRequest = inputMessage.getServletRequest(); - List requestedMediaTypes = getAcceptableMediaTypes(servletRequest); - List producibleMediaTypes = getProducibleMediaTypes(servletRequest, returnValueClass, returnValueType); + Class valueType = getReturnValueType(value, returnType); + Type declaredType = getGenericType(returnType); + HttpServletRequest request = inputMessage.getServletRequest(); + List requestedMediaTypes = getAcceptableMediaTypes(request); + List producibleMediaTypes = getProducibleMediaTypes(request, valueType, declaredType); - if (returnValue != null && producibleMediaTypes.isEmpty()) { - throw new IllegalArgumentException("No converter found for return value of type: " + returnValueClass); + if (value != null && producibleMediaTypes.isEmpty()) { + throw new IllegalArgumentException("No converter found for return value of type: " + valueType); } Set compatibleMediaTypes = new LinkedHashSet(); @@ -187,7 +188,7 @@ protected void writeWithMessageConverters(T returnValue, MethodParameter ret } } if (compatibleMediaTypes.isEmpty()) { - if (returnValue != null) { + if (value != null) { throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes); } return; @@ -212,34 +213,33 @@ else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICAT selectedMediaType = selectedMediaType.removeQualityValue(); for (HttpMessageConverter messageConverter : this.messageConverters) { if (messageConverter instanceof GenericHttpMessageConverter) { - if (((GenericHttpMessageConverter) messageConverter).canWrite(returnValueType, - returnValueClass, selectedMediaType)) { - returnValue = (T) getAdvice().beforeBodyWrite(returnValue, returnType, selectedMediaType, + if (((GenericHttpMessageConverter) messageConverter).canWrite( + declaredType, valueType, selectedMediaType)) { + value = (T) getAdvice().beforeBodyWrite(value, returnType, selectedMediaType, (Class>) messageConverter.getClass(), inputMessage, outputMessage); - if (returnValue != null) { + if (value != null) { addContentDispositionHeader(inputMessage, outputMessage); - ((GenericHttpMessageConverter) messageConverter).write(returnValue, - returnValueType, selectedMediaType, outputMessage); + ((GenericHttpMessageConverter) messageConverter).write( + value, declaredType, selectedMediaType, outputMessage); if (logger.isDebugEnabled()) { - logger.debug("Written [" + returnValue + "] as \"" + - selectedMediaType + "\" using [" + messageConverter + "]"); + logger.debug("Written [" + value + "] as \"" + selectedMediaType + + "\" using [" + messageConverter + "]"); } } return; } } - else if (messageConverter.canWrite(returnValueClass, selectedMediaType)) { - returnValue = (T) getAdvice().beforeBodyWrite(returnValue, returnType, selectedMediaType, + else if (messageConverter.canWrite(valueType, selectedMediaType)) { + value = (T) getAdvice().beforeBodyWrite(value, returnType, selectedMediaType, (Class>) messageConverter.getClass(), inputMessage, outputMessage); - if (returnValue != null) { + if (value != null) { addContentDispositionHeader(inputMessage, outputMessage); - ((HttpMessageConverter) messageConverter).write(returnValue, - selectedMediaType, outputMessage); + ((HttpMessageConverter) messageConverter).write(value, selectedMediaType, outputMessage); if (logger.isDebugEnabled()) { - logger.debug("Written [" + returnValue + "] as \"" + - selectedMediaType + "\" using [" + messageConverter + "]"); + logger.debug("Written [" + value + "] as \"" + selectedMediaType + + "\" using [" + messageConverter + "]"); } } return; @@ -247,43 +247,40 @@ else if (messageConverter.canWrite(returnValueClass, selectedMediaType)) { } } - if (returnValue != null) { + if (value != null) { throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes); } } /** - * Return the type of the value to be written to the response. Typically this - * is a simple check via getClass on the returnValue but if the returnValue is - * null, then the returnType needs to be examined possibly including generic - * type determination (e.g. {@code ResponseEntity}). + * Return the type of the value to be written to the response. Typically this is + * a simple check via getClass on the value but if the value is null, then the + * return type needs to be examined possibly including generic type determination + * (e.g. {@code ResponseEntity}). */ - protected Class getReturnValueType(Object returnValue, MethodParameter returnType) { - return (returnValue != null ? returnValue.getClass() : returnType.getParameterType()); + protected Class getReturnValueType(Object value, MethodParameter returnType) { + return (value != null ? value.getClass() : returnType.getParameterType()); } /** - * Return the generic type of the {@code returnType} (or of the nested type if it is - * a {@link HttpEntity}). + * Return the generic type of the {@code returnType} (or of the nested type + * if it is an {@link HttpEntity}). */ private Type getGenericType(MethodParameter returnType) { - Type type; if (HttpEntity.class.isAssignableFrom(returnType.getParameterType())) { - returnType.increaseNestingLevel(); - type = returnType.getNestedGenericParameterType(); + return ResolvableType.forType(returnType.getGenericParameterType()).getGeneric(0).getType(); } else { - type = returnType.getGenericParameterType(); + return returnType.getGenericParameterType(); } - return type; } /** * @see #getProducibleMediaTypes(HttpServletRequest, Class, Type) */ @SuppressWarnings({"unchecked", "unused"}) - protected List getProducibleMediaTypes(HttpServletRequest request, Class returnValueClass) { - return getProducibleMediaTypes(request, returnValueClass, null); + protected List getProducibleMediaTypes(HttpServletRequest request, Class valueClass) { + return getProducibleMediaTypes(request, valueClass, null); } /** @@ -296,7 +293,7 @@ protected List getProducibleMediaTypes(HttpServletRequest request, Cl * @since 4.2 */ @SuppressWarnings("unchecked") - protected List getProducibleMediaTypes(HttpServletRequest request, Class returnValueClass, Type returnValueType) { + protected List getProducibleMediaTypes(HttpServletRequest request, Class valueClass, Type declaredType) { Set mediaTypes = (Set) request.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE); if (!CollectionUtils.isEmpty(mediaTypes)) { return new ArrayList(mediaTypes); @@ -304,12 +301,12 @@ protected List getProducibleMediaTypes(HttpServletRequest request, Cl else if (!this.allSupportedMediaTypes.isEmpty()) { List result = new ArrayList(); for (HttpMessageConverter converter : this.messageConverters) { - if (converter instanceof GenericHttpMessageConverter && returnValueType != null) { - if (((GenericHttpMessageConverter) converter).canWrite(returnValueType, returnValueClass, null)) { + if (converter instanceof GenericHttpMessageConverter && declaredType != null) { + if (((GenericHttpMessageConverter) converter).canWrite(declaredType, valueClass, null)) { result.addAll(converter.getSupportedMediaTypes()); } } - else if (converter.canWrite(returnValueClass, null)) { + else if (converter.canWrite(valueClass, null)) { result.addAll(converter.getSupportedMediaTypes()); } } @@ -412,7 +409,7 @@ private boolean safeMediaTypesForExtension(String extension) { try { mediaTypes = this.pathStrategy.resolveMediaTypeKey(null, extension); } - catch (HttpMediaTypeNotAcceptableException e) { + catch (HttpMediaTypeNotAcceptableException ex) { // Ignore } if (CollectionUtils.isEmpty(mediaTypes)) { From fa624cd0951a1b68701331365fcef970889a0960 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sat, 2 Jul 2016 13:03:33 +0200 Subject: [PATCH 208/344] Avoid wrapping in plain RuntimeException in favor of IllegalStateException (cherry picked from commit e5122d0) --- .../AbstractMethodMessageHandler.java | 94 ++++++++++--------- 1 file changed, 49 insertions(+), 45 deletions(-) diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/AbstractMethodMessageHandler.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/AbstractMethodMessageHandler.java index 1aaa0da811..b28641a2a8 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/AbstractMethodMessageHandler.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/AbstractMethodMessageHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -83,17 +83,13 @@ public abstract class AbstractMethodMessageHandler protected final Log logger = LogFactory.getLog(getClass()); - private Collection destinationPrefixes = new ArrayList(); - private final List customArgumentResolvers = new ArrayList(4); private final List customReturnValueHandlers = new ArrayList(4); - private HandlerMethodArgumentResolverComposite argumentResolvers = new HandlerMethodArgumentResolverComposite(); + private final HandlerMethodArgumentResolverComposite argumentResolvers = new HandlerMethodArgumentResolverComposite(); - private HandlerMethodReturnValueHandlerComposite returnValueHandlers =new HandlerMethodReturnValueHandlerComposite(); - - private ApplicationContext applicationContext; + private final HandlerMethodReturnValueHandlerComposite returnValueHandlers =new HandlerMethodReturnValueHandlerComposite(); private final Map handlerMethods = new LinkedHashMap(); @@ -105,6 +101,16 @@ public abstract class AbstractMethodMessageHandler private final Map exceptionHandlerAdviceCache = new LinkedHashMap(64); + private Collection destinationPrefixes = new ArrayList(); + + private ApplicationContext applicationContext; + + /** + * Return the configured destination prefixes. + */ + public Collection getDestinationPrefixes() { + return this.destinationPrefixes; + } /** * When this property is configured only messages to destinations matching @@ -125,10 +131,10 @@ public void setDestinationPrefixes(Collection prefixes) { } /** - * Return the configured destination prefixes. + * Return the configured custom argument resolvers, if any. */ - public Collection getDestinationPrefixes() { - return this.destinationPrefixes; + public List getCustomArgumentResolvers() { + return this.customArgumentResolvers; } /** @@ -144,10 +150,10 @@ public void setCustomArgumentResolvers(List custo } /** - * Return the configured custom argument resolvers, if any. + * Return the configured custom return value handlers, if any. */ - public List getCustomArgumentResolvers() { - return this.customArgumentResolvers; + public List getCustomReturnValueHandlers() { + return this.customReturnValueHandlers; } /** @@ -163,10 +169,10 @@ public void setCustomReturnValueHandlers(List c } /** - * Return the configured custom return value handlers, if any. + * Return the configured argument resolvers, if any. */ - public List getCustomReturnValueHandlers() { - return this.customReturnValueHandlers; + public List getArgumentResolvers() { + return this.argumentResolvers.getResolvers(); } /** @@ -182,8 +188,11 @@ public void setArgumentResolvers(List argumentRes this.argumentResolvers.addResolvers(argumentResolvers); } - public List getArgumentResolvers() { - return this.argumentResolvers.getResolvers(); + /** + * Return the configured return value handlers, if any. + */ + public List getReturnValueHandlers() { + return this.returnValueHandlers.getReturnValueHandlers(); } /** @@ -199,15 +208,8 @@ public void setReturnValueHandlers(List returnV this.returnValueHandlers.addHandlers(returnValueHandlers); } - public List getReturnValueHandlers() { - return this.returnValueHandlers.getReturnValueHandlers(); - } - - /** - * Return a map with all handler methods and their mappings. - */ - public Map getHandlerMethods() { - return Collections.unmodifiableMap(this.handlerMethods); + public ApplicationContext getApplicationContext() { + return this.applicationContext; } @Override @@ -215,11 +217,6 @@ public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } - public ApplicationContext getApplicationContext() { - return this.applicationContext; - } - - @Override public void afterPropertiesSet() { if (this.argumentResolvers.getResolvers().isEmpty()) { @@ -366,6 +363,13 @@ protected void registerExceptionHandlerAdvice(MessagingAdviceBean bean, Abstract this.exceptionHandlerAdviceCache.put(bean, resolver); } + /** + * Return a map with all handler methods and their mappings. + */ + public Map getHandlerMethods() { + return Collections.unmodifiableMap(this.handlerMethods); + } + @Override public void handleMessage(Message message) throws MessagingException { @@ -427,14 +431,14 @@ protected void handleMessageInternal(Message message, String lookupDestinatio addMatchesToCollection(allMappings, message, matches); } if (matches.isEmpty()) { - handleNoMatch(handlerMethods.keySet(), lookupDestination, message); + handleNoMatch(this.handlerMethods.keySet(), lookupDestination, message); return; } Comparator comparator = new MatchComparator(getMappingComparator(message)); Collections.sort(matches, comparator); if (logger.isTraceEnabled()) { - logger.trace("Found " + matches.size() + " methods: " + matches); + logger.trace("Found " + matches.size() + " handler methods: " + matches); } Match bestMatch = matches.get(0); @@ -451,7 +455,6 @@ protected void handleMessageInternal(Message message, String lookupDestinatio handleMatch(bestMatch.mapping, bestMatch.handlerMethod, lookupDestination, message); } - private void addMatchesToCollection(Collection mappingsToCheck, Message message, List matches) { for (T mapping : mappingsToCheck) { T match = getMatchingMapping(mapping, message); @@ -470,6 +473,10 @@ private void addMatchesToCollection(Collection mappingsToCheck, Message me */ protected abstract T getMatchingMapping(T mapping, Message message); + protected void handleNoMatch(Set ts, String lookupDestination, Message message) { + logger.debug("No matching message handler methods."); + } + /** * Return a comparator for sorting matching mappings. * The returned comparator should sort 'better' matches higher. @@ -478,7 +485,6 @@ private void addMatchesToCollection(Collection mappingsToCheck, Message me */ protected abstract Comparator getMappingComparator(Message message); - protected void handleMatch(T mapping, HandlerMethod handlerMethod, String lookupDestination, Message message) { if (logger.isDebugEnabled()) { logger.debug("Invoking " + handlerMethod.getShortLogMessage()); @@ -515,7 +521,7 @@ protected void handleMatch(T mapping, HandlerMethod handlerMethod, String lookup protected void processHandlerMethodException(HandlerMethod handlerMethod, Exception ex, Message message) { InvocableHandlerMethod invocable = getExceptionHandlerMethod(handlerMethod, ex); if (invocable == null) { - logger.error("Unhandled exception", ex); + logger.error("Unhandled exception from message handler method", ex); return; } invocable.setMessageMethodArgumentResolvers(this.argumentResolvers); @@ -535,8 +541,6 @@ protected void processHandlerMethodException(HandlerMethod handlerMethod, Except } } - protected abstract AbstractExceptionHandlerMethodResolver createExceptionHandlerMethodResolverFor(Class beanType); - /** * Find an {@code @MessageExceptionHandler} method for the given exception. * The default implementation searches methods in the class hierarchy of the @@ -575,9 +579,9 @@ protected InvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handler return null; } - protected void handleNoMatch(Set ts, String lookupDestination, Message message) { - logger.debug("No matching methods."); - } + protected abstract AbstractExceptionHandlerMethodResolver createExceptionHandlerMethodResolverFor( + Class beanType); + @Override public String toString() { @@ -596,7 +600,7 @@ private class Match { private final HandlerMethod handlerMethod; - private Match(T mapping, HandlerMethod handlerMethod) { + public Match(T mapping, HandlerMethod handlerMethod) { this.mapping = mapping; this.handlerMethod = handlerMethod; } @@ -622,13 +626,13 @@ public int compare(Match match1, Match match2) { } } + private class ReturnValueListenableFutureCallback implements ListenableFutureCallback { private final InvocableHandlerMethod handlerMethod; private final Message message; - public ReturnValueListenableFutureCallback(InvocableHandlerMethod handlerMethod, Message message) { this.handlerMethod = handlerMethod; this.message = message; @@ -651,7 +655,7 @@ public void onFailure(Throwable ex) { } private void handleFailure(Throwable ex) { - Exception cause = (ex instanceof Exception ? (Exception) ex : new RuntimeException(ex)); + Exception cause = (ex instanceof Exception ? (Exception) ex : new IllegalStateException(ex)); processHandlerMethodException(this.handlerMethod, cause, this.message); } } From 6500018730c0df12eb34bef54baa5a91372adf91 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sat, 2 Jul 2016 14:08:24 +0200 Subject: [PATCH 209/344] HtmlUnitRequestBuilder decodes request parameter names (backport) Issue: SPR-14177 --- .../htmlunit/HtmlUnitRequestBuilder.java | 63 ++++++++++--------- .../htmlunit/HtmlUnitRequestBuilderTests.java | 49 ++++++++++----- 2 files changed, 65 insertions(+), 47 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/HtmlUnitRequestBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/HtmlUnitRequestBuilder.java index 771e8c542a..4fdaa22a04 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/HtmlUnitRequestBuilder.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/HtmlUnitRequestBuilder.java @@ -105,8 +105,8 @@ public MockHttpServletRequest buildRequest(ServletContext servletContext) { String httpMethod = this.webRequest.getHttpMethod().name(); UriComponents uriComponents = uriComponents(); - MockHttpServletRequest request = new HtmlUnitMockHttpServletRequest(servletContext, httpMethod, - uriComponents.getPath()); + MockHttpServletRequest request = new HtmlUnitMockHttpServletRequest( + servletContext, httpMethod, uriComponents.getPath()); parent(request, this.parentBuilder); request.setServerName(uriComponents.getHost()); // needs to be first for additional headers authType(request); @@ -123,7 +123,7 @@ public MockHttpServletRequest buildRequest(ServletContext servletContext) { request.setProtocol("HTTP/1.1"); request.setQueryString(uriComponents.getQuery()); request.setScheme(uriComponents.getScheme()); - pathInfo(uriComponents,request); + request.setPathInfo(null); return postProcess(request); } @@ -223,14 +223,14 @@ private void content(MockHttpServletRequest request, String charset) { try { request.setContent(requestBody.getBytes(charset)); } - catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); + catch (UnsupportedEncodingException ex) { + throw new IllegalStateException(ex); } } private void contentType(MockHttpServletRequest request) { String contentType = header("Content-Type"); - request.setContentType(contentType == null ? MediaType.ALL_VALUE.toString() : contentType); + request.setContentType(contentType != null ? contentType : MediaType.ALL_VALUE); } private void contextPath(MockHttpServletRequest request, UriComponents uriComponents) { @@ -245,8 +245,8 @@ private void contextPath(MockHttpServletRequest request, UriComponents uriCompon } else { if (!uriComponents.getPath().startsWith(this.contextPath)) { - throw new IllegalArgumentException(uriComponents.getPath() + " should start with contextPath " - + this.contextPath); + throw new IllegalArgumentException(uriComponents.getPath() + " should start with contextPath " + + this.contextPath); } request.setContextPath(this.contextPath); } @@ -360,14 +360,10 @@ private void locales(MockHttpServletRequest request) { private void params(MockHttpServletRequest request, UriComponents uriComponents) { for (Entry> entry : uriComponents.getQueryParams().entrySet()) { String name = entry.getKey(); + String urlDecodedName = urlDecode(name); for (String value : entry.getValue()) { - try { - value = (value != null ? URLDecoder.decode(value, "UTF-8") : ""); - request.addParameter(name, value); - } - catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } + value = (value != null ? urlDecode(value) : ""); + request.addParameter(urlDecodedName, value); } } for (NameValuePair param : this.webRequest.getRequestParameters()) { @@ -375,6 +371,15 @@ private void params(MockHttpServletRequest request, UriComponents uriComponents) } } + private String urlDecode(String value) { + try { + return URLDecoder.decode(value, "UTF-8"); + } + catch (UnsupportedEncodingException ex) { + throw new IllegalStateException(ex); + } + } + private Locale parseLocale(String locale) { Matcher matcher = LOCALE_PATTERN.matcher(locale); if (!matcher.matches()) { @@ -392,10 +397,6 @@ private Locale parseLocale(String locale) { return new Locale(language, country, qualifier); } - private void pathInfo(UriComponents uriComponents, MockHttpServletRequest request) { - request.setPathInfo(null); - } - private void servletPath(MockHttpServletRequest request, String requestPath) { String servletPath = requestPath.substring(request.getContextPath().length()); if ("".equals(servletPath)) { @@ -426,8 +427,7 @@ private void ports(UriComponents uriComponents, MockHttpServletRequest request) private UriComponents uriComponents() { URL url = this.webRequest.getUrl(); - UriComponentsBuilder uriBldr = UriComponentsBuilder.fromUriString(url.toExternalForm()); - return uriBldr.build(); + return UriComponentsBuilder.fromUriString(url.toExternalForm()).build(); } @Override @@ -450,14 +450,18 @@ public Object merge(Object parent) { return this; } + private CookieManager getCookieManager() { + return this.webClient.getCookieManager(); + } + /** - * An extension to {@link MockHttpServletRequest} that ensures that - * when a new {@link HttpSession} is created, it is added to the managed sessions. + * An extension to {@link MockHttpServletRequest} that ensures that when a + * new {@link HttpSession} is created, it is added to the managed sessions. */ private final class HtmlUnitMockHttpServletRequest extends MockHttpServletRequest { - private HtmlUnitMockHttpServletRequest(ServletContext servletContext, String method, String requestURI) { + public HtmlUnitMockHttpServletRequest(ServletContext servletContext, String method, String requestURI) { super(servletContext, method, requestURI); } @@ -486,16 +490,17 @@ public void setSession(HttpSession session) { } } + /** * An extension to {@link MockHttpSession} that ensures when - * {@link #invalidate()} is called that the {@link HttpSession} is - * removed from the managed sessions. + * {@link #invalidate()} is called that the {@link HttpSession} + * is removed from the managed sessions. */ private final class HtmlUnitMockHttpSession extends MockHttpSession { private final MockHttpServletRequest request; - private HtmlUnitMockHttpSession(MockHttpServletRequest request) { + public HtmlUnitMockHttpSession(MockHttpServletRequest request) { super(request.getServletContext()); this.request = request; } @@ -514,8 +519,4 @@ public void invalidate() { } } - private CookieManager getCookieManager() { - return this.webClient.getCookieManager(); - } - } diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/HtmlUnitRequestBuilderTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/HtmlUnitRequestBuilderTests.java index b579ea813d..ddf6830640 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/HtmlUnitRequestBuilderTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/HtmlUnitRequestBuilderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ package org.springframework.test.web.servlet.htmlunit; +import java.io.InputStreamReader; import java.net.MalformedURLException; import java.net.URL; import java.util.Collections; @@ -27,9 +28,11 @@ import javax.servlet.http.Cookie; import javax.servlet.http.HttpSession; -import org.apache.commons.io.IOUtils; +import com.gargoylesoftware.htmlunit.HttpMethod; +import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.WebRequest; +import com.gargoylesoftware.htmlunit.util.NameValuePair; import org.apache.http.auth.UsernamePasswordCredentials; - import org.junit.Before; import org.junit.Test; @@ -39,16 +42,12 @@ import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.util.FileCopyUtils; -import com.gargoylesoftware.htmlunit.HttpMethod; -import com.gargoylesoftware.htmlunit.WebClient; -import com.gargoylesoftware.htmlunit.WebRequest; -import com.gargoylesoftware.htmlunit.util.NameValuePair; - -import static java.util.Arrays.asList; +import static java.util.Arrays.*; import static org.hamcrest.Matchers.*; -import static org.junit.Assert.assertThat; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.junit.Assert.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; /** * Unit tests for {@link HtmlUnitRequestBuilder}. @@ -77,7 +76,6 @@ public void setUp() throws Exception { requestBuilder = new HtmlUnitRequestBuilder(sessions, webClient, webRequest); } - // --- constructor @Test(expected = IllegalArgumentException.class) public void constructorNullSessions() { @@ -94,8 +92,6 @@ public void constructorNullWebRequest() { new HtmlUnitRequestBuilder(sessions, webClient, null); } - // --- buildRequest - @Test @SuppressWarnings("deprecation") public void buildRequestBasicAuth() { @@ -245,7 +241,8 @@ public void buildRequestInputStream() throws Exception { MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext); - assertThat(IOUtils.toString(actualRequest.getInputStream()), equalTo(content)); + assertThat(FileCopyUtils.copyToString(new InputStreamReader(actualRequest.getInputStream(), "ISO-8859-1")), + equalTo(content)); } @Test @@ -411,6 +408,26 @@ public void buildRequestParameterMapFromSingleQueryParam() throws Exception { assertThat(actualRequest.getParameter("name"), equalTo("value")); } + @Test // SPR-14177 + public void buildRequestParameterMapDecodesParameterName() throws Exception { + webRequest.setUrl(new URL("http://example.com/example/?row%5B0%5D=value")); + + MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext); + + assertThat(actualRequest.getParameterMap().size(), equalTo(1)); + assertThat(actualRequest.getParameter("row[0]"), equalTo("value")); + } + + @Test + public void buildRequestParameterMapDecodesParameterValue() throws Exception { + webRequest.setUrl(new URL("http://example.com/example/?name=row%5B0%5D")); + + MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext); + + assertThat(actualRequest.getParameterMap().size(), equalTo(1)); + assertThat(actualRequest.getParameter("name"), equalTo("row[0]")); + } + @Test public void buildRequestParameterMapFromSingleQueryParamWithoutValueAndWithoutEqualsSign() throws Exception { webRequest.setUrl(new URL("http://example.com/example/?name")); @@ -544,7 +561,7 @@ public void buildRequestReader() throws Exception { MockHttpServletRequest actualRequest = requestBuilder.buildRequest(servletContext); - assertThat(IOUtils.toString(actualRequest.getReader()), equalTo(expectedBody)); + assertThat(FileCopyUtils.copyToString(actualRequest.getReader()), equalTo(expectedBody)); } @Test From a637213246fed065ab41e93b0d51541f54a1a8c9 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sat, 2 Jul 2016 15:15:09 +0200 Subject: [PATCH 210/344] Aligned default Map capacity --- .../AbstractMethodMessageHandler.java | 80 ++++++++++--------- 1 file changed, 43 insertions(+), 37 deletions(-) diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/AbstractMethodMessageHandler.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/AbstractMethodMessageHandler.java index b28641a2a8..004d25ed11 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/AbstractMethodMessageHandler.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/invocation/AbstractMethodMessageHandler.java @@ -83,17 +83,25 @@ public abstract class AbstractMethodMessageHandler protected final Log logger = LogFactory.getLog(getClass()); - private final List customArgumentResolvers = new ArrayList(4); + private Collection destinationPrefixes = new ArrayList(); + + private final List customArgumentResolvers = + new ArrayList(4); + + private final List customReturnValueHandlers = + new ArrayList(4); - private final List customReturnValueHandlers = new ArrayList(4); + private final HandlerMethodArgumentResolverComposite argumentResolvers = + new HandlerMethodArgumentResolverComposite(); - private final HandlerMethodArgumentResolverComposite argumentResolvers = new HandlerMethodArgumentResolverComposite(); + private final HandlerMethodReturnValueHandlerComposite returnValueHandlers = + new HandlerMethodReturnValueHandlerComposite(); - private final HandlerMethodReturnValueHandlerComposite returnValueHandlers =new HandlerMethodReturnValueHandlerComposite(); + private ApplicationContext applicationContext; - private final Map handlerMethods = new LinkedHashMap(); + private final Map handlerMethods = new LinkedHashMap(64); - private final MultiValueMap destinationLookup = new LinkedMultiValueMap(); + private final MultiValueMap destinationLookup = new LinkedMultiValueMap(64); private final Map, AbstractExceptionHandlerMethodResolver> exceptionHandlerCache = new ConcurrentHashMap, AbstractExceptionHandlerMethodResolver>(64); @@ -101,16 +109,6 @@ public abstract class AbstractMethodMessageHandler private final Map exceptionHandlerAdviceCache = new LinkedHashMap(64); - private Collection destinationPrefixes = new ArrayList(); - - private ApplicationContext applicationContext; - - /** - * Return the configured destination prefixes. - */ - public Collection getDestinationPrefixes() { - return this.destinationPrefixes; - } /** * When this property is configured only messages to destinations matching @@ -131,16 +129,15 @@ public void setDestinationPrefixes(Collection prefixes) { } /** - * Return the configured custom argument resolvers, if any. + * Return the configured destination prefixes, if any. */ - public List getCustomArgumentResolvers() { - return this.customArgumentResolvers; + public Collection getDestinationPrefixes() { + return this.destinationPrefixes; } /** * Sets the list of custom {@code HandlerMethodArgumentResolver}s that will be used * after resolvers for supported argument type. - * @param customArgumentResolvers the list of resolvers; never {@code null}. */ public void setCustomArgumentResolvers(List customArgumentResolvers) { this.customArgumentResolvers.clear(); @@ -150,16 +147,15 @@ public void setCustomArgumentResolvers(List custo } /** - * Return the configured custom return value handlers, if any. + * Return the configured custom argument resolvers, if any. */ - public List getCustomReturnValueHandlers() { - return this.customReturnValueHandlers; + public List getCustomArgumentResolvers() { + return this.customArgumentResolvers; } /** * Set the list of custom {@code HandlerMethodReturnValueHandler}s that will be used * after return value handlers for known types. - * @param customReturnValueHandlers the list of custom return value handlers, never {@code null}. */ public void setCustomReturnValueHandlers(List customReturnValueHandlers) { this.customReturnValueHandlers.clear(); @@ -169,15 +165,15 @@ public void setCustomReturnValueHandlers(List c } /** - * Return the configured argument resolvers, if any. + * Return the configured custom return value handlers, if any. */ - public List getArgumentResolvers() { - return this.argumentResolvers.getResolvers(); + public List getCustomReturnValueHandlers() { + return this.customReturnValueHandlers; } /** - * Configure the complete list of supported argument types effectively overriding - * the ones configured by default. This is an advanced option. For most use cases + * Configure the complete list of supported argument types, effectively overriding + * the ones configured by default. This is an advanced option; for most use cases * it should be sufficient to use {@link #setCustomArgumentResolvers}. */ public void setArgumentResolvers(List argumentResolvers) { @@ -189,15 +185,15 @@ public void setArgumentResolvers(List argumentRes } /** - * Return the configured return value handlers, if any. + * Return the complete list of argument resolvers. */ - public List getReturnValueHandlers() { - return this.returnValueHandlers.getReturnValueHandlers(); + public List getArgumentResolvers() { + return this.argumentResolvers.getResolvers(); } /** - * Configure the complete list of supported return value types effectively overriding - * the ones configured by default. This is an advanced option. For most use cases + * Configure the complete list of supported return value types, effectively overriding + * the ones configured by default. This is an advanced option; for most use cases * it should be sufficient to use {@link #setCustomReturnValueHandlers}. */ public void setReturnValueHandlers(List returnValueHandlers) { @@ -208,8 +204,11 @@ public void setReturnValueHandlers(List returnV this.returnValueHandlers.addHandlers(returnValueHandlers); } - public ApplicationContext getApplicationContext() { - return this.applicationContext; + /** + * Return the complete list of return value handlers. + */ + public List getReturnValueHandlers() { + return this.returnValueHandlers.getReturnValueHandlers(); } @Override @@ -217,6 +216,11 @@ public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } + public ApplicationContext getApplicationContext() { + return this.applicationContext; + } + + @Override public void afterPropertiesSet() { if (this.argumentResolvers.getResolvers().isEmpty()) { @@ -359,7 +363,9 @@ protected HandlerMethod createHandlerMethod(Object handler, Method method) { * (e.g. to support "global" {@code @MessageExceptionHandler}). * @since 4.2 */ - protected void registerExceptionHandlerAdvice(MessagingAdviceBean bean, AbstractExceptionHandlerMethodResolver resolver) { + protected void registerExceptionHandlerAdvice( + MessagingAdviceBean bean, AbstractExceptionHandlerMethodResolver resolver) { + this.exceptionHandlerAdviceCache.put(bean, resolver); } From 4b5eedfeb4dd3583f14cbe146767e3569e3ffdb7 Mon Sep 17 00:00:00 2001 From: Spring Buildmaster Date: Mon, 4 Jul 2016 10:26:51 +0000 Subject: [PATCH 211/344] Next Development Version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 3edeafeecf..b6302e7656 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=4.2.7.BUILD-SNAPSHOT +version=4.2.8.BUILD-SNAPSHOT From 7dbb1ca466aad5fc3293a8626fec4dd2c7da5561 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Mon, 4 Jul 2016 12:42:54 +0200 Subject: [PATCH 212/344] Upgrade copyright --- src/asciidoc/index-docinfo.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/asciidoc/index-docinfo.xml b/src/asciidoc/index-docinfo.xml index 78f1c77c89..57d964cffb 100644 --- a/src/asciidoc/index-docinfo.xml +++ b/src/asciidoc/index-docinfo.xml @@ -1,7 +1,7 @@ Spring Framework {revnumber} - 2004-2015 + 2004-2016 Copies of this document may be made for your own use and for distribution to From 2e075baedee8355554ae91d062677940c9d514da Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 6 Jul 2016 15:12:18 +0200 Subject: [PATCH 213/344] Avoid canonicalName call for already-seen bean name Issue: SPR-14433 (cherry picked from commit 52065a7) --- .../beans/factory/support/DefaultSingletonBeanRegistry.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java index 9042bad5cb..d3d2134702 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -449,10 +449,10 @@ protected boolean isDependent(String beanName, String dependentBeanName) { } private boolean isDependent(String beanName, String dependentBeanName, Set alreadySeen) { - String canonicalName = canonicalName(beanName); if (alreadySeen != null && alreadySeen.contains(beanName)) { return false; } + String canonicalName = canonicalName(beanName); Set dependentBeans = this.dependentBeanMap.get(canonicalName); if (dependentBeans == null) { return false; From 0445ce6c53aab48324119c68ee347a64393d76d1 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 6 Jul 2016 15:29:15 +0200 Subject: [PATCH 214/344] Polishing (backported from master) (cherry picked from commit 92d78c1) --- build.gradle | 6 +- .../beans/TypeConverterDelegate.java | 2 +- .../support/DefaultListableBeanFactory.java | 2 +- .../factory/support/RootBeanDefinition.java | 4 +- .../cache/ehcache/package-info.java | 2 +- .../CachingConfigurationSelector.java | 8 +- ...tationDrivenCacheBeanDefinitionParser.java | 9 +- .../interceptor/CacheOperationInvoker.java | 5 +- .../AnnotatedBeanDefinitionReader.java | 12 +- .../groovy/GroovyScriptFactoryTests.java | 2 +- .../jruby-with-xsd-proxy-target-class.xml | 17 -- .../core/MethodIntrospector.java | 7 +- .../core/convert/TypeDescriptor.java | 4 +- .../org/springframework/util/Base64Utils.java | 8 +- .../org/springframework/util/DigestUtils.java | 5 +- .../util/UpdateMessageDigestInputStream.java | 4 +- .../CompletableToListenableFutureAdapter.java | 5 +- .../util/concurrent/FutureAdapter.java | 9 +- .../core/env/PropertySourceTests.java | 14 +- .../expression/spel/CodeFlow.java | 147 ++++++++++-------- .../expression/spel/ast/InlineList.java | 12 +- .../jdbc/core/simple/AbstractJdbcInsert.java | 3 +- .../jdbc/core/simple/SimpleJdbcInsert.java | 3 +- .../simple/SimpleJdbcInsertOperations.java | 3 +- ...ransactionAwareConnectionFactoryProxy.java | 4 +- .../endpoint/JmsMessageEndpointFactory.java | 4 +- .../AbstractMessageBrokerConfiguration.java | 2 +- .../AbstractMessageConverterTests.java | 136 ---------------- .../converter/MessageConverterTests.java | 5 +- .../orm/jpa/SharedEntityManagerCreator.java | 6 +- spring-orm/src/main/java/overview.html | 2 +- .../oxm/config/OxmNamespaceHandlerTests.java | 10 +- .../support/TestPropertySourceUtils.java | 58 +++---- .../test/jdbc/JdbcTestUtils.java | 7 +- .../client/ClientHttpRequestExecution.java | 13 +- .../json/Jackson2ObjectMapperBuilder.java | 5 +- .../jaxws/SimpleJaxWsServiceExporter.java | 6 +- .../bind/support/WebRequestDataBinder.java | 7 +- .../web/servlet/HandlerInterceptor.java | 8 +- .../web/servlet/LocaleResolver.java | 10 +- .../handler/BeanNameUrlHandlerMapping.java | 8 +- .../handler/HandlerInterceptorAdapter.java | 7 +- .../resource/CssLinkResourceTransformer.java | 21 ++- .../servlet/resource/ResourceTransformer.java | 6 +- ...plateServletAnnotationControllerTests.java | 21 +-- .../standard/StandardWebSocketClient.java | 4 +- .../WebMvcStompEndpointRegistry.java | 19 ++- .../AbstractTyrusRequestUpgradeStrategy.java | 8 +- .../WebLogicRequestUpgradeStrategy.java | 3 +- .../sockjs/client/AbstractXhrTransport.java | 9 +- .../session/PollingSockJsSession.java | 1 - .../web/socket/config/spring-websocket.gif | Bin 0 -> 1025 bytes .../AbstractWebSocketIntegrationTests.java | 13 +- ...ests.java => WebSocketHandshakeTests.java} | 13 +- 54 files changed, 283 insertions(+), 426 deletions(-) delete mode 100644 spring-context/src/test/resources/org/springframework/scripting/groovy/jruby-with-xsd-proxy-target-class.xml delete mode 100644 spring-messaging/src/test/java/org/springframework/messaging/converter/AbstractMessageConverterTests.java create mode 100644 spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket.gif rename spring-websocket/src/test/java/org/springframework/web/socket/{WebSocketIntegrationTests.java => WebSocketHandshakeTests.java} (95%) diff --git a/build.gradle b/build.gradle index 84067b7a82..0b54de0f4c 100644 --- a/build.gradle +++ b/build.gradle @@ -879,10 +879,10 @@ project("spring-webmvc") { testCompile("commons-io:commons-io:1.3") testCompile("joda-time:joda-time:${jodaVersion}") testCompile("org.slf4j:slf4j-jcl:${slf4jVersion}") - testCompile("org.jruby:jruby:${jrubyVersion}") - testCompile("org.python:jython-standalone:2.5.3") testCompile("org.mozilla:rhino:1.7.7.1") - testCompile("org.webjars:underscorejs:1.8.3") + testRuntime("org.jruby:jruby:${jrubyVersion}") + testRuntime("org.python:jython-standalone:2.5.3") + testRuntime("org.webjars:underscorejs:1.8.3") } } diff --git a/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java b/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java index dd63e8e9c6..f55bc5f1c1 100644 --- a/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java +++ b/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java @@ -266,7 +266,7 @@ else if (convertedValue instanceof Number && Number.class.isAssignableFrom(requi } else { // convertedValue == null - if (javaUtilOptionalEmpty != null && requiredType.equals(javaUtilOptionalEmpty.getClass())) { + if (javaUtilOptionalEmpty != null && requiredType == javaUtilOptionalEmpty.getClass()) { convertedValue = javaUtilOptionalEmpty; } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index 7a32060e28..d74efb199c 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 @@ -999,7 +999,7 @@ public Object resolveDependency(DependencyDescriptor descriptor, String beanName Set autowiredBeanNames, TypeConverter typeConverter) throws BeansException { descriptor.initParameterNameDiscovery(getParameterNameDiscoverer()); - if (descriptor.getDependencyType().equals(javaUtilOptionalClass)) { + if (javaUtilOptionalClass == descriptor.getDependencyType()) { return new OptionalDependencyFactory().createOptionalDependency(descriptor, beanName); } else if (ObjectFactory.class == descriptor.getDependencyType()) { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java index d2ac971cbc..91ad5d8e96 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -125,7 +125,7 @@ public RootBeanDefinition(Class beanClass, int autowireMode, boolean dependen setBeanClass(beanClass); setAutowireMode(autowireMode); if (dependencyCheck && getResolvedAutowireMode() != AUTOWIRE_CONSTRUCTOR) { - setDependencyCheck(RootBeanDefinition.DEPENDENCY_CHECK_OBJECTS); + setDependencyCheck(DEPENDENCY_CHECK_OBJECTS); } } diff --git a/spring-context-support/src/main/java/org/springframework/cache/ehcache/package-info.java b/spring-context-support/src/main/java/org/springframework/cache/ehcache/package-info.java index 4291b2deae..edb951a4ce 100644 --- a/spring-context-support/src/main/java/org/springframework/cache/ehcache/package-info.java +++ b/spring-context-support/src/main/java/org/springframework/cache/ehcache/package-info.java @@ -7,6 +7,6 @@ *

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

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

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

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

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

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

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

    Using this method is more optimized since it avoids creating new * byte arrays for each call. - * @param messageDigest The message digest to update + * @param messageDigest the message digest to update * @param len how many bytes to read from this stream and use to update the message digest * @throws IOException when propagated from {@link #read()} */ diff --git a/spring-core/src/main/java/org/springframework/util/concurrent/CompletableToListenableFutureAdapter.java b/spring-core/src/main/java/org/springframework/util/concurrent/CompletableToListenableFutureAdapter.java index f42a88f4af..1a6cd14fbd 100644 --- a/spring-core/src/main/java/org/springframework/util/concurrent/CompletableToListenableFutureAdapter.java +++ b/spring-core/src/main/java/org/springframework/util/concurrent/CompletableToListenableFutureAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,6 @@ import org.springframework.lang.UsesJava8; - /** * Adapts a {@link CompletableFuture} into a {@link ListenableFuture}. * @@ -38,6 +37,7 @@ public class CompletableToListenableFutureAdapter implements ListenableFuture private final ListenableFutureCallbackRegistry callbacks = new ListenableFutureCallbackRegistry(); + public CompletableToListenableFutureAdapter(CompletableFuture completableFuture) { this.completableFuture = completableFuture; this.completableFuture.handle(new BiFunction() { @@ -54,6 +54,7 @@ public Object apply(T result, Throwable ex) { }); } + @Override public void addCallback(ListenableFutureCallback callback) { this.callbacks.addCallback(callback); diff --git a/spring-core/src/main/java/org/springframework/util/concurrent/FutureAdapter.java b/spring-core/src/main/java/org/springframework/util/concurrent/FutureAdapter.java index 37aac9310e..2a36c9e604 100644 --- a/spring-core/src/main/java/org/springframework/util/concurrent/FutureAdapter.java +++ b/spring-core/src/main/java/org/springframework/util/concurrent/FutureAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,10 +24,9 @@ import org.springframework.util.Assert; /** - * Abstract class that adapts a {@link Future} parameterized over S into a {@code - * Future} parameterized over T. All methods are delegated to the adaptee, where {@link - * #get()} and {@link #get(long, TimeUnit)} call {@link #adapt(Object)} on the adaptee's - * result. + * Abstract class that adapts a {@link Future} parameterized over S into a {@code Future} + * parameterized over T. All methods are delegated to the adaptee, where {@link #get()} + * and {@link #get(long, TimeUnit)} call {@link #adapt(Object)} on the adaptee's result. * * @author Arjen Poutsma * @since 4.0 diff --git a/spring-core/src/test/java/org/springframework/core/env/PropertySourceTests.java b/spring-core/src/test/java/org/springframework/core/env/PropertySourceTests.java index 28a51d0543..c6a68aa864 100644 --- a/spring-core/src/test/java/org/springframework/core/env/PropertySourceTests.java +++ b/spring-core/src/test/java/org/springframework/core/env/PropertySourceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,13 +30,15 @@ import static org.junit.Assert.*; /** - * Unit tests for {@link AbstractPropertySource} implementations. + * Unit tests for {@link PropertySource} implementations. * * @author Chris Beams * @since 3.1 */ public class PropertySourceTests { - @Test @SuppressWarnings("serial") + + @Test + @SuppressWarnings("serial") public void equals() { Map map1 = new HashMap() {{ put("a", "b"); }}; Map map2 = new HashMap() {{ put("c", "d"); }}; @@ -59,14 +61,15 @@ public void equals() { assertThat(new MapPropertySource("x", map1).equals(new PropertiesPropertySource("y", props2)), is(false)); } - @Test @SuppressWarnings("serial") + @Test + @SuppressWarnings("serial") public void collectionsOperations() { Map map1 = new HashMap() {{ put("a", "b"); }}; Map map2 = new HashMap() {{ put("c", "d"); }}; PropertySource ps1 = new MapPropertySource("ps1", map1); ps1.getSource(); - List> propertySources = new ArrayList>(); + List> propertySources = new ArrayList<>(); assertThat(propertySources.add(ps1), equalTo(true)); assertThat(propertySources.contains(ps1), is(true)); assertThat(propertySources.contains(PropertySource.named("ps1")), is(true)); @@ -116,4 +119,5 @@ public void toString_verbosityVariesOnLogLevel() { logger.setLevel(original); } } + } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java b/spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java index 2d20e195d1..decb2f1cb4 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -147,6 +147,72 @@ public void unboxBooleanIfNecessary(MethodVisitor mv) { } } + /** + * Called after the main expression evaluation method has been generated, this + * method will callback any registered FieldAdders or ClinitAdders to add any + * extra information to the class representing the compiled expression. + */ + public void finish() { + if (this.fieldAdders != null) { + for (FieldAdder fieldAdder : this.fieldAdders) { + fieldAdder.generateField(cw,this); + } + } + if (this.clinitAdders != null) { + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "", "()V", null, null); + mv.visitCode(); + this.nextFreeVariableId = 0; // To 0 because there is no 'this' in a clinit + for (ClinitAdder clinitAdder : this.clinitAdders) { + clinitAdder.generateCode(mv, this); + } + mv.visitInsn(RETURN); + mv.visitMaxs(0,0); // not supplied due to COMPUTE_MAXS + mv.visitEnd(); + } + } + + /** + * Register a FieldAdder which will add a new field to the generated + * class to support the code produced by an ast nodes primary + * generateCode() method. + */ + public void registerNewField(FieldAdder fieldAdder) { + if (this.fieldAdders == null) { + this.fieldAdders = new ArrayList(); + } + this.fieldAdders.add(fieldAdder); + } + + /** + * Register a ClinitAdder which will add code to the static + * initializer in the generated class to support the code + * produced by an ast nodes primary generateCode() method. + */ + public void registerNewClinit(ClinitAdder clinitAdder) { + if (this.clinitAdders == null) { + this.clinitAdders = new ArrayList(); + } + this.clinitAdders.add(clinitAdder); + } + + public int nextFieldId() { + return this.nextFieldId++; + } + + public int nextFreeVariableId() { + return this.nextFreeVariableId++; + } + + public String getClassName() { + return this.clazzName; + } + + @Deprecated + public String getClassname() { + return this.clazzName; + } + + /** * Insert any necessary cast and value call to convert from a boxed type to a * primitive value @@ -778,74 +844,6 @@ public static String[] toDescriptors(Class[] types) { return descriptors; } - /** - * Called after the main expression evaluation method has been generated, this - * method will callback any registered FieldAdders or ClinitAdders to add any - * extra information to the class representing the compiled expression. - */ - public void finish() { - if (fieldAdders != null) { - for (FieldAdder fieldAdder: fieldAdders) { - fieldAdder.generateField(cw,this); - } - } - if (clinitAdders != null) { - MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "", "()V", null, null); - mv.visitCode(); - nextFreeVariableId = 0; // To 0 because there is no 'this' in a clinit - for (ClinitAdder clinitAdder: clinitAdders) { - clinitAdder.generateCode(mv, this); - } - mv.visitInsn(RETURN); - mv.visitMaxs(0,0); // not supplied due to COMPUTE_MAXS - mv.visitEnd(); - } - } - - /** - * Register a FieldAdder which will add a new field to the generated - * class to support the code produced by an ast nodes primary - * generateCode() method. - */ - public void registerNewField(FieldAdder fieldAdder) { - if (fieldAdders == null) { - fieldAdders = new ArrayList(); - } - fieldAdders.add(fieldAdder); - } - - /** - * Register a ClinitAdder which will add code to the static - * initializer in the generated class to support the code - * produced by an ast nodes primary generateCode() method. - */ - public void registerNewClinit(ClinitAdder clinitAdder) { - if (clinitAdders == null) { - clinitAdders = new ArrayList(); - } - clinitAdders.add(clinitAdder); - } - - public int nextFieldId() { - return nextFieldId++; - } - - public int nextFreeVariableId() { - return nextFreeVariableId++; - } - - public String getClassname() { - return clazzName; - } - - public interface FieldAdder { - public void generateField(ClassWriter cw, CodeFlow codeflow); - } - - public interface ClinitAdder { - public void generateCode(MethodVisitor mv, CodeFlow codeflow); - } - /** * Create the optimal instruction for loading a number on the stack. * @param mv where to insert the bytecode @@ -977,4 +975,15 @@ public static void insertNumericUnboxOrPrimitiveTypeCoercion( } + public interface FieldAdder { + + void generateField(ClassWriter cw, CodeFlow codeflow); + } + + + public interface ClinitAdder { + + void generateCode(MethodVisitor mv, CodeFlow codeflow); + } + } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/InlineList.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/InlineList.java index b4134b6351..1a435b861d 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/InlineList.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/InlineList.java @@ -132,8 +132,8 @@ public boolean isCompilable() { @Override public void generateCode(MethodVisitor mv, CodeFlow codeflow) { - final String constantFieldName = "inlineList$"+codeflow.nextFieldId(); - final String clazzname = codeflow.getClassname(); + final String constantFieldName = "inlineList$" + codeflow.nextFieldId(); + final String className = codeflow.getClassName(); codeflow.registerNewField(new CodeFlow.FieldAdder() { public void generateField(ClassWriter cw, CodeFlow codeflow) { @@ -143,11 +143,11 @@ public void generateField(ClassWriter cw, CodeFlow codeflow) { codeflow.registerNewClinit(new CodeFlow.ClinitAdder() { public void generateCode(MethodVisitor mv, CodeFlow codeflow) { - generateClinitCode(clazzname,constantFieldName, mv,codeflow,false); + generateClinitCode(className, constantFieldName, mv, codeflow, false); } }); - mv.visitFieldInsn(GETSTATIC, clazzname, constantFieldName, "Ljava/util/List;"); + mv.visitFieldInsn(GETSTATIC, className, constantFieldName, "Ljava/util/List;"); codeflow.pushDescriptor("Ljava/util/List"); } @@ -158,8 +158,8 @@ void generateClinitCode(String clazzname, String constantFieldName, MethodVisito if (!nested) { mv.visitFieldInsn(PUTSTATIC, clazzname, constantFieldName, "Ljava/util/List;"); } - int childcount = getChildCount(); - for (int c=0; c < childcount; c++) { + int childCount = getChildCount(); + for (int c = 0; c < childCount; c++) { if (!nested) { mv.visitFieldInsn(GETSTATIC, clazzname, constantFieldName, "Ljava/util/List;"); } diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/AbstractJdbcInsert.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/AbstractJdbcInsert.java index 3af9f4635a..3d7c25f1d3 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/AbstractJdbcInsert.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/AbstractJdbcInsert.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -542,6 +542,7 @@ private PreparedStatement prepareStatementForGeneratedKeys(Connection con) throw * @param batch array of Maps with parameter names and values to be used in batch insert * @return array of number of rows affected */ + @SuppressWarnings("unchecked") protected int[] doExecuteBatch(Map... batch) { checkCompiled(); List> batchValues = new ArrayList>(batch.length); diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcInsert.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcInsert.java index d178cc615d..f56d7b8b60 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcInsert.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcInsert.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -148,6 +148,7 @@ public KeyHolder executeAndReturnKeyHolder(SqlParameterSource parameterSource) { } @Override + @SuppressWarnings("unchecked") public int[] executeBatch(Map... batch) { return doExecuteBatch(batch); } diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcInsertOperations.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcInsertOperations.java index 0e1dd15ceb..645a2a9785 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcInsertOperations.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/simple/SimpleJdbcInsertOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -152,6 +152,7 @@ public interface SimpleJdbcInsertOperations { * @param batch an array of Maps containing a batch of column names and corresponding value * @return the array of number of rows affected as returned by the JDBC driver */ + @SuppressWarnings("unchecked") int[] executeBatch(Map... batch); /** diff --git a/spring-jms/src/main/java/org/springframework/jms/connection/TransactionAwareConnectionFactoryProxy.java b/spring-jms/src/main/java/org/springframework/jms/connection/TransactionAwareConnectionFactoryProxy.java index 50c6140925..19a9ffc041 100644 --- a/spring-jms/src/main/java/org/springframework/jms/connection/TransactionAwareConnectionFactoryProxy.java +++ b/spring-jms/src/main/java/org/springframework/jms/connection/TransactionAwareConnectionFactoryProxy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -101,7 +101,7 @@ public TransactionAwareConnectionFactoryProxy(ConnectionFactory targetConnection * Set the target ConnectionFactory that this ConnectionFactory should delegate to. */ public final void setTargetConnectionFactory(ConnectionFactory targetConnectionFactory) { - Assert.notNull(targetConnectionFactory, "targetConnectionFactory must not be nul"); + Assert.notNull(targetConnectionFactory, "'targetConnectionFactory' must not be null"); this.targetConnectionFactory = targetConnectionFactory; } diff --git a/spring-jms/src/main/java/org/springframework/jms/listener/endpoint/JmsMessageEndpointFactory.java b/spring-jms/src/main/java/org/springframework/jms/listener/endpoint/JmsMessageEndpointFactory.java index fe448aa2bc..5beb87333e 100644 --- a/spring-jms/src/main/java/org/springframework/jms/listener/endpoint/JmsMessageEndpointFactory.java +++ b/spring-jms/src/main/java/org/springframework/jms/listener/endpoint/JmsMessageEndpointFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -61,7 +61,7 @@ public void setMessageListener(MessageListener messageListener) { * Return the JMS MessageListener for this endpoint. */ protected MessageListener getMessageListener() { - return messageListener; + return this.messageListener; } /** diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/config/AbstractMessageBrokerConfiguration.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/config/AbstractMessageBrokerConfiguration.java index fa475b6811..5c6e9793cd 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/config/AbstractMessageBrokerConfiguration.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/config/AbstractMessageBrokerConfiguration.java @@ -85,7 +85,7 @@ public abstract class AbstractMessageBrokerConfiguration implements ApplicationC private static final String MVC_VALIDATOR_NAME = "mvcValidator"; - private static final boolean jackson2Present= ClassUtils.isPresent( + private static final boolean jackson2Present = ClassUtils.isPresent( "com.fasterxml.jackson.databind.ObjectMapper", AbstractMessageBrokerConfiguration.class.getClassLoader()); diff --git a/spring-messaging/src/test/java/org/springframework/messaging/converter/AbstractMessageConverterTests.java b/spring-messaging/src/test/java/org/springframework/messaging/converter/AbstractMessageConverterTests.java deleted file mode 100644 index 3f2524e81b..0000000000 --- a/spring-messaging/src/test/java/org/springframework/messaging/converter/AbstractMessageConverterTests.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2002-2013 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.messaging.converter; - -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import org.junit.Before; -import org.junit.Test; - -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.util.MimeType; -import org.springframework.util.MimeTypeUtils; - -import static org.junit.Assert.*; - -/** - * Test fixture for {@link org.springframework.messaging.converter.AbstractMessageConverter}. - * - * @author Rossen Stoyanchev - */ -public class AbstractMessageConverterTests { - - private TestMessageConverter converter; - - - @Before - public void setup() { - this.converter = new TestMessageConverter(); - this.converter.setContentTypeResolver(new DefaultContentTypeResolver()); - } - - @Test - public void supportsTargetClass() { - Message message = MessageBuilder.withPayload("ABC").build(); - - assertEquals("success-from", this.converter.fromMessage(message, String.class)); - assertNull(this.converter.fromMessage(message, Integer.class)); - } - - @Test - public void supportsMimeType() { - Message message = MessageBuilder.withPayload( - "ABC").setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.TEXT_PLAIN).build(); - - assertEquals("success-from", this.converter.fromMessage(message, String.class)); - } - - @Test - public void supportsMimeTypeNotSupported() { - Message message = MessageBuilder.withPayload( - "ABC").setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON).build(); - - assertNull(this.converter.fromMessage(message, String.class)); - } - - @Test - public void supportsMimeTypeNotSpecified() { - Message message = MessageBuilder.withPayload("ABC").build(); - assertEquals("success-from", this.converter.fromMessage(message, String.class)); - } - - @Test - public void supportsMimeTypeNoneConfigured() { - - Message message = MessageBuilder.withPayload( - "ABC").setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON).build(); - - this.converter = new TestMessageConverter(Collections.emptyList()); - this.converter.setContentTypeResolver(new DefaultContentTypeResolver()); - - assertEquals("success-from", this.converter.fromMessage(message, String.class)); - } - - @Test - public void toMessageHeadersCopied() { - Map map = new HashMap(); - map.put("foo", "bar"); - MessageHeaders headers = new MessageHeaders(map ); - Message message = this.converter.toMessage("ABC", headers); - - assertEquals("bar", message.getHeaders().get("foo")); - } - - @Test - public void toMessageContentTypeHeader() { - Message message = this.converter.toMessage("ABC", null); - assertEquals(MimeTypeUtils.TEXT_PLAIN, message.getHeaders().get(MessageHeaders.CONTENT_TYPE)); - } - - - private static class TestMessageConverter extends AbstractMessageConverter { - - public TestMessageConverter() { - super(MimeTypeUtils.TEXT_PLAIN); - } - - public TestMessageConverter(Collection supportedMimeTypes) { - super(supportedMimeTypes); - } - - @Override - protected boolean supports(Class clazz) { - return String.class.equals(clazz); - } - - @Override - public Object convertFromInternal(Message message, Class targetClass) { - return "success-from"; - } - - @Override - public Object convertToInternal(Object payload, MessageHeaders headers) { - return "success-to"; - } - } - -} diff --git a/spring-messaging/src/test/java/org/springframework/messaging/converter/MessageConverterTests.java b/spring-messaging/src/test/java/org/springframework/messaging/converter/MessageConverterTests.java index f10f2a174e..be6d9cf672 100644 --- a/spring-messaging/src/test/java/org/springframework/messaging/converter/MessageConverterTests.java +++ b/spring-messaging/src/test/java/org/springframework/messaging/converter/MessageConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,6 +44,7 @@ public class MessageConverterTests { private TestMessageConverter converter = new TestMessageConverter(); + @Test public void supportsTargetClass() { Message message = MessageBuilder.withPayload("ABC").build(); @@ -105,7 +106,7 @@ public void setStrictContentTypeMatchWithNoSupportedMimeTypes() { @Test public void toMessageWithHeaders() { - Map map = new HashMap(); + Map map = new HashMap<>(); map.put("foo", "bar"); MessageHeaders headers = new MessageHeaders(map); Message message = this.converter.toMessage("ABC", headers); diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/SharedEntityManagerCreator.java b/spring-orm/src/main/java/org/springframework/orm/jpa/SharedEntityManagerCreator.java index cbc5e6be3d..0a474255a0 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/SharedEntityManagerCreator.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/SharedEntityManagerCreator.java @@ -115,11 +115,11 @@ public static EntityManager createSharedEntityManager(EntityManagerFactory emf, */ public static EntityManager createSharedEntityManager( EntityManagerFactory emf, Map properties, boolean synchronizedWithTransaction) { - Class entityManagerInterface = (emf instanceof EntityManagerFactoryInfo ? + + Class emIfc = (emf instanceof EntityManagerFactoryInfo ? ((EntityManagerFactoryInfo) emf).getEntityManagerInterface() : EntityManager.class); return createSharedEntityManager(emf, properties, synchronizedWithTransaction, - (entityManagerInterface == null ? NO_ENTITY_MANAGER_INTERFACES : - new Class[] { entityManagerInterface })); + (emIfc == null ? NO_ENTITY_MANAGER_INTERFACES : new Class[] {emIfc})); } /** diff --git a/spring-orm/src/main/java/overview.html b/spring-orm/src/main/java/overview.html index 37f532b39b..67b448a906 100644 --- a/spring-orm/src/main/java/overview.html +++ b/spring-orm/src/main/java/overview.html @@ -1,7 +1,7 @@

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

    \ No newline at end of file diff --git a/spring-oxm/src/test/java/org/springframework/oxm/config/OxmNamespaceHandlerTests.java b/spring-oxm/src/test/java/org/springframework/oxm/config/OxmNamespaceHandlerTests.java index 9595d8087e..4a6a46ba79 100644 --- a/spring-oxm/src/test/java/org/springframework/oxm/config/OxmNamespaceHandlerTests.java +++ b/spring-oxm/src/test/java/org/springframework/oxm/config/OxmNamespaceHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,8 +37,9 @@ @SuppressWarnings("deprecation") public class OxmNamespaceHandlerTests { - private final ApplicationContext applicationContext = new ClassPathXmlApplicationContext( - "oxmNamespaceHandlerTest.xml", getClass()); + private final ApplicationContext applicationContext = + new ClassPathXmlApplicationContext("oxmNamespaceHandlerTest.xml", getClass()); + @Test public void xmlBeansMarshaller() throws Exception { @@ -85,4 +86,5 @@ public void castorMappingLocationMarshaller() throws Exception { CastorMarshaller castorMarshaller = applicationContext.getBean("castorMappingLocationMarshaller", CastorMarshaller.class); assertNotNull(castorMarshaller); } -} \ No newline at end of file + +} diff --git a/spring-test/src/main/java/org/springframework/test/context/support/TestPropertySourceUtils.java b/spring-test/src/main/java/org/springframework/test/context/support/TestPropertySourceUtils.java index 1619ea8458..328a61c36b 100644 --- a/spring-test/src/main/java/org/springframework/test/context/support/TestPropertySourceUtils.java +++ b/spring-test/src/main/java/org/springframework/test/context/support/TestPropertySourceUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,7 +38,7 @@ import org.springframework.core.io.support.ResourcePropertySource; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.util.TestContextResourceUtils; -import org.springframework.test.util.MetaAnnotationUtils.AnnotationDescriptor; +import org.springframework.test.util.MetaAnnotationUtils.*; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -57,19 +57,15 @@ */ public abstract class TestPropertySourceUtils { - private static final Log logger = LogFactory.getLog(TestPropertySourceUtils.class); - /** * The name of the {@link MapPropertySource} created from inlined properties. * @since 4.1.5 - * @see {@link #addInlinedPropertiesToEnvironment(ConfigurableEnvironment, String[])} + * @see #addInlinedPropertiesToEnvironment */ public static final String INLINED_PROPERTIES_PROPERTY_SOURCE_NAME = "Inlined Test Properties"; + private static final Log logger = LogFactory.getLog(TestPropertySourceUtils.class); - private TestPropertySourceUtils() { - /* no-op */ - } static MergedTestPropertySources buildMergedTestPropertySources(Class testClass) { Class annotationType = TestPropertySource.class; @@ -168,15 +164,14 @@ private static String[] mergeProperties(List attri * never {@code null} * @param locations the resource locations of {@code Properties} files to add * to the environment; potentially empty but never {@code null} + * @throws IllegalStateException if an error occurs while processing a properties file * @since 4.1.5 * @see ResourcePropertySource * @see TestPropertySource#locations - * @throws IllegalStateException if an error occurs while processing a properties file */ - public static void addPropertiesFilesToEnvironment(ConfigurableApplicationContext context, - String[] locations) { - Assert.notNull(context, "context must not be null"); - Assert.notNull(locations, "locations must not be null"); + public static void addPropertiesFilesToEnvironment(ConfigurableApplicationContext context, String... locations) { + Assert.notNull(context, "'context' must not be null"); + Assert.notNull(locations, "'locations' must not be null"); try { ConfigurableEnvironment environment = context.getEnvironment(); for (String location : locations) { @@ -185,8 +180,8 @@ public static void addPropertiesFilesToEnvironment(ConfigurableApplicationContex environment.getPropertySources().addFirst(new ResourcePropertySource(resource)); } } - catch (IOException e) { - throw new IllegalStateException("Failed to add PropertySource to Environment", e); + catch (IOException ex) { + throw new IllegalStateException("Failed to add PropertySource to Environment", ex); } } @@ -203,10 +198,9 @@ public static void addPropertiesFilesToEnvironment(ConfigurableApplicationContex * @see TestPropertySource#properties * @see #addInlinedPropertiesToEnvironment(ConfigurableEnvironment, String[]) */ - public static void addInlinedPropertiesToEnvironment(ConfigurableApplicationContext context, - String[] inlinedProperties) { - Assert.notNull(context, "context must not be null"); - Assert.notNull(inlinedProperties, "inlinedProperties must not be null"); + public static void addInlinedPropertiesToEnvironment(ConfigurableApplicationContext context, String... inlinedProperties) { + Assert.notNull(context, "'context' must not be null"); + Assert.notNull(inlinedProperties, "'inlinedProperties' must not be null"); addInlinedPropertiesToEnvironment(context.getEnvironment(), inlinedProperties); } @@ -226,16 +220,16 @@ public static void addInlinedPropertiesToEnvironment(ConfigurableApplicationCont * @see TestPropertySource#properties * @see #convertInlinedPropertiesToMap */ - public static void addInlinedPropertiesToEnvironment(ConfigurableEnvironment environment, String[] inlinedProperties) { - Assert.notNull(environment, "environment must not be null"); - Assert.notNull(inlinedProperties, "inlinedProperties must not be null"); + public static void addInlinedPropertiesToEnvironment(ConfigurableEnvironment environment, String... inlinedProperties) { + Assert.notNull(environment, "'environment' must not be null"); + Assert.notNull(inlinedProperties, "'inlinedProperties' must not be null"); if (!ObjectUtils.isEmpty(inlinedProperties)) { if (logger.isDebugEnabled()) { - logger.debug("Adding inlined properties to environment: " - + ObjectUtils.nullSafeToString(inlinedProperties)); + logger.debug("Adding inlined properties to environment: " + + ObjectUtils.nullSafeToString(inlinedProperties)); } MapPropertySource ps = new MapPropertySource(INLINED_PROPERTIES_PROPERTY_SOURCE_NAME, - convertInlinedPropertiesToMap(inlinedProperties)); + convertInlinedPropertiesToMap(inlinedProperties)); environment.getPropertySources().addFirst(ps); } } @@ -257,24 +251,22 @@ public static void addInlinedPropertiesToEnvironment(ConfigurableEnvironment env * a given inlined property contains multiple key-value pairs * @see #addInlinedPropertiesToEnvironment(ConfigurableEnvironment, String[]) */ - public static Map convertInlinedPropertiesToMap(String[] inlinedProperties) { - Assert.notNull(inlinedProperties, "inlinedProperties must not be null"); + public static Map convertInlinedPropertiesToMap(String... inlinedProperties) { + Assert.notNull(inlinedProperties, "'inlinedProperties' must not be null"); Map map = new LinkedHashMap(); - Properties props = new Properties(); + for (String pair : inlinedProperties) { if (!StringUtils.hasText(pair)) { continue; } - try { props.load(new StringReader(pair)); } - catch (Exception e) { - throw new IllegalStateException("Failed to load test environment property from [" + pair + "].", e); + catch (Exception ex) { + throw new IllegalStateException("Failed to load test environment property from [" + pair + "]", ex); } - Assert.state(props.size() == 1, "Failed to load exactly one test environment property from [" + pair + "]."); - + Assert.state(props.size() == 1, "Failed to load exactly one test environment property from [" + pair + "]"); for (String name : props.stringPropertyNames()) { map.put(name, props.getProperty(name)); } diff --git a/spring-test/src/main/java/org/springframework/test/jdbc/JdbcTestUtils.java b/spring-test/src/main/java/org/springframework/test/jdbc/JdbcTestUtils.java index 8e3e9a05c8..e0e1bc0e65 100644 --- a/spring-test/src/main/java/org/springframework/test/jdbc/JdbcTestUtils.java +++ b/spring-test/src/main/java/org/springframework/test/jdbc/JdbcTestUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -124,6 +124,7 @@ public static int deleteFromTables(JdbcTemplate jdbcTemplate, String... tableNam */ public static int deleteFromTableWhere(JdbcTemplate jdbcTemplate, String tableName, String whereClause, Object... args) { + String sql = "DELETE FROM " + tableName; if (StringUtils.hasText(whereClause)) { sql += " WHERE " + whereClause; @@ -170,6 +171,7 @@ public static void dropTables(JdbcTemplate jdbcTemplate, String... tableNames) { @Deprecated public static void executeSqlScript(JdbcTemplate jdbcTemplate, ResourceLoader resourceLoader, String sqlResourcePath, boolean continueOnError) throws DataAccessException { + Resource resource = resourceLoader.getResource(sqlResourcePath); executeSqlScript(jdbcTemplate, resource, continueOnError); } @@ -197,6 +199,7 @@ public static void executeSqlScript(JdbcTemplate jdbcTemplate, ResourceLoader re @Deprecated public static void executeSqlScript(JdbcTemplate jdbcTemplate, Resource resource, boolean continueOnError) throws DataAccessException { + executeSqlScript(jdbcTemplate, new EncodedResource(resource), continueOnError); } @@ -220,6 +223,7 @@ public static void executeSqlScript(JdbcTemplate jdbcTemplate, Resource resource @Deprecated public static void executeSqlScript(JdbcTemplate jdbcTemplate, EncodedResource resource, boolean continueOnError) throws DataAccessException { + new ResourceDatabasePopulator(continueOnError, false, resource.getEncoding(), resource.getResource()).execute(jdbcTemplate.getDataSource()); } @@ -288,4 +292,5 @@ public static boolean containsSqlScriptDelimiters(String script, char delim) { public static void splitSqlScript(String script, char delim, List statements) { ScriptUtils.splitSqlScript(script, delim, statements); } + } diff --git a/spring-web/src/main/java/org/springframework/http/client/ClientHttpRequestExecution.java b/spring-web/src/main/java/org/springframework/http/client/ClientHttpRequestExecution.java index df6e10c6f0..2fe64f5287 100644 --- a/spring-web/src/main/java/org/springframework/http/client/ClientHttpRequestExecution.java +++ b/spring-web/src/main/java/org/springframework/http/client/ClientHttpRequestExecution.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,22 +23,23 @@ /** * Represents the context of a client-side HTTP request execution. * - *

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

    Used to invoke the next interceptor in the interceptor chain, + * or - if the calling interceptor is last - execute the request itself. * * @author Arjen Poutsma - * @see ClientHttpRequestInterceptor * @since 3.1 + * @see ClientHttpRequestInterceptor */ public interface ClientHttpRequestExecution { /** - * Execute the request with the given request attributes and body, and return the response. - * + * Execute the request with the given request attributes and body, + * and return the response. * @param request the request, containing method, URI, and headers * @param body the body of the request to execute * @return the response * @throws IOException in case of I/O errors */ ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException; + } diff --git a/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java b/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java index b2ee1e5b7f..d172855da8 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java +++ b/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -279,8 +279,7 @@ public Jackson2ObjectMapperBuilder mixIns(Map, Class> mixIns) { /** * Configure custom serializers. Each serializer is registered for the type - * returned by {@link JsonSerializer#handledType()}, which must not be - * {@code null}. + * returned by {@link JsonSerializer#handledType()}, which must not be {@code null}. * @see #serializersByType(Map) */ public Jackson2ObjectMapperBuilder serializers(JsonSerializer... serializers) { diff --git a/spring-web/src/main/java/org/springframework/remoting/jaxws/SimpleJaxWsServiceExporter.java b/spring-web/src/main/java/org/springframework/remoting/jaxws/SimpleJaxWsServiceExporter.java index 08c47e614c..8746786f14 100644 --- a/spring-web/src/main/java/org/springframework/remoting/jaxws/SimpleJaxWsServiceExporter.java +++ b/spring-web/src/main/java/org/springframework/remoting/jaxws/SimpleJaxWsServiceExporter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,9 +31,9 @@ *

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

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

    For explicit configuration of JAX-WS endpoints with Sun's JDK 6 * HTTP server, consider using {@link SimpleHttpServerJaxWsServiceExporter}! * * @author Juergen Hoeller diff --git a/spring-web/src/main/java/org/springframework/web/bind/support/WebRequestDataBinder.java b/spring-web/src/main/java/org/springframework/web/bind/support/WebRequestDataBinder.java index 94b40ae979..ea3c0ae844 100644 --- a/spring-web/src/main/java/org/springframework/web/bind/support/WebRequestDataBinder.java +++ b/spring-web/src/main/java/org/springframework/web/bind/support/WebRequestDataBinder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -70,6 +70,9 @@ */ public class WebRequestDataBinder extends WebDataBinder { + private static final boolean servlet3Parts = ClassUtils.hasMethod(HttpServletRequest.class, "getParts"); + + /** * Create a new WebRequestDataBinder instance, with default object name. * @param target the target object to bind onto (or {@code null} @@ -116,7 +119,7 @@ public void bind(WebRequest request) { if (multipartRequest != null) { bindMultipart(multipartRequest.getMultiFileMap(), mpvs); } - else if (ClassUtils.hasMethod(HttpServletRequest.class, "getParts")) { + else if (servlet3Parts) { HttpServletRequest serlvetRequest = ((NativeWebRequest) request).getNativeRequest(HttpServletRequest.class); new Servlet3MultipartHelper(isBindEmptyMultipartFiles()).bindParts(serlvetRequest, mpvs); } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/HandlerInterceptor.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/HandlerInterceptor.java index 6f8e803674..e49eeafbfb 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/HandlerInterceptor.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/HandlerInterceptor.java @@ -93,7 +93,7 @@ public interface HandlerInterceptor { * @throws Exception in case of errors */ boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) - throws Exception; + throws Exception; /** * Intercept the execution of a handler. Called after HandlerAdapter actually @@ -114,7 +114,8 @@ boolean preHandle(HttpServletRequest request, HttpServletResponse response, Obje * (can also be {@code null}) * @throws Exception in case of errors */ - void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) + void postHandle( + HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception; /** @@ -136,7 +137,8 @@ void postHandle(HttpServletRequest request, HttpServletResponse response, Object * @param ex exception thrown on handler execution, if any * @throws Exception in case of errors */ - void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) + void afterCompletion( + HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception; } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/LocaleResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/LocaleResolver.java index 80998d0f6e..2d0f954022 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/LocaleResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/LocaleResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -51,8 +51,8 @@ public interface LocaleResolver { /** - * Resolve the current locale via the given request. Can return a default locale as - * fallback in any case. + * Resolve the current locale via the given request. + * Can return a default locale as fallback in any case. * @param request the request to resolve the locale for * @return the current locale (never {@code null}) */ @@ -63,8 +63,8 @@ public interface LocaleResolver { * @param request the request to be used for locale modification * @param response the response to be used for locale modification * @param locale the new locale, or {@code null} to clear the locale - * @throws UnsupportedOperationException if the LocaleResolver implementation does not - * support dynamic changing of the locale + * @throws UnsupportedOperationException if the LocaleResolver + * implementation does not support dynamic changing of the locale */ void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/BeanNameUrlHandlerMapping.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/BeanNameUrlHandlerMapping.java index 397c179b03..44376cbd6a 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/BeanNameUrlHandlerMapping.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/BeanNameUrlHandlerMapping.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,9 +28,9 @@ * *

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

    The mapping is from URL to bean name. Thus an incoming URL "/foo" would map * to a handler named "/foo", or to "/foo /foo2" in case of multiple mappings to diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/HandlerInterceptorAdapter.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/HandlerInterceptorAdapter.java index 602886ba29..b00d46898a 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/HandlerInterceptorAdapter.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/HandlerInterceptorAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,7 @@ import org.springframework.web.servlet.ModelAndView; /** - * Abstract adapter class for the HandlerInterceptor interface, + * Abstract adapter class for the {@link AsyncHandlerInterceptor} interface, * for simplified implementation of pre-only/post-only interceptors. * * @author Juergen Hoeller @@ -36,7 +36,8 @@ public abstract class HandlerInterceptorAdapter implements AsyncHandlerIntercept */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) - throws Exception { + throws Exception { + return true; } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/CssLinkResourceTransformer.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/CssLinkResourceTransformer.java index 4380246e84..fa884f7a49 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/CssLinkResourceTransformer.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/CssLinkResourceTransformer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -49,12 +49,11 @@ */ public class CssLinkResourceTransformer extends ResourceTransformerSupport { - private static final Log logger = LogFactory.getLog(CssLinkResourceTransformer.class); - private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); + private static final Log logger = LogFactory.getLog(CssLinkResourceTransformer.class); - private final List linkParsers = new ArrayList(); + private final List linkParsers = new ArrayList(2); public CssLinkResourceTransformer() { @@ -81,7 +80,7 @@ public Resource transform(HttpServletRequest request, Resource resource, Resourc byte[] bytes = FileCopyUtils.copyToByteArray(resource.getInputStream()); String content = new String(bytes, DEFAULT_CHARSET); - Set infos = new HashSet(5); + Set infos = new HashSet(8); for (CssLinkParser parser : this.linkParsers) { parser.parseLink(content, infos); } @@ -123,17 +122,16 @@ public Resource transform(HttpServletRequest request, Resource resource, Resourc private boolean hasScheme(String link) { int schemeIndex = link.indexOf(":"); - return (schemeIndex > 0 && !link.substring(0, schemeIndex).contains("/")) - || link.indexOf("//") == 0; + return (schemeIndex > 0 && !link.substring(0, schemeIndex).contains("/")) || link.indexOf("//") == 0; } - protected static interface CssLinkParser { + protected interface CssLinkParser { void parseLink(String content, Set linkInfos); - } + protected static abstract class AbstractCssLinkParser implements CssLinkParser { /** @@ -189,6 +187,7 @@ protected int addLink(int index, String endKey, String content, Set } + private static class ImportStatementCssLinkParser extends AbstractCssLinkParser { @Override @@ -208,6 +207,7 @@ else if (logger.isErrorEnabled()) { } } + private static class UrlFunctionCssLinkParser extends AbstractCssLinkParser { @Override @@ -229,8 +229,7 @@ private static class CssLinkInfo implements Comparable { private final int end; - - private CssLinkInfo(int start, int end) { + public CssLinkInfo(int start, int end) { this.start = start; this.end = end; } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceTransformer.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceTransformer.java index fe941d830b..de17faf1b5 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceTransformer.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceTransformer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,10 +35,10 @@ public interface ResourceTransformer { * @param request the current request * @param resource the resource to transform * @param transformerChain the chain of remaining transformers to delegate to - * @return the transformed resource, never {@code null} + * @return the transformed resource (never {@code null}) * @throws IOException if the transformation fails */ Resource transform(HttpServletRequest request, Resource resource, ResourceTransformerChain transformerChain) throws IOException; -} \ No newline at end of file +} diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/annotation/UriTemplateServletAnnotationControllerTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/annotation/UriTemplateServletAnnotationControllerTests.java index ca2df2ca23..79d50c93a6 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/annotation/UriTemplateServletAnnotationControllerTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/annotation/UriTemplateServletAnnotationControllerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -54,6 +54,7 @@ public class UriTemplateServletAnnotationControllerTests { private DispatcherServlet servlet; + @Test public void simple() throws Exception { initServlet(SimpleUriTemplateController.class); @@ -318,8 +319,7 @@ public void customRegex() throws Exception { assertEquals("test-42", response.getContentAsString()); } - // SPR-6640 - @Test + @Test // SPR-6640 public void menuTree() throws Exception { initServlet(MenuTreeController.class); @@ -329,8 +329,7 @@ public void menuTree() throws Exception { assertEquals("M5", response.getContentAsString()); } - // SPR-6876 - @Test + @Test // SPR-6876 public void variableNames() throws Exception { initServlet(VariableNamesController.class); @@ -345,8 +344,7 @@ public void variableNames() throws Exception { assertEquals("bar-bar", response.getContentAsString()); } - // SPR-8543 - @Test + @Test // SPR-8543 public void variableNamesWithUrlExtension() throws Exception { initServlet(VariableNamesController.class); @@ -356,8 +354,7 @@ public void variableNamesWithUrlExtension() throws Exception { assertEquals("foo-foo", response.getContentAsString()); } - // SPR-9333 - @Test + @Test // SPR-9333 @SuppressWarnings("serial") public void suppressDefaultSuffixPattern() throws Exception { servlet = new DispatcherServlet() { @@ -381,8 +378,7 @@ protected WebApplicationContext createWebApplicationContext(WebApplicationContex assertEquals("foo-jsmith@mail.com", response.getContentAsString()); } - // SPR-6906 - @Test + @Test // SPR-6906 @SuppressWarnings("serial") public void controllerClassName() throws Exception { servlet = new DispatcherServlet() { @@ -416,8 +412,7 @@ protected WebApplicationContext createWebApplicationContext(WebApplicationContex assertEquals("plain-bar", response.getContentAsString()); } - // SPR-6978 - @Test + @Test // SPR-6978 public void doIt() throws Exception { initServlet(Spr6978Controller.class); diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/client/standard/StandardWebSocketClient.java b/spring-websocket/src/main/java/org/springframework/web/socket/client/standard/StandardWebSocketClient.java index 2dbd815706..faa32b6911 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/client/standard/StandardWebSocketClient.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/client/standard/StandardWebSocketClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,6 @@ import java.util.Locale; import java.util.Map; import java.util.concurrent.Callable; - import javax.websocket.ClientEndpointConfig; import javax.websocket.ClientEndpointConfig.Configurator; import javax.websocket.ContainerProvider; @@ -51,7 +50,6 @@ import org.springframework.web.socket.adapter.standard.WebSocketToStandardExtensionAdapter; import org.springframework.web.socket.client.AbstractWebSocketClient; - /** * A WebSocketClient based on standard Java WebSocket API. * diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/WebMvcStompEndpointRegistry.java b/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/WebMvcStompEndpointRegistry.java index bbe55538fc..048cd3a03b 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/WebMvcStompEndpointRegistry.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/config/annotation/WebMvcStompEndpointRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -67,8 +67,8 @@ public WebMvcStompEndpointRegistry(WebSocketHandler webSocketHandler, org.springframework.messaging.simp.user.UserSessionRegistry userSessionRegistry, TaskScheduler defaultSockJsTaskScheduler) { - Assert.notNull(webSocketHandler, "'webSocketHandler' is required "); - Assert.notNull(transportRegistration, "'transportRegistration' is required"); + Assert.notNull(webSocketHandler, "WebSocketHandler is required "); + Assert.notNull(transportRegistration, "WebSocketTransportRegistration is required"); this.webSocketHandler = webSocketHandler; this.subProtocolWebSocketHandler = unwrapSubProtocolWebSocketHandler(webSocketHandler); @@ -87,19 +87,17 @@ public WebMvcStompEndpointRegistry(WebSocketHandler webSocketHandler, this.stompHandler.setMessageSizeLimit(transportRegistration.getMessageSizeLimit()); } - this.sockJsScheduler = defaultSockJsTaskScheduler; } private static SubProtocolWebSocketHandler unwrapSubProtocolWebSocketHandler(WebSocketHandler handler) { WebSocketHandler actual = WebSocketHandlerDecorator.unwrap(handler); - Assert.isInstanceOf(SubProtocolWebSocketHandler.class, actual, "No SubProtocolWebSocketHandler in " + handler); + if (!(actual instanceof SubProtocolWebSocketHandler)) { + throw new IllegalArgumentException("No SubProtocolWebSocketHandler in " + handler); + }; return (SubProtocolWebSocketHandler) actual; } - protected void setApplicationContext(ApplicationContext applicationContext) { - this.stompHandler.setApplicationEventPublisher(applicationContext); - } @Override public StompWebSocketEndpointRegistration addEndpoint(String... paths) { @@ -144,6 +142,11 @@ public WebMvcStompEndpointRegistry setErrorHandler(StompSubProtocolErrorHandler return this; } + protected void setApplicationContext(ApplicationContext applicationContext) { + this.stompHandler.setApplicationEventPublisher(applicationContext); + } + + /** * Return a handler mapping with the mapped ViewControllers; or {@code null} * in case of no registrations. diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractTyrusRequestUpgradeStrategy.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractTyrusRequestUpgradeStrategy.java index 0e6a31df0d..35886efc72 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractTyrusRequestUpgradeStrategy.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractTyrusRequestUpgradeStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -193,7 +193,7 @@ protected static class Tyrus17EndpointHelper implements TyrusEndpointHelper { private static final Method registerMethod; - private static final Method unRegisterMethod; + private static final Method unregisterMethod; static { try { @@ -204,7 +204,7 @@ protected static class Tyrus17EndpointHelper implements TyrusEndpointHelper { throw new IllegalStateException("Expected TyrusEndpointWrapper constructor with 9 or 10 arguments"); } registerMethod = TyrusWebSocketEngine.class.getDeclaredMethod("register", TyrusEndpointWrapper.class); - unRegisterMethod = TyrusWebSocketEngine.class.getDeclaredMethod("unregister", TyrusEndpointWrapper.class); + unregisterMethod = TyrusWebSocketEngine.class.getDeclaredMethod("unregister", TyrusEndpointWrapper.class); ReflectionUtils.makeAccessible(registerMethod); } catch (Exception ex) { @@ -259,7 +259,7 @@ public void register(TyrusWebSocketEngine engine, Object endpoint) { @Override public void unregister(TyrusWebSocketEngine engine, Object endpoint) { try { - unRegisterMethod.invoke(engine, endpoint); + unregisterMethod.invoke(engine, endpoint); } catch (Exception ex) { throw new HandshakeFailureException("Failed to unregister " + endpoint, ex); diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/WebLogicRequestUpgradeStrategy.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/WebLogicRequestUpgradeStrategy.java index c16d747a80..58b294077f 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/WebLogicRequestUpgradeStrategy.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/WebLogicRequestUpgradeStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -60,7 +60,6 @@ public class WebLogicRequestUpgradeStrategy extends AbstractTyrusRequestUpgradeS private static final WebLogicServletWriterHelper servletWriterHelper = new WebLogicServletWriterHelper(); private static final Connection.CloseListener noOpCloseListener = new Connection.CloseListener() { - @Override public void close(CloseReason reason) { } diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/AbstractXhrTransport.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/AbstractXhrTransport.java index 7b7f51d22d..2d0fac299e 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/AbstractXhrTransport.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/AbstractXhrTransport.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -54,6 +54,7 @@ public abstract class AbstractXhrTransport implements XhrTransport { PRELUDE = new String(bytes, SockJsFrame.CHARSET); } + protected Log logger = LogFactory.getLog(getClass()); private boolean xhrStreamingDisabled; @@ -137,6 +138,7 @@ protected abstract void connectInternal(TransportRequest request, WebSocketHandl URI receiveUrl, HttpHeaders handshakeHeaders, XhrClientSockJsSession session, SettableListenableFuture connectFuture); + // InfoReceiver methods @Override @@ -165,6 +167,7 @@ public String executeInfoRequest(URI infoUrl, HttpHeaders headers) { protected abstract ResponseEntity executeInfoRequestInternal(URI infoUrl, HttpHeaders headers); + // XhrTransport methods @Override @@ -184,8 +187,8 @@ public void executeSendRequest(URI url, HttpHeaders headers, TextMessage message } } - protected abstract ResponseEntity executeSendRequestInternal(URI url, - HttpHeaders headers, TextMessage message); + protected abstract ResponseEntity executeSendRequestInternal( + URI url, HttpHeaders headers, TextMessage message); @Override diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/PollingSockJsSession.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/PollingSockJsSession.java index e3cb44882e..d8066c2e06 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/PollingSockJsSession.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/PollingSockJsSession.java @@ -35,7 +35,6 @@ */ public class PollingSockJsSession extends AbstractHttpSockJsSession { - public PollingSockJsSession(String sessionId, SockJsServiceConfig config, WebSocketHandler wsHandler, Map attributes) { diff --git a/spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket.gif b/spring-websocket/src/main/resources/org/springframework/web/socket/config/spring-websocket.gif new file mode 100644 index 0000000000000000000000000000000000000000..9e4c800e48d73839d489f8ad34dfe55fedd83742 GIT binary patch literal 1025 zcmZ?wbhEHb6krfw_|5x!i2%NadZP9VhWoLYspY~sI)^FL#fR$&0R-6u8 zb~14J$)dhS#i>P*$Ka8Cj!@=_g`@;VEKul6(_@1pNU*|CTi0KujOm})@*D$ zdVI>6vr|u<3O;vc%9+!XPn`@oeEwy+XHRcEePZ*Q)2E*wzxe8G z+o}A^uTEZib?ox1|NH;{zlQqM zmoLt|dU<}^rtnk8dZtazy>WHZj4Ao=UZ35vI(bS@;JRhmTi18**fin7sTJkh=&N4ntNCPc;=M(wm8FGA_HrqX z3I(2OeMuJAXC&O5lQOf==k4(oS*{ur6Ktj=+vRxbU7ZxWw<~#FWBjWl%a6B&lm?j3 zZ7o?jx%p^A==&2J@*LDV!Yq%}2G37(-BucKtRZ4&ZA_M(`qq-*O(h{2HtK7#{Wj%A ztjvj8o*Fhi!e?eo;IxpSX@LR2hz3XaD1it8#h)yU3=B&cbU;F&Ji)+mkHM2u#$!Xm zK|^t)DFzD}dmI~8bZP=09cJrc7vyPMF)`JvK}Dx+&kTk}g+4AX7TwH68V-Dtd@2c& z8jsu=)IwTpUM@Q1%)}JLR>rYNnL$O)b&mq$q5}={8Wobh+(>-dA*hsf?|`Rr!UJ|b zy{I)S9xVx&XOOFQSF$3RxmS{7%NkDM6T)-tr@rEN()5s}OGM<(1506b!9~tfq&V*w zB{?#)6fOv1CmHadK_akQZsx%+p2EsafeSVW8ng$f`K6k98hr^^AZx$a VVPfZ#726KD)qFE~B`d&S4FG&~ckciI literal 0 HcmV?d00001 diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/AbstractWebSocketIntegrationTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/AbstractWebSocketIntegrationTests.java index 7067ef25db..2309f74f85 100644 --- a/spring-websocket/src/test/java/org/springframework/web/socket/AbstractWebSocketIntegrationTests.java +++ b/spring-websocket/src/test/java/org/springframework/web/socket/AbstractWebSocketIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,7 +21,6 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -48,9 +47,7 @@ */ public abstract class AbstractWebSocketIntegrationTests { - protected Log logger = LogFactory.getLog(getClass()); - - private static Map, Class> upgradeStrategyConfigTypes = new HashMap, Class>(); + private static Map, Class> upgradeStrategyConfigTypes = new HashMap<>(); static { upgradeStrategyConfigTypes.put(JettyWebSocketTestServer.class, JettyUpgradeStrategyConfig.class); @@ -58,6 +55,7 @@ public abstract class AbstractWebSocketIntegrationTests { upgradeStrategyConfigTypes.put(UndertowTestServer.class, UndertowUpgradeStrategyConfig.class); } + @Rule public final TestName testName = new TestName(); @@ -67,12 +65,13 @@ public abstract class AbstractWebSocketIntegrationTests { @Parameter(1) public WebSocketClient webSocketClient; + protected final Log logger = LogFactory.getLog(getClass()); + protected AnnotationConfigWebApplicationContext wac; @Before public void setup() throws Exception { - logger.debug("Setting up '" + this.testName.getMethodName() + "', client=" + this.webSocketClient.getClass().getSimpleName() + ", server=" + this.server.getClass().getSimpleName()); @@ -155,6 +154,7 @@ public RequestUpgradeStrategy requestUpgradeStrategy() { } } + @Configuration static class TomcatUpgradeStrategyConfig extends AbstractRequestUpgradeStrategyConfig { @@ -164,6 +164,7 @@ public RequestUpgradeStrategy requestUpgradeStrategy() { } } + @Configuration static class UndertowUpgradeStrategyConfig extends AbstractRequestUpgradeStrategyConfig { diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/WebSocketIntegrationTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/WebSocketHandshakeTests.java similarity index 95% rename from spring-websocket/src/test/java/org/springframework/web/socket/WebSocketIntegrationTests.java rename to spring-websocket/src/test/java/org/springframework/web/socket/WebSocketHandshakeTests.java index 1d86998dae..813b7b5fbf 100644 --- a/spring-websocket/src/test/java/org/springframework/web/socket/WebSocketIntegrationTests.java +++ b/spring-websocket/src/test/java/org/springframework/web/socket/WebSocketHandshakeTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -46,9 +46,10 @@ * Client and server-side WebSocket integration tests. * * @author Rossen Stoyanchev + * @author Juergen Hoeller */ @RunWith(Parameterized.class) -public class WebSocketIntegrationTests extends AbstractWebSocketIntegrationTests { +public class WebSocketHandshakeTests extends AbstractWebSocketIntegrationTests { @Parameters(name = "server [{0}], client [{1}]") public static Iterable arguments() { @@ -62,7 +63,7 @@ public static Iterable arguments() { @Override protected Class[] getAnnotatedConfigClasses() { - return new Class[] { TestConfig.class }; + return new Class[] {TestConfig.class}; } @Test @@ -75,11 +76,8 @@ public void subProtocolNegotiation() throws Exception { session.close(); } - // SPR-12727 - - @Test + @Test // SPR-12727 public void unsolicitedPongWithEmptyPayload() throws Exception { - String url = getWsBaseUrl() + "/ws"; WebSocketSession session = this.webSocketClient.doHandshake(new AbstractWebSocketHandler() {}, url).get(); @@ -126,7 +124,6 @@ private static class TestWebSocketHandler extends AbstractWebSocketHandler { private Throwable transportError; - public void setWaitMessageCount(int waitMessageCount) { this.waitMessageCount = waitMessageCount; } From b02744d42e5466281b0c8f6de8ccbea708781a5b Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 7 Jul 2016 01:05:25 +0200 Subject: [PATCH 215/344] Polishing (cherry picked from commit e304290) --- .../propertyeditors/ReaderEditorTests.java | 4 +- .../standard/DateTimeFormattingTests.java | 2 +- .../support/ObjectToObjectConverter.java | 5 +- .../env/ConfigurablePropertyResolver.java | 20 ++++---- .../AnnotationReadingVisitorUtils.java | 30 +++++------ .../org/springframework/util/StopWatch.java | 4 +- .../core/SerializableTypeWrapperTests.java | 50 +++++++++---------- 7 files changed, 57 insertions(+), 58 deletions(-) diff --git a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/ReaderEditorTests.java b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/ReaderEditorTests.java index 2729922ad9..d4e19b8bae 100644 --- a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/ReaderEditorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/ReaderEditorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,7 +34,7 @@ public class ReaderEditorTests { @Test(expected = IllegalArgumentException.class) public void testCtorWithNullResourceEditor() throws Exception { - new InputStreamEditor(null); + new ReaderEditor(null); } @Test diff --git a/spring-context/src/test/java/org/springframework/format/datetime/standard/DateTimeFormattingTests.java b/spring-context/src/test/java/org/springframework/format/datetime/standard/DateTimeFormattingTests.java index a60fbd69db..968530b39d 100644 --- a/spring-context/src/test/java/org/springframework/format/datetime/standard/DateTimeFormattingTests.java +++ b/spring-context/src/test/java/org/springframework/format/datetime/standard/DateTimeFormattingTests.java @@ -124,7 +124,7 @@ public void testBindLocalDateWithSpecificFormatter() throws Exception { @Test public void testBindLocalDateArray() { MutablePropertyValues propertyValues = new MutablePropertyValues(); - propertyValues.add("localDate", new String[]{"10/31/09"}); + propertyValues.add("localDate", new String[] {"10/31/09"}); binder.bind(propertyValues); assertEquals(0, binder.getBindingResult().getErrorCount()); } diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/ObjectToObjectConverter.java b/spring-core/src/main/java/org/springframework/core/convert/support/ObjectToObjectConverter.java index fed1e45dec..e4d000e6be 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/support/ObjectToObjectConverter.java +++ b/spring-core/src/main/java/org/springframework/core/convert/support/ObjectToObjectConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -185,9 +185,6 @@ private static Method determineFactoryMethod(Class targetClass, Class sour method = ClassUtils.getStaticMethod(targetClass, "of", sourceClass); if (method == null) { method = ClassUtils.getStaticMethod(targetClass, "from", sourceClass); - if (method == null) { - return null; - } } } return method; diff --git a/spring-core/src/main/java/org/springframework/core/env/ConfigurablePropertyResolver.java b/spring-core/src/main/java/org/springframework/core/env/ConfigurablePropertyResolver.java index e1ec8a2732..bc626d6431 100644 --- a/spring-core/src/main/java/org/springframework/core/env/ConfigurablePropertyResolver.java +++ b/spring-core/src/main/java/org/springframework/core/env/ConfigurablePropertyResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,10 +19,10 @@ import org.springframework.core.convert.support.ConfigurableConversionService; /** - * Configuration interface to be implemented by most if not all {@link PropertyResolver - * PropertyResolver} types. Provides facilities for accessing and customizing the - * {@link org.springframework.core.convert.ConversionService ConversionService} used when - * converting property values from one type to another. + * Configuration interface to be implemented by most if not all {@link PropertyResolver} + * types. Provides facilities for accessing and customizing the + * {@link org.springframework.core.convert.ConversionService ConversionService} + * used when converting property values from one type to another. * * @author Chris Beams * @since 3.1 @@ -30,7 +30,7 @@ public interface ConfigurablePropertyResolver extends PropertyResolver { /** - * @return the {@link ConfigurableConversionService} used when performing type + * Return the {@link ConfigurableConversionService} used when performing type * conversions on properties. *

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

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

    Note: as an alternative to fully replacing the + * {@code ConversionService}, consider adding or removing individual + * {@code Converter} instances by drilling into {@link #getConversionService()} + * and calling methods such as {@code #addConverter}. * @see PropertyResolver#getProperty(String, Class) * @see #getConversionService() * @see org.springframework.core.convert.converter.ConverterRegistry#addConverter diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationReadingVisitorUtils.java b/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationReadingVisitorUtils.java index 0d7ba35f25..60acdd3150 100644 --- a/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationReadingVisitorUtils.java +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationReadingVisitorUtils.java @@ -41,8 +41,8 @@ */ abstract class AnnotationReadingVisitorUtils { - public static AnnotationAttributes convertClassValues(ClassLoader classLoader, AnnotationAttributes original, - boolean classValuesAsString) { + public static AnnotationAttributes convertClassValues( + ClassLoader classLoader, AnnotationAttributes original, boolean classValuesAsString) { if (original == null) { return null; @@ -60,6 +60,7 @@ else if (value instanceof AnnotationAttributes[]) { for (int i = 0; i < values.length; i++) { values[i] = convertClassValues(classLoader, values[i], classValuesAsString); } + value = values; } else if (value instanceof Type) { value = (classValuesAsString ? ((Type) value).getClassName() : @@ -67,7 +68,8 @@ else if (value instanceof Type) { } else if (value instanceof Type[]) { Type[] array = (Type[]) value; - Object[] convArray = (classValuesAsString ? new String[array.length] : new Class[array.length]); + Object[] convArray = + (classValuesAsString ? new String[array.length] : new Class[array.length]); for (int i = 0; i < array.length; i++) { convArray[i] = (classValuesAsString ? array[i].getClassName() : classLoader.loadClass(array[i].getClassName())); @@ -75,11 +77,11 @@ else if (value instanceof Type[]) { value = convArray; } else if (classValuesAsString) { - if (value instanceof Class) { + if (value instanceof Class) { value = ((Class) value).getName(); } - else if (value instanceof Class[]) { - Class[] clazzArray = (Class[]) value; + else if (value instanceof Class[]) { + Class[] clazzArray = (Class[]) value; String[] newValue = new String[clazzArray.length]; for (int i = 0; i < clazzArray.length; i++) { newValue[i] = clazzArray[i].getName(); @@ -94,6 +96,7 @@ else if (value instanceof Class[]) { result.put(entry.getKey(), ex); } } + return result; } @@ -123,13 +126,12 @@ public static AnnotationAttributes getMergedAnnotationAttributes( return null; } - // To start with, we populate the results with a copy of all attribute - // values from the target annotation. A copy is necessary so that we do - // not inadvertently mutate the state of the metadata passed to this - // method. - AnnotationAttributes results = new AnnotationAttributes(attributesList.get(0)); + // To start with, we populate the result with a copy of all attribute values + // from the target annotation. A copy is necessary so that we do not + // inadvertently mutate the state of the metadata passed to this method. + AnnotationAttributes result = new AnnotationAttributes(attributesList.get(0)); - Set overridableAttributeNames = new HashSet(results.keySet()); + Set overridableAttributeNames = new HashSet(result.keySet()); overridableAttributeNames.remove(AnnotationUtils.VALUE); // Since the map is a LinkedMultiValueMap, we depend on the ordering of @@ -152,14 +154,14 @@ public static AnnotationAttributes getMergedAnnotationAttributes( if (value != null) { // Store the value, potentially overriding a value from an attribute // of the same name found higher in the annotation hierarchy. - results.put(overridableAttributeName, value); + result.put(overridableAttributeName, value); } } } } } - return results; + return result; } } diff --git a/spring-core/src/main/java/org/springframework/util/StopWatch.java b/spring-core/src/main/java/org/springframework/util/StopWatch.java index 1ba3988003..b470116436 100644 --- a/spring-core/src/main/java/org/springframework/util/StopWatch.java +++ b/spring-core/src/main/java/org/springframework/util/StopWatch.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -134,7 +134,7 @@ public void start(String taskName) throws IllegalStateException { /** * Stop the current task. The results are undefined if timing * methods are called without invoking at least one pair - * {@code #start()} / {@code #stop()} methods. + * {@code start()} / {@code stop()} methods. * @see #start() */ public void stop() throws IllegalStateException { diff --git a/spring-core/src/test/java/org/springframework/core/SerializableTypeWrapperTests.java b/spring-core/src/test/java/org/springframework/core/SerializableTypeWrapperTests.java index ac68dacec6..9cb3d53928 100644 --- a/spring-core/src/test/java/org/springframework/core/SerializableTypeWrapperTests.java +++ b/spring-core/src/test/java/org/springframework/core/SerializableTypeWrapperTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -46,7 +46,7 @@ public class SerializableTypeWrapperTests { public void forField() throws Exception { Type type = SerializableTypeWrapper.forField(Fields.class.getField("parameterizedType")); assertThat(type.toString(), equalTo("java.util.List")); - assertSerialzable(type); + assertSerializable(type); } @Test @@ -54,7 +54,7 @@ public void forMethodParameter() throws Exception { Method method = Methods.class.getDeclaredMethod("method", Class.class, Object.class); Type type = SerializableTypeWrapper.forMethodParameter(MethodParameter.forMethodOrConstructor(method, 0)); assertThat(type.toString(), equalTo("java.lang.Class")); - assertSerialzable(type); + assertSerializable(type); } @Test @@ -62,62 +62,62 @@ public void forConstructor() throws Exception { Constructor constructor = Constructors.class.getDeclaredConstructor(List.class); Type type = SerializableTypeWrapper.forMethodParameter(MethodParameter.forMethodOrConstructor(constructor, 0)); assertThat(type.toString(), equalTo("java.util.List")); - assertSerialzable(type); + assertSerializable(type); } @Test public void forGenericSuperClass() throws Exception { Type type = SerializableTypeWrapper.forGenericSuperclass(ArrayList.class); assertThat(type.toString(), equalTo("java.util.AbstractList")); - assertSerialzable(type); + assertSerializable(type); } @Test public void forGenericInterfaces() throws Exception { Type type = SerializableTypeWrapper.forGenericInterfaces(List.class)[0]; assertThat(type.toString(), equalTo("java.util.Collection")); - assertSerialzable(type); + assertSerializable(type); } @Test public void forTypeParamters() throws Exception { Type type = SerializableTypeWrapper.forTypeParameters(List.class)[0]; assertThat(type.toString(), equalTo("E")); - assertSerialzable(type); + assertSerializable(type); } @Test public void classType() throws Exception { Type type = SerializableTypeWrapper.forField(Fields.class.getField("classType")); assertThat(type.toString(), equalTo("class java.lang.String")); - assertSerialzable(type); + assertSerializable(type); } @Test public void genericArrayType() throws Exception { GenericArrayType type = (GenericArrayType) SerializableTypeWrapper.forField(Fields.class.getField("genericArrayType")); assertThat(type.toString(), equalTo("java.util.List[]")); - assertSerialzable(type); - assertSerialzable(type.getGenericComponentType()); + assertSerializable(type); + assertSerializable(type.getGenericComponentType()); } @Test public void parameterizedType() throws Exception { ParameterizedType type = (ParameterizedType) SerializableTypeWrapper.forField(Fields.class.getField("parameterizedType")); assertThat(type.toString(), equalTo("java.util.List")); - assertSerialzable(type); - assertSerialzable(type.getOwnerType()); - assertSerialzable(type.getRawType()); - assertSerialzable(type.getActualTypeArguments()); - assertSerialzable(type.getActualTypeArguments()[0]); + assertSerializable(type); + assertSerializable(type.getOwnerType()); + assertSerializable(type.getRawType()); + assertSerializable(type.getActualTypeArguments()); + assertSerializable(type.getActualTypeArguments()[0]); } @Test public void typeVariableType() throws Exception { TypeVariable type = (TypeVariable) SerializableTypeWrapper.forField(Fields.class.getField("typeVariableType")); assertThat(type.toString(), equalTo("T")); - assertSerialzable(type); - assertSerialzable(type.getBounds()); + assertSerializable(type); + assertSerializable(type.getBounds()); } @Test @@ -125,13 +125,13 @@ public void wildcardType() throws Exception { ParameterizedType typeSource = (ParameterizedType) SerializableTypeWrapper.forField(Fields.class.getField("wildcardType")); WildcardType type = (WildcardType) typeSource.getActualTypeArguments()[0]; assertThat(type.toString(), equalTo("? extends java.lang.CharSequence")); - assertSerialzable(type); - assertSerialzable(type.getLowerBounds()); - assertSerialzable(type.getUpperBounds()); + assertSerializable(type); + assertSerializable(type.getLowerBounds()); + assertSerializable(type.getUpperBounds()); } - private void assertSerialzable(Object source) throws Exception { + private void assertSerializable(Object source) throws Exception { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(source); @@ -152,19 +152,19 @@ static class Fields { public T typeVariableType; public List wildcardType; - } - static interface Methods { - List method(Class p1, T p2); + interface Methods { + List method(Class p1, T p2); } + static class Constructors { public Constructors(List p) { } - } + } From f51c90c1cc776d7f9d89508706e8c8a735fbb4c1 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 8 Jul 2016 12:20:49 +0200 Subject: [PATCH 216/344] DigestUtils processes InputStream with buffered read instead of full copy Issue: SPR-14427 (cherry picked from commit a1b58ee) --- .../org/springframework/util/DigestUtils.java | 21 +++++++----- .../util/DigestUtilsTests.java | 32 +++++++++++++++---- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/util/DigestUtils.java b/spring-core/src/main/java/org/springframework/util/DigestUtils.java index 6251c7aa96..36b1a583c8 100644 --- a/spring-core/src/main/java/org/springframework/util/DigestUtils.java +++ b/spring-core/src/main/java/org/springframework/util/DigestUtils.java @@ -23,11 +23,13 @@ /** * Miscellaneous methods for calculating digests. + * *

    Mainly for internal use within the framework; consider * Apache Commons Codec * for a more comprehensive suite of digest utilities. * * @author Arjen Poutsma + * @author Juergen Hoeller * @author Craig Andrews * @since 3.0 */ @@ -49,8 +51,8 @@ public static byte[] md5Digest(byte[] bytes) { } /** - * Calculate the MD5 digest of the given InputStream. - * @param inputStream the inputStream to calculate the digest over + * Calculate the MD5 digest of the given stream. + * @param inputStream the InputStream to calculate the digest over * @return the digest * @since 4.2 */ @@ -59,8 +61,7 @@ public static byte[] md5Digest(InputStream inputStream) throws IOException { } /** - * Return a hexadecimal string representation of the MD5 digest of the given - * bytes. + * Return a hexadecimal string representation of the MD5 digest of the given bytes. * @param bytes the bytes to calculate the digest over * @return a hexadecimal digest string */ @@ -69,9 +70,8 @@ public static String md5DigestAsHex(byte[] bytes) { } /** - * Return a hexadecimal string representation of the MD5 digest of the given - * inputStream. - * @param inputStream the inputStream to calculate the digest over + * Return a hexadecimal string representation of the MD5 digest of the given stream. + * @param inputStream the InputStream to calculate the digest over * @return a hexadecimal digest string * @since 4.2 */ @@ -127,7 +127,12 @@ private static byte[] digest(String algorithm, InputStream inputStream) throws I return messageDigest.digest(); } else { - return messageDigest.digest(StreamUtils.copyToByteArray(inputStream)); + final byte[] buffer = new byte[StreamUtils.BUFFER_SIZE]; + int bytesRead = -1; + while ((bytesRead = inputStream.read(buffer)) != -1) { + messageDigest.update(buffer, 0, bytesRead); + } + return messageDigest.digest(); } } diff --git a/spring-core/src/test/java/org/springframework/util/DigestUtilsTests.java b/spring-core/src/test/java/org/springframework/util/DigestUtilsTests.java index 6842bfee1d..5db3ea51b3 100644 --- a/spring-core/src/test/java/org/springframework/util/DigestUtilsTests.java +++ b/spring-core/src/test/java/org/springframework/util/DigestUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ package org.springframework.util; +import java.io.ByteArrayInputStream; +import java.io.IOException; import java.io.UnsupportedEncodingException; import org.junit.Before; @@ -25,6 +27,7 @@ /** * @author Arjen Poutsma + * @author Juergen Hoeller */ public class DigestUtilsTests { @@ -38,24 +41,39 @@ public void createBytes() throws UnsupportedEncodingException { @Test - public void md5() { - byte[] result = DigestUtils.md5Digest(bytes); + public void md5() throws IOException { byte[] expected = new byte[] {-0x4f, 0xa, -0x73, -0x4f, 0x64, -0x20, 0x75, 0x41, 0x5, -0x49, -0x57, -0x65, -0x19, 0x2e, 0x3f, -0x1b}; + + byte[] result = DigestUtils.md5Digest(bytes); + assertArrayEquals("Invalid hash", expected, result); + + result = DigestUtils.md5Digest(new ByteArrayInputStream(bytes)); assertArrayEquals("Invalid hash", expected, result); } @Test - public void md5Hex() throws UnsupportedEncodingException { + public void md5Hex() throws IOException { + String expected = "b10a8db164e0754105b7a99be72e3fe5"; + String hash = DigestUtils.md5DigestAsHex(bytes); - assertEquals("Invalid hash", "b10a8db164e0754105b7a99be72e3fe5", hash); + assertEquals("Invalid hash", expected, hash); + + hash = DigestUtils.md5DigestAsHex(new ByteArrayInputStream(bytes)); + assertEquals("Invalid hash", expected, hash); } @Test - public void md5StringBuilder() throws UnsupportedEncodingException { + public void md5StringBuilder() throws IOException { + String expected = "b10a8db164e0754105b7a99be72e3fe5"; + StringBuilder builder = new StringBuilder(); DigestUtils.appendMd5DigestAsHex(bytes, builder); - assertEquals("Invalid hash", "b10a8db164e0754105b7a99be72e3fe5", builder.toString()); + assertEquals("Invalid hash", expected, builder.toString()); + + builder = new StringBuilder(); + DigestUtils.appendMd5DigestAsHex(new ByteArrayInputStream(bytes), builder); + assertEquals("Invalid hash", expected, builder.toString()); } } From 482dd2cf37dc0bb8ce40f1d7e9e8a2f17b681cd0 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 8 Jul 2016 15:12:16 +0200 Subject: [PATCH 217/344] Fixed typo: "occured"->"occurred" (cherry picked from commit be0b71c) --- .../beans/AbstractPropertyAccessor.java | 2 +- .../springframework/beans/PropertyAccessor.java | 14 +++++++------- .../beans/factory/BeanCreationException.java | 2 +- .../beans/factory/config/AbstractFactoryBean.java | 4 ++-- .../factory/config/PropertiesFactoryBean.java | 4 ++-- .../context/annotation/AutoProxyRegistrar.java | 4 ++-- .../jmx/support/ConnectorServerFactoryBean.java | 2 +- .../jms/UncategorizedJmsException.java | 4 ++-- .../springframework/oxm/jaxb/Jaxb2Marshaller.java | 4 ++-- .../springframework/oxm/jibx/JibxMarshaller.java | 4 ++-- 10 files changed, 22 insertions(+), 22 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/AbstractPropertyAccessor.java b/spring-beans/src/main/java/org/springframework/beans/AbstractPropertyAccessor.java index 078712179a..216e5a4aab 100644 --- a/spring-beans/src/main/java/org/springframework/beans/AbstractPropertyAccessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/AbstractPropertyAccessor.java @@ -148,7 +148,7 @@ public Class getPropertyType(String propertyPath) { * @throws InvalidPropertyException if there is no such property or * if the property isn't writable * @throws PropertyAccessException if the property was valid but the - * accessor method failed or a type mismatch occured + * accessor method failed or a type mismatch occurred */ @Override public abstract void setPropertyValue(String propertyName, Object value) throws BeansException; diff --git a/spring-beans/src/main/java/org/springframework/beans/PropertyAccessor.java b/spring-beans/src/main/java/org/springframework/beans/PropertyAccessor.java index 00068ea4b6..c7faf35524 100644 --- a/spring-beans/src/main/java/org/springframework/beans/PropertyAccessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/PropertyAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -120,7 +120,7 @@ public interface PropertyAccessor { * @throws InvalidPropertyException if there is no such property or * if the property isn't writable * @throws PropertyAccessException if the property was valid but the - * accessor method failed or a type mismatch occured + * accessor method failed or a type mismatch occurred */ void setPropertyValue(String propertyName, Object value) throws BeansException; @@ -130,7 +130,7 @@ public interface PropertyAccessor { * @throws InvalidPropertyException if there is no such property or * if the property isn't writable * @throws PropertyAccessException if the property was valid but the - * accessor method failed or a type mismatch occured + * accessor method failed or a type mismatch occurred */ void setPropertyValue(PropertyValue pv) throws BeansException; @@ -144,7 +144,7 @@ public interface PropertyAccessor { * @throws InvalidPropertyException if there is no such property or * if the property isn't writable * @throws PropertyBatchUpdateException if one or more PropertyAccessExceptions - * occured for specific properties during the batch update. This exception bundles + * occurred for specific properties during the batch update. This exception bundles * all individual PropertyAccessExceptions. All other properties will have been * successfully updated. */ @@ -164,7 +164,7 @@ public interface PropertyAccessor { * @throws InvalidPropertyException if there is no such property or * if the property isn't writable * @throws PropertyBatchUpdateException if one or more PropertyAccessExceptions - * occured for specific properties during the batch update. This exception bundles + * occurred for specific properties during the batch update. This exception bundles * all individual PropertyAccessExceptions. All other properties will have been * successfully updated. * @see #setPropertyValues(PropertyValues, boolean, boolean) @@ -185,7 +185,7 @@ public interface PropertyAccessor { * @throws InvalidPropertyException if there is no such property or * if the property isn't writable * @throws PropertyBatchUpdateException if one or more PropertyAccessExceptions - * occured for specific properties during the batch update. This exception bundles + * occurred for specific properties during the batch update. This exception bundles * all individual PropertyAccessExceptions. All other properties will have been * successfully updated. * @see #setPropertyValues(PropertyValues, boolean, boolean) @@ -208,7 +208,7 @@ void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown) * @throws InvalidPropertyException if there is no such property or * if the property isn't writable * @throws PropertyBatchUpdateException if one or more PropertyAccessExceptions - * occured for specific properties during the batch update. This exception bundles + * occurred for specific properties during the batch update. This exception bundles * all individual PropertyAccessExceptions. All other properties will have been * successfully updated. */ diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/BeanCreationException.java b/spring-beans/src/main/java/org/springframework/beans/factory/BeanCreationException.java index f0c2fa1dae..7af4087576 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/BeanCreationException.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/BeanCreationException.java @@ -123,7 +123,7 @@ public String getResourceDescription() { /** * Add a related cause to this bean creation exception, - * not being a direct cause of the failure but having occured + * not being a direct cause of the failure but having occurred * earlier in the creation of the same bean instance. * @param ex the related cause to add */ diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/AbstractFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/AbstractFactoryBean.java index bc8a42f71b..85faab006b 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/AbstractFactoryBean.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/AbstractFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -208,7 +208,7 @@ public void destroy() throws Exception { *

    Invoked on initialization of this FactoryBean in case of * a singleton; else, on each {@link #getObject()} call. * @return the object returned by this factory - * @throws Exception if an exception occured during object creation + * @throws Exception if an exception occurred during object creation * @see #getObject() */ protected abstract T createInstance() throws Exception; diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertiesFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertiesFactoryBean.java index 93a97a8dcf..adc415d0d5 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertiesFactoryBean.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertiesFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -95,7 +95,7 @@ public Class getObjectType() { *

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

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

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

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

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

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

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

    By default this is not set. */ - public void setContentNegotiationManager(ContentNegotiationManager manager) { - this.contentNegotiationManager = manager; + public void setContentNegotiationManager(ContentNegotiationManager contentNegotiationManager) { + this.contentNegotiationManager = contentNegotiationManager; } + /** + * Return the ContentNegotiationManager to use for the ProducesRequestCondition, + * if any. + */ public ContentNegotiationManager getContentNegotiationManager() { return this.contentNegotiationManager; } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java index 5ffb6aaf5c..9365f0474b 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java @@ -115,7 +115,7 @@ public void setEmbeddedValueResolver(StringValueResolver resolver) { @Override public void afterPropertiesSet() { this.config = new RequestMappingInfo.BuilderConfiguration(); - this.config.setPathHelper(getUrlPathHelper()); + this.config.setUrlPathHelper(getUrlPathHelper()); this.config.setPathMatcher(getPathMatcher()); this.config.setSuffixPatternMatch(this.useSuffixPatternMatch); this.config.setTrailingSlashMatch(this.useTrailingSlashMatch); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlEncodingFilter.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlEncodingFilter.java index 471ae15461..901d0c5eac 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlEncodingFilter.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlEncodingFilter.java @@ -27,6 +27,7 @@ import org.apache.commons.logging.LogFactory; import org.springframework.web.filter.OncePerRequestFilter; +import org.springframework.web.util.UrlPathHelper; /** * A filter that wraps the {@link HttpServletResponse} and overrides its @@ -96,13 +97,14 @@ private ResourceUrlProvider getResourceUrlProvider() { private void initLookupPath(ResourceUrlProvider urlProvider) { if (this.indexLookupPath == null) { - String requestUri = urlProvider.getPathHelper().getRequestUri(this.request); - String lookupPath = urlProvider.getPathHelper().getLookupPathForRequest(this.request); + UrlPathHelper pathHelper = urlProvider.getUrlPathHelper(); + String requestUri = pathHelper.getRequestUri(this.request); + String lookupPath = pathHelper.getLookupPathForRequest(this.request); this.indexLookupPath = requestUri.lastIndexOf(lookupPath); this.prefixLookupPath = requestUri.substring(0, this.indexLookupPath); if ("/".equals(lookupPath) && !"/".equals(requestUri)) { - String contextPath = urlProvider.getPathHelper().getContextPath(this.request); + String contextPath = pathHelper.getContextPath(this.request); if (requestUri.equals(contextPath)) { this.indexLookupPath = requestUri.length(); this.prefixLookupPath = requestUri; diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlProvider.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlProvider.java index 9ee0c5a8b7..f39e02ff22 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlProvider.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -51,7 +51,7 @@ public class ResourceUrlProvider implements ApplicationListener Date: Wed, 13 Jul 2016 15:42:34 +0200 Subject: [PATCH 219/344] AbstractHandlerMethodMapping adds type+method info to getMappingForMethod exceptions Issue: SPR-14452 (cherry picked from commit f0a826e) --- .../web/servlet/handler/AbstractHandlerMethodMapping.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java index d84d1601f9..2ce03f153d 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 @@ -230,7 +230,13 @@ protected void detectHandlerMethods(final Object handler) { new MethodIntrospector.MetadataLookup() { @Override public T inspect(Method method) { - return getMappingForMethod(method, userType); + try { + return getMappingForMethod(method, userType); + } + catch (Throwable ex) { + throw new IllegalStateException("Invalid mapping on handler class [" + + userType.getName() + "]: " + method, ex); + } } }); From c627c408a07688861b92143484d6b5580eedff16 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 13 Jul 2016 22:31:41 +0200 Subject: [PATCH 220/344] Aspect actually applies in PersistenceExceptionTranslationPostProcessorTests Issue: SPR-14457 (cherry picked from commit 52f46c7) --- .../PersistenceExceptionTranslationPostProcessorTests.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spring-tx/src/test/java/org/springframework/dao/annotation/PersistenceExceptionTranslationPostProcessorTests.java b/spring-tx/src/test/java/org/springframework/dao/annotation/PersistenceExceptionTranslationPostProcessorTests.java index 39c2bf78ea..968eab46ca 100644 --- a/spring-tx/src/test/java/org/springframework/dao/annotation/PersistenceExceptionTranslationPostProcessorTests.java +++ b/spring-tx/src/test/java/org/springframework/dao/annotation/PersistenceExceptionTranslationPostProcessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.junit.Test; + import org.springframework.aop.Advisor; import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator; import org.springframework.aop.framework.Advised; @@ -138,7 +139,7 @@ public DataAccessException translateExceptionIfPossible(RuntimeException ex) { @Aspect public static class LogAllAspect { - @Before("execution(void *.additionalMethod())") + @Before("execution(void *.additionalMethod(*))") public void log(JoinPoint jp) { System.out.println("Before " + jp.getSignature().getName()); } From 97d73eb70c46865f877c146978d96d7a380d2483 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 14 Jul 2016 21:10:24 +0200 Subject: [PATCH 221/344] StandardTypeConverter initializes default ConversionService against volatile field Issue: SPR-14465 (cherry picked from commit 0065a16) --- .../expression/spel/support/StandardTypeConverter.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java index 690a7e5726..253e4bb389 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,7 +36,7 @@ */ public class StandardTypeConverter implements TypeConverter { - private static ConversionService defaultConversionService; + private static volatile ConversionService defaultConversionService; private final ConversionService conversionService; @@ -45,10 +45,8 @@ public class StandardTypeConverter implements TypeConverter { * Create a StandardTypeConverter for the default ConversionService. */ public StandardTypeConverter() { - synchronized (this) { - if (defaultConversionService == null) { - defaultConversionService = new DefaultConversionService(); - } + if (defaultConversionService == null) { + defaultConversionService = new DefaultConversionService(); } this.conversionService = defaultConversionService; } From 44152ce4019f58337806f09bf0bf37d7f9e2a6bc Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 14 Jul 2016 21:11:28 +0200 Subject: [PATCH 222/344] CronSequenceGenerator prevents stack overflow in case of inverted range Issue: SPR-14462 (cherry picked from commit da59b4d) --- .../support/CronSequenceGenerator.java | 6 +++- .../support/CronSequenceGeneratorTests.java | 32 +++++++++++++++---- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/scheduling/support/CronSequenceGenerator.java b/spring-context/src/main/java/org/springframework/scheduling/support/CronSequenceGenerator.java index f53894c794..f20bb57c15 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/support/CronSequenceGenerator.java +++ b/spring-context/src/main/java/org/springframework/scheduling/support/CronSequenceGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -379,6 +379,10 @@ private int[] getRange(String field, int min, int max) { throw new IllegalArgumentException("Range less than minimum (" + min + "): '" + field + "' in expression \"" + this.expression + "\""); } + if (result[0] > result[1]) { + throw new IllegalArgumentException("Invalid inverted range: '" + field + + "' in expression \"" + this.expression + "\""); + } return result; } diff --git a/spring-context/src/test/java/org/springframework/scheduling/support/CronSequenceGeneratorTests.java b/spring-context/src/test/java/org/springframework/scheduling/support/CronSequenceGeneratorTests.java index 6c4df6b01e..9bc71fb465 100644 --- a/spring-context/src/test/java/org/springframework/scheduling/support/CronSequenceGeneratorTests.java +++ b/spring-context/src/test/java/org/springframework/scheduling/support/CronSequenceGeneratorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,31 +29,51 @@ public class CronSequenceGeneratorTests { @Test - public void testAt50Seconds() { + public void at50Seconds() { assertEquals(new Date(2012, 6, 2, 1, 0), new CronSequenceGenerator("*/15 * 1-4 * * *").next(new Date(2012, 6, 1, 9, 53, 50))); } @Test - public void testAt0Seconds() { + public void at0Seconds() { assertEquals(new Date(2012, 6, 2, 1, 0), new CronSequenceGenerator("*/15 * 1-4 * * *").next(new Date(2012, 6, 1, 9, 53))); } @Test - public void testAt0Minutes() { + public void at0Minutes() { assertEquals(new Date(2012, 6, 2, 1, 0), new CronSequenceGenerator("0 */2 1-4 * * *").next(new Date(2012, 6, 1, 9, 0))); } @Test(expected = IllegalArgumentException.class) - public void testWith0Increment() { + public void with0Increment() { new CronSequenceGenerator("*/0 * * * * *").next(new Date(2012, 6, 1, 9, 0)); } @Test(expected = IllegalArgumentException.class) - public void testWithNegativeIncrement() { + public void withNegativeIncrement() { new CronSequenceGenerator("*/-1 * * * * *").next(new Date(2012, 6, 1, 9, 0)); } + @Test(expected = IllegalArgumentException.class) + public void withInvertedMinuteRange() { + new CronSequenceGenerator("* 6-5 * * * *").next(new Date(2012, 6, 1, 9, 0)); + } + + @Test(expected = IllegalArgumentException.class) + public void withInvertedHourRange() { + new CronSequenceGenerator("* * 6-5 * * *").next(new Date(2012, 6, 1, 9, 0)); + } + + @Test + public void withSameMinuteRange() { + new CronSequenceGenerator("* 6-6 * * * *").next(new Date(2012, 6, 1, 9, 0)); + } + + @Test + public void withSameHourRange() { + new CronSequenceGenerator("* * 6-6 * * *").next(new Date(2012, 6, 1, 9, 0)); + } + } From 4be5541c0ebc951acae59a841f9df53213f9f0e8 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 15 Jul 2016 00:15:46 +0200 Subject: [PATCH 223/344] MessageHeaderAccessor properly removes header even in case of null value Issue: SPR-14468 (cherry picked from commit 4ea5f07) --- .../support/MessageHeaderAccessor.java | 15 ++++++++---- .../support/MessageHeaderAccessorTests.java | 24 ++++++++++++++++--- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/spring-messaging/src/main/java/org/springframework/messaging/support/MessageHeaderAccessor.java b/spring-messaging/src/main/java/org/springframework/messaging/support/MessageHeaderAccessor.java index 8a84f6f872..b6b1ba7925 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/support/MessageHeaderAccessor.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/support/MessageHeaderAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -306,12 +306,17 @@ public void setHeader(String name, Object value) { throw new IllegalArgumentException("'" + name + "' header is read-only"); } verifyType(name, value); - if (!ObjectUtils.nullSafeEquals(value, getHeader(name))) { - this.modified = true; - if (value != null) { + if (value != null) { + // Modify header if necessary + if (!ObjectUtils.nullSafeEquals(value, getHeader(name))) { + this.modified = true; this.headers.getRawHeaders().put(name, value); } - else { + } + else { + // Remove header if available + if (this.headers.containsKey(name)) { + this.modified = true; this.headers.getRawHeaders().remove(name); } } diff --git a/spring-messaging/src/test/java/org/springframework/messaging/support/MessageHeaderAccessorTests.java b/spring-messaging/src/test/java/org/springframework/messaging/support/MessageHeaderAccessorTests.java index a09429fb62..7d350e8e7a 100644 --- a/spring-messaging/src/test/java/org/springframework/messaging/support/MessageHeaderAccessorTests.java +++ b/spring-messaging/src/test/java/org/springframework/messaging/support/MessageHeaderAccessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ import java.nio.charset.Charset; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -40,6 +41,7 @@ * * @author Rossen Stoyanchev * @author Sebastien Deleuze + * @author Juergen Hoeller */ public class MessageHeaderAccessorTests { @@ -89,6 +91,24 @@ public void existingHeadersModification() throws InterruptedException { assertEquals("baz", actual.get("bar")); } + @Test + public void testRemoveHeader() { + Message message = new GenericMessage<>("payload", Collections.singletonMap("foo", "bar")); + MessageHeaderAccessor accessor = new MessageHeaderAccessor(message); + accessor.removeHeader("foo"); + Map headers = accessor.toMap(); + assertFalse(headers.containsKey("foo")); + } + + @Test + public void testRemoveHeaderEvenIfNull() { + Message message = new GenericMessage<>("payload", Collections.singletonMap("foo", null)); + MessageHeaderAccessor accessor = new MessageHeaderAccessor(message); + accessor.removeHeader("foo"); + Map headers = accessor.toMap(); + assertFalse(headers.containsKey("foo")); + } + @Test public void removeHeaders() { Map map = new HashMap<>(); @@ -153,7 +173,6 @@ public void copyHeadersFromNullMap() { @Test public void toMap() { - MessageHeaderAccessor accessor = new MessageHeaderAccessor(); accessor.setHeader("foo", "bar1"); @@ -380,7 +399,6 @@ public String toString() { } - public static class TestMessageHeaderAccessor extends MessageHeaderAccessor { private TestMessageHeaderAccessor() { From 3500bdce0a1a793f9ed13355c8298300bbbc69e8 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 15 Jul 2016 16:02:06 +0200 Subject: [PATCH 224/344] ConfigurationClassParser load annotations through source class loader Issue: SPR-10343 (cherry picked from commit 9e93403) --- .../context/annotation/ConfigurationClassParser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java index 023371b4d3..18ac3f7dea 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java @@ -836,7 +836,7 @@ public Collection getAnnotationAttributes(String annotationType, St private SourceClass getRelated(String className) throws IOException { if (this.source instanceof Class) { try { - Class clazz = resourceLoader.getClassLoader().loadClass(className); + Class clazz = ((Class) this.source).getClassLoader().loadClass(className); return asSourceClass(clazz); } catch (ClassNotFoundException ex) { From 392f9c8deb2e2526d43d7867576df0c721d4db04 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 15 Jul 2016 22:47:05 +0200 Subject: [PATCH 225/344] Javadoc fixes and pruning of deprecated references (cherry picked from commit 69dd40e) --- .../tomcat/TomcatLoadTimeWeaver.java | 4 +-- .../jdbc/datasource/ConnectionHandle.java | 4 +-- .../MarshallingMessageConverter.java | 2 +- .../handler/HandlerMethodSelector.java | 2 +- .../web/method/HandlerMethodSelector.java | 2 +- .../ModelAttributeMethodProcessor.java | 34 +++++++++++-------- .../springframework/web/util/HtmlUtils.java | 3 +- .../servlet/mvc/WebContentInterceptor.java | 3 +- .../annotation/MvcUriComponentsBuilder.java | 17 ++++------ .../web/servlet/support/RequestContext.java | 17 +++++----- 10 files changed, 44 insertions(+), 44 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/instrument/classloading/tomcat/TomcatLoadTimeWeaver.java b/spring-context/src/main/java/org/springframework/instrument/classloading/tomcat/TomcatLoadTimeWeaver.java index 1ef36c9551..6cb876e847 100644 --- a/spring-context/src/main/java/org/springframework/instrument/classloading/tomcat/TomcatLoadTimeWeaver.java +++ b/spring-context/src/main/java/org/springframework/instrument/classloading/tomcat/TomcatLoadTimeWeaver.java @@ -25,8 +25,8 @@ import org.springframework.util.ClassUtils; /** - * {@link org.springframework.instrument.classloading.LoadTimeWeaver} implementation for Tomcat's - * new {@link org.apache.tomcat.InstrumentableClassLoader InstrumentableClassLoader}. + * {@link org.springframework.instrument.classloading.LoadTimeWeaver} implementation + * for Tomcat's new {@code org.apache.tomcat.InstrumentableClassLoader}. * Also capable of handling Spring's TomcatInstrumentableClassLoader when encountered. * * @author Juergen Hoeller diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/ConnectionHandle.java b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/ConnectionHandle.java index 2343aef442..7b361385ac 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/ConnectionHandle.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/ConnectionHandle.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ * @since 1.1 * @see SimpleConnectionHandle * @see ConnectionHolder - * @see org.springframework.orm.jdo.JpaDialect#getJdbcConnection + * @see org.springframework.orm.jpa.JpaDialect#getJdbcConnection * @see org.springframework.orm.jdo.JdoDialect#getJdbcConnection */ public interface ConnectionHandle { diff --git a/spring-messaging/src/main/java/org/springframework/messaging/converter/MarshallingMessageConverter.java b/spring-messaging/src/main/java/org/springframework/messaging/converter/MarshallingMessageConverter.java index 12d9d8d8bb..965af15880 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/converter/MarshallingMessageConverter.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/converter/MarshallingMessageConverter.java @@ -40,7 +40,7 @@ * {@link Marshaller} and {@link Unmarshaller} abstractions. * *

    This converter requires a {@code Marshaller} and {@code Unmarshaller} before it can - * be used. These can be injected by the {@linkplain MarshallingMessageConverter(Marshaller) + * be used. These can be injected by the {@linkplain #MarshallingMessageConverter(Marshaller) * constructor} or {@linkplain #setMarshaller(Marshaller) bean properties}. * * @author Arjen Poutsma diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/HandlerMethodSelector.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/HandlerMethodSelector.java index 69bd0c1aca..3d438200c7 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/HandlerMethodSelector.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/HandlerMethodSelector.java @@ -39,7 +39,7 @@ public abstract class HandlerMethodSelector { * @param handlerType the handler type to search handler methods on * @param handlerMethodFilter a {@link MethodFilter} to help recognize handler methods of interest * @return the selected methods, or an empty set - * @see MethodIntrospector#selectMethods(Class, MethodFilter) + * @see MethodIntrospector#selectMethods */ public static Set selectMethods(Class handlerType, MethodFilter handlerMethodFilter) { return MethodIntrospector.selectMethods(handlerType, handlerMethodFilter); diff --git a/spring-web/src/main/java/org/springframework/web/method/HandlerMethodSelector.java b/spring-web/src/main/java/org/springframework/web/method/HandlerMethodSelector.java index 82acd2c3bd..4fc0545b15 100644 --- a/spring-web/src/main/java/org/springframework/web/method/HandlerMethodSelector.java +++ b/spring-web/src/main/java/org/springframework/web/method/HandlerMethodSelector.java @@ -39,7 +39,7 @@ public abstract class HandlerMethodSelector { * @param handlerType the handler type to search handler methods on * @param handlerMethodFilter a {@link MethodFilter} to help recognize handler methods of interest * @return the selected methods, or an empty set - * @see MethodIntrospector#selectMethods(Class, MethodFilter) + * @see MethodIntrospector#selectMethods */ public static Set selectMethods(Class handlerType, MethodFilter handlerMethodFilter) { return MethodIntrospector.selectMethods(handlerType, handlerMethodFilter); diff --git a/spring-web/src/main/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessor.java b/spring-web/src/main/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessor.java index 72e43cd1b6..000debe454 100644 --- a/spring-web/src/main/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessor.java +++ b/spring-web/src/main/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,23 +38,24 @@ import org.springframework.web.method.support.ModelAndViewContainer; /** - * Resolves method arguments annotated with {@code @ModelAttribute} and handles - * return values from methods annotated with {@code @ModelAttribute}. + * Resolve {@code @ModelAttribute} annotated method arguments and handle + * return values from {@code @ModelAttribute} annotated methods. * - *

    Model attributes are obtained from the model or if not found possibly - * created with a default constructor if it is available. Once created, the - * attributed is populated with request data via data binding and also - * validation may be applied if the argument is annotated with - * {@code @javax.validation.Valid}. + *

    Model attributes are obtained from the model or created with a default + * constructor (and then added to the model). Once created the attribute is + * populated via data binding to Servlet request parameters. Validation may be + * applied if the argument is annotated with {@code @javax.validation.Valid}. + * or Spring's own {@code @org.springframework.validation.annotation.Validated}. * - *

    When this handler is created with {@code annotationNotRequired=true}, + *

    When this handler is created with {@code annotationNotRequired=true} * any non-simple type argument and return value is regarded as a model * attribute with or without the presence of an {@code @ModelAttribute}. * * @author Rossen Stoyanchev * @since 3.1 */ -public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler { +public class ModelAttributeMethodProcessor + implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler { protected final Log logger = LogFactory.getLog(getClass()); @@ -62,6 +63,7 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol /** + * Class constructor. * @param annotationNotRequired if "true", non-simple method arguments and * return values are considered model attributes with or without a * {@code @ModelAttribute} annotation. @@ -72,8 +74,9 @@ public ModelAttributeMethodProcessor(boolean annotationNotRequired) { /** - * Returns {@code true} if the parameter is annotated with {@link ModelAttribute} - * or in default resolution mode, and also if it is not a simple type. + * Returns {@code true} if the parameter is annotated with + * {@link ModelAttribute} or, if in default resolution mode, for any + * method parameter that is not a simple type. */ @Override public boolean supportsParameter(MethodParameter parameter) { @@ -102,8 +105,8 @@ public final Object resolveArgument(MethodParameter parameter, ModelAndViewConta NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { String name = ModelFactory.getNameForParameter(parameter); - Object attribute = (mavContainer.containsAttribute(name) ? - mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, webRequest)); + Object attribute = (mavContainer.containsAttribute(name) ? mavContainer.getModel().get(name) : + createAttribute(name, parameter, binderFactory, webRequest)); WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name); if (binder.getTarget() != null) { @@ -182,7 +185,8 @@ protected boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter /** * Return {@code true} if there is a method-level {@code @ModelAttribute} - * or if it is a non-simple type when {@code annotationNotRequired=true}. + * or, in default resolution mode, for any return value type that is not + * a simple type. */ @Override public boolean supportsReturnType(MethodParameter returnType) { diff --git a/spring-web/src/main/java/org/springframework/web/util/HtmlUtils.java b/spring-web/src/main/java/org/springframework/web/util/HtmlUtils.java index cb43aa9e04..6210945626 100644 --- a/spring-web/src/main/java/org/springframework/web/util/HtmlUtils.java +++ b/spring-web/src/main/java/org/springframework/web/util/HtmlUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,7 +36,6 @@ * @author Martin Kersten * @author Craig Andrews * @since 01.03.2003 - * @see org.apache.commons.lang.StringEscapeUtils */ public abstract class HtmlUtils { diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/WebContentInterceptor.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/WebContentInterceptor.java index 038667f4b6..5e5810e5ea 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/WebContentInterceptor.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/WebContentInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -102,7 +102,6 @@ public void setUrlDecode(boolean urlDecode) { *

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

    For example, given this controller: *

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

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

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

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

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

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

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

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

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

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

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

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

    The default is the application-wide setting (the "defaultHtmlEscape" context-param in web.xml). - * @see org.springframework.web.util.WebUtils#isDefaultHtmlEscape + * @see org.springframework.web.util.WebUtils#getDefaultHtmlEscape */ public void setDefaultHtmlEscape(boolean defaultHtmlEscape) { this.defaultHtmlEscape = defaultHtmlEscape; From 068186ca599fae258f1530420b68291dc769e5eb Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 15 Jul 2016 22:47:12 +0200 Subject: [PATCH 226/344] Polishing (cherry picked from commit afe106e) --- .../web/socket/sockjs/client/UndertowXhrTransport.java | 8 +++----- .../config/annotation/WebSocketConfigurationTests.java | 4 ++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/UndertowXhrTransport.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/UndertowXhrTransport.java index 863059a4e5..4f70016a12 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/UndertowXhrTransport.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/UndertowXhrTransport.java @@ -1,11 +1,11 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -326,14 +326,12 @@ protected void stringDone(String string) { result.getResponse().putAttachment(RESPONSE_BODY, string); latch.countDown(); } - @Override protected void error(IOException ex) { onFailure(latch, ex); } }.setup(result.getResponseChannel()); } - @Override public void failed(IOException ex) { onFailure(latch, ex); @@ -473,7 +471,7 @@ public void onSuccess() { public void onFailure(Throwable failure) { IoUtils.safeClose(this.connection); - if (connectFuture.setException(failure)) { + if (this.connectFuture.setException(failure)) { return; } if (this.session.isDisconnected()) { diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/config/annotation/WebSocketConfigurationTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/config/annotation/WebSocketConfigurationTests.java index 1a66397b7b..f4aa046048 100644 --- a/spring-websocket/src/test/java/org/springframework/web/socket/config/annotation/WebSocketConfigurationTests.java +++ b/spring-websocket/src/test/java/org/springframework/web/socket/config/annotation/WebSocketConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -61,7 +61,7 @@ public static Iterable arguments() { @Override protected Class[] getAnnotatedConfigClasses() { - return new Class[] { TestConfig.class }; + return new Class[] {TestConfig.class}; } @Test From 5a479b87933afec8f2b810f5fd7281dacb7187ee Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 15 Jul 2016 23:01:41 +0200 Subject: [PATCH 227/344] Upgrade to Netty 4.0.39 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 0b54de0f4c..520723b053 100644 --- a/build.gradle +++ b/build.gradle @@ -56,7 +56,7 @@ configure(allprojects) { project -> ext.jtaVersion = "1.2" ext.junitVersion = "4.12" ext.log4jVersion = "1.2.17" - ext.nettyVersion = "4.0.37.Final" + ext.nettyVersion = "4.0.39.Final" ext.okhttpVersion = "2.7.5" ext.openjpaVersion = "2.4.1" ext.poiVersion = "3.13" From a9136d9638e4f6d078d9b8884cf7fa77308c49a3 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 19 Jul 2016 16:43:03 -0400 Subject: [PATCH 228/344] Remove isAsyncStarted assertion Issue: SPR-14444 --- .../web/context/request/async/WebAsyncManager.java | 1 - 1 file changed, 1 deletion(-) diff --git a/spring-web/src/main/java/org/springframework/web/context/request/async/WebAsyncManager.java b/spring-web/src/main/java/org/springframework/web/context/request/async/WebAsyncManager.java index a20cf23d95..f57566de3d 100644 --- a/spring-web/src/main/java/org/springframework/web/context/request/async/WebAsyncManager.java +++ b/spring-web/src/main/java/org/springframework/web/context/request/async/WebAsyncManager.java @@ -105,7 +105,6 @@ public final class WebAsyncManager { */ public void setAsyncWebRequest(final AsyncWebRequest asyncWebRequest) { Assert.notNull(asyncWebRequest, "AsyncWebRequest must not be null"); - Assert.state(!isConcurrentHandlingStarted(), "Can't set AsyncWebRequest with concurrent handling in progress"); this.asyncWebRequest = asyncWebRequest; this.asyncWebRequest.addCompletionHandler(new Runnable() { @Override From b5127dc152e40add517b7cf2dd4f8604ec90bb95 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 20 Jul 2016 22:16:35 +0200 Subject: [PATCH 229/344] Polishing --- .../factory/support/ConstructorResolver.java | 21 ++++---- .../MethodNameBasedMBeanInfoAssembler.java | 4 +- ...ameBasedMBeanInfoAssemblerMappedTests.java | 2 +- ...ethodNameBasedMBeanInfoAssemblerTests.java | 2 +- .../core/ConfigurableObjectInputStream.java | 12 ++--- .../io/AbstractFileResolvingResource.java | 4 +- .../springframework/core/io/PathResource.java | 6 +-- .../org/springframework/core/io/Resource.java | 23 ++++----- .../springframework/core/io/UrlResource.java | 3 +- .../org/springframework/util/ClassUtils.java | 1 - .../springframework/util/ResourceUtils.java | 4 +- .../core/SerializableTypeWrapperTests.java | 2 +- .../CollectionToCollectionConverterTests.java | 48 +++++++++---------- .../jdbc/core/BeanPropertyRowMapper.java | 4 +- .../support/MessageHeaderAccessor.java | 2 +- spring-oxm/oxm.gradle | 4 +- .../org/springframework/http/HttpStatus.java | 30 ++++++------ .../web/util/UrlPathHelper.java | 10 ++-- .../util/HtmlCharacterEntityReferences.dtd | 10 ++-- .../mvc/ServletWrappingController.java | 16 +++---- .../web/servlet/view/xslt/XsltView.java | 11 ++--- .../support/AbstractHandshakeHandler.java | 18 ++++--- 22 files changed, 116 insertions(+), 121 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java index 50c5679112..250b8cf95e 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -151,7 +151,7 @@ public BeanWrapper autowireConstructor(final String beanName, final RootBeanDefi catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Resolution of declared constructors on bean Class [" + beanClass.getName() + - "] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex); + "] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex); } } AutowireUtils.sortConstructors(candidates); @@ -609,8 +609,8 @@ public Object run() { private int resolveConstructorArguments(String beanName, RootBeanDefinition mbd, BeanWrapper bw, ConstructorArgumentValues cargs, ConstructorArgumentValues resolvedValues) { - TypeConverter converter = (this.beanFactory.getCustomTypeConverter() != null ? - this.beanFactory.getCustomTypeConverter() : bw); + TypeConverter customConverter = this.beanFactory.getCustomTypeConverter(); + TypeConverter converter = (customConverter != null ? customConverter : bw); BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this.beanFactory, beanName, mbd, converter); @@ -666,8 +666,8 @@ private ArgumentsHolder createArgumentArray( boolean autowiring) throws UnsatisfiedDependencyException { String methodType = (methodOrCtor instanceof Constructor ? "constructor" : "factory method"); - TypeConverter converter = (this.beanFactory.getCustomTypeConverter() != null ? - this.beanFactory.getCustomTypeConverter() : bw); + TypeConverter customConverter = this.beanFactory.getCustomTypeConverter(); + TypeConverter converter = (customConverter != null ? customConverter : bw); ArgumentsHolder args = new ArgumentsHolder(paramTypes.length); Set usedValueHolders = @@ -768,12 +768,13 @@ private ArgumentsHolder createArgumentArray( private Object[] resolvePreparedArguments( String beanName, RootBeanDefinition mbd, BeanWrapper bw, Member methodOrCtor, Object[] argsToResolve) { - Class[] paramTypes = (methodOrCtor instanceof Method ? - ((Method) methodOrCtor).getParameterTypes() : ((Constructor) methodOrCtor).getParameterTypes()); - TypeConverter converter = (this.beanFactory.getCustomTypeConverter() != null ? - this.beanFactory.getCustomTypeConverter() : bw); + TypeConverter customConverter = this.beanFactory.getCustomTypeConverter(); + TypeConverter converter = (customConverter != null ? customConverter : bw); BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this.beanFactory, beanName, mbd, converter); + Class[] paramTypes = (methodOrCtor instanceof Method ? + ((Method) methodOrCtor).getParameterTypes() : ((Constructor) methodOrCtor).getParameterTypes()); + Object[] resolvedArgs = new Object[argsToResolve.length]; for (int argIndex = 0; argIndex < argsToResolve.length; argIndex++) { Object argValue = argsToResolve[argIndex]; diff --git a/spring-context/src/main/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssembler.java b/spring-context/src/main/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssembler.java index 3df5350dfd..30f5da7638 100644 --- a/spring-context/src/main/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssembler.java +++ b/spring-context/src/main/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssembler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -72,7 +72,7 @@ public class MethodNameBasedMBeanInfoAssembler extends AbstractConfigurableMBean * @param methodNames an array of method names indicating the methods to use * @see #setMethodMappings */ - public void setManagedMethods(String[] methodNames) { + public void setManagedMethods(String... methodNames) { this.managedMethods = new HashSet(Arrays.asList(methodNames)); } diff --git a/spring-context/src/test/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssemblerMappedTests.java b/spring-context/src/test/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssemblerMappedTests.java index 21efe0ba52..9b8ec774e0 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssemblerMappedTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssemblerMappedTests.java @@ -47,7 +47,7 @@ public void testGetAgeIsReadOnly() throws Exception { public void testWithFallThrough() throws Exception { MethodNameBasedMBeanInfoAssembler assembler = getWithMapping("foobar", "add,myOperation,getName,setName,getAge"); - assembler.setManagedMethods(new String[]{"getNickName", "setNickName"}); + assembler.setManagedMethods("getNickName", "setNickName"); ModelMBeanInfo inf = assembler.getMBeanInfo(getBean(), getObjectName()); MBeanAttributeInfo attr = inf.getAttribute("NickName"); diff --git a/spring-context/src/test/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssemblerTests.java b/spring-context/src/test/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssemblerTests.java index 3b51e92bc8..dae9a8a367 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssemblerTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/assembler/MethodNameBasedMBeanInfoAssemblerTests.java @@ -52,7 +52,7 @@ protected int getExpectedAttributeCount() { @Override protected MBeanInfoAssembler getAssembler() { MethodNameBasedMBeanInfoAssembler assembler = new MethodNameBasedMBeanInfoAssembler(); - assembler.setManagedMethods(new String[] {"add", "myOperation", "getName", "setName", "getAge"}); + assembler.setManagedMethods("add", "myOperation", "getName", "setName", "getAge"); return assembler; } diff --git a/spring-core/src/main/java/org/springframework/core/ConfigurableObjectInputStream.java b/spring-core/src/main/java/org/springframework/core/ConfigurableObjectInputStream.java index 5af282f61d..70a0a87c88 100644 --- a/spring-core/src/main/java/org/springframework/core/ConfigurableObjectInputStream.java +++ b/spring-core/src/main/java/org/springframework/core/ConfigurableObjectInputStream.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,7 +21,6 @@ import java.io.NotSerializableException; import java.io.ObjectInputStream; import java.io.ObjectStreamClass; -import java.lang.reflect.Proxy; import org.springframework.util.ClassUtils; @@ -101,7 +100,7 @@ protected Class resolveProxyClass(String[] interfaces) throws IOException, Cl } } try { - return Proxy.getProxyClass(this.classLoader, resolvedInterfaces); + return ClassUtils.createCompositeInterface(resolvedInterfaces, this.classLoader); } catch (IllegalArgumentException ex) { throw new ClassNotFoundException(null, ex); @@ -117,7 +116,7 @@ protected Class resolveProxyClass(String[] interfaces) throws IOException, Cl for (int i = 0; i < interfaces.length; i++) { resolvedInterfaces[i] = resolveFallbackIfPossible(interfaces[i], ex); } - return Proxy.getProxyClass(getFallbackClassLoader(), resolvedInterfaces); + return ClassUtils.createCompositeInterface(resolvedInterfaces, getFallbackClassLoader()); } } } @@ -139,8 +138,9 @@ protected Class resolveFallbackIfPossible(String className, ClassNotFoundExce /** * Return the fallback ClassLoader to use when no ClassLoader was specified - * and ObjectInputStream's own default ClassLoader failed. - *

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

    The default implementation simply returns {@code null}, indicating + * that no specific fallback is available. */ protected ClassLoader getFallbackClassLoader() throws IOException { return null; diff --git a/spring-core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java b/spring-core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java index a81f6c3818..8f61f36b94 100644 --- a/spring-core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -72,7 +72,7 @@ protected File getFileForLastModifiedCheck() throws IOException { } /** - * This implementation returns a File reference for the underlying class path + * This implementation returns a File reference for the given URI-identified * resource, provided that it refers to a file in the file system. * @see org.springframework.util.ResourceUtils#getFile(java.net.URI, String) */ diff --git a/spring-core/src/main/java/org/springframework/core/io/PathResource.java b/spring-core/src/main/java/org/springframework/core/io/PathResource.java index f84ccfa908..af046049c3 100644 --- a/spring-core/src/main/java/org/springframework/core/io/PathResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/PathResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -182,8 +182,8 @@ public File getFile() throws IOException { return this.path.toFile(); } catch (UnsupportedOperationException ex) { - // only Paths on the default file system can be converted to a File - // do exception translation for cases where conversion is not possible + // Only paths on the default file system can be converted to a File: + // Do exception translation for cases where conversion is not possible. throw new FileNotFoundException(this.path + " cannot be resolved to " + "absolute file path"); } } diff --git a/spring-core/src/main/java/org/springframework/core/io/Resource.java b/spring-core/src/main/java/org/springframework/core/io/Resource.java index e3fc2a4e59..ac4fcb2967 100644 --- a/spring-core/src/main/java/org/springframework/core/io/Resource.java +++ b/spring-core/src/main/java/org/springframework/core/io/Resource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,26 +37,26 @@ * @see #getFile() * @see WritableResource * @see ContextResource - * @see FileSystemResource - * @see ClassPathResource * @see UrlResource + * @see ClassPathResource + * @see FileSystemResource + * @see PathResource * @see ByteArrayResource * @see InputStreamResource - * @see PathResource */ public interface Resource extends InputStreamSource { /** - * Return whether this resource actually exists in physical form. + * Determine whether this resource actually exists in physical form. *

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

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

    Will be {@code false} for typical resource descriptors. */ @@ -84,6 +84,7 @@ public interface Resource extends InputStreamSource { * Return a URI handle for this resource. * @throws IOException if the resource cannot be resolved as URI, * i.e. if the resource is not available as descriptor + * @since 2.5 */ URI getURI() throws IOException; diff --git a/spring-core/src/main/java/org/springframework/core/io/UrlResource.java b/spring-core/src/main/java/org/springframework/core/io/UrlResource.java index 41aa4a03b4..44e6516ed8 100644 --- a/spring-core/src/main/java/org/springframework/core/io/UrlResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/UrlResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -61,6 +61,7 @@ public class UrlResource extends AbstractFileResolvingResource { * Create a new {@code UrlResource} based on the given URI object. * @param uri a URI * @throws MalformedURLException if the given URL path is not valid + * @since 2.5 */ public UrlResource(URI uri) throws MalformedURLException { Assert.notNull(uri, "URI must not be null"); diff --git a/spring-core/src/main/java/org/springframework/util/ClassUtils.java b/spring-core/src/main/java/org/springframework/util/ClassUtils.java index 93dd3d616e..ce09285746 100644 --- a/spring-core/src/main/java/org/springframework/util/ClassUtils.java +++ b/spring-core/src/main/java/org/springframework/util/ClassUtils.java @@ -1158,7 +1158,6 @@ public static Set> getAllInterfacesForClassAsSet(Class clazz, ClassL */ public static Class createCompositeInterface(Class[] interfaces, ClassLoader classLoader) { Assert.notEmpty(interfaces, "Interfaces must not be empty"); - Assert.notNull(classLoader, "ClassLoader must not be null"); return Proxy.getProxyClass(classLoader, interfaces); } diff --git a/spring-core/src/main/java/org/springframework/util/ResourceUtils.java b/spring-core/src/main/java/org/springframework/util/ResourceUtils.java index 3ea92707e1..9108053f80 100644 --- a/spring-core/src/main/java/org/springframework/util/ResourceUtils.java +++ b/spring-core/src/main/java/org/springframework/util/ResourceUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -235,6 +235,7 @@ public static File getFile(URL resourceUrl, String description) throws FileNotFo * @return a corresponding File object * @throws FileNotFoundException if the URL cannot be resolved to * a file in the file system + * @since 2.5 */ public static File getFile(URI resourceUri) throws FileNotFoundException { return getFile(resourceUri, "URI"); @@ -249,6 +250,7 @@ public static File getFile(URI resourceUri) throws FileNotFoundException { * @return a corresponding File object * @throws FileNotFoundException if the URL cannot be resolved to * a file in the file system + * @since 2.5 */ public static File getFile(URI resourceUri, String description) throws FileNotFoundException { Assert.notNull(resourceUri, "Resource URI must not be null"); diff --git a/spring-core/src/test/java/org/springframework/core/SerializableTypeWrapperTests.java b/spring-core/src/test/java/org/springframework/core/SerializableTypeWrapperTests.java index 9cb3d53928..ba669e0b53 100644 --- a/spring-core/src/test/java/org/springframework/core/SerializableTypeWrapperTests.java +++ b/spring-core/src/test/java/org/springframework/core/SerializableTypeWrapperTests.java @@ -80,7 +80,7 @@ public void forGenericInterfaces() throws Exception { } @Test - public void forTypeParamters() throws Exception { + public void forTypeParameters() throws Exception { Type type = SerializableTypeWrapper.forTypeParameters(List.class)[0]; assertThat(type.toString(), equalTo("E")); assertSerializable(type); diff --git a/spring-core/src/test/java/org/springframework/core/convert/support/CollectionToCollectionConverterTests.java b/spring-core/src/test/java/org/springframework/core/convert/support/CollectionToCollectionConverterTests.java index c65ba7d3d8..131e739ebb 100644 --- a/spring-core/src/test/java/org/springframework/core/convert/support/CollectionToCollectionConverterTests.java +++ b/spring-core/src/test/java/org/springframework/core/convert/support/CollectionToCollectionConverterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -75,10 +75,10 @@ public void scalarList() throws Exception { conversionService.addConverterFactory(new StringToNumberConverterFactory()); assertTrue(conversionService.canConvert(sourceType, targetType)); @SuppressWarnings("unchecked") - List result = (List) conversionService.convert(list, sourceType, targetType); + List result = (List) conversionService.convert(list, sourceType, targetType); assertFalse(list.equals(result)); - assertEquals(9, result.get(0)); - assertEquals(37, result.get(1)); + assertEquals(9, result.get(0).intValue()); + assertEquals(37, result.get(1).intValue()); } @Test @@ -262,6 +262,25 @@ public void testStringToEnumSet() throws Exception { } + public ArrayList scalarListTarget; + + public List emptyListTarget; + + public LinkedList emptyListDifferentTarget; + + public List>> objectToCollection; + + public List strings; + + public List list = Collections.emptyList(); + + public Collection wildcardCollection = Collections.emptyList(); + + public List resources; + + public EnumSet enumSet; + + public static abstract class BaseResource implements Resource { @Override @@ -330,25 +349,6 @@ public static class TestResource extends BaseResource { } - public static enum MyEnum {A, B, C} - - - public ArrayList scalarListTarget; - - public List emptyListTarget; - - public LinkedList emptyListDifferentTarget; - - public List>> objectToCollection; - - public List strings; - - public List list = Collections.emptyList(); - - public Collection wildcardCollection = Collections.emptyList(); - - public List resources; - - public EnumSet enumSet; + public enum MyEnum {A, B, C} } diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/BeanPropertyRowMapper.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/BeanPropertyRowMapper.java index 88baff51d8..a245aebd2e 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/BeanPropertyRowMapper.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/BeanPropertyRowMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -58,7 +58,7 @@ *

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

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

    For 'null' values read from the database, we will attempt to call the setter, but in the case of * Java primitives, this causes a TypeMismatchException. This class can be configured (using the * primitivesDefaultedForNullValue property) to trap this exception and use the primitives default value. * Be aware that if you use the values from the generated bean to update the database the primitive value diff --git a/spring-messaging/src/main/java/org/springframework/messaging/support/MessageHeaderAccessor.java b/spring-messaging/src/main/java/org/springframework/messaging/support/MessageHeaderAccessor.java index b6b1ba7925..92c5d0ad24 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/support/MessageHeaderAccessor.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/support/MessageHeaderAccessor.java @@ -107,7 +107,7 @@ *

    * *

    Note that the above examples aim to demonstrate the general idea of using - * header accessors. The most likely usage however is through sub-classes. + * header accessors. The most likely usage however is through subclasses. * * @author Rossen Stoyanchev * @author Juergen Hoeller diff --git a/spring-oxm/oxm.gradle b/spring-oxm/oxm.gradle index 140b35130e..23ce60ffd9 100644 --- a/spring-oxm/oxm.gradle +++ b/spring-oxm/oxm.gradle @@ -6,11 +6,9 @@ configurations { } dependencies { castor "org.codehaus.castor:castor-anttasks:1.4.1" - castor "org.apache.velocity:velocity:1.7" + jibx "org.jibx:jibx-bind:1.2.6" xjc "com.sun.xml.bind:jaxb-xjc:2.1.17" xmlbeans "org.apache.xmlbeans:xmlbeans:2.6.0" - jibx "org.jibx:jibx-bind:1.2.6" - jibx "bcel:bcel:5.1" } ext.genSourcesDir = "${buildDir}/generated-sources" diff --git a/spring-web/src/main/java/org/springframework/http/HttpStatus.java b/spring-web/src/main/java/org/springframework/http/HttpStatus.java index d0f9b7c416..4b61e9e61b 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-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,12 +17,13 @@ package org.springframework.http; /** - * Java 5 enumeration of HTTP status codes. + * Enumeration of HTTP status codes. * *

    The HTTP status code series can be retrieved via {@link #series()}. * * @author Arjen Poutsma * @author Sebastien Deleuze + * @since 3.0 * @see HttpStatus.Series * @see HTTP Status Code Registry * @see List of HTTP status codes - Wikipedia @@ -385,17 +386,17 @@ public enum HttpStatus { NETWORK_AUTHENTICATION_REQUIRED(511, "Network Authentication Required"); - private final int value; private final String reasonPhrase; - private HttpStatus(int value, String reasonPhrase) { + HttpStatus(int value, String reasonPhrase) { this.value = value; this.reasonPhrase = reasonPhrase; } + /** * Return the integer value of this status code. */ @@ -407,7 +408,7 @@ public int value() { * Return the reason phrase of this status code. */ public String getReasonPhrase() { - return reasonPhrase; + return this.reasonPhrase; } /** @@ -416,7 +417,7 @@ public String getReasonPhrase() { * This is a shortcut for checking the value of {@link #series()}. */ public boolean is1xxInformational() { - return (Series.INFORMATIONAL.equals(series())); + return Series.INFORMATIONAL.equals(series()); } /** @@ -425,7 +426,7 @@ public boolean is1xxInformational() { * This is a shortcut for checking the value of {@link #series()}. */ public boolean is2xxSuccessful() { - return (Series.SUCCESSFUL.equals(series())); + return Series.SUCCESSFUL.equals(series()); } /** @@ -434,7 +435,7 @@ public boolean is2xxSuccessful() { * This is a shortcut for checking the value of {@link #series()}. */ public boolean is3xxRedirection() { - return (Series.REDIRECTION.equals(series())); + return Series.REDIRECTION.equals(series()); } @@ -444,7 +445,7 @@ public boolean is3xxRedirection() { * This is a shortcut for checking the value of {@link #series()}. */ public boolean is4xxClientError() { - return (Series.CLIENT_ERROR.equals(series())); + return Series.CLIENT_ERROR.equals(series()); } /** @@ -453,7 +454,7 @@ public boolean is4xxClientError() { * This is a shortcut for checking the value of {@link #series()}. */ public boolean is5xxServerError() { - return (Series.SERVER_ERROR.equals(series())); + return Series.SERVER_ERROR.equals(series()); } /** @@ -469,7 +470,7 @@ public Series series() { */ @Override public String toString() { - return Integer.toString(value); + return Integer.toString(this.value); } @@ -490,10 +491,10 @@ public static HttpStatus valueOf(int statusCode) { /** - * Java 5 enumeration of HTTP status series. + * Enumeration of HTTP status series. *

    Retrievable via {@link HttpStatus#series()}. */ - public static enum Series { + public enum Series { INFORMATIONAL(1), SUCCESSFUL(2), @@ -503,7 +504,7 @@ public static enum Series { private final int value; - private Series(int value) { + Series(int value) { this.value = value; } @@ -527,7 +528,6 @@ public static Series valueOf(int status) { public static Series valueOf(HttpStatus status) { return valueOf(status.value); } - } } diff --git a/spring-web/src/main/java/org/springframework/web/util/UrlPathHelper.java b/spring-web/src/main/java/org/springframework/web/util/UrlPathHelper.java index 1a7a4fe322..a1cd6bf63b 100644 --- a/spring-web/src/main/java/org/springframework/web/util/UrlPathHelper.java +++ b/spring-web/src/main/java/org/springframework/web/util/UrlPathHelper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -179,8 +179,8 @@ public String getPathWithinServletMapping(HttpServletRequest request) { String sanitizedPathWithinApp = getSanitizedPath(pathWithinApp); String path; - // if the app container sanitized the servletPath, check against the sanitized version - if (servletPath.indexOf(sanitizedPathWithinApp) != -1) { + // If the app container sanitized the servletPath, check against the sanitized version + if (servletPath.contains(sanitizedPathWithinApp)) { path = getRemainingPath(sanitizedPathWithinApp, servletPath, false); } else { @@ -485,8 +485,8 @@ protected String determineEncoding(HttpServletRequest request) { * @return the updated URI string */ public String removeSemicolonContent(String requestUri) { - return this.removeSemicolonContent ? - removeSemicolonContentInternal(requestUri) : removeJsessionid(requestUri); + return (this.removeSemicolonContent ? + removeSemicolonContentInternal(requestUri) : removeJsessionid(requestUri)); } private String removeSemicolonContentInternal(String requestUri) { diff --git a/spring-web/src/test/resources/org/springframework/web/util/HtmlCharacterEntityReferences.dtd b/spring-web/src/test/resources/org/springframework/web/util/HtmlCharacterEntityReferences.dtd index 31aa2524bf..86e8cbab0c 100644 --- a/spring-web/src/test/resources/org/springframework/web/util/HtmlCharacterEntityReferences.dtd +++ b/spring-web/src/test/resources/org/springframework/web/util/HtmlCharacterEntityReferences.dtd @@ -1,11 +1,9 @@ - - - - + http://www.w3.org/TR/html4/charset.html. --> + - + - + ---- The bean identified as __commandManager__ calls its own method `createCommand()` -whenever it needs a new instance of the __command__ bean. You must be careful to deploy -the `command` bean as a prototype, if that is actually what is needed. If it is deployed -as a <>, the same instance of the `command` +whenever it needs a new instance of the __myCommand__ bean. You must be careful to deploy +the `myCommand` bean as a prototype, if that is actually what is needed. If it is + as a <>, the same instance of the `myCommand` bean is returned each time. +Alternatively, within the annotation-based component model, you may declare a lookup +method through the `@Lookup` annotation: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + public abstract class CommandManager { + + public Object process(Object commandState) { + Command command = createCommand(); + command.setState(commandState); + return command.execute(); + } + + @Lookup("myCommand") + protected abstract Command createCommand(); + } +---- + +Or, more idiomatically, you may rely on the target bean getting resolved against the +declared return type of the lookup method: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + public abstract class CommandManager { + + public Object process(Object commandState) { + MyCommand command = createCommand(); + command.setState(commandState); + return command.execute(); + } + + @Lookup + protected abstract MyCommand createCommand(); + } +---- + +Note that you will typically declare such annotated lookup methods with a concrete +stub implementation, in order for them to be compatible with Spring's component +scanning rules where abstract classes get ignored by default. This limitation does not +apply in case of explicitly registered or explicitly imported bean classes. + [TIP] ==== +Another way of accessing differently scoped target beans is an `ObjectFactory`/ +`Provider` injection point. Check out <>. + The interested reader may also find the `ServiceLocatorFactoryBean` (in the -`org.springframework.beans.factory.config` package) to be of use. The approach used in -ServiceLocatorFactoryBean is similar to that of another utility class, -`ObjectFactoryCreatingFactoryBean`, but it allows you to specify your own lookup -interface as opposed to a Spring-specific lookup interface. Consult the javadocs of -these classes for additional information. +`org.springframework.beans.factory.config` package) to be of use. ==== @@ -6532,10 +6573,9 @@ type of configuration provides a natural means for implementing this pattern. public Object process(Object commandState) { // grab a new instance of the appropriate Command interface Command command = createCommand(); - // set the state on the (hopefully brand new) Command instance command.setState(commandState); - return command.execute(); + return command.execute(); } // okay... but where is the implementation of this method? From ea9e00f8194016c5819168db9967fe071941b1f0 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 4 Oct 2016 23:00:36 +0200 Subject: [PATCH 303/344] Consistent final logger fields (cherry picked from commit f2ac416) --- .../aop/target/dynamic/AbstractRefreshableTargetSource.java | 4 ++-- .../web/socket/server/support/OriginHandshakeInterceptor.java | 4 ++-- .../web/socket/sockjs/client/AbstractXhrTransport.java | 4 +--- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/spring-aop/src/main/java/org/springframework/aop/target/dynamic/AbstractRefreshableTargetSource.java b/spring-aop/src/main/java/org/springframework/aop/target/dynamic/AbstractRefreshableTargetSource.java index c96c21e193..7bab7e0771 100644 --- a/spring-aop/src/main/java/org/springframework/aop/target/dynamic/AbstractRefreshableTargetSource.java +++ b/spring-aop/src/main/java/org/springframework/aop/target/dynamic/AbstractRefreshableTargetSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,7 +39,7 @@ public abstract class AbstractRefreshableTargetSource implements TargetSource, Refreshable { /** Logger available to subclasses */ - protected Log logger = LogFactory.getLog(getClass()); + protected final Log logger = LogFactory.getLog(getClass()); protected Object targetObject; diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/support/OriginHandshakeInterceptor.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/support/OriginHandshakeInterceptor.java index 0401a80c3b..ac03be750e 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/server/support/OriginHandshakeInterceptor.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/support/OriginHandshakeInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,7 +42,7 @@ */ public class OriginHandshakeInterceptor implements HandshakeInterceptor { - protected Log logger = LogFactory.getLog(getClass()); + protected final Log logger = LogFactory.getLog(getClass()); private final Set allowedOrigins = new LinkedHashSet(); diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/AbstractXhrTransport.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/AbstractXhrTransport.java index 2d0fac299e..c6c6b9dc1e 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/AbstractXhrTransport.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/client/AbstractXhrTransport.java @@ -55,7 +55,7 @@ public abstract class AbstractXhrTransport implements XhrTransport { } - protected Log logger = LogFactory.getLog(getClass()); + protected final Log logger = LogFactory.getLog(getClass()); private boolean xhrStreamingDisabled; @@ -73,11 +73,9 @@ public List getTransportTypes() { * An {@code XhrTransport} can support both the "xhr_streaming" and "xhr" * SockJS server transports. From a client perspective there is no * implementation difference. - * *

    Typically an {@code XhrTransport} is used as "XHR streaming" first and * then, if that fails, as "XHR". In some cases however it may be helpful to * suppress XHR streaming so that only XHR is attempted. - * *

    By default this property is set to {@code false} which means both * "XHR streaming" and "XHR" apply. */ From 306452720f485f22777eb6c81f58d23c67b60439 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Thu, 6 Oct 2016 15:36:48 +0200 Subject: [PATCH 304/344] Fix NumberFormatException with X-Forwarded-Host This commit fixes `NumberFormatException`s that were thrown when parsing IPv6 host values in `X-Forwarded-Host` request headers. Issue: SPR-14761 (cherry picked from ea5ff87) --- .../web/util/UriComponentsBuilder.java | 11 +- .../web/util/UriComponentsBuilderTests.java | 145 +++++++++++------- 2 files changed, 93 insertions(+), 63 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java b/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java index 3c3251deec..1308ec7e0b 100644 --- a/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java +++ b/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java @@ -51,6 +51,7 @@ * @author Rossen Stoyanchev * @author Phillip Webb * @author Oliver Gierke + * @author Brian Clozel * @since 3.1 * @see #newInstance() * @see #fromPath(String) @@ -686,11 +687,11 @@ UriComponentsBuilder adaptFromForwardedHeaders(HttpHeaders headers) { else { String hostHeader = headers.getFirst("X-Forwarded-Host"); if (StringUtils.hasText(hostHeader)) { - String hostToUse = StringUtils.commaDelimitedListToStringArray(hostHeader)[0]; - if (hostToUse.contains(":")) { - String[] hostAndPort = StringUtils.split(hostToUse, ":"); - host(hostAndPort[0]); - port(Integer.parseInt(hostAndPort[1])); + String hostToUse = StringUtils.tokenizeToStringArray(hostHeader, ",")[0]; + int portSeparatorIdx = hostToUse.lastIndexOf(":"); + if (portSeparatorIdx > hostToUse.lastIndexOf("]")) { + host(hostToUse.substring(0, portSeparatorIdx)); + port(Integer.parseInt(hostToUse.substring(portSeparatorIdx + 1))); } else { host(hostToUse); diff --git a/spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java b/spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java index 6b5e40e806..546496c2c7 100644 --- a/spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java +++ b/spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java @@ -125,9 +125,7 @@ public void fromOpaqueUri() throws URISyntaxException { assertEquals("Invalid result URI", uri, result.toUri()); } - // SPR-9317 - - @Test + @Test // SPR-9317 public void fromUriEncodedQuery() throws URISyntaxException { URI uri = new URI("http://www.example.org/?param=aGVsbG9Xb3JsZA%3D%3D"); String fromUri = UriComponentsBuilder.fromUri(uri).build().getQueryParams().get("param").get(0); @@ -182,9 +180,7 @@ public void fromUriString() { assertEquals("28", result.getFragment()); } - // SPR-9832 - - @Test + @Test // SPR-9832 public void fromUriStringQueryParamWithReservedCharInValue() throws URISyntaxException { String uri = "http://www.google.com/ig/calculator?q=1USD=?EUR"; UriComponents result = UriComponentsBuilder.fromUriString(uri).build(); @@ -193,41 +189,35 @@ public void fromUriStringQueryParamWithReservedCharInValue() throws URISyntaxExc assertEquals("1USD=?EUR", result.getQueryParams().getFirst("q")); } - // SPR-10779 - - @Test + @Test // SPR-10779 public void fromHttpUrlStringCaseInsesitiveScheme() { assertEquals("http", UriComponentsBuilder.fromHttpUrl("HTTP://www.google.com").build().getScheme()); assertEquals("https", UriComponentsBuilder.fromHttpUrl("HTTPS://www.google.com").build().getScheme()); } - // SPR-10539 - @Test(expected = IllegalArgumentException.class) + + @Test(expected = IllegalArgumentException.class) // SPR-10539 public void fromHttpUrlStringInvalidIPv6Host() throws URISyntaxException { UriComponentsBuilder.fromHttpUrl("http://[1abc:2abc:3abc::5ABC:6abc:8080/resource").build().encode(); } - // SPR-10539 - - @Test + @Test // SPR-10539 public void fromUriStringIPv6Host() throws URISyntaxException { - UriComponents result = UriComponentsBuilder - .fromUriString("http://[1abc:2abc:3abc::5ABC:6abc]:8080/resource").build().encode(); - assertEquals("[1abc:2abc:3abc::5ABC:6abc]", result.getHost()); + UriComponents result = UriComponentsBuilder + .fromUriString("http://[1abc:2abc:3abc::5ABC:6abc]:8080/resource").build().encode(); + assertEquals("[1abc:2abc:3abc::5ABC:6abc]", result.getHost()); - UriComponents resultWithScopeId = UriComponentsBuilder - .fromUriString("http://[1abc:2abc:3abc::5ABC:6abc%eth0]:8080/resource").build().encode(); + UriComponents resultWithScopeId = UriComponentsBuilder + .fromUriString("http://[1abc:2abc:3abc::5ABC:6abc%eth0]:8080/resource").build().encode(); assertEquals("[1abc:2abc:3abc::5ABC:6abc%25eth0]", resultWithScopeId.getHost()); - UriComponents resultIPv4compatible = UriComponentsBuilder - .fromUriString("http://[::192.168.1.1]:8080/resource").build().encode(); + UriComponents resultIPv4compatible = UriComponentsBuilder + .fromUriString("http://[::192.168.1.1]:8080/resource").build().encode(); assertEquals("[::192.168.1.1]", resultIPv4compatible.getHost()); } - // SPR-11970 - - @Test + @Test // SPR-11970 public void fromUriStringNoPathWithReservedCharInQuery() { UriComponents result = UriComponentsBuilder.fromUriString("http://example.com?foo=bar@baz").build(); assertTrue(StringUtils.isEmpty(result.getUserInfo())); @@ -253,9 +243,7 @@ public void fromHttpRequest() throws URISyntaxException { assertEquals("a=1", result.getQuery()); } - // SPR-12771 - - @Test + @Test // SPR-12771 public void fromHttpRequestResetsPortBeforeSettingIt() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest(); request.addHeader("X-Forwarded-Proto", "https"); @@ -275,6 +263,67 @@ public void fromHttpRequestResetsPortBeforeSettingIt() throws Exception { assertEquals("/rest/mobile/users/1", result.getPath()); } + @Test //SPR-14761 + public void fromHttpRequestWithForwardedIPv4Host() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setScheme("http"); + request.setServerName("localhost"); + request.setServerPort(-1); + request.setRequestURI("/mvc-showcase"); + request.addHeader("Forwarded", "host=192.168.0.1"); + + HttpRequest httpRequest = new ServletServerHttpRequest(request); + UriComponents result = UriComponentsBuilder.fromHttpRequest(httpRequest).build(); + + assertEquals("http://192.168.0.1/mvc-showcase", result.toString()); + } + + @Test //SPR-14761 + public void fromHttpRequestWithForwardedIPv6() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setScheme("http"); + request.setServerName("localhost"); + request.setServerPort(-1); + request.setRequestURI("/mvc-showcase"); + request.addHeader("Forwarded", "host=[1abc:2abc:3abc::5ABC:6abc]"); + + HttpRequest httpRequest = new ServletServerHttpRequest(request); + UriComponents result = UriComponentsBuilder.fromHttpRequest(httpRequest).build(); + + assertEquals("http://[1abc:2abc:3abc::5ABC:6abc]/mvc-showcase", result.toString()); + } + + @Test //SPR-14761 + public void fromHttpRequestWithForwardedIPv6Host() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setScheme("http"); + request.setServerName("localhost"); + request.setServerPort(-1); + request.setRequestURI("/mvc-showcase"); + request.addHeader("X-Forwarded-Host", "[1abc:2abc:3abc::5ABC:6abc]"); + + HttpRequest httpRequest = new ServletServerHttpRequest(request); + UriComponents result = UriComponentsBuilder.fromHttpRequest(httpRequest).build(); + + assertEquals("http://[1abc:2abc:3abc::5ABC:6abc]/mvc-showcase", result.toString()); + } + + @Test //SPR-14761 + public void fromHttpRequestWithForwardedIPv6HostAndPort() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setScheme("http"); + request.setServerName("localhost"); + request.setServerPort(-1); + request.setRequestURI("/mvc-showcase"); + request.addHeader("X-Forwarded-Host", "[1abc:2abc:3abc::5ABC:6abc]:8080"); + + HttpRequest httpRequest = new ServletServerHttpRequest(request); + UriComponents result = UriComponentsBuilder.fromHttpRequest(httpRequest).build(); + + assertEquals("http://[1abc:2abc:3abc::5ABC:6abc]:8080/mvc-showcase", result.toString()); + } + + @Test public void fromHttpRequestWithForwardedHost() { MockHttpServletRequest request = new MockHttpServletRequest(); @@ -290,9 +339,7 @@ public void fromHttpRequestWithForwardedHost() { assertEquals("http://anotherHost/mvc-showcase", result.toString()); } - // SPR-10701 - - @Test + @Test // SPR-10701 public void fromHttpRequestWithForwardedHostIncludingPort() { MockHttpServletRequest request = new MockHttpServletRequest(); request.setScheme("http"); @@ -308,9 +355,7 @@ public void fromHttpRequestWithForwardedHostIncludingPort() { assertEquals(443, result.getPort()); } - // SPR-11140 - - @Test + @Test // SPR-11140 public void fromHttpRequestWithForwardedHostMultiValuedHeader() { MockHttpServletRequest request = new MockHttpServletRequest(); request.setScheme("http"); @@ -325,9 +370,7 @@ public void fromHttpRequestWithForwardedHostMultiValuedHeader() { assertEquals(-1, result.getPort()); } - // SPR-11855 - - @Test + @Test // SPR-11855 public void fromHttpRequestWithForwardedHostAndPort() { MockHttpServletRequest request = new MockHttpServletRequest(); request.setScheme("http"); @@ -343,9 +386,7 @@ public void fromHttpRequestWithForwardedHostAndPort() { assertEquals(9090, result.getPort()); } - // SPR-11872 - - @Test + @Test // SPR-11872 public void fromHttpRequestWithForwardedHostWithDefaultPort() { MockHttpServletRequest request = new MockHttpServletRequest(); request.setScheme("http"); @@ -378,9 +419,7 @@ public void fromHttpRequestWithForwardedHostWithForwardedScheme() { assertEquals(-1, result.getPort()); } - // SPR-12771 - - @Test + @Test // SPR-12771 public void fromHttpRequestWithForwardedProtoAndDefaultPort() { MockHttpServletRequest request = new MockHttpServletRequest(); request.setScheme("http"); @@ -397,9 +436,7 @@ public void fromHttpRequestWithForwardedProtoAndDefaultPort() { assertEquals("https://84.198.58.199/mvc-showcase", result.toString()); } - // SPR-12813 - - @Test + @Test // SPR-12813 public void fromHttpRequestWithForwardedPortMultiValueHeader() { MockHttpServletRequest request = new MockHttpServletRequest(); request.setScheme("http"); @@ -415,9 +452,7 @@ public void fromHttpRequestWithForwardedPortMultiValueHeader() { assertEquals("http://a.example.org/mvc-showcase", result.toString()); } - // SPR-12816 - - @Test + @Test // SPR-12816 public void fromHttpRequestWithForwardedProtoMultiValueHeader() { MockHttpServletRequest request = new MockHttpServletRequest(); request.setScheme("http"); @@ -434,9 +469,7 @@ public void fromHttpRequestWithForwardedProtoMultiValueHeader() { assertEquals("https://a.example.org/mvc-showcase", result.toString()); } - // SPR-12742 - - @Test + @Test // SPR-12742 public void fromHttpRequestWithTrailingSlash() throws Exception { UriComponents before = UriComponentsBuilder.fromPath("/foo/").build(); UriComponents after = UriComponentsBuilder.newInstance().uriComponents(before).build(); @@ -506,9 +539,7 @@ public void pathSegmentsSomeEmpty() { assertEquals(Arrays.asList("foo", "bar"), result.getPathSegments()); } - // SPR-12398 - - @Test + @Test // SPR-12398 public void pathWithDuplicateSlashes() throws URISyntaxException { UriComponents uriComponents = UriComponentsBuilder.fromPath("/foo/////////bar").build(); assertEquals("/foo/bar", uriComponents.getPath()); @@ -645,7 +676,7 @@ public void relativeUrls() throws Exception { @Test public void emptySegments() throws Exception { assertThat(UriComponentsBuilder.fromUriString("http://example.com/abc/").path("/x/y/z").build().toString(), equalTo("http://example.com/abc/x/y/z")); - assertThat(UriComponentsBuilder.fromUriString("http://example.com/abc/").pathSegment("x", "y", "z").build().toString(), equalTo("http://example.com/abc/x/y/z")); + assertThat(UriComponentsBuilder.fromUriString("http://example.com/abc/").pathSegment("x", "y", "z").build().toString(), equalTo("http://example.com/abc/x/y/z")); assertThat(UriComponentsBuilder.fromUriString("http://example.com/abc/").path("/x/").path("/y/z").build().toString(), equalTo("http://example.com/abc/x/y/z")); assertThat(UriComponentsBuilder.fromUriString("http://example.com/abc/").pathSegment("x").path("y").build().toString(), equalTo("http://example.com/abc/x/y")); } @@ -686,9 +717,7 @@ public void testClone() throws URISyntaxException { assertEquals("f2", result2.getFragment()); } - // SPR-11856 - - @Test + @Test // SPR-11856 public void fromHttpRequestForwardedHeader() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest(); request.addHeader("Forwarded", "proto=https; host=84.198.58.199"); From fe59bc26aba5cc430e8c14d3368ec05629553b63 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 11 Oct 2016 08:21:52 +0200 Subject: [PATCH 305/344] Polish EnableAsync javadoc Issue: SPR-14793 (cherry picked from commit 7a8bf8e) --- .../scheduling/annotation/EnableAsync.java | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableAsync.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableAsync.java index e4910e9f1b..8b7eb4c026 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableAsync.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/EnableAsync.java @@ -32,16 +32,26 @@ * Enables Spring's asynchronous method execution capability, similar to functionality * found in Spring's {@code } XML namespace. * - *

    To be used on @{@link Configuration} classes as follows, where {@code MyAsyncBean} - * is a user-defined type with one or more methods annotated with either Spring's - * {@code @Async} annotation, the EJB 3.1 {@code @javax.ejb.Asynchronous} annotation, - * or any custom annotation specified via the {@link #annotation} attribute. + *

    To be used together with @{@link Configuration Configuration} classes as follows, + * enabling annotation-driven async processing for an entire Spring application context: * *

      * @Configuration
      * @EnableAsync
      * public class AppConfig {
      *
    + * }
    + * + * {@code MyAsyncBean} is a user-defined type with one or more methods annotated with + * either Spring's {@code @Async} annotation, the EJB 3.1 {@code @javax.ejb.Asynchronous} + * annotation, or any custom annotation specified via the {@link #annotation} attribute. + * The aspect is added transparently for any registered bean, for instance via this + * configuration: + * + *
    + * @Configuration
    + * public class AnotherAppConfig {
    + *
      *     @Bean
      *     public MyAsyncBean asyncBean() {
      *         return new MyAsyncBean();
    @@ -79,11 +89,6 @@
      * @EnableAsync
      * public class AppConfig implements AsyncConfigurer {
      *
    - *     @Bean
    - *     public MyAsyncBean asyncBean() {
    - *         return new MyAsyncBean();
    - *     }
    - *
      *     @Override
      *     public Executor getAsyncExecutor() {
      *         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    
    From aff5d211f4a5f65d99781ad37c7f023b9179f52e Mon Sep 17 00:00:00 2001
    From: Juergen Hoeller 
    Date: Wed, 12 Oct 2016 17:08:05 +0200
    Subject: [PATCH 306/344] StompSubProtocolHandler does not insist on
     SimpMessageHeaderAccessor
    
    Issue: SPR-14791
    (cherry picked from commit f5cd538)
    ---
     .../messaging/StompSubProtocolHandler.java    | 21 +++++--------------
     1 file changed, 5 insertions(+), 16 deletions(-)
    
    diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/messaging/StompSubProtocolHandler.java b/spring-websocket/src/main/java/org/springframework/web/socket/messaging/StompSubProtocolHandler.java
    index 98a3d65aea..b83a1dc5e4 100644
    --- a/spring-websocket/src/main/java/org/springframework/web/socket/messaging/StompSubProtocolHandler.java
    +++ b/spring-websocket/src/main/java/org/springframework/web/socket/messaging/StompSubProtocolHandler.java
    @@ -34,7 +34,6 @@
     import org.springframework.context.ApplicationEventPublisherAware;
     import org.springframework.messaging.Message;
     import org.springframework.messaging.MessageChannel;
    -import org.springframework.messaging.MessageHeaders;
     import org.springframework.messaging.simp.SimpAttributes;
     import org.springframework.messaging.simp.SimpAttributesContextHolder;
     import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
    @@ -463,18 +462,13 @@ private void sendToClient(WebSocketSession session, StompHeaderAccessor stompAcc
     		}
     	}
     
    -	private  StompHeaderAccessor getStompHeaderAccessor(Message message) {
    +	private StompHeaderAccessor getStompHeaderAccessor(Message message) {
     		MessageHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, MessageHeaderAccessor.class);
    -		if (accessor == null) {
    -			// Shouldn't happen (only broker broadcasts directly to clients)
    -			throw new IllegalStateException("No header accessor in " + message);
    -		}
    -		StompHeaderAccessor stompAccessor;
     		if (accessor instanceof StompHeaderAccessor) {
    -			stompAccessor = (StompHeaderAccessor) accessor;
    +			return (StompHeaderAccessor) accessor;
     		}
    -		else if (accessor instanceof SimpMessageHeaderAccessor) {
    -			stompAccessor = StompHeaderAccessor.wrap(message);
    +		else {
    +			StompHeaderAccessor stompAccessor = StompHeaderAccessor.wrap(message);
     			SimpMessageType messageType = SimpMessageHeaderAccessor.getMessageType(message.getHeaders());
     			if (SimpMessageType.CONNECT_ACK.equals(messageType)) {
     				stompAccessor = convertConnectAcktoStompConnected(stompAccessor);
    @@ -496,13 +490,8 @@ else if (SimpMessageType.HEARTBEAT.equals(messageType)) {
     			else if (stompAccessor.getCommand() == null || StompCommand.SEND.equals(stompAccessor.getCommand())) {
     				stompAccessor.updateStompCommandAsServerMessage();
     			}
    +			return stompAccessor;
     		}
    -		else {
    -			// Shouldn't happen (only broker broadcasts directly to clients)
    -			throw new IllegalStateException(
    -					"Unexpected header accessor type: " + accessor.getClass() + " in " + message);
    -		}
    -		return stompAccessor;
     	}
     
     	/**
    
    From 8174f3bfcaa1298b37efcd6550ae91e2aa665d63 Mon Sep 17 00:00:00 2001
    From: Juergen Hoeller 
    Date: Wed, 12 Oct 2016 17:11:04 +0200
    Subject: [PATCH 307/344] Polishing (cherry picked from commit 29a9461)
    
    ---
     .../AnnotationCacheOperationSource.java       | 11 +++----
     .../context/annotation/Primary.java           | 31 ++++++++++---------
     .../support/MessageHeaderAccessor.java        |  2 +-
     .../JtaTransactionAnnotationParser.java       |  5 +--
     4 files changed, 26 insertions(+), 23 deletions(-)
    
    diff --git a/spring-context/src/main/java/org/springframework/cache/annotation/AnnotationCacheOperationSource.java b/spring-context/src/main/java/org/springframework/cache/annotation/AnnotationCacheOperationSource.java
    index 438c8e633f..418e652154 100644
    --- a/spring-context/src/main/java/org/springframework/cache/annotation/AnnotationCacheOperationSource.java
    +++ b/spring-context/src/main/java/org/springframework/cache/annotation/AnnotationCacheOperationSource.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright 2002-2014 the original author or authors.
    + * Copyright 2002-2016 the original author or authors.
      *
      * Licensed under the Apache License, Version 2.0 (the "License");
      * you may not use this file except in compliance with the License.
    @@ -43,8 +43,7 @@
      * @since 3.1
      */
     @SuppressWarnings("serial")
    -public class AnnotationCacheOperationSource extends AbstractFallbackCacheOperationSource
    -		implements Serializable {
    +public class AnnotationCacheOperationSource extends AbstractFallbackCacheOperationSource implements Serializable {
     
     	private final boolean publicMethodsOnly;
     
    @@ -177,6 +176,7 @@ public int hashCode() {
     		return this.annotationParsers.hashCode();
     	}
     
    +
     	/**
     	 * Callback interface providing {@link CacheOperation} instance(s) based on
     	 * a given {@link CacheAnnotationParser}.
    @@ -184,10 +184,9 @@ public int hashCode() {
     	protected interface CacheOperationProvider {
     
     		/**
    -		 * Returns the {@link CacheOperation} instance(s) provided by the specified parser.
    -		 *
    +		 * Return the {@link CacheOperation} instance(s) provided by the specified parser.
     		 * @param parser the parser to use
    -		 * @return the cache operations or {@code null} if none is found
    +		 * @return the cache operations, or {@code null} if none found
     		 */
     		Collection getCacheOperations(CacheAnnotationParser parser);
     	}
    diff --git a/spring-context/src/main/java/org/springframework/context/annotation/Primary.java b/spring-context/src/main/java/org/springframework/context/annotation/Primary.java
    index b9ffc6037f..37b6480e6f 100644
    --- a/spring-context/src/main/java/org/springframework/context/annotation/Primary.java
    +++ b/spring-context/src/main/java/org/springframework/context/annotation/Primary.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright 2002-2011 the original author or authors.
    + * Copyright 2002-2016 the original author or authors.
      *
      * Licensed under the Apache License, Version 2.0 (the "License");
      * you may not use this file except in compliance with the License.
    @@ -25,19 +25,20 @@
     
     /**
      * Indicates that a bean should be given preference when multiple candidates
    - * are qualified to autowire a single-valued dependency. If exactly one 'primary'
    - * bean exists among the candidates, it will be the autowired value. This annotation
    - * is semantically equivalent to the {@code } element's {@code primary} attribute
    - * in Spring XML.
    + * are qualified to autowire a single-valued dependency. If exactly one
    + * 'primary' bean exists among the candidates, it will be the autowired value.
      *
    - * 

    May be used on any class directly or indirectly annotated with @{@link - * org.springframework.stereotype.Component Component} or on methods annotated - * with @{@link Bean}. + *

    This annotation is semantically equivalent to the {@code } element's + * {@code primary} attribute in Spring XML. + * + *

    May be used on any class directly or indirectly annotated with + * {@code @Component} or on methods annotated with @{@link Bean}. * *

    Example

    *
      * @Component
      * public class FooService {
    + *
      *     private FooRepository fooRepository;
      *
      *     @Autowired
    @@ -48,6 +49,7 @@
      *
      * @Component
      * public class JdbcFooRepository {
    + *
      *     public JdbcFooService(DataSource dataSource) {
      *         // ...
      *     }
    @@ -56,20 +58,21 @@
      * @Primary
      * @Component
      * public class HibernateFooRepository {
    + *
      *     public HibernateFooService(SessionFactory sessionFactory) {
      *         // ...
      *     }
      * }
      * 
    * - *

    Because {@code HibernateFooRepository} is marked with {@code @Primary}, it will - * be injected preferentially over the jdbc-based variant assuming both are present as - * beans within the same Spring application context, which is often the case when - * component-scanning is applied liberally. + *

    Because {@code HibernateFooRepository} is marked with {@code @Primary}, + * it will be injected preferentially over the jdbc-based variant assuming both + * are present as beans within the same Spring application context, which is + * often the case when component-scanning is applied liberally. * *

    Note that using {@code @Primary} at the class level has no effect unless - * component-scanning is being used. If a {@code @Primary}-annotated class is declared via - * XML, {@code @Primary} annotation metadata is ignored, and + * component-scanning is being used. If a {@code @Primary}-annotated class is + * declared via XML, {@code @Primary} annotation metadata is ignored, and * {@code } is respected instead. * * @author Chris Beams diff --git a/spring-messaging/src/main/java/org/springframework/messaging/support/MessageHeaderAccessor.java b/spring-messaging/src/main/java/org/springframework/messaging/support/MessageHeaderAccessor.java index 92c5d0ad24..9706f459ee 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/support/MessageHeaderAccessor.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/support/MessageHeaderAccessor.java @@ -566,7 +566,7 @@ public String toString() { * of the given {@code Message}, or {@code null} if that's not available or if * its type does not match the required type. *

    This is for cases where the existence of an accessor is strongly expected - * (to be followed up with an assertion) or will created if not provided. + * (followed up with an assertion) or where an accessor will be created otherwise. * @return an accessor instance of the specified type, or {@code null} if none * @since 4.1 */ diff --git a/spring-tx/src/main/java/org/springframework/transaction/annotation/JtaTransactionAnnotationParser.java b/spring-tx/src/main/java/org/springframework/transaction/annotation/JtaTransactionAnnotationParser.java index dcecefb54c..dedd95f9c0 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/annotation/JtaTransactionAnnotationParser.java +++ b/spring-tx/src/main/java/org/springframework/transaction/annotation/JtaTransactionAnnotationParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,7 +39,8 @@ public class JtaTransactionAnnotationParser implements TransactionAnnotationPars @Override public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) { - AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ae, javax.transaction.Transactional.class); + AnnotationAttributes attributes = + AnnotatedElementUtils.getMergedAnnotationAttributes(ae, javax.transaction.Transactional.class); if (attributes != null) { return parseTransactionAnnotation(attributes); } From d216051b037ff7fc3235471a13e0674de6121228 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 12 Oct 2016 18:10:23 +0200 Subject: [PATCH 308/344] Upgrade to Tomcat 8.0.38 and JRuby 1.7.26 --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 87145c37a6..db93202f76 100644 --- a/build.gradle +++ b/build.gradle @@ -52,7 +52,7 @@ configure(allprojects) { project -> ext.javamailVersion = "1.5.5" ext.jettyVersion = "9.3.9.v20160517" ext.jodaVersion = "2.9.4" - ext.jrubyVersion = "1.7.25" // JRuby 9000 only supported through JSR-223 (StandardScriptFactory) + ext.jrubyVersion = "1.7.26" // JRuby 9000 only supported through JSR-223 (StandardScriptFactory) ext.jtaVersion = "1.2" ext.junitVersion = "4.12" ext.log4jVersion = "1.2.17" @@ -68,7 +68,7 @@ configure(allprojects) { project -> ext.testngVersion = "6.9.10" ext.tiles2Version = "2.2.2" ext.tiles3Version = "3.0.5" - ext.tomcatVersion = "8.0.36" + ext.tomcatVersion = "8.0.38" ext.tyrusVersion = "1.3.5" // constrained by WebLogic 12.1.3 support ext.undertowVersion = "1.3.25.Final" ext.xmlunitVersion = "1.6" From 02541022b3279b9a75affd52bddfc99245f78284 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 25 Oct 2016 17:45:12 +0200 Subject: [PATCH 309/344] Clarify SpEL usage on `@EventListener` Issue: SPR-14812 --- .../context/event/EventListener.java | 15 +++++++++++++-- src/asciidoc/core-beans.adoc | 12 ++++++------ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/event/EventListener.java b/spring-context/src/main/java/org/springframework/context/event/EventListener.java index 9f4fefb4c5..6536e45929 100644 --- a/spring-context/src/main/java/org/springframework/context/event/EventListener.java +++ b/spring-context/src/main/java/org/springframework/context/event/EventListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -87,7 +87,18 @@ /** * Spring Expression Language (SpEL) attribute used for making the * event handling conditional. - *

    Default is "", meaning the event is always handled. + *

    Default is {@code ""}, meaning the event is always handled. + *

    The SpEL expression evaluates against a dedicated context that + * provides the following meta-data: + *

      + *
    • {@code #root.event}, {@code #root.args} for + * references to the {@link ApplicationEvent} and method arguments + * respectively.
    • + *
    • Method arguments can be accessed by index. For instance the + * first argument can be accessed via {@code #root.args[0]}, {@code #p0} + * or {@code #a0}. Arguments can also be accessed by name if that + * information is available.
    • + *
    */ String condition() default ""; diff --git a/src/asciidoc/core-beans.adoc b/src/asciidoc/core-beans.adoc index d4e136bf77..43ec4b5bbc 100644 --- a/src/asciidoc/core-beans.adoc +++ b/src/asciidoc/core-beans.adoc @@ -8191,8 +8191,8 @@ event is equal to `foo`: [source,java,indent=0] [subs="verbatim,quotes"] ---- - @EventListener(condition = "#event.test == 'foo'") - public void processBlackListEvent(BlackListEvent event) { + @EventListener(condition = "#blEvent.test == 'foo'") + public void processBlackListEvent(BlackListEvent blEvent) { // notify appropriate parties via notificationAddress... } ---- @@ -8205,22 +8205,22 @@ available to the context so one can use them for conditional event processing: |=== | Name| Location| Description| Example -| event +| Event | root object | The actual `ApplicationEvent` | `#root.event` -| args +| Arguments array | root object | The arguments (as array) used for invoking the target | `#root.args[0]` -| __argument name__ +| __Argument name__ | evaluation context | Name of any of the method arguments. If for some reason the names are not available (e.g. no debug information), the argument names are also available under the `#a<#arg>` where __#arg__ stands for the argument index (starting from 0). -| `#iban` or `#a0` (one can also use `#p0` or `#p<#arg>` notation as an alias). +| `#blEvent` or `#a0` (one can also use `#p0` or `#p<#arg>` notation as an alias). |=== Note that `#root.event` allows you to access to the underlying event, even if your method From fe333e49bc1bb51593b3195569b9c06cc3f7e77e Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Fri, 28 Oct 2016 17:41:48 +0300 Subject: [PATCH 310/344] Better handling for AsyncRequestTimeoutException Avoid call to sendError when response is committed and log a short error message instead. Issue: SPR-14739 --- .../ResponseEntityExceptionHandler.java | 20 +++++++++++++++---- .../DefaultHandlerExceptionResolver.java | 8 +++++++- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java index c591be11c7..ca3a4705e4 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java @@ -18,6 +18,8 @@ import java.util.List; import java.util.Set; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -43,6 +45,7 @@ import org.springframework.web.bind.ServletRequestBindingException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.context.request.WebRequest; import org.springframework.web.context.request.async.AsyncRequestTimeoutException; import org.springframework.web.multipart.support.MissingServletRequestPartException; @@ -462,14 +465,23 @@ protected ResponseEntity handleNoHandlerFoundException( * @param ex the exception * @param headers the headers to be written to the response * @param status the selected response status - * @param request the current request + * @param webRequest the current request * @return a {@code ResponseEntity} instance * @since 4.2.8 */ protected ResponseEntity handleAsyncRequestTimeoutException( - AsyncRequestTimeoutException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { - - return handleExceptionInternal(ex, null, headers, status, request); + AsyncRequestTimeoutException ex, HttpHeaders headers, HttpStatus status, WebRequest webRequest) { + + if (webRequest instanceof ServletWebRequest) { + ServletWebRequest servletRequest = (ServletWebRequest) webRequest; + HttpServletRequest request = servletRequest.getNativeRequest(HttpServletRequest.class); + HttpServletResponse response = servletRequest.getNativeResponse(HttpServletResponse.class); + if (response.isCommitted()) { + logger.error("Async timeout for " + request.getMethod() + " [" + request.getRequestURI() + "]"); + return null; + } + } + return handleExceptionInternal(ex, null, headers, status, webRequest); } } 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 f5b0343c3f..d3796b3e5e 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 @@ -496,7 +496,12 @@ protected ModelAndView handleNoHandlerFoundException(NoHandlerFoundException ex, protected ModelAndView handleAsyncRequestTimeoutException(AsyncRequestTimeoutException ex, HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException { - response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); + if (!response.isCommitted()) { + response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); + } + else if (logger.isErrorEnabled()) { + logger.error("Async timeout for " + request.getMethod() + " [" + request.getRequestURI() + "]"); + } return new ModelAndView(); } @@ -508,6 +513,7 @@ protected ModelAndView handleAsyncRequestTimeoutException(AsyncRequestTimeoutExc protected void sendServerError(Exception ex, HttpServletRequest request, HttpServletResponse response) throws IOException { + request.setAttribute("javax.servlet.error.exception", ex); response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } From 021e8e44e52c83d2fd134ee5def903c41acbb193 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 28 Oct 2016 15:15:38 +0200 Subject: [PATCH 311/344] AccessorLValue reliably downcasts to CompilablePropertyAccessor in concurrent scenarios Issue: SPR-14850 (cherry picked from commit 5697cb6) --- .../spel/ast/PropertyOrFieldReference.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java index 89b9081db7..a6d36cf3f3 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -371,10 +371,12 @@ public AccessorLValue(PropertyOrFieldReference propertyOrFieldReference, TypedVa @Override public TypedValue getValue() { - TypedValue value = this.ref.getValueInternal(this.contextObject, this.evalContext, this.autoGrowNullReferences); - if (this.ref.cachedReadAccessor instanceof CompilablePropertyAccessor) { - CompilablePropertyAccessor accessor = (CompilablePropertyAccessor) this.ref.cachedReadAccessor; - this.ref.exitTypeDescriptor = CodeFlow.toDescriptor(accessor.getPropertyType()); + TypedValue value = + this.ref.getValueInternal(this.contextObject, this.evalContext, this.autoGrowNullReferences); + PropertyAccessor accessorToUse = this.ref.cachedReadAccessor; + if (accessorToUse instanceof CompilablePropertyAccessor) { + this.ref.exitTypeDescriptor = + CodeFlow.toDescriptor(((CompilablePropertyAccessor) accessorToUse).getPropertyType()); } return value; } From 2b1809239628de6fa70118f49c0201804ea09dba Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 28 Oct 2016 15:25:22 +0200 Subject: [PATCH 312/344] SimpleApplicationEventMulticaster just swallows event downcast exceptions Issue: SPR-14846 (cherry picked from commit fbad637) --- .../SimpleApplicationEventMulticaster.java | 9 ++- .../event/ApplicationContextEventTests.java | 58 ++++++++++++++++--- 2 files changed, 58 insertions(+), 9 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/event/SimpleApplicationEventMulticaster.java b/spring-context/src/main/java/org/springframework/context/event/SimpleApplicationEventMulticaster.java index 04815d6989..95c8886a74 100644 --- a/spring-context/src/main/java/org/springframework/context/event/SimpleApplicationEventMulticaster.java +++ b/spring-context/src/main/java/org/springframework/context/event/SimpleApplicationEventMulticaster.java @@ -166,8 +166,13 @@ protected void invokeListener(ApplicationListener listener, ApplicationEvent eve listener.onApplicationEvent(event); } catch (ClassCastException ex) { - // Possibly a lambda-defined listener which we could not resolve the generic event type for - LogFactory.getLog(getClass()).debug("Non-matching event type for listener: " + listener, ex); + if (ex.getMessage().startsWith(event.getClass().getName())) { + // Possibly a lambda-defined listener which we could not resolve the generic event type for + LogFactory.getLog(getClass()).debug("Non-matching event type for listener: " + listener, ex); + } + else { + throw ex; + } } } } diff --git a/spring-context/src/test/java/org/springframework/context/event/ApplicationContextEventTests.java b/spring-context/src/test/java/org/springframework/context/event/ApplicationContextEventTests.java index a56c77be07..73e9ebd505 100644 --- a/spring-context/src/test/java/org/springframework/context/event/ApplicationContextEventTests.java +++ b/spring-context/src/test/java/org/springframework/context/event/ApplicationContextEventTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -379,6 +379,50 @@ public void innerBeanAsListener() { context.close(); } + @Test + public void anonymousClassAsListener() { + final Set seenEvents = new HashSet<>(); + StaticApplicationContext context = new StaticApplicationContext(); + context.addApplicationListener(new ApplicationListener() { + @Override + public void onApplicationEvent(MyEvent event) { + seenEvents.add(event); + } + }); + context.refresh(); + + MyEvent event1 = new MyEvent(context); + context.publishEvent(event1); + context.publishEvent(new MyOtherEvent(context)); + MyEvent event2 = new MyEvent(context); + context.publishEvent(event2); + assertSame(2, seenEvents.size()); + assertTrue(seenEvents.contains(event1)); + assertTrue(seenEvents.contains(event2)); + + context.close(); + } + + @Test + public void lambdaAsListener() { + final Set seenEvents = new HashSet<>(); + StaticApplicationContext context = new StaticApplicationContext(); + ApplicationListener listener = seenEvents::add; + context.addApplicationListener(listener); + context.refresh(); + + MyEvent event1 = new MyEvent(context); + context.publishEvent(event1); + context.publishEvent(new MyOtherEvent(context)); + MyEvent event2 = new MyEvent(context); + context.publishEvent(event2); + assertSame(2, seenEvents.size()); + assertTrue(seenEvents.contains(event1)); + assertTrue(seenEvents.contains(event2)); + + context.close(); + } + @Test public void beanPostProcessorPublishesEvents() { GenericApplicationContext context = new GenericApplicationContext(); @@ -415,7 +459,7 @@ public MyOtherEvent(Object source) { public static class MyOrderedListener1 implements ApplicationListener, Ordered { - public final Set seenEvents = new HashSet(); + public final Set seenEvents = new HashSet<>(); @Override public void onApplicationEvent(ApplicationEvent event) { @@ -452,14 +496,14 @@ public MyOrderedListener2(MyOrderedListener1 otherListener) { @Override public void onApplicationEvent(MyEvent event) { - assertTrue(otherListener.seenEvents.contains(event)); + assertTrue(this.otherListener.seenEvents.contains(event)); } } public static class MyPayloadListener implements ApplicationListener { - public final Set seenPayloads = new HashSet(); + public final Set seenPayloads = new HashSet<>(); @Override public void onApplicationEvent(PayloadApplicationEvent event) { @@ -470,7 +514,7 @@ public void onApplicationEvent(PayloadApplicationEvent event) { public static class MyNonSingletonListener implements ApplicationListener { - public static final Set seenEvents = new HashSet(); + public static final Set seenEvents = new HashSet<>(); @Override public void onApplicationEvent(ApplicationEvent event) { @@ -482,7 +526,7 @@ public void onApplicationEvent(ApplicationEvent event) { @Order(5) public static class MyOrderedListener3 implements ApplicationListener { - public final Set seenEvents = new HashSet(); + public final Set seenEvents = new HashSet<>(); @Override public void onApplicationEvent(ApplicationEvent event) { @@ -503,7 +547,7 @@ public MyOrderedListener4(MyOrderedListener3 otherListener) { @Override public void onApplicationEvent(MyEvent event) { - assertTrue(otherListener.seenEvents.contains(event)); + assertTrue(this.otherListener.seenEvents.contains(event)); } } From 0d6dc5760cb58543ce47fd0228402a76d36c7bcc Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 28 Oct 2016 15:37:10 +0200 Subject: [PATCH 313/344] Test for multi-character delimiter Issue: SPR-14808 (cherry picked from commit 5578a2e) --- .../datasource/init/ScriptUtilsUnitTests.java | 48 ++++++++----------- 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/ScriptUtilsUnitTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/ScriptUtilsUnitTests.java index ab9a5a5eb9..3cb03b129f 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/ScriptUtilsUnitTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/ScriptUtilsUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -50,7 +50,7 @@ public void splitSqlScriptDelimitedWithSemicolon() { String cleanedStatement3 = "insert into orders(id, order_date, customer_id) values (1, '2008-01-02', 2)"; char delim = ';'; String script = rawStatement1 + delim + rawStatement2 + delim + rawStatement3 + delim; - List statements = new ArrayList(); + List statements = new ArrayList<>(); splitSqlScript(script, delim, statements); assertEquals("wrong number of statements", 3, statements.size()); assertEquals("statement 1 not split correctly", cleanedStatement1, statements.get(0)); @@ -65,7 +65,7 @@ public void splitSqlScriptDelimitedWithNewLine() { String statement3 = "insert into orders(id, order_date, customer_id) values (1, '2008-01-02', 2)"; char delim = '\n'; String script = statement1 + delim + statement2 + delim + statement3 + delim; - List statements = new ArrayList(); + List statements = new ArrayList<>(); splitSqlScript(script, delim, statements); assertEquals("wrong number of statements", 3, statements.size()); assertEquals("statement 1 not split correctly", statement1, statements.get(0)); @@ -79,36 +79,30 @@ public void splitSqlScriptDelimitedWithNewLineButDefaultDelimiterSpecified() { String statement2 = "do something else"; char delim = '\n'; String script = statement1 + delim + statement2 + delim; - List statements = new ArrayList(); + List statements = new ArrayList<>(); splitSqlScript(script, DEFAULT_STATEMENT_SEPARATOR, statements); assertEquals("wrong number of statements", 1, statements.size()); assertEquals("script should have been 'stripped' but not actually 'split'", script.replace('\n', ' '), statements.get(0)); } - /** - * See SPR-13218 - */ - @Test + @Test // SPR-13218 public void splitScriptWithSingleQuotesNestedInsideDoubleQuotes() throws Exception { String statement1 = "select '1' as \"Dogbert's owner's\" from dual"; String statement2 = "select '2' as \"Dilbert's\" from dual"; char delim = ';'; String script = statement1 + delim + statement2 + delim; - List statements = new ArrayList(); + List statements = new ArrayList<>(); splitSqlScript(script, ';', statements); assertEquals("wrong number of statements", 2, statements.size()); assertEquals("statement 1 not split correctly", statement1, statements.get(0)); assertEquals("statement 2 not split correctly", statement2, statements.get(1)); } - /** - * See SPR-11560 - */ - @Test + @Test // SPR-11560 public void readAndSplitScriptWithMultipleNewlinesAsSeparator() throws Exception { String script = readScript("db-test-data-multi-newline.sql"); - List statements = new ArrayList(); + List statements = new ArrayList<>(); splitSqlScript(script, "\n\n", statements); String statement1 = "insert into T_TEST (NAME) values ('Keith')"; @@ -122,7 +116,7 @@ public void readAndSplitScriptWithMultipleNewlinesAsSeparator() throws Exception @Test public void readAndSplitScriptContainingComments() throws Exception { String script = readScript("test-data-with-comments.sql"); - List statements = new ArrayList(); + List statements = new ArrayList<>(); splitSqlScript(script, ';', statements); String statement1 = "insert into customer (id, name) values (1, 'Rod; Johnson'), (2, 'Adrian Collier')"; @@ -138,13 +132,10 @@ public void readAndSplitScriptContainingComments() throws Exception { assertEquals("statement 4 not split correctly", statement4, statements.get(3)); } - /** - * See SPR-10330 - */ - @Test + @Test // SPR-10330 public void readAndSplitScriptContainingCommentsWithLeadingTabs() throws Exception { String script = readScript("test-data-with-comments-and-leading-tabs.sql"); - List statements = new ArrayList(); + List statements = new ArrayList<>(); splitSqlScript(script, ';', statements); String statement1 = "insert into customer (id, name) values (1, 'Sam Brannen')"; @@ -157,13 +148,10 @@ public void readAndSplitScriptContainingCommentsWithLeadingTabs() throws Excepti assertEquals("statement 3 not split correctly", statement3, statements.get(2)); } - /** - * See SPR-9531 - */ - @Test + @Test // SPR-9531 public void readAndSplitScriptContainingMuliLineComments() throws Exception { String script = readScript("test-data-with-multi-line-comments.sql"); - List statements = new ArrayList(); + List statements = new ArrayList<>(); splitSqlScript(script, ';', statements); String statement1 = "INSERT INTO users(first_name, last_name) VALUES('Juergen', 'Hoeller')"; @@ -176,10 +164,12 @@ public void readAndSplitScriptContainingMuliLineComments() throws Exception { @Test public void containsDelimiters() { - assertTrue("test with ';' is wrong", !containsSqlScriptDelimiters("select 1\n select ';'", ";")); - assertTrue("test with delimiter ; is wrong", containsSqlScriptDelimiters("select 1; select 2", ";")); - assertTrue("test with '\\n' is wrong", !containsSqlScriptDelimiters("select 1; select '\\n\n';", "\n")); - assertTrue("test with delimiter \\n is wrong", containsSqlScriptDelimiters("select 1\n select 2", "\n")); + assertFalse(containsSqlScriptDelimiters("select 1\n select ';'", ";")); + assertTrue(containsSqlScriptDelimiters("select 1; select 2", ";")); + assertFalse(containsSqlScriptDelimiters("select 1; select '\\n\n';", "\n")); + assertTrue(containsSqlScriptDelimiters("select 1\n select 2", "\n")); + assertFalse(containsSqlScriptDelimiters("select 1\n select 2", "\n\n")); + assertTrue(containsSqlScriptDelimiters("select 1\n\n select 2", "\n\n")); } private String readScript(String path) throws Exception { From 4ce0e6b3c3311c0f2d2ff1a67401a696ba4f8da5 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 28 Oct 2016 23:34:19 +0200 Subject: [PATCH 314/344] Avoid deadlock between SockJS heartbeat and XHR polling Issue: SPR-14833 (cherry picked from commit 1c80d2a) --- .../session/AbstractHttpSockJsSession.java | 21 +++---------------- .../session/AbstractSockJsSession.java | 13 ++++++------ 2 files changed, 10 insertions(+), 24 deletions(-) diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractHttpSockJsSession.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractHttpSockJsSession.java index df0e2771c4..92da22d701 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractHttpSockJsSession.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractHttpSockJsSession.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -51,6 +51,7 @@ */ public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession { + private final Queue messageCache; private volatile URI uri; @@ -64,20 +65,13 @@ public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession { private volatile String acceptedProtocol; - private volatile ServerHttpResponse response; private volatile SockJsFrameFormat frameFormat; - private volatile ServerHttpAsyncRequestControl asyncRequestControl; - private final Object responseLock = new Object(); - - private volatile boolean readyToSend; - - - private final Queue messageCache; + private boolean readyToSend; public AbstractHttpSockJsSession(String id, SockJsServiceConfig config, @@ -205,14 +199,10 @@ public void handleInitialRequest(ServerHttpRequest request, ServerHttpResponse r this.frameFormat = frameFormat; this.asyncRequestControl = request.getAsyncRequestControl(response); this.asyncRequestControl.start(-1); - disableShallowEtagHeaderFilter(request); - // Let "our" handler know before sending the open frame to the remote handler delegateConnectionEstablished(); - handleRequestInternal(request, response, true); - // Request might have been reset (e.g. polling sessions do after writing) this.readyToSend = isActive(); } @@ -248,9 +238,7 @@ public void handleSuccessiveRequest(ServerHttpRequest request, ServerHttpRespons this.frameFormat = frameFormat; this.asyncRequestControl = request.getAsyncRequestControl(response); this.asyncRequestControl.start(-1); - disableShallowEtagHeaderFilter(request); - handleRequestInternal(request, response, false); this.readyToSend = isActive(); } @@ -322,14 +310,11 @@ protected void disconnect(CloseStatus status) { protected void resetRequest() { synchronized (this.responseLock) { - ServerHttpAsyncRequestControl control = this.asyncRequestControl; this.asyncRequestControl = null; this.readyToSend = false; this.response = null; - updateLastActiveTime(); - if (control != null && !control.isCompleted()) { if (control.isStarted()) { try { diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractSockJsSession.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractSockJsSession.java index d9ca92960e..7211c97049 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractSockJsSession.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractSockJsSession.java @@ -30,6 +30,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.core.NestedCheckedException; import org.springframework.util.Assert; import org.springframework.web.socket.CloseStatus; @@ -90,6 +91,8 @@ private enum State {NEW, OPEN, CLOSED} protected final Log logger = LogFactory.getLog(getClass()); + protected final Object responseLock = new Object(); + private final String id; private final SockJsServiceConfig config; @@ -108,8 +111,6 @@ private enum State {NEW, OPEN, CLOSED} private HeartbeatTask heartbeatTask; - private final Object heartbeatLock = new Object(); - private volatile boolean heartbeatDisabled; @@ -249,7 +250,7 @@ public void disableHeartbeat() { } public void sendHeartbeat() throws SockJsTransportFailureException { - synchronized (this.heartbeatLock) { + synchronized (this.responseLock) { if (isActive() && !this.heartbeatDisabled) { writeFrame(SockJsFrame.heartbeatFrame()); scheduleHeartbeat(); @@ -261,7 +262,7 @@ protected void scheduleHeartbeat() { if (this.heartbeatDisabled) { return; } - synchronized (this.heartbeatLock) { + synchronized (this.responseLock) { cancelHeartbeat(); if (!isActive()) { return; @@ -276,7 +277,7 @@ protected void scheduleHeartbeat() { } protected void cancelHeartbeat() { - synchronized (this.heartbeatLock) { + synchronized (this.responseLock) { if (this.heartbeatFuture != null) { if (logger.isTraceEnabled()) { logger.trace("Cancelling heartbeat in session " + getId()); @@ -444,7 +445,7 @@ private class HeartbeatTask implements Runnable { @Override public void run() { - synchronized (heartbeatLock) { + synchronized (responseLock) { if (!this.expired) { try { sendHeartbeat(); From 6b7f3fd94247bb3bc17351021b09c096ffaeec03 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 28 Oct 2016 23:35:01 +0200 Subject: [PATCH 315/344] No external locking for singleton advice/aspect beans Issue: SPR-14324 (cherry picked from commit e18e7ec) --- .../BeanFactoryAspectInstanceFactory.java | 15 ++++++++-- ...ngletonAspectInstanceFactoryDecorator.java | 11 ++++++-- .../AbstractBeanFactoryPointcutAdvisor.java | 28 +++++++++++++++---- 3 files changed, 43 insertions(+), 11 deletions(-) diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/BeanFactoryAspectInstanceFactory.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/BeanFactoryAspectInstanceFactory.java index 713fb44fce..eb78d0bfbf 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/BeanFactoryAspectInstanceFactory.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/BeanFactoryAspectInstanceFactory.java @@ -93,8 +93,19 @@ public AspectMetadata getAspectMetadata() { } public Object getAspectCreationMutex() { - return (this.beanFactory instanceof ConfigurableBeanFactory ? - ((ConfigurableBeanFactory) this.beanFactory).getSingletonMutex() : this); + if (this.beanFactory != null) { + if (this.beanFactory.isSingleton(name)) { + // Rely on singleton semantics provided by the factory -> no local lock. + return null; + } + else if (this.beanFactory instanceof ConfigurableBeanFactory) { + // No singleton guarantees from the factory -> let's lock locally but + // reuse the factory's singleton lock, just in case a lazy dependency + // of our advice bean happens to trigger the singleton lock implicitly... + return ((ConfigurableBeanFactory) this.beanFactory).getSingletonMutex(); + } + } + return this; } /** diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/LazySingletonAspectInstanceFactoryDecorator.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/LazySingletonAspectInstanceFactoryDecorator.java index 78df54658c..0f793cafef 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/LazySingletonAspectInstanceFactoryDecorator.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/LazySingletonAspectInstanceFactoryDecorator.java @@ -49,9 +49,14 @@ public Object getAspectInstance() { if (this.maaif instanceof BeanFactoryAspectInstanceFactory) { mutex = ((BeanFactoryAspectInstanceFactory) this.maaif).getAspectCreationMutex(); } - synchronized (mutex) { - if (this.materialized == null) { - this.materialized = this.maaif.getAspectInstance(); + if (mutex == null) { + this.materialized = this.maaif.getAspectInstance(); + } + else { + synchronized (mutex) { + if (this.materialized == null) { + this.materialized = this.maaif.getAspectInstance(); + } } } } diff --git a/spring-aop/src/main/java/org/springframework/aop/support/AbstractBeanFactoryPointcutAdvisor.java b/spring-aop/src/main/java/org/springframework/aop/support/AbstractBeanFactoryPointcutAdvisor.java index 2c2eff5feb..5401bb96a7 100644 --- a/spring-aop/src/main/java/org/springframework/aop/support/AbstractBeanFactoryPointcutAdvisor.java +++ b/spring-aop/src/main/java/org/springframework/aop/support/AbstractBeanFactoryPointcutAdvisor.java @@ -46,7 +46,7 @@ public abstract class AbstractBeanFactoryPointcutAdvisor extends AbstractPointcu private BeanFactory beanFactory; - private transient Advice advice; + private transient volatile Advice advice; private transient volatile Object adviceMonitor = new Object(); @@ -98,12 +98,28 @@ public void setAdvice(Advice advice) { @Override public Advice getAdvice() { - synchronized (this.adviceMonitor) { - if (this.advice == null && this.adviceBeanName != null) { - Assert.state(this.beanFactory != null, "BeanFactory must be set to resolve 'adviceBeanName'"); - this.advice = this.beanFactory.getBean(this.adviceBeanName, Advice.class); + Advice advice = this.advice; + if (advice != null || this.adviceBeanName == null) { + return advice; + } + + Assert.state(this.beanFactory != null, "BeanFactory must be set to resolve 'adviceBeanName'"); + if (this.beanFactory.isSingleton(this.adviceBeanName)) { + // Rely on singleton semantics provided by the factory. + advice = this.beanFactory.getBean(this.adviceBeanName, Advice.class); + this.advice = advice; + return advice; + } + else { + // No singleton guarantees from the factory -> let's lock locally but + // reuse the factory's singleton lock, just in case a lazy dependency + // of our advice bean happens to trigger the singleton lock implicitly... + synchronized (this.adviceMonitor) { + if (this.advice == null) { + this.advice = this.beanFactory.getBean(this.adviceBeanName, Advice.class); + } + return this.advice; } - return this.advice; } } From 17d622176d1673e42345739c8acbdc9282bc3e12 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sun, 30 Oct 2016 22:29:53 +0100 Subject: [PATCH 316/344] ObjectToObjectConverter properly handles constructors on non-public classes Issue: SPR-14304 (cherry picked from commit edf1df3) --- .../core/convert/support/ObjectToObjectConverter.java | 1 + 1 file changed, 1 insertion(+) diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/ObjectToObjectConverter.java b/spring-core/src/main/java/org/springframework/core/convert/support/ObjectToObjectConverter.java index e4d000e6be..7fe5f3c1c0 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/support/ObjectToObjectConverter.java +++ b/spring-core/src/main/java/org/springframework/core/convert/support/ObjectToObjectConverter.java @@ -102,6 +102,7 @@ public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor t } else if (member instanceof Constructor) { Constructor ctor = (Constructor) member; + ReflectionUtils.makeAccessible(ctor); return ctor.newInstance(source); } } From 377780a3c7ba98457b41e7cd74cc12341dcf07ef Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 31 Oct 2016 19:24:45 +0100 Subject: [PATCH 317/344] Polishing (cherry picked from commit ade139f) --- .../ConfigurableApplicationContext.java | 6 +-- .../support/AbstractApplicationContext.java | 12 +++-- .../support/FormattingConversionService.java | 4 +- .../support/CustomNamespaceHandlerTests.java | 33 ++++++------- .../core/convert/TypeDescriptor.java | 13 +++-- .../convert/converter/ConverterRegistry.java | 17 +++---- .../support/GenericConversionService.java | 20 ++++---- .../core/io/support/EncodedResource.java | 5 +- .../tests/TestResourceUtils.java | 15 ++---- .../expression/spel/ast/OpEQ.java | 30 ++++++------ .../expression/spel/ast/OpNE.java | 17 +++---- .../expression/spel/ast/Operator.java | 48 ++++++++++--------- .../init/DatabasePopulatorUtils.java | 9 ++-- .../init/ResourceDatabasePopulator.java | 41 +++++----------- .../init/ResourceDatabasePopulatorTests.java | 16 +++---- .../messaging/MessageChannel.java | 4 +- .../DefaultTransactionAttribute.java | 11 +++-- 17 files changed, 144 insertions(+), 157 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/ConfigurableApplicationContext.java b/spring-context/src/main/java/org/springframework/context/ConfigurableApplicationContext.java index 09048cbe1f..dfc3c2dbba 100644 --- a/spring-context/src/main/java/org/springframework/context/ConfigurableApplicationContext.java +++ b/spring-context/src/main/java/org/springframework/context/ConfigurableApplicationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -113,9 +113,9 @@ public interface ConfigurableApplicationContext extends ApplicationContext, Life * Add a new BeanFactoryPostProcessor that will get applied to the internal * bean factory of this application context on refresh, before any of the * bean definitions get evaluated. To be invoked during context configuration. - * @param beanFactoryPostProcessor the factory processor to register + * @param postProcessor the factory processor to register */ - void addBeanFactoryPostProcessor(BeanFactoryPostProcessor beanFactoryPostProcessor); + void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor); /** * Add a new ApplicationListener that will be notified on context events diff --git a/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java b/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java index d21d28177b..791ed80ce8 100644 --- a/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java +++ b/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,6 +47,7 @@ import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.context.ApplicationListener; import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.EmbeddedValueResolverAware; import org.springframework.context.EnvironmentAware; import org.springframework.context.HierarchicalMessageSource; import org.springframework.context.LifecycleProcessor; @@ -461,8 +462,9 @@ public void setParent(ApplicationContext parent) { } @Override - public void addBeanFactoryPostProcessor(BeanFactoryPostProcessor beanFactoryPostProcessor) { - this.beanFactoryPostProcessors.add(beanFactoryPostProcessor); + public void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor) { + Assert.notNull(postProcessor, "BeanFactoryPostProcessor must not be null"); + this.beanFactoryPostProcessors.add(postProcessor); } @@ -476,6 +478,7 @@ public List getBeanFactoryPostProcessors() { @Override public void addApplicationListener(ApplicationListener listener) { + Assert.notNull(listener, "ApplicationListener must not be null"); if (this.applicationEventMulticaster != null) { this.applicationEventMulticaster.addApplicationListener(listener); } @@ -627,11 +630,12 @@ protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { // Configure the bean factory with context callbacks. beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this)); + beanFactory.ignoreDependencyInterface(EnvironmentAware.class); + beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class); beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class); beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class); beanFactory.ignoreDependencyInterface(MessageSourceAware.class); beanFactory.ignoreDependencyInterface(ApplicationContextAware.class); - beanFactory.ignoreDependencyInterface(EnvironmentAware.class); // BeanFactory interface not registered as resolvable type in a plain factory. // MessageSource registered (and found for autowiring) as a bean. diff --git a/spring-context/src/main/java/org/springframework/format/support/FormattingConversionService.java b/spring-context/src/main/java/org/springframework/format/support/FormattingConversionService.java index 342607ed34..6e37317b6f 100644 --- a/spring-context/src/main/java/org/springframework/format/support/FormattingConversionService.java +++ b/spring-context/src/main/java/org/springframework/format/support/FormattingConversionService.java @@ -98,8 +98,8 @@ public void addFormatterForFieldAnnotation(AnnotationFormatterFactory getFieldType(Formatter formatter) { Class fieldType = GenericTypeResolver.resolveTypeArgument(formatter.getClass(), Formatter.class); if (fieldType == null) { - throw new IllegalArgumentException("Unable to extract parameterized field type argument from Formatter [" + - formatter.getClass().getName() + "]; does the formatter parameterize the generic type?"); + throw new IllegalArgumentException("Unable to extract the parameterized field type from Formatter [" + + formatter.getClass().getName() + "]; does the class parameterize the generic type?"); } return fieldType; } diff --git a/spring-context/src/test/java/org/springframework/beans/factory/xml/support/CustomNamespaceHandlerTests.java b/spring-context/src/test/java/org/springframework/beans/factory/xml/support/CustomNamespaceHandlerTests.java index 48efe37957..000135a65e 100644 --- a/spring-context/src/test/java/org/springframework/beans/factory/xml/support/CustomNamespaceHandlerTests.java +++ b/spring-context/src/test/java/org/springframework/beans/factory/xml/support/CustomNamespaceHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -82,6 +82,7 @@ public class CustomNamespaceHandlerTests { private GenericApplicationContext beanFactory; + @Before public void setUp() throws Exception { NamespaceHandlerResolver resolver = new DefaultNamespaceHandlerResolver(CLASS.getClassLoader(), NS_PROPS); @@ -114,7 +115,7 @@ public void testProxyingDecorator() throws Exception { assertTrue(AopUtils.isAopProxy(bean)); Advisor[] advisors = ((Advised) bean).getAdvisors(); assertEquals("Incorrect number of advisors", 1, advisors.length); - assertEquals("Incorrect advice class.", DebugInterceptor.class, advisors[0].getAdvice().getClass()); + assertEquals("Incorrect advice class", DebugInterceptor.class, advisors[0].getAdvice().getClass()); } @Test @@ -138,8 +139,8 @@ public void testChainedDecorators() throws Exception { assertTrue(AopUtils.isAopProxy(bean)); Advisor[] advisors = ((Advised) bean).getAdvisors(); assertEquals("Incorrect number of advisors", 2, advisors.length); - assertEquals("Incorrect advice class.", DebugInterceptor.class, advisors[0].getAdvice().getClass()); - assertEquals("Incorrect advice class.", NopInterceptor.class, advisors[1].getAdvice().getClass()); + assertEquals("Incorrect advice class", DebugInterceptor.class, advisors[0].getAdvice().getClass()); + assertEquals("Incorrect advice class", NopInterceptor.class, advisors[1].getAdvice().getClass()); } @Test @@ -148,30 +149,21 @@ public void testDecorationViaAttribute() throws Exception { assertEquals("foo", beanDefinition.getAttribute("objectName")); } - /** - * http://opensource.atlassian.com/projects/spring/browse/SPR-2728 - */ - @Test + @Test // SPR-2728 public void testCustomElementNestedWithinUtilList() throws Exception { List things = (List) this.beanFactory.getBean("list.of.things"); assertNotNull(things); assertEquals(2, things.size()); } - /** - * http://opensource.atlassian.com/projects/spring/browse/SPR-2728 - */ - @Test + @Test // SPR-2728 public void testCustomElementNestedWithinUtilSet() throws Exception { Set things = (Set) this.beanFactory.getBean("set.of.things"); assertNotNull(things); assertEquals(2, things.size()); } - /** - * http://opensource.atlassian.com/projects/spring/browse/SPR-2728 - */ - @Test + @Test // SPR-2728 public void testCustomElementNestedWithinUtilMap() throws Exception { Map things = (Map) this.beanFactory.getBean("map.of.things"); assertNotNull(things); @@ -229,6 +221,7 @@ public void init() { registerBeanDefinitionDecoratorForAttribute("object-name", new ObjectNameBeanDefinitionDecorator()); } + private static class TestBeanDefinitionParser implements BeanDefinitionParser { @Override @@ -242,11 +235,11 @@ public BeanDefinition parse(Element element, ParserContext parserContext) { definition.setPropertyValues(mpvs); parserContext.getRegistry().registerBeanDefinition(element.getAttribute("id"), definition); - return null; } } + private static final class PersonDefinitionParser extends AbstractSingleBeanDefinitionParser { @Override @@ -261,6 +254,7 @@ protected void doParse(Element element, BeanDefinitionBuilder builder) { } } + private static class PropertyModifyingBeanDefinitionDecorator implements BeanDefinitionDecorator { @Override @@ -277,6 +271,7 @@ public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, } } + private static class DebugBeanDefinitionDecorator extends AbstractInterceptorDrivenBeanDefinitionDecorator { @Override @@ -285,6 +280,7 @@ protected BeanDefinition createInterceptorDefinition(Node node) { } } + private static class NopInterceptorBeanDefinitionDecorator extends AbstractInterceptorDrivenBeanDefinitionDecorator { @Override @@ -293,6 +289,7 @@ protected BeanDefinition createInterceptorDefinition(Node node) { } } + private static class ObjectNameBeanDefinitionDecorator implements BeanDefinitionDecorator { @Override @@ -302,5 +299,5 @@ public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, return definition; } } -} +} diff --git a/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java b/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java index 21b367dc07..2d03ed810b 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java +++ b/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java @@ -83,9 +83,8 @@ public TypeDescriptor(MethodParameter methodParameter) { Assert.notNull(methodParameter, "MethodParameter must not be null"); this.resolvableType = ResolvableType.forMethodParameter(methodParameter); this.type = this.resolvableType.resolve(methodParameter.getParameterType()); - this.annotations = (methodParameter.getParameterIndex() == -1 ? - nullSafeAnnotations(methodParameter.getMethodAnnotations()) : - nullSafeAnnotations(methodParameter.getParameterAnnotations())); + this.annotations = nullSafeAnnotations(methodParameter.getParameterIndex() == -1 ? + methodParameter.getMethodAnnotations() :methodParameter.getParameterAnnotations()); } /** @@ -384,7 +383,7 @@ public boolean isMap() { * @throws IllegalStateException if this type is not a {@code java.util.Map} */ public TypeDescriptor getMapKeyTypeDescriptor() { - Assert.state(isMap(), "Not a java.util.Map"); + Assert.state(isMap(), "Not a [java.util.Map]"); return getRelatedIfResolvable(this, this.resolvableType.asMap().getGeneric(0)); } @@ -419,7 +418,7 @@ public TypeDescriptor getMapKeyTypeDescriptor(Object mapKey) { * @throws IllegalStateException if this type is not a {@code java.util.Map} */ public TypeDescriptor getMapValueTypeDescriptor() { - Assert.state(isMap(), "Not a java.util.Map"); + Assert.state(isMap(), "Not a [java.util.Map]"); return getRelatedIfResolvable(this, this.resolvableType.asMap().getGeneric(1)); } @@ -529,9 +528,9 @@ public static TypeDescriptor valueOf(Class type) { * @return the collection type descriptor */ public static TypeDescriptor collection(Class collectionType, TypeDescriptor elementTypeDescriptor) { - Assert.notNull(collectionType, "collectionType must not be null"); + Assert.notNull(collectionType, "Collection type must not be null"); if (!Collection.class.isAssignableFrom(collectionType)) { - throw new IllegalArgumentException("collectionType must be a java.util.Collection"); + throw new IllegalArgumentException("Collection type must be a [java.util.Collection]"); } ResolvableType element = (elementTypeDescriptor != null ? elementTypeDescriptor.resolvableType : null); return new TypeDescriptor(ResolvableType.forClassWithGenerics(collectionType, element), null, null); diff --git a/spring-core/src/main/java/org/springframework/core/convert/converter/ConverterRegistry.java b/spring-core/src/main/java/org/springframework/core/convert/converter/ConverterRegistry.java index 9280c4eac3..5331ed6191 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/converter/ConverterRegistry.java +++ b/spring-core/src/main/java/org/springframework/core/convert/converter/ConverterRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,15 +27,16 @@ public interface ConverterRegistry { /** * Add a plain converter to this registry. - * The convertible sourceType/targetType pair is derived from the Converter's parameterized types. + * The convertible source/target type pair is derived from the Converter's parameterized types. * @throws IllegalArgumentException if the parameterized types could not be resolved */ void addConverter(Converter converter); /** * Add a plain converter to this registry. - * The convertible sourceType/targetType pair is specified explicitly. - * Allows for a Converter to be reused for multiple distinct pairs without having to create a Converter class for each pair. + * The convertible source/target type pair is specified explicitly. + *

    Allows for a Converter to be reused for multiple distinct pairs without + * having to create a Converter class for each pair. * @since 3.1 */ void addConverter(Class sourceType, Class targetType, Converter converter); @@ -47,13 +48,13 @@ public interface ConverterRegistry { /** * Add a ranged converter factory to this registry. - * The convertible sourceType/rangeType pair is derived from the ConverterFactory's parameterized types. - * @throws IllegalArgumentException if the parameterized types could not be resolved. + * The convertible source/target type pair is derived from the ConverterFactory's parameterized types. + * @throws IllegalArgumentException if the parameterized types could not be resolved */ - void addConverterFactory(ConverterFactory converterFactory); + void addConverterFactory(ConverterFactory factory); /** - * Remove any converters from sourceType to targetType. + * Remove any converters from {@code sourceType} to {@code targetType}. * @param sourceType the source type * @param targetType the target type */ diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java b/spring-core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java index 52df6a7766..17868a3765 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java +++ b/spring-core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -97,8 +97,10 @@ public class GenericConversionService implements ConfigurableConversionService { @Override public void addConverter(Converter converter) { ResolvableType[] typeInfo = getRequiredTypeInfo(converter, Converter.class); - Assert.notNull(typeInfo, "Unable to the determine sourceType and targetType " + - " which your Converter converts between; declare these generic types."); + if (typeInfo == null) { + throw new IllegalArgumentException("Unable to determine source type and target type for your " + + "Converter [" + converter.getClass().getName() + "]; does the class parameterize those types?"); + } addConverter(new ConverterAdapter(converter, typeInfo[0], typeInfo[1])); } @@ -115,11 +117,13 @@ public void addConverter(GenericConverter converter) { } @Override - public void addConverterFactory(ConverterFactory converterFactory) { - ResolvableType[] typeInfo = getRequiredTypeInfo(converterFactory, ConverterFactory.class); - Assert.notNull(typeInfo, "Unable to the determine source type and target range type R which your " + - "ConverterFactory converts between; declare these generic types."); - addConverter(new ConverterFactoryAdapter(converterFactory, + public void addConverterFactory(ConverterFactory factory) { + ResolvableType[] typeInfo = getRequiredTypeInfo(factory, ConverterFactory.class); + if (typeInfo == null) { + throw new IllegalArgumentException("Unable to determine source type and target type for your " + + "ConverterFactory [" + factory.getClass().getName() + "]; does the class parameterize those types?"); + } + addConverter(new ConverterFactoryAdapter(factory, new ConvertiblePair(typeInfo[0].resolve(), typeInfo[1].resolve()))); } diff --git a/spring-core/src/main/java/org/springframework/core/io/support/EncodedResource.java b/spring-core/src/main/java/org/springframework/core/io/support/EncodedResource.java index 73c9b158b2..7f1c730517 100644 --- a/spring-core/src/main/java/org/springframework/core/io/support/EncodedResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/support/EncodedResource.java @@ -37,6 +37,7 @@ * @author Juergen Hoeller * @author Sam Brannen * @since 1.2.6 + * @see Resource#getInputStream() * @see java.io.Reader * @see java.nio.charset.Charset */ @@ -142,8 +143,8 @@ else if (this.encoding != null) { } /** - * Open a {@code java.io.InputStream} for the specified resource, ignoring any - * specified {@link #getCharset() Charset} or {@linkplain #getEncoding() encoding}. + * Open an {@code InputStream} for the specified resource, ignoring any specified + * {@link #getCharset() Charset} or {@linkplain #getEncoding() encoding}. * @throws IOException if opening the InputStream failed * @see #requiresReader() * @see #getReader() diff --git a/spring-core/src/test/java/org/springframework/tests/TestResourceUtils.java b/spring-core/src/test/java/org/springframework/tests/TestResourceUtils.java index 66be13072e..0144f65d79 100644 --- a/spring-core/src/test/java/org/springframework/tests/TestResourceUtils.java +++ b/spring-core/src/test/java/org/springframework/tests/TestResourceUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,29 +18,22 @@ import org.springframework.core.io.ClassPathResource; -import static java.lang.String.*; - /** * Convenience utilities for common operations with test resources. * * @author Chris Beams */ -public class TestResourceUtils { +public abstract class TestResourceUtils { /** - * Loads a {@link ClassPathResource} qualified by the simple name of clazz, + * Load a {@link ClassPathResource} qualified by the simple name of clazz, * and relative to the package for clazz. - * *

    Example: given a clazz 'com.foo.BarTests' and a resourceSuffix of 'context.xml', * this method will return a ClassPathResource representing com/foo/BarTests-context.xml - * *

    Intended for use loading context configuration XML files within JUnit tests. - * - * @param clazz - * @param resourceSuffix */ public static ClassPathResource qualifiedResource(Class clazz, String resourceSuffix) { - return new ClassPathResource(format("%s-%s", clazz.getSimpleName(), resourceSuffix), clazz); + return new ClassPathResource(String.format("%s-%s", clazz.getSimpleName(), resourceSuffix), clazz); } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpEQ.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpEQ.java index 73ad67603e..f40bbf3014 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpEQ.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpEQ.java @@ -58,8 +58,8 @@ public boolean isCompilable() { String leftDesc = left.exitTypeDescriptor; String rightDesc = right.exitTypeDescriptor; - DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftDesc, rightDesc, - this.leftActualDescriptor, this.rightActualDescriptor); + DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility( + leftDesc, rightDesc, this.leftActualDescriptor, this.rightActualDescriptor); return (!dc.areNumbers || dc.areCompatible); } @@ -73,17 +73,15 @@ public void generateCode(MethodVisitor mv, CodeFlow cf) { boolean leftPrim = CodeFlow.isPrimitive(leftDesc); boolean rightPrim = CodeFlow.isPrimitive(rightDesc); - DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftDesc, rightDesc, - this.leftActualDescriptor, this.rightActualDescriptor); + DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility( + leftDesc, rightDesc, this.leftActualDescriptor, this.rightActualDescriptor); if (dc.areNumbers && dc.areCompatible) { char targetType = dc.compatibleType; - getLeftOperand().generateCode(mv, cf); if (!leftPrim) { CodeFlow.insertUnboxInsns(mv, targetType, leftDesc); } - cf.enterCompilationScope(); getRightOperand().generateCode(mv, cf); cf.exitCompilationScope(); @@ -91,23 +89,23 @@ public void generateCode(MethodVisitor mv, CodeFlow cf) { CodeFlow.insertUnboxInsns(mv, targetType, rightDesc); } // assert: SpelCompiler.boxingCompatible(leftDesc, rightDesc) - if (targetType=='D') { + if (targetType == 'D') { mv.visitInsn(DCMPL); mv.visitJumpInsn(IFNE, elseTarget); } - else if (targetType=='F') { + else if (targetType == 'F') { mv.visitInsn(FCMPL); mv.visitJumpInsn(IFNE, elseTarget); } - else if (targetType=='J') { + else if (targetType == 'J') { mv.visitInsn(LCMP); mv.visitJumpInsn(IFNE, elseTarget); } - else if (targetType=='I' || targetType=='Z') { + else if (targetType == 'I' || targetType == 'Z') { mv.visitJumpInsn(IF_ICMPNE, elseTarget); } else { - throw new IllegalStateException("Unexpected descriptor "+leftDesc); + throw new IllegalStateException("Unexpected descriptor " + leftDesc); } } else { @@ -120,11 +118,11 @@ else if (targetType=='I' || targetType=='Z') { CodeFlow.insertBoxIfNecessary(mv, rightDesc.charAt(0)); } Label leftNotNull = new Label(); - mv.visitInsn(DUP_X1); // Dup right on the top of the stack - mv.visitJumpInsn(IFNONNULL,leftNotNull); + mv.visitInsn(DUP_X1); // dup right on the top of the stack + mv.visitJumpInsn(IFNONNULL, leftNotNull); // Right is null! mv.visitInsn(SWAP); - mv.visitInsn(POP); // remove it + mv.visitInsn(POP); // remove it Label rightNotNull = new Label(); mv.visitJumpInsn(IFNONNULL, rightNotNull); // Left is null too @@ -132,7 +130,7 @@ else if (targetType=='I' || targetType=='Z') { mv.visitJumpInsn(GOTO, endOfIf); mv.visitLabel(rightNotNull); mv.visitInsn(ICONST_0); - mv.visitJumpInsn(GOTO,endOfIf); + mv.visitJumpInsn(GOTO, endOfIf); mv.visitLabel(leftNotNull); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "equals", "(Ljava/lang/Object;)Z", false); mv.visitLabel(endOfIf); @@ -140,7 +138,7 @@ else if (targetType=='I' || targetType=='Z') { return; } mv.visitInsn(ICONST_1); - mv.visitJumpInsn(GOTO,endOfIf); + mv.visitJumpInsn(GOTO, endOfIf); mv.visitLabel(elseTarget); mv.visitInsn(ICONST_0); mv.visitLabel(endOfIf); diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpNE.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpNE.java index 543e459ebe..4f96b3bbdd 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpNE.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OpNE.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,12 +36,13 @@ public OpNE(int pos, SpelNodeImpl... operands) { this.exitTypeDescriptor = "Z"; } + @Override public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException { Object left = getLeftOperand().getValueInternal(state).getValue(); Object right = getRightOperand().getValueInternal(state).getValue(); - leftActualDescriptor = CodeFlow.toDescriptorFromObject(left); - rightActualDescriptor = CodeFlow.toDescriptorFromObject(right); + this.leftActualDescriptor = CodeFlow.toDescriptorFromObject(left); + this.rightActualDescriptor = CodeFlow.toDescriptorFromObject(right); return BooleanTypedValue.forValue(!equalityCheck(state, left, right)); } @@ -57,7 +58,8 @@ public boolean isCompilable() { String leftDesc = left.exitTypeDescriptor; String rightDesc = right.exitTypeDescriptor; - DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftDesc, rightDesc, leftActualDescriptor, rightActualDescriptor); + DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility( + leftDesc, rightDesc, this.leftActualDescriptor, this.rightActualDescriptor); return (!dc.areNumbers || dc.areCompatible); } @@ -70,16 +72,15 @@ public void generateCode(MethodVisitor mv, CodeFlow cf) { boolean leftPrim = CodeFlow.isPrimitive(leftDesc); boolean rightPrim = CodeFlow.isPrimitive(rightDesc); - DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftDesc, rightDesc, leftActualDescriptor, rightActualDescriptor); + DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility( + leftDesc, rightDesc, this.leftActualDescriptor, this.rightActualDescriptor); if (dc.areNumbers && dc.areCompatible) { char targetType = dc.compatibleType; - getLeftOperand().generateCode(mv, cf); if (!leftPrim) { CodeFlow.insertUnboxInsns(mv, targetType, leftDesc); } - cf.enterCompilationScope(); getRightOperand().generateCode(mv, cf); cf.exitCompilationScope(); @@ -103,7 +104,7 @@ else if (targetType == 'I' || targetType == 'Z') { mv.visitJumpInsn(IF_ICMPEQ, elseTarget); } else { - throw new IllegalStateException("Unexpected descriptor "+leftDesc); + throw new IllegalStateException("Unexpected descriptor " + leftDesc); } } else { diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Operator.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Operator.java index 7172dd966b..73ed4a75c7 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Operator.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Operator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -84,6 +84,7 @@ public String toStringAST() { return sb.toString(); } + protected boolean isCompilableOperatorUsingNumerics() { SpelNodeImpl left = getLeftOperand(); SpelNodeImpl right= getRightOperand(); @@ -94,8 +95,8 @@ protected boolean isCompilableOperatorUsingNumerics() { // Supported operand types for equals (at the moment) String leftDesc = left.exitTypeDescriptor; String rightDesc = right.exitTypeDescriptor; - DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftDesc, rightDesc, - this.leftActualDescriptor, this.rightActualDescriptor); + DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility( + leftDesc, rightDesc, this.leftActualDescriptor, this.rightActualDescriptor); return (dc.areNumbers && dc.areCompatible); } @@ -109,9 +110,9 @@ protected void generateComparisonCode(MethodVisitor mv, CodeFlow cf, int compIns boolean unboxLeft = !CodeFlow.isPrimitive(leftDesc); boolean unboxRight = !CodeFlow.isPrimitive(rightDesc); - DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility(leftDesc, rightDesc, - this.leftActualDescriptor, this.rightActualDescriptor); - char targetType = dc.compatibleType;//CodeFlow.toPrimitiveTargetDesc(leftDesc); + DescriptorComparison dc = DescriptorComparison.checkNumericCompatibility( + leftDesc, rightDesc, this.leftActualDescriptor, this.rightActualDescriptor); + char targetType = dc.compatibleType; // CodeFlow.toPrimitiveTargetDesc(leftDesc); getLeftOperand().generateCode(mv, cf); if (unboxLeft) { @@ -128,23 +129,23 @@ protected void generateComparisonCode(MethodVisitor mv, CodeFlow cf, int compIns // assert: SpelCompiler.boxingCompatible(leftDesc, rightDesc) Label elseTarget = new Label(); Label endOfIf = new Label(); - if (targetType=='D') { + if (targetType == 'D') { mv.visitInsn(DCMPG); mv.visitJumpInsn(compInstruction1, elseTarget); } - else if (targetType=='F') { + else if (targetType == 'F') { mv.visitInsn(FCMPG); mv.visitJumpInsn(compInstruction1, elseTarget); } - else if (targetType=='J') { + else if (targetType == 'J') { mv.visitInsn(LCMP); mv.visitJumpInsn(compInstruction1, elseTarget); } - else if (targetType=='I') { + else if (targetType == 'I') { mv.visitJumpInsn(compInstruction2, elseTarget); } else { - throw new IllegalStateException("Unexpected descriptor "+leftDesc); + throw new IllegalStateException("Unexpected descriptor " + leftDesc); } // Other numbers are not yet supported (isCompilable will not have returned true) @@ -215,8 +216,8 @@ else if (leftNumber instanceof Byte || rightNumber instanceof Byte) { /** - * A descriptor comparison encapsulates the result of comparing descriptor for two operands and - * describes at what level they are compatible. + * A descriptor comparison encapsulates the result of comparing descriptor + * for two operands and describes at what level they are compatible. */ protected static class DescriptorComparison { @@ -224,12 +225,12 @@ protected static class DescriptorComparison { static DescriptorComparison INCOMPATIBLE_NUMBERS = new DescriptorComparison(true, false, ' '); - final boolean areNumbers; // Were the two compared descriptor both for numbers? + final boolean areNumbers; // Were the two compared descriptor both for numbers? - final boolean areCompatible; // If they were numbers, were they compatible? + final boolean areCompatible; // If they were numbers, were they compatible? + + final char compatibleType; // When compatible, what is the descriptor of the common type - final char compatibleType; // When compatible, what is the descriptor of the common type - private DescriptorComparison(boolean areNumbers, boolean areCompatible, char compatibleType) { this.areNumbers = areNumbers; this.areCompatible = areCompatible; @@ -237,12 +238,13 @@ private DescriptorComparison(boolean areNumbers, boolean areCompatible, char com } /** - * Returns an object that indicates whether the input descriptors are compatible. A declared descriptor - * is what could statically be determined (e.g. from looking at the return value of a property accessor - * method) whilst an actual descriptor is the type of an actual object that was returned, which may differ. - * For generic types with unbound type variables the declared descriptor discovered may be 'Object' but - * from the actual descriptor it is possible to observe that the objects are really numeric values (e.g. - * ints). + * Return an object that indicates whether the input descriptors are compatible. + *

    A declared descriptor is what could statically be determined (e.g. from looking + * at the return value of a property accessor method) whilst an actual descriptor + * is the type of an actual object that was returned, which may differ. + *

    For generic types with unbound type variables, the declared descriptor + * discovered may be 'Object' but from the actual descriptor it is possible to + * observe that the objects are really numeric values (e.g. ints). * @param leftDeclaredDescriptor the statically determinable left descriptor * @param rightDeclaredDescriptor the statically determinable right descriptor * @param leftActualDescriptor the dynamic/runtime left object descriptor diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/DatabasePopulatorUtils.java b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/DatabasePopulatorUtils.java index 31fb497c8a..e153f3790d 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/DatabasePopulatorUtils.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/DatabasePopulatorUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,8 +40,8 @@ public abstract class DatabasePopulatorUtils { * @throws DataAccessException if an error occurs, specifically a {@link ScriptException} */ public static void execute(DatabasePopulator populator, DataSource dataSource) throws DataAccessException { - Assert.notNull(populator, "DatabasePopulator must be provided"); - Assert.notNull(dataSource, "DataSource must be provided"); + Assert.notNull(populator, "DatabasePopulator must not be null"); + Assert.notNull(dataSource, "DataSource must not be null"); try { Connection connection = DataSourceUtils.getConnection(dataSource); try { @@ -53,11 +53,10 @@ public static void execute(DatabasePopulator populator, DataSource dataSource) t } } } - catch (Exception ex) { + catch (Throwable ex) { if (ex instanceof ScriptException) { throw (ScriptException) ex; } - throw new UncategorizedScriptException("Failed to execute database script", ex); } } diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ResourceDatabasePopulator.java b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ResourceDatabasePopulator.java index 75a668d516..513a9c6085 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ResourceDatabasePopulator.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/datasource/init/ResourceDatabasePopulator.java @@ -52,7 +52,7 @@ */ public class ResourceDatabasePopulator implements DatabasePopulator { - private List scripts = new ArrayList(); + List scripts = new ArrayList(); private String sqlScriptEncoding; @@ -117,7 +117,7 @@ public ResourceDatabasePopulator(boolean continueOnError, boolean ignoreFailedDr */ public void addScript(Resource script) { Assert.notNull(script, "Script must not be null"); - getScripts().add(script); + this.scripts.add(script); } /** @@ -126,7 +126,7 @@ public void addScript(Resource script) { */ public void addScripts(Resource... scripts) { assertContentsOfScriptArray(scripts); - getScripts().addAll(Arrays.asList(scripts)); + this.scripts.addAll(Arrays.asList(scripts)); } /** @@ -140,6 +140,11 @@ public void setScripts(Resource... scripts) { this.scripts = new ArrayList(Arrays.asList(scripts)); } + private void assertContentsOfScriptArray(Resource... scripts) { + Assert.notNull(scripts, "Scripts array must not be null"); + Assert.noNullElements(scripts, "Scripts array must not contain null elements"); + } + /** * Specify the encoding for the configured SQL scripts, if different from the * platform encoding. @@ -220,6 +225,7 @@ public void setIgnoreFailedDrops(boolean ignoreFailedDrops) { this.ignoreFailedDrops = ignoreFailedDrops; } + /** * {@inheritDoc} * @see #execute(DataSource) @@ -227,10 +233,10 @@ public void setIgnoreFailedDrops(boolean ignoreFailedDrops) { @Override public void populate(Connection connection) throws ScriptException { Assert.notNull(connection, "Connection must not be null"); - for (Resource script : getScripts()) { - ScriptUtils.executeSqlScript(connection, encodeScript(script), this.continueOnError, - this.ignoreFailedDrops, this.commentPrefix, this.separator, this.blockCommentStartDelimiter, - this.blockCommentEndDelimiter); + for (Resource script : this.scripts) { + EncodedResource encodedScript = new EncodedResource(script, this.sqlScriptEncoding); + ScriptUtils.executeSqlScript(connection, encodedScript, this.continueOnError, this.ignoreFailedDrops, + this.commentPrefix, this.separator, this.blockCommentStartDelimiter, this.blockCommentEndDelimiter); } } @@ -244,28 +250,7 @@ public void populate(Connection connection) throws ScriptException { * @see #populate(Connection) */ public void execute(DataSource dataSource) throws ScriptException { - Assert.notNull(dataSource, "DataSource must not be null"); DatabasePopulatorUtils.execute(this, dataSource); } - final List getScripts() { - return this.scripts; - } - - /** - * {@link EncodedResource} is not a sub-type of {@link Resource}. Thus we - * always need to wrap each script resource in an {@code EncodedResource} - * using the configured {@linkplain #setSqlScriptEncoding encoding}. - * @param script the script to wrap (never {@code null}) - */ - private EncodedResource encodeScript(Resource script) { - Assert.notNull(script, "Script must not be null"); - return new EncodedResource(script, this.sqlScriptEncoding); - } - - private void assertContentsOfScriptArray(Resource... scripts) { - Assert.notNull(scripts, "Scripts must not be null"); - Assert.noNullElements(scripts, "Scripts array must not contain null elements"); - } - } diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/ResourceDatabasePopulatorTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/ResourceDatabasePopulatorTests.java index 0dcaf48283..50a84f9677 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/ResourceDatabasePopulatorTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/datasource/init/ResourceDatabasePopulatorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -50,22 +50,22 @@ public void constructWithNullResourceArray() { @Test public void constructWithResource() { ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator(script1); - assertEquals(1, databasePopulator.getScripts().size()); + assertEquals(1, databasePopulator.scripts.size()); } @Test public void constructWithMultipleResources() { ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator(script1, script2); - assertEquals(2, databasePopulator.getScripts().size()); + assertEquals(2, databasePopulator.scripts.size()); } @Test public void constructWithMultipleResourcesAndThenAddScript() { ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator(script1, script2); - assertEquals(2, databasePopulator.getScripts().size()); + assertEquals(2, databasePopulator.scripts.size()); databasePopulator.addScript(script3); - assertEquals(3, databasePopulator.getScripts().size()); + assertEquals(3, databasePopulator.scripts.size()); } @Test(expected = IllegalArgumentException.class) @@ -95,13 +95,13 @@ public void setScriptsWithNullResourceArray() { @Test public void setScriptsAndThenAddScript() { ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator(); - assertEquals(0, databasePopulator.getScripts().size()); + assertEquals(0, databasePopulator.scripts.size()); databasePopulator.setScripts(script1, script2); - assertEquals(2, databasePopulator.getScripts().size()); + assertEquals(2, databasePopulator.scripts.size()); databasePopulator.addScript(script3); - assertEquals(3, databasePopulator.getScripts().size()); + assertEquals(3, databasePopulator.scripts.size()); } } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/MessageChannel.java b/spring-messaging/src/main/java/org/springframework/messaging/MessageChannel.java index f64ff0bf49..d871290019 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/MessageChannel.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/MessageChannel.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,7 +27,7 @@ public interface MessageChannel { /** * Constant for sending a message without a prescribed timeout. */ - public static final long INDEFINITE_TIMEOUT = -1; + long INDEFINITE_TIMEOUT = -1; /** diff --git a/spring-tx/src/main/java/org/springframework/transaction/interceptor/DefaultTransactionAttribute.java b/spring-tx/src/main/java/org/springframework/transaction/interceptor/DefaultTransactionAttribute.java index 8e016f2533..cdff08625c 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/interceptor/DefaultTransactionAttribute.java +++ b/spring-tx/src/main/java/org/springframework/transaction/interceptor/DefaultTransactionAttribute.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,10 +19,11 @@ import org.springframework.transaction.support.DefaultTransactionDefinition; /** - * Transaction attribute that takes the EJB approach to rolling - * back on runtime, but not checked, exceptions. + * Spring's common transaction attribute implementation. + * Rolls back on runtime, but not checked, exceptions by default. * * @author Rod Johnson + * @author Juergen Hoeller * @since 16.03.2003 */ @SuppressWarnings("serial") @@ -57,7 +58,7 @@ public DefaultTransactionAttribute(TransactionAttribute other) { } /** - * Create a new DefaultTransactionAttribute with the the given + * Create a new DefaultTransactionAttribute with the given * propagation behavior. Can be modified through bean property setters. * @param propagationBehavior one of the propagation constants in the * TransactionDefinition interface @@ -74,6 +75,7 @@ public DefaultTransactionAttribute(int propagationBehavior) { * Associate a qualifier value with this transaction attribute. *

    This may be used for choosing a corresponding transaction manager * to process this specific transaction. + * @since 3.0 */ public void setQualifier(String qualifier) { this.qualifier = qualifier; @@ -81,6 +83,7 @@ public void setQualifier(String qualifier) { /** * Return a qualifier value associated with this transaction attribute. + * @since 3.0 */ @Override public String getQualifier() { From 1f551a83718827201433846fef4d3ea81a02e6e5 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 31 Oct 2016 20:32:26 +0100 Subject: [PATCH 318/344] Upgrade to Netty 4.0.42 and H2 1.4.193 --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index db93202f76..d2c336d6fa 100644 --- a/build.gradle +++ b/build.gradle @@ -56,7 +56,7 @@ configure(allprojects) { project -> ext.jtaVersion = "1.2" ext.junitVersion = "4.12" ext.log4jVersion = "1.2.17" - ext.nettyVersion = "4.0.41.Final" + ext.nettyVersion = "4.0.42.Final" ext.okhttpVersion = "2.7.5" ext.openjpaVersion = "2.4.1" ext.poiVersion = "3.13" @@ -618,7 +618,7 @@ project("spring-jdbc") { optional("javax.transaction:javax.transaction-api:${jtaVersion}") optional("com.mchange:c3p0:0.9.5.2") optional("org.hsqldb:hsqldb:${hsqldbVersion}") - optional("com.h2database:h2:1.4.192") + optional("com.h2database:h2:1.4.193") optional("org.apache.derby:derby:10.12.1.1") optional("org.apache.derby:derbyclient:10.12.1.1") } From c8bb32b5c3685b6e28b54751b2e0feb215b6f8c9 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 4 Nov 2016 12:54:24 +0100 Subject: [PATCH 319/344] @Async docs explicitly mention ListenableFuture and CompletableFuture Issue: SPR-14881 (cherry picked from commit 9be1710) --- .../scheduling/annotation/Async.java | 18 ++++--- src/asciidoc/integration.adoc | 50 +++++++++++++------ 2 files changed, 48 insertions(+), 20 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/scheduling/annotation/Async.java b/spring-context/src/main/java/org/springframework/scheduling/annotation/Async.java index bd58fec696..2b7075a494 100644 --- a/spring-context/src/main/java/org/springframework/scheduling/annotation/Async.java +++ b/spring-context/src/main/java/org/springframework/scheduling/annotation/Async.java @@ -29,12 +29,18 @@ * *

    In terms of target method signatures, any parameter types are supported. * However, the return type is constrained to either {@code void} or - * {@link java.util.concurrent.Future}. In the latter case, the {@code Future} handle - * returned from the proxy will be an actual asynchronous {@code Future} that can be used - * to track the result of the asynchronous method execution. However, since the - * target method needs to implement the same signature, it will have to return - * a temporary {@code Future} handle that just passes the return value through: e.g. - * Spring's {@link AsyncResult} or EJB 3.1's {@link javax.ejb.AsyncResult}. + * {@link java.util.concurrent.Future}. In the latter case, you may declare the + * more specific {@link org.springframework.util.concurrent.ListenableFuture} or + * {@link java.util.concurrent.CompletableFuture} types which allow for richer + * interaction with the asynchronous task and for immediate composition with + * further processing steps. + * + *

    A {@code Future} handle returned from the proxy will be an actual asynchronous + * {@code Future} that can be used to track the result of the asynchronous method + * execution. However, since the target method needs to implement the same signature, + * it will have to return a temporary {@code Future} handle that just passes a value + * through: e.g. Spring's {@link AsyncResult}, EJB 3.1's {@link javax.ejb.AsyncResult}, + * or {@link java.util.concurrent.CompletableFuture#completedFuture(Object)}. * * @author Juergen Hoeller * @author Chris Beams diff --git a/src/asciidoc/integration.adoc b/src/asciidoc/integration.adoc index 43903b45aa..0486893195 100644 --- a/src/asciidoc/integration.adoc +++ b/src/asciidoc/integration.adoc @@ -877,6 +877,8 @@ proxy will take care of forwarding the call to the server-side object via JMS. ---- + + [[remoting-amqp]] === AMQP Refer to the {doc-spring-amqp}/html/_reference.html#remoting[Spring AMQP Reference Document @@ -1098,6 +1100,7 @@ Note that the `java.net` implementation for HTTP requests may raise an exception accessing the status of a response that represents an error (e.g. 401). If this is an issue, switch to `HttpComponentsClientHttpRequestFactory` instead. ==== + The previous example using Apache HttpComponents `HttpClient` directly rewritten to use the `RestTemplate` is shown below @@ -1135,6 +1138,7 @@ construct a `HttpComponentsClientHttpRequestFactory` like so: RestTemplate restTemplate = new RestTemplate(requestFactory); ---- ==== + The general callback interface is `RequestCallback` and is called when the execute method is invoked. @@ -2602,13 +2606,16 @@ what the < - + + customizer-ref="tracingCustomizer"/> ---- If you are not using the Spring namespace support, you can still use the @@ -7813,7 +7835,7 @@ If you are not using the Spring namespace support, you can still use the - + From 0b47c54cda53217397841fe912fd331c991e0559 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 4 Nov 2016 13:54:11 +0100 Subject: [PATCH 320/344] Upgrade to Joda-Time 2.9.5 and EhCache 2.10.3 --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index d2c336d6fa..4abc9b424f 100644 --- a/build.gradle +++ b/build.gradle @@ -29,7 +29,7 @@ configure(allprojects) { project -> ext.aspectjVersion = "1.8.9" ext.eclipselinkVersion = "2.4.2" - ext.ehcacheVersion = "2.10.2" + ext.ehcacheVersion = "2.10.3" ext.ehcachejcacheVersion = "1.0.1" ext.ehcache3Version = "3.0.3" ext.ejbVersion = "3.0" @@ -51,7 +51,7 @@ configure(allprojects) { project -> ext.jasperreportsVersion = "6.2.1" ext.javamailVersion = "1.5.5" ext.jettyVersion = "9.3.9.v20160517" - ext.jodaVersion = "2.9.4" + ext.jodaVersion = "2.9.5" ext.jrubyVersion = "1.7.26" // JRuby 9000 only supported through JSR-223 (StandardScriptFactory) ext.jtaVersion = "1.2" ext.junitVersion = "4.12" From c2a1a41eb665bcae8efac84d25e8dd63aba448fc Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 7 Nov 2016 20:30:27 +0100 Subject: [PATCH 321/344] Polishing --- .../RequiredAnnotationBeanPostProcessor.java | 5 +- .../InstantiationAwareBeanPostProcessor.java | 7 +- ...ntiationAwareBeanPostProcessorAdapter.java | 5 +- .../beans/factory/config/Scope.java | 9 +- .../AbstractAutowireCapableBeanFactory.java | 57 ++++--- .../factory/support/AbstractBeanFactory.java | 48 +++--- .../support/DefaultSingletonBeanRegistry.java | 2 +- ...ApplicationListenerMethodAdapterTests.java | 154 ++++++++---------- .../springframework/util/ReflectionUtils.java | 2 + .../AbstractAsyncClientHttpRequest.java | 9 +- .../web/client/HttpClientErrorException.java | 30 ++-- .../web/client/HttpServerErrorException.java | 37 +++-- 12 files changed, 186 insertions(+), 179 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/RequiredAnnotationBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/RequiredAnnotationBeanPostProcessor.java index ebc3cbf53c..0d7eb6ee3b 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/RequiredAnnotationBeanPostProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/RequiredAnnotationBeanPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -141,8 +141,7 @@ public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, C @Override public PropertyValues postProcessPropertyValues( - PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) - throws BeansException { + PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException { if (!this.validatedBeanNames.contains(beanName)) { if (!shouldSkip(this.beanFactory, beanName)) { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessor.java index f508e3fff1..0186221b58 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -96,14 +96,13 @@ public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor { * dependency types - which the factory handles specifically - already filtered out) * @param bean the bean instance created, but whose properties have not yet been set * @param beanName the name of the bean - * @return the actual property values to apply to to the given bean + * @return the actual property values to apply to the given bean * (can be the passed-in PropertyValues instance), or {@code null} * to skip property population * @throws org.springframework.beans.BeansException in case of errors * @see org.springframework.beans.MutablePropertyValues */ PropertyValues postProcessPropertyValues( - PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) - throws BeansException; + PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException; } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessorAdapter.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessorAdapter.java index 3e02846fcb..3f7e1746b7 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessorAdapter.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessorAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -66,8 +66,7 @@ public boolean postProcessAfterInstantiation(Object bean, String beanName) throw @Override public PropertyValues postProcessPropertyValues( - PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) - throws BeansException { + PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException { return pvs; } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/Scope.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/Scope.java index 8777fa61e0..4e313c7919 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/Scope.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/Scope.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -68,6 +68,7 @@ public interface Scope { * @param objectFactory the {@link ObjectFactory} to use to create the scoped * object if it is not present in the underlying storage mechanism * @return the desired object (never {@code null}) + * @throws IllegalStateException if the underlying scope is not currently active */ Object get(String name, ObjectFactory objectFactory); @@ -84,6 +85,7 @@ public interface Scope { * removing an object. * @param name the name of the object to remove * @return the removed object, or {@code null} if no object was present + * @throws IllegalStateException if the underlying scope is not currently active * @see #registerDestructionCallback */ Object remove(String name); @@ -100,7 +102,7 @@ public interface Scope { * at the appropriate time. If such a callback is not supported by the * underlying runtime environment at all, the callback must be * ignored and a corresponding warning should be logged. - *

    Note that 'destruction' refers to to automatic destruction of + *

    Note that 'destruction' refers to automatic destruction of * the object as part of the scope's own lifecycle, not to the individual * scoped object having been explicitly removed by the application. * If a scoped object gets removed via this facade's {@link #remove(String)} @@ -112,6 +114,7 @@ public interface Scope { * so it can safely be executed without an enclosing try-catch block. * Furthermore, the Runnable will usually be serializable, provided * that its target object is serializable as well. + * @throws IllegalStateException if the underlying scope is not currently active * @see org.springframework.beans.factory.DisposableBean * @see org.springframework.beans.factory.support.AbstractBeanDefinition#getDestroyMethodName() * @see DestructionAwareBeanPostProcessor @@ -123,6 +126,7 @@ public interface Scope { * E.g. the HttpServletRequest object for key "request". * @param key the contextual key * @return the corresponding object, or {@code null} if none found + * @throws IllegalStateException if the underlying scope is not currently active */ Object resolveContextualObject(String key); @@ -139,6 +143,7 @@ public interface Scope { * underlying storage mechanism has no obvious candidate for such an ID. * @return the conversation ID, or {@code null} if there is no * conversation ID for the current scope + * @throws IllegalStateException if the underlying scope is not currently active */ String getConversationId(); diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java index f8bd381883..1273d61f4c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java @@ -500,7 +500,9 @@ protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] ar * @see #instantiateUsingFactoryMethod * @see #autowireConstructor */ - protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) { + protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) + throws BeanCreationException { + // Instantiate the bean. BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { @@ -515,7 +517,13 @@ protected Object doCreateBean(final String beanName, final RootBeanDefinition mb // Allow post-processors to modify the merged bean definition. synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { - applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); + try { + applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); + } + catch (Throwable ex) { + throw new BeanCreationException(mbd.getResourceDescription(), beanName, + "Post-processing of merged bean definition failed", ex); + } mbd.postProcessed = true; } } @@ -550,7 +558,8 @@ public Object getObject() throws BeansException { throw (BeanCreationException) ex; } else { - throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); + throw new BeanCreationException( + mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); } } @@ -586,7 +595,8 @@ else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { - throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); + throw new BeanCreationException( + mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); } return exposedObject; @@ -624,7 +634,8 @@ protected Class predictBeanType(String beanName, RootBeanDefinition mbd, Clas protected Class determineTargetType(String beanName, RootBeanDefinition mbd, Class... typesToMatch) { Class targetType = mbd.getTargetType(); if (targetType == null) { - targetType = (mbd.getFactoryMethodName() != null ? getTypeForFactoryMethod(beanName, mbd, typesToMatch) : + targetType = (mbd.getFactoryMethodName() != null ? + getTypeForFactoryMethod(beanName, mbd, typesToMatch) : resolveBeanClass(mbd, beanName, typesToMatch)); if (ObjectUtils.isEmpty(typesToMatch) || getTempClassLoader() == null) { mbd.setTargetType(targetType); @@ -772,10 +783,11 @@ class Holder { Class value = null; } ReflectionUtils.doWithMethods(fbClass, new ReflectionUtils.MethodCallback() { @Override - public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException { + public void doWith(Method method) { if (method.getName().equals(factoryMethodName) && FactoryBean.class.isAssignableFrom(method.getReturnType())) { - objectType.value = GenericTypeResolver.resolveReturnTypeArgument(method, FactoryBean.class); + objectType.value = GenericTypeResolver.resolveReturnTypeArgument( + method, FactoryBean.class); } } }); @@ -926,24 +938,15 @@ private FactoryBean getNonSingletonFactoryBeanForTypeCheck(String beanName, R * @param mbd the merged bean definition for the bean * @param beanType the actual type of the managed bean instance * @param beanName the name of the bean - * @throws BeansException if any post-processing failed * @see MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition */ - protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class beanType, String beanName) - throws BeansException { - - try { - for (BeanPostProcessor bp : getBeanPostProcessors()) { - if (bp instanceof MergedBeanDefinitionPostProcessor) { - MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp; - bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName); - } + protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class beanType, String beanName) { + for (BeanPostProcessor bp : getBeanPostProcessors()) { + if (bp instanceof MergedBeanDefinitionPostProcessor) { + MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp; + bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName); } } - catch (Exception ex) { - throw new BeanCreationException(mbd.getResourceDescription(), beanName, - "Post-processing failed of bean type [" + beanType + "] failed", ex); - } } /** @@ -980,12 +983,9 @@ protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition * @param beanClass the class of the bean to be instantiated * @param beanName the name of the bean * @return the bean object to use instead of a default instance of the target bean, or {@code null} - * @throws BeansException if any post-processing failed * @see InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation */ - protected Object applyBeanPostProcessorsBeforeInstantiation(Class beanClass, String beanName) - throws BeansException { - + protected Object applyBeanPostProcessorsBeforeInstantiation(Class beanClass, String beanName) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; @@ -1106,7 +1106,8 @@ public Object run() { return bw; } catch (Throwable ex) { - throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex); + throw new BeanCreationException( + mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex); } } @@ -1658,7 +1659,9 @@ public Object run() throws Exception { * methods with arguments. * @see #invokeInitMethods */ - protected void invokeCustomInitMethod(String beanName, final Object bean, RootBeanDefinition mbd) throws Throwable { + protected void invokeCustomInitMethod(String beanName, final Object bean, RootBeanDefinition mbd) + throws Throwable { + String initMethodName = mbd.getInitMethodName(); final Method initMethod = (mbd.isNonPublicAccessAllowed() ? BeanUtils.findMethod(bean.getClass(), initMethodName) : 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 80e91528cf..cc6c614939 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 @@ -287,13 +287,13 @@ protected T doGetBean( // Guarantee initialization of beans that the current bean depends on. String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { - for (String dependsOnBean : dependsOn) { - if (isDependent(beanName, dependsOnBean)) { + for (String dep : dependsOn) { + if (isDependent(beanName, dep)) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, - "Circular depends-on relationship between '" + beanName + "' and '" + dependsOnBean + "'"); + "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'"); } - registerDependentBean(dependsOnBean, beanName); - getBean(dependsOnBean); + registerDependentBean(dep, beanName); + getBean(dep); } } @@ -460,19 +460,19 @@ public boolean isPrototype(String name) throws NoSuchBeanDefinitionException { return false; } if (isFactoryBean(beanName, mbd)) { - final FactoryBean factoryBean = (FactoryBean) getBean(FACTORY_BEAN_PREFIX + beanName); + final FactoryBean fb = (FactoryBean) getBean(FACTORY_BEAN_PREFIX + beanName); if (System.getSecurityManager() != null) { return AccessController.doPrivileged(new PrivilegedAction() { @Override public Boolean run() { - return ((factoryBean instanceof SmartFactoryBean && ((SmartFactoryBean) factoryBean).isPrototype()) || - !factoryBean.isSingleton()); + return ((fb instanceof SmartFactoryBean && ((SmartFactoryBean) fb).isPrototype()) || + !fb.isSingleton()); } }, getAccessControlContext()); } else { - return ((factoryBean instanceof SmartFactoryBean && ((SmartFactoryBean) factoryBean).isPrototype()) || - !factoryBean.isSingleton()); + return ((fb instanceof SmartFactoryBean && ((SmartFactoryBean) fb).isPrototype()) || + !fb.isSingleton()); } } else { @@ -800,12 +800,15 @@ public void addEmbeddedValueResolver(StringValueResolver valueResolver) { @Override public String resolveEmbeddedValue(String value) { + if (value == null) { + return null; + } String result = value; for (StringValueResolver resolver : this.embeddedValueResolvers) { + result = resolver.resolveStringValue(result); if (result == null) { return null; } - result = resolver.resolveStringValue(result); } return result; } @@ -1045,11 +1048,11 @@ public void destroyBean(String beanName, Object beanInstance) { * Destroy the given bean instance (usually a prototype instance * obtained from this factory) according to the given bean definition. * @param beanName the name of the bean definition - * @param beanInstance the bean instance to destroy + * @param bean the bean instance to destroy * @param mbd the merged bean definition */ - protected void destroyBean(String beanName, Object beanInstance, RootBeanDefinition mbd) { - new DisposableBeanAdapter(beanInstance, beanName, mbd, getBeanPostProcessors(), getAccessControlContext()).destroy(); + protected void destroyBean(String beanName, Object bean, RootBeanDefinition mbd) { + new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), getAccessControlContext()).destroy(); } @Override @@ -1230,12 +1233,13 @@ protected RootBeanDefinition getMergedBeanDefinition( pbd = getMergedBeanDefinition(parentBeanName); } else { - if (getParentBeanFactory() instanceof ConfigurableBeanFactory) { - pbd = ((ConfigurableBeanFactory) getParentBeanFactory()).getMergedBeanDefinition(parentBeanName); + BeanFactory parent = getParentBeanFactory(); + if (parent instanceof ConfigurableBeanFactory) { + pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName); } else { - throw new NoSuchBeanDefinitionException(bd.getParentName(), - "Parent name '" + bd.getParentName() + "' is equal to bean name '" + beanName + + throw new NoSuchBeanDefinitionException(parentBeanName, + "Parent name '" + parentBeanName + "' is equal to bean name '" + beanName + "': cannot be resolved without an AbstractBeanFactory parent"); } } @@ -1351,12 +1355,14 @@ public Class run() throws Exception { catch (ClassNotFoundException ex) { throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), ex); } - catch (LinkageError err) { - throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), err); + catch (LinkageError ex) { + throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), ex); } } - private Class doResolveBeanClass(RootBeanDefinition mbd, Class... typesToMatch) throws ClassNotFoundException { + private Class doResolveBeanClass(RootBeanDefinition mbd, Class... typesToMatch) + throws ClassNotFoundException { + ClassLoader beanClassLoader = getBeanClassLoader(); ClassLoader classLoaderToUse = beanClassLoader; if (!ObjectUtils.isEmpty(typesToMatch)) { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java index d3d2134702..17b949a1c4 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java @@ -214,7 +214,7 @@ public Object getSingleton(String beanName, ObjectFactory singletonFactory) { if (singletonObject == null) { if (this.singletonsCurrentlyInDestruction) { throw new BeanCreationNotAllowedException(beanName, - "Singleton bean creation not allowed while the singletons of this factory are in destruction " + + "Singleton bean creation not allowed while singletons of this factory are in destruction " + "(Do not request a bean from a BeanFactory in a destroy method implementation!)"); } if (logger.isDebugEnabled()) { diff --git a/spring-context/src/test/java/org/springframework/context/event/ApplicationListenerMethodAdapterTests.java b/spring-context/src/test/java/org/springframework/context/event/ApplicationListenerMethodAdapterTests.java index b9cc4dd0d8..be5d1be873 100644 --- a/spring-context/src/test/java/org/springframework/context/event/ApplicationListenerMethodAdapterTests.java +++ b/spring-context/src/test/java/org/springframework/context/event/ApplicationListenerMethodAdapterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -50,52 +50,48 @@ public class ApplicationListenerMethodAdapterTests extends AbstractApplicationEv private final ApplicationContext context = mock(ApplicationContext.class); + @Test public void rawListener() { - Method method = ReflectionUtils.findMethod(SampleEvents.class, - "handleRaw", ApplicationEvent.class); + Method method = ReflectionUtils.findMethod(SampleEvents.class, "handleRaw", ApplicationEvent.class); supportsEventType(true, method, getGenericApplicationEventType("applicationEvent")); } @Test public void rawListenerWithGenericEvent() { - Method method = ReflectionUtils.findMethod(SampleEvents.class, - "handleRaw", ApplicationEvent.class); + Method method = ReflectionUtils.findMethod(SampleEvents.class, "handleRaw", ApplicationEvent.class); supportsEventType(true, method, getGenericApplicationEventType("stringEvent")); } @Test public void genericListener() { - Method method = ReflectionUtils.findMethod(SampleEvents.class, - "handleGenericString", GenericTestEvent.class); + Method method = ReflectionUtils.findMethod( + SampleEvents.class, "handleGenericString", GenericTestEvent.class); supportsEventType(true, method, getGenericApplicationEventType("stringEvent")); } @Test public void genericListenerWrongParameterizedType() { - Method method = ReflectionUtils.findMethod(SampleEvents.class, - "handleGenericString", GenericTestEvent.class); + Method method = ReflectionUtils.findMethod( + SampleEvents.class, "handleGenericString", GenericTestEvent.class); supportsEventType(false, method, getGenericApplicationEventType("longEvent")); } @Test public void listenerWithPayloadAndGenericInformation() { - Method method = ReflectionUtils.findMethod(SampleEvents.class, - "handleString", String.class); + Method method = ReflectionUtils.findMethod(SampleEvents.class, "handleString", String.class); supportsEventType(true, method, createGenericEventType(String.class)); } @Test public void listenerWithInvalidPayloadAndGenericInformation() { - Method method = ReflectionUtils.findMethod(SampleEvents.class, - "handleString", String.class); + Method method = ReflectionUtils.findMethod(SampleEvents.class, "handleString", String.class); supportsEventType(false, method, createGenericEventType(Integer.class)); } @Test - public void listenerWithPayloadTypeErasure() { // Always accept such event when the type is unknown - Method method = ReflectionUtils.findMethod(SampleEvents.class, - "handleString", String.class); + public void listenerWithPayloadTypeErasure() { // Always accept such event when the type is unknown + Method method = ReflectionUtils.findMethod(SampleEvents.class, "handleString", String.class); supportsEventType(true, method, ResolvableType.forClass(PayloadApplicationEvent.class)); } @@ -108,36 +104,32 @@ public void listenerWithSubTypeSeveralGenerics() { @Test public void listenerWithSubTypeSeveralGenericsResolved() { - Method method = ReflectionUtils.findMethod(SampleEvents.class, - "handleString", String.class); + Method method = ReflectionUtils.findMethod(SampleEvents.class, "handleString", String.class); supportsEventType(true, method, ResolvableType.forClass(PayloadStringTestEvent.class)); } @Test public void listenerWithAnnotationValue() { - Method method = ReflectionUtils.findMethod(SampleEvents.class, - "handleStringAnnotationValue"); + Method method = ReflectionUtils.findMethod(SampleEvents.class, "handleStringAnnotationValue"); supportsEventType(true, method, createGenericEventType(String.class)); } @Test public void listenerWithAnnotationClasses() { - Method method = ReflectionUtils.findMethod(SampleEvents.class, - "handleStringAnnotationClasses"); + Method method = ReflectionUtils.findMethod(SampleEvents.class, "handleStringAnnotationClasses"); supportsEventType(true, method, createGenericEventType(String.class)); } @Test public void listenerWithAnnotationValueAndParameter() { - Method method = ReflectionUtils.findMethod(SampleEvents.class, - "handleStringAnnotationValueAndParameter", String.class); + Method method = ReflectionUtils.findMethod( + SampleEvents.class, "handleStringAnnotationValueAndParameter", String.class); supportsEventType(true, method, createGenericEventType(String.class)); } @Test public void listenerWithSeveralTypes() { - Method method = ReflectionUtils.findMethod(SampleEvents.class, - "handleStringOrInteger"); + Method method = ReflectionUtils.findMethod(SampleEvents.class, "handleStringOrInteger"); supportsEventType(true, method, createGenericEventType(String.class)); supportsEventType(true, method, createGenericEventType(Integer.class)); supportsEventType(false, method, createGenericEventType(Double.class)); @@ -145,51 +137,47 @@ public void listenerWithSeveralTypes() { @Test public void listenerWithTooManyParameters() { - Method method = ReflectionUtils.findMethod(SampleEvents.class, - "tooManyParameters", String.class, String.class); - - thrown.expect(IllegalStateException.class); + Method method = ReflectionUtils.findMethod( + SampleEvents.class, "tooManyParameters", String.class, String.class); + this.thrown.expect(IllegalStateException.class); createTestInstance(method); } @Test public void listenerWithNoParameter() { - Method method = ReflectionUtils.findMethod(SampleEvents.class, - "noParameter"); - - thrown.expect(IllegalStateException.class); + Method method = ReflectionUtils.findMethod(SampleEvents.class, "noParameter"); + this.thrown.expect(IllegalStateException.class); createTestInstance(method); } @Test public void listenerWithMoreThanOneParameter() { - Method method = ReflectionUtils.findMethod(SampleEvents.class, - "moreThanOneParameter", String.class, Integer.class); - - thrown.expect(IllegalStateException.class); + Method method = ReflectionUtils.findMethod( + SampleEvents.class, "moreThanOneParameter", String.class, Integer.class); + this.thrown.expect(IllegalStateException.class); createTestInstance(method); } @Test public void defaultOrder() { - Method method = ReflectionUtils.findMethod(SampleEvents.class, - "handleGenericString", GenericTestEvent.class); + Method method = ReflectionUtils.findMethod( + SampleEvents.class, "handleGenericString", GenericTestEvent.class); ApplicationListenerMethodAdapter adapter = createTestInstance(method); assertEquals(0, adapter.getOrder()); } @Test public void specifiedOrder() { - Method method = ReflectionUtils.findMethod(SampleEvents.class, - "handleRaw", ApplicationEvent.class); + Method method = ReflectionUtils.findMethod( + SampleEvents.class, "handleRaw", ApplicationEvent.class); ApplicationListenerMethodAdapter adapter = createTestInstance(method); assertEquals(42, adapter.getOrder()); } @Test public void invokeListener() { - Method method = ReflectionUtils.findMethod(SampleEvents.class, - "handleGenericString", GenericTestEvent.class); + Method method = ReflectionUtils.findMethod( + SampleEvents.class, "handleGenericString", GenericTestEvent.class); GenericTestEvent event = createGenericTestEvent("test"); invokeListener(method, event); verify(this.sampleEvents, times(1)).handleGenericString(event); @@ -197,8 +185,8 @@ public void invokeListener() { @Test public void invokeListenerWithGenericEvent() { - Method method = ReflectionUtils.findMethod(SampleEvents.class, - "handleGenericString", GenericTestEvent.class); + Method method = ReflectionUtils.findMethod( + SampleEvents.class, "handleGenericString", GenericTestEvent.class); GenericTestEvent event = new SmartGenericTestEvent<>(this, "test"); invokeListener(method, event); verify(this.sampleEvents, times(1)).handleGenericString(event); @@ -206,8 +194,8 @@ public void invokeListenerWithGenericEvent() { @Test public void invokeListenerWithGenericPayload() { - Method method = ReflectionUtils.findMethod(SampleEvents.class, - "handleGenericStringPayload", EntityWrapper.class); + Method method = ReflectionUtils.findMethod( + SampleEvents.class, "handleGenericStringPayload", EntityWrapper.class); EntityWrapper payload = new EntityWrapper<>("test"); invokeListener(method, new PayloadApplicationEvent<>(this, payload)); verify(this.sampleEvents, times(1)).handleGenericStringPayload(payload); @@ -215,8 +203,8 @@ public void invokeListenerWithGenericPayload() { @Test public void invokeListenerWithWrongGenericPayload() { - Method method = ReflectionUtils.findMethod(SampleEvents.class, - "handleGenericStringPayload", EntityWrapper.class); + Method method = ReflectionUtils.findMethod + (SampleEvents.class, "handleGenericStringPayload", EntityWrapper.class); EntityWrapper payload = new EntityWrapper<>(123); invokeListener(method, new PayloadApplicationEvent<>(this, payload)); verify(this.sampleEvents, times(0)).handleGenericStringPayload(any()); @@ -224,8 +212,8 @@ public void invokeListenerWithWrongGenericPayload() { @Test public void invokeListenerWithAnyGenericPayload() { - Method method = ReflectionUtils.findMethod(SampleEvents.class, - "handleGenericAnyPayload", EntityWrapper.class); + Method method = ReflectionUtils.findMethod( + SampleEvents.class, "handleGenericAnyPayload", EntityWrapper.class); EntityWrapper payload = new EntityWrapper<>("test"); invokeListener(method, new PayloadApplicationEvent<>(this, payload)); verify(this.sampleEvents, times(1)).handleGenericAnyPayload(payload); @@ -233,24 +221,24 @@ public void invokeListenerWithAnyGenericPayload() { @Test public void invokeListenerRuntimeException() { - Method method = ReflectionUtils.findMethod(SampleEvents.class, - "generateRuntimeException", GenericTestEvent.class); + Method method = ReflectionUtils.findMethod( + SampleEvents.class, "generateRuntimeException", GenericTestEvent.class); GenericTestEvent event = createGenericTestEvent("fail"); - thrown.expect(IllegalStateException.class); - thrown.expectMessage("Test exception"); - thrown.expectCause(is(isNull(Throwable.class))); + this.thrown.expect(IllegalStateException.class); + this.thrown.expectMessage("Test exception"); + this.thrown.expectCause(is((Throwable) isNull())); invokeListener(method, event); } @Test public void invokeListenerCheckedException() { - Method method = ReflectionUtils.findMethod(SampleEvents.class, - "generateCheckedException", GenericTestEvent.class); + Method method = ReflectionUtils.findMethod( + SampleEvents.class, "generateCheckedException", GenericTestEvent.class); GenericTestEvent event = createGenericTestEvent("fail"); - thrown.expect(UndeclaredThrowableException.class); - thrown.expectCause(is(instanceOf(IOException.class))); + this.thrown.expect(UndeclaredThrowableException.class); + this.thrown.expectCause(is(instanceOf(IOException.class))); invokeListener(method, event); } @@ -262,18 +250,18 @@ public void invokeListenerInvalidProxy() { proxyFactory.addInterface(SimpleService.class); Object bean = proxyFactory.getProxy(getClass().getClassLoader()); - Method method = ReflectionUtils.findMethod(InvalidProxyTestBean.class, "handleIt2", ApplicationEvent.class); + Method method = ReflectionUtils.findMethod( + InvalidProxyTestBean.class, "handleIt2", ApplicationEvent.class); StaticApplicationListenerMethodAdapter listener = new StaticApplicationListenerMethodAdapter(method, bean); - thrown.expect(IllegalStateException.class); - thrown.expectMessage("handleIt2"); + this.thrown.expect(IllegalStateException.class); + this.thrown.expectMessage("handleIt2"); listener.onApplicationEvent(createGenericTestEvent("test")); } @Test public void invokeListenerWithPayload() { - Method method = ReflectionUtils.findMethod(SampleEvents.class, - "handleString", String.class); + Method method = ReflectionUtils.findMethod(SampleEvents.class, "handleString", String.class); PayloadApplicationEvent event = new PayloadApplicationEvent<>(this, "test"); invokeListener(method, event); verify(this.sampleEvents, times(1)).handleString("test"); @@ -281,8 +269,7 @@ public void invokeListenerWithPayload() { @Test public void invokeListenerWithPayloadWrongType() { - Method method = ReflectionUtils.findMethod(SampleEvents.class, - "handleString", String.class); + Method method = ReflectionUtils.findMethod(SampleEvents.class, "handleString", String.class); PayloadApplicationEvent event = new PayloadApplicationEvent<>(this, 123L); invokeListener(method, event); verify(this.sampleEvents, never()).handleString(anyString()); @@ -290,8 +277,7 @@ public void invokeListenerWithPayloadWrongType() { @Test public void invokeListenerWithAnnotationValue() { - Method method = ReflectionUtils.findMethod(SampleEvents.class, - "handleStringAnnotationClasses"); + Method method = ReflectionUtils.findMethod(SampleEvents.class, "handleStringAnnotationClasses"); PayloadApplicationEvent event = new PayloadApplicationEvent<>(this, "test"); invokeListener(method, event); verify(this.sampleEvents, times(1)).handleStringAnnotationClasses(); @@ -299,8 +285,8 @@ public void invokeListenerWithAnnotationValue() { @Test public void invokeListenerWithAnnotationValueAndParameter() { - Method method = ReflectionUtils.findMethod(SampleEvents.class, - "handleStringAnnotationValueAndParameter", String.class); + Method method = ReflectionUtils.findMethod( + SampleEvents.class, "handleStringAnnotationValueAndParameter", String.class); PayloadApplicationEvent event = new PayloadApplicationEvent<>(this, "test"); invokeListener(method, event); verify(this.sampleEvents, times(1)).handleStringAnnotationValueAndParameter("test"); @@ -308,8 +294,7 @@ public void invokeListenerWithAnnotationValueAndParameter() { @Test public void invokeListenerWithSeveralTypes() { - Method method = ReflectionUtils.findMethod(SampleEvents.class, - "handleStringOrInteger"); + Method method = ReflectionUtils.findMethod(SampleEvents.class, "handleStringOrInteger"); PayloadApplicationEvent event = new PayloadApplicationEvent<>(this, "test"); invokeListener(method, event); verify(this.sampleEvents, times(1)).handleStringOrInteger(); @@ -321,11 +306,10 @@ public void invokeListenerWithSeveralTypes() { verify(this.sampleEvents, times(2)).handleStringOrInteger(); } - @Test public void beanInstanceRetrievedAtEveryInvocation() { - Method method = ReflectionUtils.findMethod(SampleEvents.class, - "handleGenericString", GenericTestEvent.class); + Method method = ReflectionUtils.findMethod( + SampleEvents.class, "handleGenericString", GenericTestEvent.class); when(this.context.getBean("testBean")).thenReturn(this.sampleEvents); ApplicationListenerMethodAdapter listener = new ApplicationListenerMethodAdapter( "testBean", GenericTestEvent.class, method); @@ -342,6 +326,7 @@ public void beanInstanceRetrievedAtEveryInvocation() { verify(this.context, times(2)).getBean("testBean"); } + private void supportsEventType(boolean match, Method method, ResolvableType eventType) { ApplicationListenerMethodAdapter adapter = createTestInstance(method); assertEquals("Wrong match for event '" + eventType + "' on " + method, @@ -361,8 +346,8 @@ private ResolvableType createGenericEventType(Class payloadType) { return ResolvableType.forClassWithGenerics(PayloadApplicationEvent.class, payloadType); } - private static class StaticApplicationListenerMethodAdapter - extends ApplicationListenerMethodAdapter { + + private static class StaticApplicationListenerMethodAdapter extends ApplicationListenerMethodAdapter { private final Object targetBean; @@ -373,14 +358,13 @@ public StaticApplicationListenerMethodAdapter(Method method, Object targetBean) @Override public Object getTargetBean() { - return targetBean; + return this.targetBean; } } private static class SampleEvents { - @EventListener @Order(42) public void handleRaw(ApplicationEvent event) { @@ -449,13 +433,15 @@ public void generateCheckedException(GenericTestEvent event) throws IOEx } } + interface SimpleService { void handleIt(ApplicationEvent event); - } + private static class EntityWrapper implements ResolvableTypeProvider { + private final T entity; public EntityWrapper(T entity) { @@ -468,6 +454,7 @@ public ResolvableType getResolvableType() { } } + static class InvalidProxyTestBean implements SimpleService { @Override @@ -479,6 +466,7 @@ public void handleIt2(ApplicationEvent event) { } } + @SuppressWarnings({"unused", "serial"}) static class PayloadTestEvent extends PayloadApplicationEvent { @@ -490,8 +478,10 @@ public PayloadTestEvent(Object source, T payload, V something) { } } + @SuppressWarnings({ "serial" }) static class PayloadStringTestEvent extends PayloadTestEvent { + public PayloadStringTestEvent(Object source, String payload, Long something) { super(source, payload, something); } 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 301ad5a71a..8209d79b67 100644 --- a/spring-core/src/main/java/org/springframework/util/ReflectionUtils.java +++ b/spring-core/src/main/java/org/springframework/util/ReflectionUtils.java @@ -607,6 +607,7 @@ public void doWith(Method method) { * @see Class#getDeclaredMethods() */ private static Method[] getDeclaredMethods(Class clazz) { + Assert.notNull(clazz, "Class must not be null"); Method[] result = declaredMethodsCache.get(clazz); if (result == null) { Method[] declaredMethods = clazz.getDeclaredMethods(); @@ -708,6 +709,7 @@ public static void doWithFields(Class clazz, FieldCallback fc, FieldFilter ff * @see Class#getDeclaredFields() */ private static Field[] getDeclaredFields(Class clazz) { + Assert.notNull(clazz, "Class must not be null"); Field[] result = declaredFieldsCache.get(clazz); if (result == null) { result = clazz.getDeclaredFields(); diff --git a/spring-web/src/main/java/org/springframework/http/client/AbstractAsyncClientHttpRequest.java b/spring-web/src/main/java/org/springframework/http/client/AbstractAsyncClientHttpRequest.java index 594c2fb9dd..e33a15381c 100644 --- a/spring-web/src/main/java/org/springframework/http/client/AbstractAsyncClientHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/client/AbstractAsyncClientHttpRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -73,12 +73,11 @@ protected void assertNotExecuted() { protected abstract OutputStream getBodyInternal(HttpHeaders headers) throws IOException; /** - * Abstract template method that writes the given headers and content to the HTTP - * request. + * Abstract template method that writes the given headers and content to the HTTP request. * @param headers the HTTP headers * @return the response object for the executed request */ - protected abstract ListenableFuture executeInternal( - HttpHeaders headers) throws IOException; + protected abstract ListenableFuture executeInternal(HttpHeaders headers) + throws IOException; } diff --git a/spring-web/src/main/java/org/springframework/web/client/HttpClientErrorException.java b/spring-web/src/main/java/org/springframework/web/client/HttpClientErrorException.java index a768dfb78f..14883542a9 100644 --- a/spring-web/src/main/java/org/springframework/web/client/HttpClientErrorException.java +++ b/spring-web/src/main/java/org/springframework/web/client/HttpClientErrorException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,8 +34,8 @@ public class HttpClientErrorException extends HttpStatusCodeException { /** - * Construct a new instance of {@code HttpClientErrorException} based on an - * {@link HttpStatus}. + * Construct a new instance of {@code HttpClientErrorException} based on + * an {@link HttpStatus}. * @param statusCode the status code */ public HttpClientErrorException(HttpStatus statusCode) { @@ -43,8 +43,8 @@ public HttpClientErrorException(HttpStatus statusCode) { } /** - * Construct a new instance of {@code HttpClientErrorException} based on an - * {@link HttpStatus} and status text. + * Construct a new instance of {@code HttpClientErrorException} based on + * an {@link HttpStatus} and status text. * @param statusCode the status code * @param statusText the status text */ @@ -53,30 +53,32 @@ public HttpClientErrorException(HttpStatus statusCode, String statusText) { } /** - * Construct a new instance of {@code HttpClientErrorException} based on an - * {@link HttpStatus}, status text, and response body content. + * Construct a new instance of {@code HttpClientErrorException} based on + * an {@link HttpStatus}, status text, and response body content. * @param statusCode the status code * @param statusText the status text - * @param responseBody the response body content, may be {@code null} - * @param responseCharset the response body charset, may be {@code null} + * @param responseBody the response body content (may be {@code null}) + * @param responseCharset the response body charset (may be {@code null}) */ public HttpClientErrorException(HttpStatus statusCode, String statusText, byte[] responseBody, Charset responseCharset) { + super(statusCode, statusText, responseBody, responseCharset); } /** - * Construct a new instance of {@code HttpClientErrorException} based on an - * {@link HttpStatus}, status text, and response body content. + * Construct a new instance of {@code HttpClientErrorException} based on + * an {@link HttpStatus}, status text, and response body content. * @param statusCode the status code * @param statusText the status text - * @param responseHeaders the response headers, may be {@code null} - * @param responseBody the response body content, may be {@code null} - * @param responseCharset the response body charset, may be {@code null} + * @param responseHeaders the response headers (may be {@code null}) + * @param responseBody the response body content (may be {@code null}) + * @param responseCharset the response body charset (may be {@code null}) * @since 3.1.2 */ public HttpClientErrorException(HttpStatus statusCode, String statusText, HttpHeaders responseHeaders, byte[] responseBody, Charset responseCharset) { + super(statusCode, statusText, responseHeaders, responseBody, responseCharset); } diff --git a/spring-web/src/main/java/org/springframework/web/client/HttpServerErrorException.java b/spring-web/src/main/java/org/springframework/web/client/HttpServerErrorException.java index 5f9ba72cd5..f4e494d931 100644 --- a/spring-web/src/main/java/org/springframework/web/client/HttpServerErrorException.java +++ b/spring-web/src/main/java/org/springframework/web/client/HttpServerErrorException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,8 +25,8 @@ * Exception thrown when an HTTP 5xx is received. * * @author Arjen Poutsma - * @see DefaultResponseErrorHandler * @since 3.0 + * @see DefaultResponseErrorHandler */ public class HttpServerErrorException extends HttpStatusCodeException { @@ -34,8 +34,8 @@ public class HttpServerErrorException extends HttpStatusCodeException { /** - * Construct a new instance of {@code HttpServerErrorException} based on an - * {@link HttpStatus}. + * Construct a new instance of {@code HttpServerErrorException} based on + * an {@link HttpStatus}. * @param statusCode the status code */ public HttpServerErrorException(HttpStatus statusCode) { @@ -43,8 +43,8 @@ public HttpServerErrorException(HttpStatus statusCode) { } /** - * Construct a new instance of {@code HttpServerErrorException} based on an - * {@link HttpStatus} and status text. + * Construct a new instance of {@code HttpServerErrorException} based on + * an {@link HttpStatus} and status text. * @param statusCode the status code * @param statusText the status text */ @@ -53,31 +53,34 @@ public HttpServerErrorException(HttpStatus statusCode, String statusText) { } /** - * Construct a new instance of {@code HttpServerErrorException} based on an - * {@link HttpStatus}, status text, and response body content. - * @param statusCode the status code - * @param statusText the status text - * @param responseBody the response body content, may be {@code null} - * @param responseCharset the response body charset, may be {@code null} + * Construct a new instance of {@code HttpServerErrorException} based on + * an {@link HttpStatus}, status text, and response body content. + * @param statusCode the status code + * @param statusText the status text + * @param responseBody the response body content (may be {@code null}) + * @param responseCharset the response body charset (may be {@code null}) * @since 3.0.5 */ public HttpServerErrorException(HttpStatus statusCode, String statusText, byte[] responseBody, Charset responseCharset) { + super(statusCode, statusText, responseBody, responseCharset); } /** - * Construct a new instance of {@code HttpServerErrorException} based on a - * {@link HttpStatus}, status text, and response body content. + * Construct a new instance of {@code HttpServerErrorException} based on + * an {@link HttpStatus}, status text, and response body content. * @param statusCode the status code * @param statusText the status text - * @param responseHeaders the response headers, may be {@code null} - * @param responseBody the response body content, may be {@code null} - * @param responseCharset the response body charset, may be {@code null} + * @param responseHeaders the response headers (may be {@code null}) + * @param responseBody the response body content (may be {@code null}) + * @param responseCharset the response body charset (may be {@code null}) * @since 3.1.2 */ public HttpServerErrorException(HttpStatus statusCode, String statusText, HttpHeaders responseHeaders, byte[] responseBody, Charset responseCharset) { + super(statusCode, statusText, responseHeaders, responseBody, responseCharset); } + } From 31df09013280c62bf0ca49c1807e4e171340fd7b Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Wed, 23 Nov 2016 20:49:24 -0500 Subject: [PATCH 322/344] Avoid locking in WebSocket session "close" callback When processing a "close" notification from the server make an effort to cancel any outstanding heartbeat but avoid going as far as acquiring the responseLock since the server itself may already hold a lock of its own leading to a potential deadlock. The heartbeat task is now also further protected with an isClosed() check in case the heartbeat does not get cancelled in a concurrent scenario. Issue: SPR-14917 --- .../sockjs/transport/session/AbstractSockJsSession.java | 9 +++++++-- .../sockjs/transport/session/SockJsSessionTests.java | 1 - 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractSockJsSession.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractSockJsSession.java index 7211c97049..515f55333f 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractSockJsSession.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractSockJsSession.java @@ -396,7 +396,12 @@ public final void delegateConnectionClosed(CloseStatus status) throws Exception if (!isClosed()) { try { updateLastActiveTime(); - cancelHeartbeat(); + // Avoid cancelHeartbeat() and responseLock within server "close" callback + ScheduledFuture future = this.heartbeatFuture; + if (future != null) { + this.heartbeatFuture = null; + future.cancel(false); + } } finally { this.state = State.CLOSED; @@ -446,7 +451,7 @@ private class HeartbeatTask implements Runnable { @Override public void run() { synchronized (responseLock) { - if (!this.expired) { + if (!this.expired && !isClosed()) { try { sendHeartbeat(); } diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/session/SockJsSessionTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/session/SockJsSessionTests.java index 8135623027..a2299ac6d5 100644 --- a/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/session/SockJsSessionTests.java +++ b/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/session/SockJsSessionTests.java @@ -144,7 +144,6 @@ public void delegateConnectionClosed() throws Exception { assertClosed(); assertEquals(1, this.session.getNumberOfLastActiveTimeUpdates()); - assertTrue(this.session.didCancelHeartbeat()); verify(this.webSocketHandler).afterConnectionClosed(this.session, CloseStatus.GOING_AWAY); } From 01214bfb545fae6d706811f5e8da03d1a877942b Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 21 Nov 2016 17:24:44 +0100 Subject: [PATCH 323/344] WebSocketServerSockJsSession uses dedicated disconnect lock Issue: SPR-14917 (cherry picked from commit ac30bcb) --- .../session/WebSocketServerSockJsSession.java | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/WebSocketServerSockJsSession.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/WebSocketServerSockJsSession.java index 403b8609f1..b3c8c3ca40 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/WebSocketServerSockJsSession.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/WebSocketServerSockJsSession.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,6 +42,7 @@ * A SockJS session for use with the WebSocket transport. * * @author Rossen Stoyanchev + * @author Juergen Hoeller * @since 4.0 */ public class WebSocketServerSockJsSession extends AbstractSockJsSession implements NativeWebSocketSession { @@ -54,6 +55,8 @@ public class WebSocketServerSockJsSession extends AbstractSockJsSession implemen private final Object initSessionLock = new Object(); + private final Object disconnectLock = new Object(); + private volatile boolean disconnected; @@ -136,18 +139,14 @@ private void checkDelegateSessionInitialized() { @Override public Object getNativeSession() { - if ((this.webSocketSession != null) && (this.webSocketSession instanceof NativeWebSocketSession)) { - return ((NativeWebSocketSession) this.webSocketSession).getNativeSession(); - } - return null; + return (this.webSocketSession instanceof NativeWebSocketSession ? + ((NativeWebSocketSession) this.webSocketSession).getNativeSession() : null); } @Override public T getNativeSession(Class requiredType) { - if ((this.webSocketSession != null) && (this.webSocketSession instanceof NativeWebSocketSession)) { - return ((NativeWebSocketSession) this.webSocketSession).getNativeSession(requiredType); - } - return null; + return (this.webSocketSession instanceof NativeWebSocketSession ? + ((NativeWebSocketSession) this.webSocketSession).getNativeSession(requiredType) : null); } @@ -166,7 +165,7 @@ public void initializeDelegateSession(WebSocketSession session) { scheduleHeartbeat(); this.openFrameSent = true; } - catch (Exception ex) { + catch (Throwable ex) { tryCloseWithSockJsTransportError(ex, CloseStatus.SERVER_ERROR); } } @@ -196,10 +195,8 @@ public void handleMessage(TextMessage message, WebSocketSession wsSession) throw @Override public void sendMessageInternal(String message) throws SockJsTransportFailureException { - // Open frame not sent yet? // If in the session initialization thread, then cache, otherwise wait. - if (!this.openFrameSent) { synchronized (this.initSessionLock) { if (!this.openFrameSent) { @@ -208,6 +205,7 @@ public void sendMessageInternal(String message) throws SockJsTransportFailureExc } } } + cancelHeartbeat(); writeFrame(SockJsFrame.messageFrame(getMessageCodec(), message)); scheduleHeartbeat(); @@ -224,10 +222,12 @@ protected void writeFrameInternal(SockJsFrame frame) throws IOException { @Override protected void disconnect(CloseStatus status) throws IOException { - synchronized (this) { - if (isActive()) { - this.disconnected = true; - this.webSocketSession.close(status); + if (isActive()) { + synchronized (this.disconnectLock) { + if (isActive()) { + this.disconnected = true; + this.webSocketSession.close(status); + } } } } From 23e91e1ae6f9c38dcd3728b34b0abebc92ac2322 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 1 Dec 2016 12:30:10 +0100 Subject: [PATCH 324/344] Consistent use of "URI variables" terminology Issue: SPR-14969 (cherry picked from commit 2b02935) --- .../MockHttpServletRequestBuilder.java | 2 +- ...ockMultipartHttpServletRequestBuilder.java | 8 +-- .../request/MockMvcRequestBuilders.java | 58 ++++++++-------- .../web/client/AsyncRestTemplate.java | 22 +++---- .../web/client/RestTemplate.java | 66 +++++++++---------- 5 files changed, 78 insertions(+), 78 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java index a83f2f5f10..6325471b28 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java @@ -116,7 +116,7 @@ public class MockHttpServletRequestBuilder * {@link #with(RequestPostProcessor)}. * @param httpMethod the HTTP method (GET, POST, etc) * @param url a URL template; the resulting URL will be encoded - * @param vars zero or more URL variables + * @param vars zero or more URI variables */ MockHttpServletRequestBuilder(HttpMethod httpMethod, String url, Object... vars) { this(httpMethod, UriComponentsBuilder.fromUriString(url).buildAndExpand(vars).encode().toUri()); diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockMultipartHttpServletRequestBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockMultipartHttpServletRequestBuilder.java index b56e8a9f15..9b7d1f14df 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockMultipartHttpServletRequestBuilder.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockMultipartHttpServletRequestBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -46,10 +46,10 @@ public class MockMultipartHttpServletRequestBuilder extends MockHttpServletReque * see {@link #with(RequestPostProcessor)} and the * {@link RequestPostProcessor} extension point. * @param urlTemplate a URL template; the resulting URL will be encoded - * @param urlVariables zero or more URL variables + * @param uriVariables zero or more URI variables */ - MockMultipartHttpServletRequestBuilder(String urlTemplate, Object... urlVariables) { - super(HttpMethod.POST, urlTemplate, urlVariables); + MockMultipartHttpServletRequestBuilder(String urlTemplate, Object... uriVariables) { + super(HttpMethod.POST, urlTemplate, uriVariables); super.contentType(MediaType.MULTIPART_FORM_DATA); } diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockMvcRequestBuilders.java b/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockMvcRequestBuilders.java index 571699e614..bfd74887ae 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockMvcRequestBuilders.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockMvcRequestBuilders.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -49,10 +49,10 @@ public abstract class MockMvcRequestBuilders { /** * Create a {@link MockHttpServletRequestBuilder} for a GET request. * @param urlTemplate a URL template; the resulting URL will be encoded - * @param urlVariables zero or more URL variables + * @param uriVars zero or more URI variables */ - public static MockHttpServletRequestBuilder get(String urlTemplate, Object... urlVariables) { - return new MockHttpServletRequestBuilder(HttpMethod.GET, urlTemplate, urlVariables); + public static MockHttpServletRequestBuilder get(String urlTemplate, Object... uriVars) { + return new MockHttpServletRequestBuilder(HttpMethod.GET, urlTemplate, uriVars); } /** @@ -67,10 +67,10 @@ public static MockHttpServletRequestBuilder get(URI uri) { /** * Create a {@link MockHttpServletRequestBuilder} for a POST request. * @param urlTemplate a URL template; the resulting URL will be encoded - * @param urlVariables zero or more URL variables + * @param uriVars zero or more URI variables */ - public static MockHttpServletRequestBuilder post(String urlTemplate, Object... urlVariables) { - return new MockHttpServletRequestBuilder(HttpMethod.POST, urlTemplate, urlVariables); + public static MockHttpServletRequestBuilder post(String urlTemplate, Object... uriVars) { + return new MockHttpServletRequestBuilder(HttpMethod.POST, urlTemplate, uriVars); } /** @@ -85,10 +85,10 @@ public static MockHttpServletRequestBuilder post(URI uri) { /** * Create a {@link MockHttpServletRequestBuilder} for a PUT request. * @param urlTemplate a URL template; the resulting URL will be encoded - * @param urlVariables zero or more URL variables + * @param uriVars zero or more URI variables */ - public static MockHttpServletRequestBuilder put(String urlTemplate, Object... urlVariables) { - return new MockHttpServletRequestBuilder(HttpMethod.PUT, urlTemplate, urlVariables); + public static MockHttpServletRequestBuilder put(String urlTemplate, Object... uriVars) { + return new MockHttpServletRequestBuilder(HttpMethod.PUT, urlTemplate, uriVars); } /** @@ -103,10 +103,10 @@ public static MockHttpServletRequestBuilder put(URI uri) { /** * Create a {@link MockHttpServletRequestBuilder} for a PATCH request. * @param urlTemplate a URL template; the resulting URL will be encoded - * @param urlVariables zero or more URL variables + * @param uriVars zero or more URI variables */ - public static MockHttpServletRequestBuilder patch(String urlTemplate, Object... urlVariables) { - return new MockHttpServletRequestBuilder(HttpMethod.PATCH, urlTemplate, urlVariables); + public static MockHttpServletRequestBuilder patch(String urlTemplate, Object... uriVars) { + return new MockHttpServletRequestBuilder(HttpMethod.PATCH, urlTemplate, uriVars); } /** @@ -121,10 +121,10 @@ public static MockHttpServletRequestBuilder patch(URI uri) { /** * Create a {@link MockHttpServletRequestBuilder} for a DELETE request. * @param urlTemplate a URL template; the resulting URL will be encoded - * @param urlVariables zero or more URL variables + * @param uriVars zero or more URI variables */ - public static MockHttpServletRequestBuilder delete(String urlTemplate, Object... urlVariables) { - return new MockHttpServletRequestBuilder(HttpMethod.DELETE, urlTemplate, urlVariables); + public static MockHttpServletRequestBuilder delete(String urlTemplate, Object... uriVars) { + return new MockHttpServletRequestBuilder(HttpMethod.DELETE, urlTemplate, uriVars); } /** @@ -139,10 +139,10 @@ public static MockHttpServletRequestBuilder delete(URI uri) { /** * Create a {@link MockHttpServletRequestBuilder} for an OPTIONS request. * @param urlTemplate a URL template; the resulting URL will be encoded - * @param urlVariables zero or more URL variables + * @param uriVars zero or more URI variables */ - public static MockHttpServletRequestBuilder options(String urlTemplate, Object... urlVariables) { - return new MockHttpServletRequestBuilder(HttpMethod.OPTIONS, urlTemplate, urlVariables); + public static MockHttpServletRequestBuilder options(String urlTemplate, Object... uriVars) { + return new MockHttpServletRequestBuilder(HttpMethod.OPTIONS, urlTemplate, uriVars); } /** @@ -157,11 +157,11 @@ public static MockHttpServletRequestBuilder options(URI uri) { /** * Create a {@link MockHttpServletRequestBuilder} for a HEAD request. * @param urlTemplate a URL template; the resulting URL will be encoded - * @param urlVariables zero or more URL variables + * @param uriVars zero or more URI variables * @since 4.1 */ - public static MockHttpServletRequestBuilder head(String urlTemplate, Object... urlVariables) { - return new MockHttpServletRequestBuilder(HttpMethod.HEAD, urlTemplate, urlVariables); + public static MockHttpServletRequestBuilder head(String urlTemplate, Object... uriVars) { + return new MockHttpServletRequestBuilder(HttpMethod.HEAD, urlTemplate, uriVars); } /** @@ -175,12 +175,12 @@ public static MockHttpServletRequestBuilder head(URI uri) { /** * Create a {@link MockHttpServletRequestBuilder} for a request with the given HTTP method. - * @param httpMethod the HTTP method + * @param method the HTTP method (GET, POST, etc) * @param urlTemplate a URL template; the resulting URL will be encoded - * @param urlVariables zero or more URL variables + * @param uriVars zero or more URI variables */ - public static MockHttpServletRequestBuilder request(HttpMethod httpMethod, String urlTemplate, Object... urlVariables) { - return new MockHttpServletRequestBuilder(httpMethod, urlTemplate, urlVariables); + public static MockHttpServletRequestBuilder request(HttpMethod method, String urlTemplate, Object... uriVars) { + return new MockHttpServletRequestBuilder(method, urlTemplate, uriVars); } /** @@ -196,10 +196,10 @@ public static MockHttpServletRequestBuilder request(HttpMethod httpMethod, URI u /** * Create a {@link MockMultipartHttpServletRequestBuilder} for a multipart request. * @param urlTemplate a URL template; the resulting URL will be encoded - * @param urlVariables zero or more URL variables + * @param uriVars zero or more URI variables */ - public static MockMultipartHttpServletRequestBuilder fileUpload(String urlTemplate, Object... urlVariables) { - return new MockMultipartHttpServletRequestBuilder(urlTemplate, urlVariables); + public static MockMultipartHttpServletRequestBuilder fileUpload(String urlTemplate, Object... uriVars) { + return new MockMultipartHttpServletRequestBuilder(urlTemplate, uriVars); } /** diff --git a/spring-web/src/main/java/org/springframework/web/client/AsyncRestTemplate.java b/spring-web/src/main/java/org/springframework/web/client/AsyncRestTemplate.java index eb6b523012..4a4a5a57f3 100644 --- a/spring-web/src/main/java/org/springframework/web/client/AsyncRestTemplate.java +++ b/spring-web/src/main/java/org/springframework/web/client/AsyncRestTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -206,11 +206,11 @@ public ListenableFuture> getForEntity(String url, Class @Override public ListenableFuture> getForEntity(String url, Class responseType, - Map urlVariables) throws RestClientException { + Map uriVariables) throws RestClientException { AsyncRequestCallback requestCallback = acceptHeaderRequestCallback(responseType); ResponseExtractor> responseExtractor = responseEntityExtractor(responseType); - return execute(url, HttpMethod.GET, requestCallback, responseExtractor, urlVariables); + return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables); } @Override @@ -371,13 +371,13 @@ public ListenableFuture put(URI url, HttpEntity request) throws RestClient // DELETE @Override - public ListenableFuture delete(String url, Object... urlVariables) throws RestClientException { - return execute(url, HttpMethod.DELETE, null, null, urlVariables); + public ListenableFuture delete(String url, Object... uriVariables) throws RestClientException { + return execute(url, HttpMethod.DELETE, null, null, uriVariables); } @Override - public ListenableFuture delete(String url, Map urlVariables) throws RestClientException { - return execute(url, HttpMethod.DELETE, null, null, urlVariables); + public ListenableFuture delete(String url, Map uriVariables) throws RestClientException { + return execute(url, HttpMethod.DELETE, null, null, uriVariables); } @Override @@ -518,17 +518,17 @@ public ListenableFuture> exchange(URI url, HttpMethod meth @Override public ListenableFuture execute(String url, HttpMethod method, AsyncRequestCallback requestCallback, - ResponseExtractor responseExtractor, Object... urlVariables) throws RestClientException { + ResponseExtractor responseExtractor, Object... uriVariables) throws RestClientException { - URI expanded = getUriTemplateHandler().expand(url, urlVariables); + URI expanded = getUriTemplateHandler().expand(url, uriVariables); return doExecute(expanded, method, requestCallback, responseExtractor); } @Override public ListenableFuture execute(String url, HttpMethod method, AsyncRequestCallback requestCallback, - ResponseExtractor responseExtractor, Map urlVariables) throws RestClientException { + ResponseExtractor responseExtractor, Map uriVariables) throws RestClientException { - URI expanded = getUriTemplateHandler().expand(url, urlVariables); + URI expanded = getUriTemplateHandler().expand(url, uriVariables); return doExecute(expanded, method, requestCallback, responseExtractor); } diff --git a/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java b/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java index 8c7c04334f..e96a74d145 100644 --- a/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java +++ b/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java @@ -5,7 +5,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -257,19 +257,19 @@ public UriTemplateHandler getUriTemplateHandler() { // GET @Override - public T getForObject(String url, Class responseType, Object... urlVariables) throws RestClientException { + public T getForObject(String url, Class responseType, Object... uriVariables) throws RestClientException { RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); HttpMessageConverterExtractor responseExtractor = new HttpMessageConverterExtractor(responseType, getMessageConverters(), logger); - return execute(url, HttpMethod.GET, requestCallback, responseExtractor, urlVariables); + return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables); } @Override - public T getForObject(String url, Class responseType, Map urlVariables) throws RestClientException { + public T getForObject(String url, Class responseType, Map uriVariables) throws RestClientException { RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); HttpMessageConverterExtractor responseExtractor = new HttpMessageConverterExtractor(responseType, getMessageConverters(), logger); - return execute(url, HttpMethod.GET, requestCallback, responseExtractor, urlVariables); + return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables); } @Override @@ -281,21 +281,21 @@ public T getForObject(URI url, Class responseType) throws RestClientExcep } @Override - public ResponseEntity getForEntity(String url, Class responseType, Object... urlVariables) + public ResponseEntity getForEntity(String url, Class responseType, Object... uriVariables) throws RestClientException { RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); ResponseExtractor> responseExtractor = responseEntityExtractor(responseType); - return execute(url, HttpMethod.GET, requestCallback, responseExtractor, urlVariables); + return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables); } @Override - public ResponseEntity getForEntity(String url, Class responseType, Map urlVariables) + public ResponseEntity getForEntity(String url, Class responseType, Map uriVariables) throws RestClientException { RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); ResponseExtractor> responseExtractor = responseEntityExtractor(responseType); - return execute(url, HttpMethod.GET, requestCallback, responseExtractor, urlVariables); + return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables); } @Override @@ -309,13 +309,13 @@ public ResponseEntity getForEntity(URI url, Class responseType) throws // HEAD @Override - public HttpHeaders headForHeaders(String url, Object... urlVariables) throws RestClientException { - return execute(url, HttpMethod.HEAD, null, headersExtractor(), urlVariables); + public HttpHeaders headForHeaders(String url, Object... uriVariables) throws RestClientException { + return execute(url, HttpMethod.HEAD, null, headersExtractor(), uriVariables); } @Override - public HttpHeaders headForHeaders(String url, Map urlVariables) throws RestClientException { - return execute(url, HttpMethod.HEAD, null, headersExtractor(), urlVariables); + public HttpHeaders headForHeaders(String url, Map uriVariables) throws RestClientException { + return execute(url, HttpMethod.HEAD, null, headersExtractor(), uriVariables); } @Override @@ -327,16 +327,16 @@ public HttpHeaders headForHeaders(URI url) throws RestClientException { // POST @Override - public URI postForLocation(String url, Object request, Object... urlVariables) throws RestClientException { + public URI postForLocation(String url, Object request, Object... uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request); - HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, headersExtractor(), urlVariables); + HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, headersExtractor(), uriVariables); return headers.getLocation(); } @Override - public URI postForLocation(String url, Object request, Map urlVariables) throws RestClientException { + public URI postForLocation(String url, Object request, Map uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request); - HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, headersExtractor(), urlVariables); + HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, headersExtractor(), uriVariables); return headers.getLocation(); } @@ -404,15 +404,15 @@ public ResponseEntity postForEntity(URI url, Object request, Class res // PUT @Override - public void put(String url, Object request, Object... urlVariables) throws RestClientException { + public void put(String url, Object request, Object... uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request); - execute(url, HttpMethod.PUT, requestCallback, null, urlVariables); + execute(url, HttpMethod.PUT, requestCallback, null, uriVariables); } @Override - public void put(String url, Object request, Map urlVariables) throws RestClientException { + public void put(String url, Object request, Map uriVariables) throws RestClientException { RequestCallback requestCallback = httpEntityCallback(request); - execute(url, HttpMethod.PUT, requestCallback, null, urlVariables); + execute(url, HttpMethod.PUT, requestCallback, null, uriVariables); } @Override @@ -425,13 +425,13 @@ public void put(URI url, Object request) throws RestClientException { // DELETE @Override - public void delete(String url, Object... urlVariables) throws RestClientException { - execute(url, HttpMethod.DELETE, null, null, urlVariables); + public void delete(String url, Object... uriVariables) throws RestClientException { + execute(url, HttpMethod.DELETE, null, null, uriVariables); } @Override - public void delete(String url, Map urlVariables) throws RestClientException { - execute(url, HttpMethod.DELETE, null, null, urlVariables); + public void delete(String url, Map uriVariables) throws RestClientException { + execute(url, HttpMethod.DELETE, null, null, uriVariables); } @Override @@ -443,16 +443,16 @@ public void delete(URI url) throws RestClientException { // OPTIONS @Override - public Set optionsForAllow(String url, Object... urlVariables) throws RestClientException { + public Set optionsForAllow(String url, Object... uriVariables) throws RestClientException { ResponseExtractor headersExtractor = headersExtractor(); - HttpHeaders headers = execute(url, HttpMethod.OPTIONS, null, headersExtractor, urlVariables); + HttpHeaders headers = execute(url, HttpMethod.OPTIONS, null, headersExtractor, uriVariables); return headers.getAllow(); } @Override - public Set optionsForAllow(String url, Map urlVariables) throws RestClientException { + public Set optionsForAllow(String url, Map uriVariables) throws RestClientException { ResponseExtractor headersExtractor = headersExtractor(); - HttpHeaders headers = execute(url, HttpMethod.OPTIONS, null, headersExtractor, urlVariables); + HttpHeaders headers = execute(url, HttpMethod.OPTIONS, null, headersExtractor, uriVariables); return headers.getAllow(); } @@ -551,17 +551,17 @@ public ResponseEntity exchange(RequestEntity requestEntity, Parameteri @Override public T execute(String url, HttpMethod method, RequestCallback requestCallback, - ResponseExtractor responseExtractor, Object... urlVariables) throws RestClientException { + ResponseExtractor responseExtractor, Object... uriVariables) throws RestClientException { - URI expanded = getUriTemplateHandler().expand(url, urlVariables); + URI expanded = getUriTemplateHandler().expand(url, uriVariables); return doExecute(expanded, method, requestCallback, responseExtractor); } @Override public T execute(String url, HttpMethod method, RequestCallback requestCallback, - ResponseExtractor responseExtractor, Map urlVariables) throws RestClientException { + ResponseExtractor responseExtractor, Map uriVariables) throws RestClientException { - URI expanded = getUriTemplateHandler().expand(url, urlVariables); + URI expanded = getUriTemplateHandler().expand(url, uriVariables); return doExecute(expanded, method, requestCallback, responseExtractor); } From 0b76b13d8894a3f36d7e9b4f3cf96126421ba822 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 1 Dec 2016 14:13:23 +0100 Subject: [PATCH 325/344] Polishing (cherry picked from commit 5fee5f3) --- .../beans/PropertyMatches.java | 35 +++++----- .../support/ConversionServiceFactoryBean.java | 12 ++-- .../support/ConnectorServerFactoryBean.java | 3 +- .../support/ScriptFactoryPostProcessor.java | 8 +-- .../ExpressionCachingIntegrationTests.java | 30 +++++--- .../core/convert/Property.java | 4 +- .../core/convert/support/ConversionUtils.java | 9 ++- .../support/DefaultConversionService.java | 3 +- .../support/GenericConversionService.java | 4 +- .../expression/EvaluationException.java | 37 +++++----- .../expression/ExpressionException.java | 49 +++++++------ .../expression/ParseException.java | 8 +-- .../spel/SpelEvaluationException.java | 42 +++++------ .../expression/spel/SpelMessage.java | 18 +++-- .../expression/spel/SpelParseException.java | 27 ++++--- .../SimpleClientHttpRequestFactory.java | 5 +- .../springframework/web/util/UriTemplate.java | 16 ++--- .../client/AbstractJettyServerTestCase.java | 70 +++++++++---------- .../AsyncRestTemplateIntegrationTests.java | 2 - .../web/servlet/ResourceServlet.java | 40 ++++++----- .../web/socket/WebSocketMessage.java | 5 +- .../session/AbstractSockJsSession.java | 10 +-- 22 files changed, 220 insertions(+), 217 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/PropertyMatches.java b/spring-beans/src/main/java/org/springframework/beans/PropertyMatches.java index 87cea92830..87ccf2a95c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/PropertyMatches.java +++ b/spring-beans/src/main/java/org/springframework/beans/PropertyMatches.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,8 +30,8 @@ * Helper class for calculating property matches, according to a configurable * distance. Provide the list of potential matches and an easy way to generate * an error message. Works for both java bean properties and fields. - *

    - * Mainly for use within the framework and in particular the binding facility + * + *

    Mainly for use within the framework and in particular the binding facility. * * @author Alef Arendsen * @author Arjen Poutsma @@ -43,14 +43,12 @@ */ public abstract class PropertyMatches { - //--------------------------------------------------------------------- - // Static section - //--------------------------------------------------------------------- - /** Default maximum property distance: 2 */ public static final int DEFAULT_MAX_DISTANCE = 2; + // Static factory methods + /** * Create PropertyMatches for the given bean property. * @param propertyName the name of the property to find possible matches for @@ -90,9 +88,7 @@ public static PropertyMatches forField(String propertyName, Class beanClass, } - //--------------------------------------------------------------------- - // Instance section - //--------------------------------------------------------------------- + // Instance state private final String propertyName; @@ -107,18 +103,19 @@ private PropertyMatches(String propertyName, String[] possibleMatches) { this.possibleMatches = possibleMatches; } + /** * Return the name of the requested property. */ public String getPropertyName() { - return propertyName; + return this.propertyName; } /** * Return the calculated possible matches. */ public String[] getPossibleMatches() { - return possibleMatches; + return this.possibleMatches; } /** @@ -127,6 +124,9 @@ public String[] getPossibleMatches() { */ public abstract String buildErrorMessage(); + + // Implementation support for subclasses + protected void appendHintMessage(StringBuilder msg) { msg.append("Did you mean "); for (int i = 0; i < this.possibleMatches.length; i++) { @@ -184,9 +184,12 @@ private static int calculateStringDistance(String s1, String s2) { return d[s1.length()][s2.length()]; } + + // Concrete subclasses + private static class BeanPropertyMatches extends PropertyMatches { - private BeanPropertyMatches(String propertyName, Class beanClass, int maxDistance) { + public BeanPropertyMatches(String propertyName, Class beanClass, int maxDistance) { super(propertyName, calculateMatches(propertyName, BeanUtils.getPropertyDescriptors(beanClass), maxDistance)); } @@ -231,12 +234,12 @@ public String buildErrorMessage() { } return msg.toString(); } - } + private static class FieldPropertyMatches extends PropertyMatches { - private FieldPropertyMatches(String propertyName, Class beanClass, int maxDistance) { + public FieldPropertyMatches(String propertyName, Class beanClass, int maxDistance) { super(propertyName, calculateMatches(propertyName, beanClass, maxDistance)); } @@ -255,7 +258,6 @@ public void doWith(Field field) throws IllegalArgumentException, IllegalAccessEx return StringUtils.toStringArray(candidates); } - @Override public String buildErrorMessage() { String propertyName = getPropertyName(); @@ -270,7 +272,6 @@ public String buildErrorMessage() { } return msg.toString(); } - } } diff --git a/spring-context/src/main/java/org/springframework/context/support/ConversionServiceFactoryBean.java b/spring-context/src/main/java/org/springframework/context/support/ConversionServiceFactoryBean.java index 5cf2e81746..2bba3f2c61 100644 --- a/spring-context/src/main/java/org/springframework/context/support/ConversionServiceFactoryBean.java +++ b/spring-context/src/main/java/org/springframework/context/support/ConversionServiceFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,12 +27,12 @@ /** * A factory providing convenient access to a ConversionService configured with - * converters appropriate for most environments. Set the {@link #setConverters - * "converters"} property to supplement the default converters. + * converters appropriate for most environments. Set the + * {@link #setConverters "converters"} property to supplement the default converters. * - *

    This implementation creates a {@link DefaultConversionService}. Subclasses - * may override {@link #createConversionService()} in order to return a - * {@link GenericConversionService} instance of their choosing. + *

    This implementation creates a {@link DefaultConversionService}. + * Subclasses may override {@link #createConversionService()} in order to return + * a {@link GenericConversionService} instance of their choosing. * *

    Like all {@code FactoryBean} implementations, this class is suitable for * use when configuring a Spring application context using Spring {@code } diff --git a/spring-context/src/main/java/org/springframework/jmx/support/ConnectorServerFactoryBean.java b/spring-context/src/main/java/org/springframework/jmx/support/ConnectorServerFactoryBean.java index 48d8eb7ffb..964b94aafb 100644 --- a/spring-context/src/main/java/org/springframework/jmx/support/ConnectorServerFactoryBean.java +++ b/spring-context/src/main/java/org/springframework/jmx/support/ConnectorServerFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -49,7 +49,6 @@ * @author Rob Harrop * @author Juergen Hoeller * @since 1.2 - * @see FactoryBean * @see JMXConnectorServer * @see MBeanServer */ diff --git a/spring-context/src/main/java/org/springframework/scripting/support/ScriptFactoryPostProcessor.java b/spring-context/src/main/java/org/springframework/scripting/support/ScriptFactoryPostProcessor.java index d91573d03a..a0c7f4de3d 100644 --- a/spring-context/src/main/java/org/springframework/scripting/support/ScriptFactoryPostProcessor.java +++ b/spring-context/src/main/java/org/springframework/scripting/support/ScriptFactoryPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -206,8 +206,8 @@ public void setBeanClassLoader(ClassLoader classLoader) { @Override public void setBeanFactory(BeanFactory beanFactory) { if (!(beanFactory instanceof ConfigurableBeanFactory)) { - throw new IllegalStateException("ScriptFactoryPostProcessor doesn't work with a BeanFactory " - + "which does not implement ConfigurableBeanFactory: " + beanFactory.getClass()); + throw new IllegalStateException("ScriptFactoryPostProcessor doesn't work with " + + "non-ConfigurableBeanFactory: " + beanFactory.getClass()); } this.beanFactory = (ConfigurableBeanFactory) beanFactory; @@ -381,7 +381,7 @@ protected void prepareScriptBeans(BeanDefinition bd, String scriptFactoryBeanNam * If the {@link BeanDefinition} has a * {@link org.springframework.core.AttributeAccessor metadata attribute} * under the key {@link #REFRESH_CHECK_DELAY_ATTRIBUTE} which is a valid {@link Number} - * type, then this value is used. Otherwise, the the {@link #defaultRefreshCheckDelay} + * type, then this value is used. Otherwise, the {@link #defaultRefreshCheckDelay} * value is used. * @param beanDefinition the BeanDefinition to check * @return the refresh check delay diff --git a/spring-context/src/test/java/org/springframework/cache/config/ExpressionCachingIntegrationTests.java b/spring-context/src/test/java/org/springframework/cache/config/ExpressionCachingIntegrationTests.java index 3f1f5a9b46..493b65178f 100644 --- a/spring-context/src/test/java/org/springframework/cache/config/ExpressionCachingIntegrationTests.java +++ b/spring-context/src/test/java/org/springframework/cache/config/ExpressionCachingIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,13 +29,12 @@ import org.springframework.context.annotation.Configuration; /** - * * @author Stephane Nicoll */ public class ExpressionCachingIntegrationTests { + @Test // SPR-11692 @SuppressWarnings("unchecked") - @Test // SPR-11692 public void expressionIsCacheBasedOnActualMethod() { ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(SharedConfig.class, Spr11692Config.class); @@ -50,9 +49,9 @@ public void expressionIsCacheBasedOnActualMethod() { } - @Configuration static class Spr11692Config { + @Bean public BaseDao userDao() { return new UserDaoImpl(); @@ -65,11 +64,14 @@ public BaseDao orderDao() { } - private static interface BaseDao { + private interface BaseDao { + T persist(T t); } + private static class UserDaoImpl implements BaseDao { + @Override @CachePut(value = "users", key = "#user.id") public User persist(User user) { @@ -77,7 +79,9 @@ public User persist(User user) { } } + private static class OrderDaoImpl implements BaseDao { + @Override @CachePut(value = "orders", key = "#order.id") public Order persist(Order order) { @@ -85,33 +89,41 @@ public Order persist(Order order) { } } + private static class User { + private final String id; - private User(String id) { + public User(String id) { this.id = id; } + @SuppressWarnings("unused") public String getId() { - return id; + return this.id; } } + private static class Order { + private final String id; - private Order(String id) { + public Order(String id) { this.id = id; } + @SuppressWarnings("unused") public String getId() { - return id; + return this.id; } } + @Configuration @EnableCaching static class SharedConfig extends CachingConfigurerSupport { + @Override @Bean public CacheManager cacheManager() { diff --git a/spring-core/src/main/java/org/springframework/core/convert/Property.java b/spring-core/src/main/java/org/springframework/core/convert/Property.java index 84258e9d96..0da18ee69d 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/Property.java +++ b/spring-core/src/main/java/org/springframework/core/convert/Property.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -72,7 +72,7 @@ public Property(Class objectType, Method readMethod, Method writeMethod, Stri this.readMethod = readMethod; this.writeMethod = writeMethod; this.methodParameter = resolveMethodParameter(); - this.name = (name == null ? resolveName() : name); + this.name = (name != null ? name : resolveName()); } diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/ConversionUtils.java b/spring-core/src/main/java/org/springframework/core/convert/support/ConversionUtils.java index e93eabe90e..d6011a0b47 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/support/ConversionUtils.java +++ b/spring-core/src/main/java/org/springframework/core/convert/support/ConversionUtils.java @@ -31,6 +31,7 @@ abstract class ConversionUtils { public static Object invokeConverter(GenericConverter converter, Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { + try { return converter.convert(source, sourceType, targetType); } @@ -42,7 +43,9 @@ public static Object invokeConverter(GenericConverter converter, Object source, } } - public static boolean canConvertElements(TypeDescriptor sourceElementType, TypeDescriptor targetElementType, ConversionService conversionService) { + public static boolean canConvertElements(TypeDescriptor sourceElementType, TypeDescriptor targetElementType, + ConversionService conversionService) { + if (targetElementType == null) { // yes return true; @@ -56,11 +59,11 @@ public static boolean canConvertElements(TypeDescriptor sourceElementType, TypeD return true; } else if (sourceElementType.getType().isAssignableFrom(targetElementType.getType())) { - // maybe; + // maybe return true; } else { - // no; + // no return false; } } diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/DefaultConversionService.java b/spring-core/src/main/java/org/springframework/core/convert/support/DefaultConversionService.java index 408d1367b5..7437d490a0 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/support/DefaultConversionService.java +++ b/spring-core/src/main/java/org/springframework/core/convert/support/DefaultConversionService.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -53,7 +53,6 @@ public class DefaultConversionService extends GenericConversionService { "java.util.stream.Stream", DefaultConversionService.class.getClassLoader()); - /** * Create a new {@code DefaultConversionService} with the set of * {@linkplain DefaultConversionService#addDefaultConverters(ConverterRegistry) default converters}. diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java b/spring-core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java index 17868a3765..e06b246dc9 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java +++ b/spring-core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java @@ -66,8 +66,8 @@ public class GenericConversionService implements ConfigurableConversionService { private static final GenericConverter NO_OP_CONVERTER = new NoOpConverter("NO_OP"); /** - * Used as a cache entry when no converter is available. This converter is never - * returned. + * Used as a cache entry when no converter is available. + * This converter is never returned. */ private static final GenericConverter NO_MATCH = new NoOpConverter("NO_MATCH"); diff --git a/spring-expression/src/main/java/org/springframework/expression/EvaluationException.java b/spring-expression/src/main/java/org/springframework/expression/EvaluationException.java index bca535d26c..f4132b881d 100644 --- a/spring-expression/src/main/java/org/springframework/expression/EvaluationException.java +++ b/spring-expression/src/main/java/org/springframework/expression/EvaluationException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,24 @@ public class EvaluationException extends ExpressionException { /** - * Creates a new expression evaluation exception. + * Create a new expression evaluation exception. + * @param message description of the problem that occurred + */ + public EvaluationException(String message) { + super(message); + } + + /** + * Create a new expression evaluation exception. + * @param message description of the problem that occurred + * @param cause the underlying cause of this exception + */ + public EvaluationException(String message, Throwable cause) { + super(message,cause); + } + + /** + * Create a new expression evaluation exception. * @param position the position in the expression where the problem occurred * @param message description of the problem that occurred */ @@ -35,7 +52,7 @@ public EvaluationException(int position, String message) { } /** - * Creates a new expression evaluation exception. + * Create a new expression evaluation exception. * @param expressionString the expression that could not be evaluated * @param message description of the problem that occurred */ @@ -44,7 +61,7 @@ public EvaluationException(String expressionString, String message) { } /** - * Creates a new expression evaluation exception. + * Create a new expression evaluation exception. * @param position the position in the expression where the problem occurred * @param message description of the problem that occurred * @param cause the underlying cause of this exception @@ -53,16 +70,4 @@ public EvaluationException(int position, String message, Throwable cause) { super(position, message, cause); } - /** - * Creates a new expression evaluation exception. - * @param message description of the problem that occurred - */ - public EvaluationException(String message) { - super(message); - } - - public EvaluationException(String message, Throwable cause) { - super(message,cause); - } - } diff --git a/spring-expression/src/main/java/org/springframework/expression/ExpressionException.java b/spring-expression/src/main/java/org/springframework/expression/ExpressionException.java index 767f5cee3b..a300693e2f 100644 --- a/spring-expression/src/main/java/org/springframework/expression/ExpressionException.java +++ b/spring-expression/src/main/java/org/springframework/expression/ExpressionException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ * Super class for exceptions that can occur whilst processing expressions. * * @author Andy Clement + * @author Phil Webb * @since 3.0 */ @SuppressWarnings("serial") @@ -27,68 +28,68 @@ public class ExpressionException extends RuntimeException { protected String expressionString; - protected int position; // -1 if not known - but should be known in all reasonable cases + protected int position; // -1 if not known; should be known in all reasonable cases /** * Construct a new expression exception. - * @param expressionString the expression string * @param message a descriptive message */ - public ExpressionException(String expressionString, String message) { + public ExpressionException(String message) { super(message); - this.position = -1; - this.expressionString = expressionString; } /** * Construct a new expression exception. - * @param expressionString the expression string - * @param position the position in the expression string where the problem occurred * @param message a descriptive message + * @param cause the underlying cause of this exception */ - public ExpressionException(String expressionString, int position, String message) { - super(message); - this.position = position; - this.expressionString = expressionString; + public ExpressionException(String message, Throwable cause) { + super(message, cause); } /** * Construct a new expression exception. - * @param position the position in the expression string where the problem occurred + * @param expressionString the expression string * @param message a descriptive message */ - public ExpressionException(int position, String message) { + public ExpressionException(String expressionString, String message) { super(message); - this.position = position; + this.expressionString = expressionString; + this.position = -1; } /** * Construct a new expression exception. + * @param expressionString the expression string * @param position the position in the expression string where the problem occurred * @param message a descriptive message - * @param cause the underlying cause of this exception */ - public ExpressionException(int position, String message, Throwable cause) { - super(message,cause); + public ExpressionException(String expressionString, int position, String message) { + super(message); + this.expressionString = expressionString; this.position = position; } /** * Construct a new expression exception. + * @param position the position in the expression string where the problem occurred * @param message a descriptive message */ - public ExpressionException(String message) { + public ExpressionException(int position, String message) { super(message); + this.position = position; } /** * Construct a new expression exception. + * @param position the position in the expression string where the problem occurred * @param message a descriptive message * @param cause the underlying cause of this exception */ - public ExpressionException(String message, Throwable cause) { - super(message,cause); + public ExpressionException(int position, String message, Throwable cause) { + super(message, cause); + this.position = position; } @@ -107,8 +108,9 @@ public final int getPosition() { } /** - * Return the exception message. Since Spring 4.0 this method returns the - * same result as {@link #toDetailedString()}. + * Return the exception message. + * As of Spring 4.0, this method returns the same result as {@link #toDetailedString()}. + * @see #getSimpleMessage() * @see java.lang.Throwable#getMessage() */ @Override @@ -142,6 +144,7 @@ public String toDetailedString() { /** * Return the exception simple message without including the expression * that caused the failure. + * @since 4.0 */ public String getSimpleMessage() { return super.getMessage(); diff --git a/spring-expression/src/main/java/org/springframework/expression/ParseException.java b/spring-expression/src/main/java/org/springframework/expression/ParseException.java index e8db01acc0..64cb2cadf8 100644 --- a/spring-expression/src/main/java/org/springframework/expression/ParseException.java +++ b/spring-expression/src/main/java/org/springframework/expression/ParseException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ public class ParseException extends ExpressionException { /** - * Creates a new expression parsing exception. + * Create a new expression parsing exception. * @param expressionString the expression string that could not be parsed * @param position the position in the expression string where the problem occurred * @param message description of the problem that occurred @@ -36,7 +36,7 @@ public ParseException(String expressionString, int position, String message) { } /** - * Creates a new expression parsing exception. + * Create a new expression parsing exception. * @param position the position in the expression string where the problem occurred * @param message description of the problem that occurred * @param cause the underlying cause of this exception @@ -46,7 +46,7 @@ public ParseException(int position, String message, Throwable cause) { } /** - * Creates a new expression parsing exception. + * Create a new expression parsing exception. * @param position the position in the expression string where the problem occurred * @param message description of the problem that occurred */ diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/SpelEvaluationException.java b/spring-expression/src/main/java/org/springframework/expression/spel/SpelEvaluationException.java index 80a604f690..35f37cd7fa 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/SpelEvaluationException.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/SpelEvaluationException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel; import org.springframework.expression.EvaluationException; @@ -34,7 +35,7 @@ public class SpelEvaluationException extends EvaluationException { public SpelEvaluationException(SpelMessage message, Object... inserts) { - super(message.formatMessage(0, inserts)); // TODO poor position information, can the callers not really supply something? + super(message.formatMessage(0, inserts)); this.message = message; this.inserts = inserts; } @@ -45,54 +46,43 @@ public SpelEvaluationException(int position, SpelMessage message, Object... inse this.inserts = inserts; } - public SpelEvaluationException(int position, Throwable cause, - SpelMessage message, Object... inserts) { - super(position,message.formatMessage(position,inserts),cause); + public SpelEvaluationException(int position, Throwable cause, SpelMessage message, Object... inserts) { + super(position,message.formatMessage(position, inserts),cause); this.message = message; this.inserts = inserts; } public SpelEvaluationException(Throwable cause, SpelMessage message, Object... inserts) { - super(message.formatMessage(0,inserts),cause); + super(message.formatMessage(0, inserts),cause); this.message = message; this.inserts = inserts; } /** - * @return a formatted message with inserts applied + * Set the position in the related expression which gave rise to this exception. */ - @Override - public String getMessage() { - if (this.message != null) { - return this.message.formatMessage(this.position, this.inserts); - } - else { - return super.getMessage(); - } + public void setPosition(int position) { + this.position = position; } /** - * @return the message code + * Return the message code. */ public SpelMessage getMessageCode() { return this.message; } /** - * Set the position in the related expression which gave rise to this exception. - * - * @param position the position in the expression that gave rise to the exception - */ - public void setPosition(int position) { - this.position = position; - } - - /** - * @return the message inserts + * Return the message inserts. */ public Object[] getInserts() { return this.inserts; } + @Override + public String getMessage() { + return getSimpleMessage(); + } + } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java b/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java index 1431554a49..ebfa97600d 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,15 +24,13 @@ * expect particular code numbers rather than particular text, enabling the message text * to more easily be modified and the tests to run successfully in different locales. * - *

    When a message is formatted, it will have this kind of form + *

    When a message is formatted, it will have this kind of form, capturing the prefix + * and the error kind, including the position if it is known: * *

    - * EL1004E: (pos 34): Type cannot be found 'String'
    + * EL1004E:(pos 34): Type cannot be found 'String'
      * 
    * - * The prefix captures the code and the error kind, whilst the position is included - * if it is known. - * * @author Andy Clement * @since 3.0 */ @@ -175,7 +173,7 @@ public enum SpelMessage { "Cannot find terminating \" for string"), NON_TERMINATING_QUOTED_STRING(Kind.ERROR, 1046, - "Cannot find terminating ' for string"), + "Cannot find terminating '' for string"), MISSING_LEADING_ZERO_FOR_NUMBER(Kind.ERROR, 1047, "A real number must be prefixed by zero, it cannot start with just ''.''"), @@ -190,7 +188,7 @@ public enum SpelMessage { "The arguments '(...)' for the constructor call are missing"), RUN_OUT_OF_ARGUMENTS(Kind.ERROR, 1051, - "Unexpected ran out of arguments"), + "Unexpectedly ran out of arguments"), UNABLE_TO_GROW_COLLECTION(Kind.ERROR, 1052, "Unable to grow collection"), @@ -262,7 +260,7 @@ public enum SpelMessage { private final String message; - private SpelMessage(Kind kind, int code, String message) { + SpelMessage(Kind kind, int code, String message) { this.kind = kind; this.code = code; this.message = message; @@ -293,6 +291,6 @@ public String formatMessage(int pos, Object... inserts) { } - public static enum Kind { INFO, WARNING, ERROR } + public enum Kind { INFO, WARNING, ERROR } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/SpelParseException.java b/spring-expression/src/main/java/org/springframework/expression/spel/SpelParseException.java index b498c5f16d..55ec34a84d 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/SpelParseException.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/SpelParseException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.expression.spel; import org.springframework.expression.ParseException; @@ -34,21 +35,21 @@ public class SpelParseException extends ParseException { public SpelParseException(String expressionString, int position, SpelMessage message, Object... inserts) { - super(expressionString, position, message.formatMessage(position,inserts)); + super(expressionString, position, message.formatMessage(position, inserts)); this.position = position; this.message = message; this.inserts = inserts; } public SpelParseException(int position, SpelMessage message, Object... inserts) { - super(position, message.formatMessage(position,inserts)); + super(position, message.formatMessage(position, inserts)); this.position = position; this.message = message; this.inserts = inserts; } public SpelParseException(int position, Throwable cause, SpelMessage message, Object... inserts) { - super(position, message.formatMessage(position,inserts), cause); + super(position, message.formatMessage(position, inserts), cause); this.position = position; this.message = message; this.inserts = inserts; @@ -56,26 +57,22 @@ public SpelParseException(int position, Throwable cause, SpelMessage message, Ob /** - * @return a formatted message with inserts applied - */ - @Override - public String getMessage() { - return (this.message != null ? this.message.formatMessage(this.position, this.inserts) - : super.getMessage()); - } - - /** - * @return the message code + * Return the message code. */ public SpelMessage getMessageCode() { return this.message; } /** - * @return the message inserts + * Return the message inserts. */ public Object[] getInserts() { return this.inserts; } + @Override + public String getMessage() { + return getSimpleMessage(); + } + } diff --git a/spring-web/src/main/java/org/springframework/http/client/SimpleClientHttpRequestFactory.java b/spring-web/src/main/java/org/springframework/http/client/SimpleClientHttpRequestFactory.java index 076e7b37ed..bf982d6e24 100644 --- a/spring-web/src/main/java/org/springframework/http/client/SimpleClientHttpRequestFactory.java +++ b/spring-web/src/main/java/org/springframework/http/client/SimpleClientHttpRequestFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -151,8 +151,7 @@ public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IO */ @Override public AsyncClientHttpRequest createAsyncRequest(URI uri, HttpMethod httpMethod) throws IOException { - Assert.state(this.taskExecutor != null, - "Asynchronous execution requires an AsyncTaskExecutor to be set"); + Assert.state(this.taskExecutor != null, "Asynchronous execution requires TaskExecutor to be set"); HttpURLConnection connection = openConnection(uri.toURL(), this.proxy); prepareConnection(connection, httpMethod.name()); diff --git a/spring-web/src/main/java/org/springframework/web/util/UriTemplate.java b/spring-web/src/main/java/org/springframework/web/util/UriTemplate.java index 43d6094396..becfcefd1e 100644 --- a/spring-web/src/main/java/org/springframework/web/util/UriTemplate.java +++ b/spring-web/src/main/java/org/springframework/web/util/UriTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,14 +44,14 @@ @SuppressWarnings("serial") public class UriTemplate implements Serializable { + private final String uriTemplate; + private final UriComponents uriComponents; private final List variableNames; private final Pattern matchPattern; - private final String uriTemplate; - /** * Construct a new {@code UriTemplate} with the given URI String. @@ -105,7 +105,7 @@ public URI expand(Map uriVariables) { *

    Example: *

          * UriTemplate template = new UriTemplate("http://example.com/hotels/{hotel}/bookings/{booking}");
    -     * System.out.println(template.expand("Rest & Relax", "42));
    +     * System.out.println(template.expand("Rest & Relax", 42));
          * 
    * will print:
    {@code http://example.com/hotels/Rest%20%26%20Relax/bookings/42}
    * @param uriVariableValues the array of URI variables @@ -173,7 +173,6 @@ private static class TemplateInfo { private final Pattern pattern; - private TemplateInfo(List vars, Pattern pattern) { this.variableNames = vars; this.pattern = pattern; @@ -187,7 +186,7 @@ public Pattern getMatchPattern() { return this.pattern; } - private static TemplateInfo parse(String uriTemplate) { + public static TemplateInfo parse(String uriTemplate) { int level = 0; List variableNames = new ArrayList(); StringBuilder pattern = new StringBuilder(); @@ -216,8 +215,7 @@ else if (c == '}') { else { if (idx + 1 == variable.length()) { throw new IllegalArgumentException( - "No custom regular expression specified after ':' " + - "in \"" + variable + "\""); + "No custom regular expression specified after ':' in \"" + variable + "\""); } String regex = variable.substring(idx + 1, variable.length()); pattern.append('('); @@ -238,7 +236,7 @@ else if (c == '}') { } private static String quote(StringBuilder builder) { - return builder.length() != 0 ? Pattern.quote(builder.toString()) : ""; + return (builder.length() > 0 ? Pattern.quote(builder.toString()) : ""); } } diff --git a/spring-web/src/test/java/org/springframework/web/client/AbstractJettyServerTestCase.java b/spring-web/src/test/java/org/springframework/web/client/AbstractJettyServerTestCase.java index adf5449f0a..0b58c74096 100644 --- a/spring-web/src/test/java/org/springframework/web/client/AbstractJettyServerTestCase.java +++ b/spring-web/src/test/java/org/springframework/web/client/AbstractJettyServerTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,13 +33,11 @@ import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; - import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.NetworkConnector; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; - import org.junit.AfterClass; import org.junit.BeforeClass; @@ -56,11 +54,12 @@ public class AbstractJettyServerTestCase { protected static final String helloWorld = "H\u00e9llo W\u00f6rld"; - protected static final MediaType textContentType = new MediaType("text", "plain", - Collections.singletonMap("charset", "UTF-8")); + protected static final MediaType textContentType = + new MediaType("text", "plain", Collections.singletonMap("charset", "UTF-8")); + + protected static final MediaType jsonContentType = + new MediaType("application", "json", Collections.singletonMap("charset", "UTF-8")); - protected static final MediaType jsonContentType = new MediaType("application", - "json", Collections.singletonMap("charset", "utf-8")); private static Server jettyServer; @@ -71,7 +70,6 @@ public class AbstractJettyServerTestCase { @BeforeClass public static void startJettyServer() throws Exception { - // Let server pick its own random, available port. jettyServer = new Server(0); @@ -114,39 +112,41 @@ public static void stopJettyServer() throws Exception { } } + /** Servlet that sets the given status code. */ @SuppressWarnings("serial") private static class StatusCodeServlet extends GenericServlet { private final int sc; - private StatusCodeServlet(int sc) { + public StatusCodeServlet(int sc) { this.sc = sc; } @Override - public void service(ServletRequest request, ServletResponse response) throws - ServletException, IOException { + public void service(ServletRequest request, ServletResponse response) throws IOException { ((HttpServletResponse) response).setStatus(sc); } } + /** Servlet that returns an error message for a given status code. */ @SuppressWarnings("serial") private static class ErrorServlet extends GenericServlet { private final int sc; - private ErrorServlet(int sc) { + public ErrorServlet(int sc) { this.sc = sc; } @Override - public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException { + public void service(ServletRequest request, ServletResponse response) throws IOException { ((HttpServletResponse) response).sendError(sc); } } + @SuppressWarnings("serial") private static class GetServlet extends HttpServlet { @@ -154,14 +154,13 @@ private static class GetServlet extends HttpServlet { private final MediaType contentType; - private GetServlet(byte[] buf, MediaType contentType) { + public GetServlet(byte[] buf, MediaType contentType) { this.buf = buf; this.contentType = contentType; } @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { if (contentType != null) { response.setContentType(contentType.toString()); } @@ -170,10 +169,11 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) } } + @SuppressWarnings("serial") private static class PostServlet extends HttpServlet { - private final String s; + private final String content; private final String location; @@ -181,20 +181,19 @@ private static class PostServlet extends HttpServlet { private final MediaType contentType; - private PostServlet(String s, String location, byte[] buf, MediaType contentType) { - this.s = s; + public PostServlet(String content, String location, byte[] buf, MediaType contentType) { + this.content = content; this.location = location; this.buf = buf; this.contentType = contentType; } @Override - protected void doPost(HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { assertTrue("Invalid request content-length", request.getContentLength() > 0); assertNotNull("No content-type", request.getContentType()); String body = FileCopyUtils.copyToString(request.getReader()); - assertEquals("Invalid request body", s, body); + assertEquals("Invalid request body", content, body); response.setStatus(HttpServletResponse.SC_CREATED); response.setHeader("Location", baseUrl + location); response.setContentLength(buf.length); @@ -203,6 +202,7 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) } } + @SuppressWarnings("serial") private static class JsonPostServlet extends HttpServlet { @@ -210,14 +210,13 @@ private static class JsonPostServlet extends HttpServlet { private final MediaType contentType; - private JsonPostServlet(String location, MediaType contentType) { + public JsonPostServlet(String location, MediaType contentType) { this.location = location; this.contentType = contentType; } @Override - protected void doPost(HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { assertTrue("Invalid request content-length", request.getContentLength() > 0); assertNotNull("No content-type", request.getContentType()); String body = FileCopyUtils.copyToString(request.getReader()); @@ -230,18 +229,18 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) } } + @SuppressWarnings("serial") private static class PutServlet extends HttpServlet { private final String s; - private PutServlet(String s, byte[] buf, MediaType contentType) { + public PutServlet(String s, byte[] buf, MediaType contentType) { this.s = s; } @Override - protected void doPut(HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { + protected void doPut(HttpServletRequest request, HttpServletResponse response) throws IOException { assertTrue("Invalid request content-length", request.getContentLength() > 0); assertNotNull("No content-type", request.getContentType()); String body = FileCopyUtils.copyToString(request.getReader()); @@ -250,17 +249,19 @@ protected void doPut(HttpServletRequest request, HttpServletResponse response) } } + @SuppressWarnings("serial") private static class UriServlet extends HttpServlet { @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { resp.setContentType("text/plain"); resp.setCharacterEncoding("utf-8"); resp.getWriter().write(req.getRequestURI()); } } + @SuppressWarnings("serial") private static class MultipartServlet extends HttpServlet { @@ -300,13 +301,13 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws S } } + @SuppressWarnings("serial") private static class FormServlet extends HttpServlet { @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - assertEquals(MediaType.APPLICATION_FORM_URLENCODED_VALUE, - req.getContentType()); + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { + assertEquals(MediaType.APPLICATION_FORM_URLENCODED_VALUE, req.getContentType()); Map parameters = req.getParameterMap(); assertEquals(2, parameters.size()); @@ -322,15 +323,14 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws S } } + @SuppressWarnings("serial") private static class DeleteServlet extends HttpServlet { @Override - protected void doDelete(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException { resp.setStatus(200); } - } } diff --git a/spring-web/src/test/java/org/springframework/web/client/AsyncRestTemplateIntegrationTests.java b/spring-web/src/test/java/org/springframework/web/client/AsyncRestTemplateIntegrationTests.java index 7788956378..56bcdd9f9e 100644 --- a/spring-web/src/test/java/org/springframework/web/client/AsyncRestTemplateIntegrationTests.java +++ b/spring-web/src/test/java/org/springframework/web/client/AsyncRestTemplateIntegrationTests.java @@ -114,7 +114,6 @@ public void getNoResponse() throws Exception { assertNull("Invalid content", entity.getBody()); } - @Test public void getNoContentTypeHeader() throws Exception { Future> futureEntity = template.getForEntity(baseUrl + "/get/nocontenttype", byte[].class); @@ -122,7 +121,6 @@ public void getNoContentTypeHeader() throws Exception { assertArrayEquals("Invalid content", helloWorld.getBytes("UTF-8"), responseEntity.getBody()); } - @Test public void getNoContent() throws Exception { Future> responseFuture = template.getForEntity(baseUrl + "/status/nocontent", String.class); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/ResourceServlet.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/ResourceServlet.java index 9905909acc..446c2bf1fc 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/ResourceServlet.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/ResourceServlet.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -175,8 +175,8 @@ protected void initServletBean() { } /** - * Return a PathMatcher to use for matching the "allowedResources" URL pattern. - * Default is AntPathMatcher. + * Return a {@link PathMatcher} to use for matching the "allowedResources" URL pattern. + *

    The default is {@link AntPathMatcher}. * @see #setAllowedResources * @see org.springframework.util.AntPathMatcher */ @@ -191,9 +191,9 @@ protected PathMatcher getPathMatcher() { */ @Override protected final void doGet(HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { + throws ServletException, IOException { - // determine URL of resource to include + // Determine URL of resource to include... String resourceUrl = determineResourceUrl(request); if (resourceUrl != null) { @@ -220,7 +220,7 @@ protected final void doGet(HttpServletRequest request, HttpServletResponse respo } } - // no resource URL specified -> try to include default URL. + // No resource URL specified -> try to include default URL. else if (!includeDefaultUrl(request, response)) { throw new ServletException("No target resource URL found for request"); } @@ -265,23 +265,23 @@ private boolean includeDefaultUrl(HttpServletRequest request, HttpServletRespons * @throws IOException if thrown by the RequestDispatcher */ private void doInclude(HttpServletRequest request, HttpServletResponse response, String resourceUrl) - throws ServletException, IOException { + throws ServletException, IOException { if (this.contentType != null) { response.setContentType(this.contentType); } - String[] resourceUrls = - StringUtils.tokenizeToStringArray(resourceUrl, RESOURCE_URL_DELIMITERS); - for (int i = 0; i < resourceUrls.length; i++) { - // check whether URL matches allowed resources - if (this.allowedResources != null && !this.pathMatcher.match(this.allowedResources, resourceUrls[i])) { - throw new ServletException("Resource [" + resourceUrls[i] + + + String[] resourceUrls = StringUtils.tokenizeToStringArray(resourceUrl, RESOURCE_URL_DELIMITERS); + for (String url : resourceUrls) { + // Check whether URL matches allowed resources + if (this.allowedResources != null && !this.pathMatcher.match(this.allowedResources, url)) { + throw new ServletException("Resource [" + url + "] does not match allowed pattern [" + this.allowedResources + "]"); } if (logger.isDebugEnabled()) { - logger.debug("Including resource [" + resourceUrls[i] + "]"); + logger.debug("Including resource [" + url + "]"); } - RequestDispatcher rd = request.getRequestDispatcher(resourceUrls[i]); + RequestDispatcher rd = request.getRequestDispatcher(url); rd.include(request, response); } } @@ -309,8 +309,8 @@ protected final long getLastModified(HttpServletRequest request) { if (resourceUrl != null) { String[] resourceUrls = StringUtils.tokenizeToStringArray(resourceUrl, RESOURCE_URL_DELIMITERS); long latestTimestamp = -1; - for (int i = 0; i < resourceUrls.length; i++) { - long timestamp = getFileTimestamp(resourceUrls[i]); + for (String url : resourceUrls) { + long timestamp = getFileTimestamp(url); if (timestamp > latestTimestamp) { latestTimestamp = timestamp; } @@ -336,8 +336,10 @@ protected long getFileTimestamp(String resourceUrl) { return lastModifiedTime; } catch (IOException ex) { - logger.warn("Couldn't retrieve last-modified timestamp of [" + resource + - "] - using ResourceServlet startup time"); + if (logger.isWarnEnabled()) { + logger.warn("Couldn't retrieve last-modified timestamp of " + resource + + " - using ResourceServlet startup time"); + } return -1; } } diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/WebSocketMessage.java b/spring-websocket/src/main/java/org/springframework/web/socket/WebSocketMessage.java index 5881ed0c34..fd5383d87e 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/WebSocketMessage.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/WebSocketMessage.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,11 +25,10 @@ public interface WebSocketMessage { /** - * Returns the message payload. This will never be {@code null}. + * Return the message payload (never {@code null}). */ T getPayload(); - /** * Return the number of bytes contained in the message. */ diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractSockJsSession.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractSockJsSession.java index 515f55333f..5905bf5819 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractSockJsSession.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/session/AbstractSockJsSession.java @@ -80,10 +80,10 @@ private enum State {NEW, OPEN, CLOSED} private static final Set disconnectedClientExceptions; static { - Set set = new HashSet(2); - set.add("ClientAbortException"); // Tomcat - set.add("EOFException"); // Tomcat - set.add("EofException"); // Jetty + Set set = new HashSet(4); + set.add("ClientAbortException"); // Tomcat + set.add("EOFException"); // Tomcat + set.add("EofException"); // Jetty // java.io.IOException "Broken pipe" on WildFly, Glassfish (already covered) disconnectedClientExceptions = Collections.unmodifiableSet(set); } @@ -208,7 +208,7 @@ public final void close(CloseStatus status) throws IOException { writeFrameInternal(SockJsFrame.closeFrame(status.getCode(), status.getReason())); } catch (Throwable ex) { - logger.debug("Failure while send SockJS close frame", ex); + logger.debug("Failure while sending SockJS close frame", ex); } } updateLastActiveTime(); From 2a2d5aa6d13ae5322b1b671543153e7075a6bb9d Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 1 Dec 2016 18:54:04 +0100 Subject: [PATCH 326/344] Class identity comparisons wherever possible Issue: SPR-12926 --- .../support/FormattingConversionService.java | 9 ++++---- .../core/convert/TypeDescriptor.java | 22 +++++++++---------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/format/support/FormattingConversionService.java b/spring-context/src/main/java/org/springframework/format/support/FormattingConversionService.java index 6e37317b6f..5d3daf888b 100644 --- a/spring-context/src/main/java/org/springframework/format/support/FormattingConversionService.java +++ b/spring-context/src/main/java/org/springframework/format/support/FormattingConversionService.java @@ -226,6 +226,7 @@ private class AnnotationPrinterConverter implements ConditionalGenericConverter public AnnotationPrinterConverter(Class annotationType, AnnotationFormatterFactory annotationFormatterFactory, Class fieldType) { + this.annotationType = annotationType; this.annotationFormatterFactory = annotationFormatterFactory; this.fieldType = fieldType; @@ -279,6 +280,7 @@ private class AnnotationParserConverter implements ConditionalGenericConverter { public AnnotationParserConverter(Class annotationType, AnnotationFormatterFactory annotationFormatterFactory, Class fieldType) { + this.annotationType = annotationType; this.annotationFormatterFactory = annotationFormatterFactory; this.fieldType = fieldType; @@ -345,16 +347,13 @@ public boolean equals(Object other) { if (this == other) { return true; } - if (!(other instanceof AnnotationConverterKey)) { - return false; - } AnnotationConverterKey otherKey = (AnnotationConverterKey) other; - return (this.annotation.equals(otherKey.annotation) && this.fieldType.equals(otherKey.fieldType)); + return (this.fieldType == otherKey.fieldType && this.annotation.equals(otherKey.annotation)); } @Override public int hashCode() { - return (this.annotation.hashCode() + 29 * this.fieldType.hashCode()); + return (this.fieldType.hashCode() * 29 + this.annotation.hashCode()); } } diff --git a/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java b/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java index 2d03ed810b..ec016f04f5 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java +++ b/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java @@ -80,7 +80,6 @@ public class TypeDescriptor implements Serializable { * @param methodParameter the method parameter */ public TypeDescriptor(MethodParameter methodParameter) { - Assert.notNull(methodParameter, "MethodParameter must not be null"); this.resolvableType = ResolvableType.forMethodParameter(methodParameter); this.type = this.resolvableType.resolve(methodParameter.getParameterType()); this.annotations = nullSafeAnnotations(methodParameter.getParameterIndex() == -1 ? @@ -93,7 +92,6 @@ public TypeDescriptor(MethodParameter methodParameter) { * @param field the field */ public TypeDescriptor(Field field) { - Assert.notNull(field, "Field must not be null"); this.resolvableType = ResolvableType.forField(field); this.type = this.resolvableType.resolve(field.getType()); this.annotations = nullSafeAnnotations(field.getAnnotations()); @@ -451,31 +449,31 @@ private TypeDescriptor narrow(Object value, TypeDescriptor typeDescriptor) { } @Override - public boolean equals(Object obj) { - if (this == obj) { + public boolean equals(Object other) { + if (this == other) { return true; } - if (!(obj instanceof TypeDescriptor)) { + if (!(other instanceof TypeDescriptor)) { return false; } - TypeDescriptor other = (TypeDescriptor) obj; - if (!ObjectUtils.nullSafeEquals(this.type, other.type)) { + TypeDescriptor otherDesc = (TypeDescriptor) other; + if (getType() != otherDesc.getType()) { return false; } - if (getAnnotations().length != other.getAnnotations().length) { + if (getAnnotations().length != otherDesc.getAnnotations().length) { return false; } for (Annotation ann : getAnnotations()) { - if (!other.hasAnnotation(ann.annotationType())) { + if (!otherDesc.hasAnnotation(ann.annotationType())) { return false; } } if (isCollection() || isArray()) { - return ObjectUtils.nullSafeEquals(getElementTypeDescriptor(), other.getElementTypeDescriptor()); + return ObjectUtils.nullSafeEquals(getElementTypeDescriptor(), otherDesc.getElementTypeDescriptor()); } else if (isMap()) { - return ObjectUtils.nullSafeEquals(getMapKeyTypeDescriptor(), other.getMapKeyTypeDescriptor()) && - ObjectUtils.nullSafeEquals(getMapValueTypeDescriptor(), other.getMapValueTypeDescriptor()); + return (ObjectUtils.nullSafeEquals(getMapKeyTypeDescriptor(), otherDesc.getMapKeyTypeDescriptor()) && + ObjectUtils.nullSafeEquals(getMapValueTypeDescriptor(), otherDesc.getMapValueTypeDescriptor())); } else { return true; From d518ac65ed01651ba83e38cc51c1a34f6da5cf77 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 1 Dec 2016 18:54:28 +0100 Subject: [PATCH 327/344] Upgrade to Tomcat 8.0.39 and Joda-Time 2.9.6 --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 4abc9b424f..dd68ea7f8e 100644 --- a/build.gradle +++ b/build.gradle @@ -51,7 +51,7 @@ configure(allprojects) { project -> ext.jasperreportsVersion = "6.2.1" ext.javamailVersion = "1.5.5" ext.jettyVersion = "9.3.9.v20160517" - ext.jodaVersion = "2.9.5" + ext.jodaVersion = "2.9.6" ext.jrubyVersion = "1.7.26" // JRuby 9000 only supported through JSR-223 (StandardScriptFactory) ext.jtaVersion = "1.2" ext.junitVersion = "4.12" @@ -68,7 +68,7 @@ configure(allprojects) { project -> ext.testngVersion = "6.9.10" ext.tiles2Version = "2.2.2" ext.tiles3Version = "3.0.5" - ext.tomcatVersion = "8.0.38" + ext.tomcatVersion = "8.0.39" ext.tyrusVersion = "1.3.5" // constrained by WebLogic 12.1.3 support ext.undertowVersion = "1.3.25.Final" ext.xmlunitVersion = "1.6" From de3734ac3bad5c3f1753a4b5a92ca34c43de501f Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 1 Dec 2016 23:05:11 +0100 Subject: [PATCH 328/344] Polishing --- ...tractNamedValueMethodArgumentResolver.java | 23 ++++++++++++------- .../RequestParamMethodArgumentResolver.java | 4 ++-- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/method/annotation/AbstractNamedValueMethodArgumentResolver.java b/spring-web/src/main/java/org/springframework/web/method/annotation/AbstractNamedValueMethodArgumentResolver.java index 78a3a34500..840bab1983 100644 --- a/spring-web/src/main/java/org/springframework/web/method/annotation/AbstractNamedValueMethodArgumentResolver.java +++ b/spring-web/src/main/java/org/springframework/web/method/annotation/AbstractNamedValueMethodArgumentResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,6 +38,7 @@ * Abstract base class for resolving method arguments from a named value. * Request parameters, request headers, and path variables are examples of named * values. Each may have a name, a required flag, and a default value. + * *

    Subclasses define how to do the following: *

      *
    • Obtain named value information for a method parameter @@ -45,9 +46,11 @@ *
    • Handle missing argument values when argument values are required *
    • Optionally handle a resolved value *
    + * *

    A default value string can contain ${...} placeholders and Spring Expression * Language #{...} expressions. For this to work a * {@link ConfigurableBeanFactory} must be supplied to the class constructor. + * *

    A {@link WebDataBinder} is created to apply type conversion to the resolved * argument value if it doesn't match the method parameter type. * @@ -61,7 +64,8 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle private final BeanExpressionContext expressionContext; - private Map namedValueInfoCache = new ConcurrentHashMap(256); + private final Map namedValueInfoCache = + new ConcurrentHashMap(256); public AbstractNamedValueMethodArgumentResolver() { @@ -76,7 +80,8 @@ public AbstractNamedValueMethodArgumentResolver() { */ public AbstractNamedValueMethodArgumentResolver(ConfigurableBeanFactory beanFactory) { this.configurableBeanFactory = beanFactory; - this.expressionContext = (beanFactory != null ? new BeanExpressionContext(beanFactory, new RequestScope()) : null); + this.expressionContext = + (beanFactory != null ? new BeanExpressionContext(beanFactory, new RequestScope()) : null); } @@ -151,7 +156,8 @@ private NamedValueInfo updateNamedValueInfo(MethodParameter parameter, NamedValu if (info.name.length() == 0) { name = parameter.getParameterName(); if (name == null) { - throw new IllegalArgumentException("Name for argument type [" + parameter.getParameterType().getName() + + throw new IllegalArgumentException( + "Name for argument type [" + parameter.getParameterType().getName() + "] not available, and parameter name information not found in class file either."); } } @@ -160,18 +166,19 @@ private NamedValueInfo updateNamedValueInfo(MethodParameter parameter, NamedValu } /** - * Resolves the given parameter type and value name into an argument value. + * Resolve the given parameter type and value name into an argument value. * @param name the name of the value being resolved * @param parameter the method parameter to resolve to an argument value * @param request the current request - * @return the resolved argument. May be {@code null} + * @return the resolved argument (may be {@code null}) * @throws Exception in case of errors */ protected abstract Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception; /** - * Resolves the given default value into an argument value. + * Resolve the given annotation-specified value, + * potentially containing placeholders and expressions. */ private Object resolveDefaultValue(String defaultValue) { if (this.configurableBeanFactory == null) { @@ -215,7 +222,7 @@ else if (paramType.isPrimitive()) { * @param arg the resolved argument value * @param name the argument name * @param parameter the argument parameter type - * @param mavContainer the {@link ModelAndViewContainer}, which may be {@code null} + * @param mavContainer the {@link ModelAndViewContainer} (may be {@code null}) * @param webRequest the current request */ protected void handleResolvedValue(Object arg, String name, MethodParameter parameter, 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 c481b152f6..8b438f6bc0 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 @@ -85,7 +85,7 @@ public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethod /** * @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 it isn't annotated, the + * is treated as a request parameter even if it isn't annotated, the * request parameter name is derived from the method parameter name. */ public RequestParamMethodArgumentResolver(boolean useDefaultResolution) { @@ -98,7 +98,7 @@ public RequestParamMethodArgumentResolver(boolean useDefaultResolution) { * values are not expected to contain expressions * @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 it isn't annotated, the + * is treated as a request parameter even if it isn't annotated, the * request parameter name is derived from the method parameter name. */ public RequestParamMethodArgumentResolver(ConfigurableBeanFactory beanFactory, boolean useDefaultResolution) { From f64cf779da59766e5944eacd89783b91dba6802f Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 8 Dec 2016 18:11:21 +0100 Subject: [PATCH 329/344] Consistent use of "URI variables" terminology Issue: SPR-14969 (cherry picked from commit 71977e8) --- src/asciidoc/integration.adoc | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/asciidoc/integration.adoc b/src/asciidoc/integration.adoc index 0486893195..358210520c 100644 --- a/src/asciidoc/integration.adoc +++ b/src/asciidoc/integration.adoc @@ -1014,21 +1014,21 @@ RestTemplate has an asynchronous counter-part: see <>. | HEAD | {api-spring-framework}/web/client/RestTemplate.html#headForHeaders(String,%20Object...)[headForHeaders(String - url, String... urlVariables)] + url, String... uriVariables)] | OPTIONS | {api-spring-framework}/web/client/RestTemplate.html#optionsForAllow(String,%20Object...)[optionsForAllow(String - url, String... urlVariables)] + url, String... uriVariables)] | POST | {api-spring-framework}/web/client/RestTemplate.html#postForLocation(String,%20Object,%20Object...)[postForLocation(String - url, Object request, String... urlVariables)] + url, Object request, String... uriVariables)] {api-spring-framework}/web/client/RestTemplate.html#postForObject(java.lang.String,%20java.lang.Object,%20java.lang.Class,%20java.lang.String...)[postForObject(String url, Object request, Class responseType, String... uriVariables)] | PUT | {api-spring-framework}/web/client/RestTemplate.html#put(String,%20Object,%20Object...)[put(String - url, Object request, String...urlVariables)] + url, Object request, String...uriVariables)] | PATCH and others | {api-spring-framework}/web/client/RestTemplate.html#exchange(java.lang.String,%20org.springframework.http.HttpMethod,%20org.springframework.http.HttpEntity,%20java.lang.Class,%20java.lang.Object...)[exchange] @@ -1063,8 +1063,8 @@ template are `ByteArrayHttpMessageConverter`, `StringHttpMessageConverter`, defaults using the `messageConverters()` bean property as would be required if using the `MarshallingHttpMessageConverter` or `MappingJackson2HttpMessageConverter`. -Each method takes URI template arguments in two forms, either as a `String` variable -length argument or a `Map`. For example, +Each method takes URI template arguments in two forms, either as a `String` +variable-length argument or a `Map`. For example, [source,java,indent=0] [subs="verbatim,quotes"] @@ -1073,7 +1073,7 @@ length argument or a `Map`. For example, "http://example.com/hotels/{hotel}/bookings/{booking}", String.class,"42", "21"); ---- -using variable length arguments and +using variable-length arguments and [source,java,indent=0] [subs="verbatim,quotes"] @@ -1146,9 +1146,9 @@ method is invoked. [subs="verbatim,quotes"] ---- public T execute(String url, HttpMethod method, RequestCallback requestCallback, - ResponseExtractor responseExtractor, String... urlVariables) + ResponseExtractor responseExtractor, String... uriVariables) - // also has an overload with urlVariables as a Map. + // also has an overload with uriVariables as a Map. ---- The `RequestCallback` interface is defined as @@ -1173,7 +1173,7 @@ other method arguments. For each of the main HTTP methods, the `RestTemplate` provides variants that either take a String URI or `java.net.URI` as the first argument. -The String URI variants accept template arguments as a String variable length argument +The String URI variants accept template arguments as a String variable-length argument or as a `Map`. They also assume the URL String is not encoded and needs to be encoded. For example the following: From c22cad145bc9194641d92dbeab01ff67a0ca9b13 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 8 Dec 2016 18:16:48 +0100 Subject: [PATCH 330/344] SpEL docs: supported literals, null comparisons Issue: SPR-14361 Issue: SPR-14987 (cherry picked from commit 5ccc8e3) --- src/asciidoc/core-expressions.adoc | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/asciidoc/core-expressions.adoc b/src/asciidoc/core-expressions.adoc index ec15a381e7..61fa575b51 100644 --- a/src/asciidoc/core-expressions.adoc +++ b/src/asciidoc/core-expressions.adoc @@ -220,6 +220,7 @@ example for Spring bean or Spring Web Flow definitions. In this case, the parser evaluation context, root object and any predefined variables are all set up implicitly, requiring the user to specify nothing other than the expressions. ==== + As a final introductory example, the use of a boolean operator is shown using the Inventor object in the previous example. @@ -583,12 +584,13 @@ Autowired methods and constructors can also use the `@Value` annotation. [[expressions-ref-literal]] === Literal expressions -The types of literal expressions supported are strings, dates, numeric values (int, -real, and hex), boolean and null. Strings are delimited by single quotes. To put a -single quote itself in a string use two single quote characters. The following listing -shows simple usage of literals. Typically they would not be used in isolation like this, -but as part of a more complex expression, for example using a literal on one side of a -logical comparison operator. +The types of literal expressions supported are strings, numeric values (int, real, hex), +boolean and null. Strings are delimited by single quotes. To put a single quote itself +in a string, use two single quote characters. + +The following listing shows simple usage of literals. Typically they would not be used +in isolation like this but rather as part of a more complex expression, for example +using a literal on one side of a logical comparison operator. [source,java,indent=0] [subs="verbatim,quotes"] @@ -777,6 +779,17 @@ and greater than or equal are supported using standard operator notation. boolean trueValue = parser.parseExpression("'black' < 'block'").getValue(Boolean.class); ---- +[NOTE] +==== +Greater/less-than comparisons against `null` follow a simple rule: `null` is treated as +nothing here (i.e. NOT as zero). As a consequence, any other value is always greater +than `null` (`X > null` is always `true`) and no other value is ever less than nothing +(`X < null` is always `false`). + +If you prefer numeric comparisons instead, please avoid number-based `null` comparisons +in favor of comparisons against zero (e.g. `X > 0` or `X < 0`). +==== + In addition to standard relational operators SpEL supports the `instanceof` and regular expression based `matches` operator. From 3556d4be64ddb1c2e64604fc8b3bfd88b8d9ef88 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 8 Dec 2016 18:28:03 +0100 Subject: [PATCH 331/344] Revised forClass argument names Issue: SPR-14976 (cherry picked from commit ced7503) --- .../springframework/core/ResolvableType.java | 62 +++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/ResolvableType.java b/spring-core/src/main/java/org/springframework/core/ResolvableType.java index c6aafdce82..b18ea08f11 100644 --- a/spring-core/src/main/java/org/springframework/core/ResolvableType.java +++ b/spring-core/src/main/java/org/springframework/core/ResolvableType.java @@ -174,8 +174,8 @@ private ResolvableType( * Avoids all {@code instanceof} checks in order to create a straight {@link Class} wrapper. * @since 4.2 */ - private ResolvableType(Class sourceClass) { - this.resolved = (sourceClass != null ? sourceClass : Object.class); + private ResolvableType(Class clazz) { + this.resolved = (clazz != null ? clazz : Object.class); this.type = this.resolved; this.typeProvider = null; this.variableResolver = null; @@ -408,7 +408,7 @@ public ResolvableType asMap() { * {@link #getSuperType() supertype} and {@link #getInterfaces() interface} * hierarchies to find a match, returning {@link #NONE} if this type does not * implement or extend the specified class. - * @param type the required class type + * @param type the required type (typically narrowed) * @return a {@link ResolvableType} representing this object as the specified * type, or {@link #NONE} if not resolvable as that type * @see #asCollection() @@ -923,14 +923,14 @@ public String toString() { * Return a {@link ResolvableType} for the specified {@link Class}, * using the full generic type information for assignability checks. * For example: {@code ResolvableType.forClass(MyArrayList.class)}. - * @param sourceClass the source class ({@code null} is semantically + * @param clazz the class to introspect ({@code null} is semantically * equivalent to {@code Object.class} for typical use cases here} * @return a {@link ResolvableType} for the specified class * @see #forClass(Class, Class) * @see #forClassWithGenerics(Class, Class...) */ - public static ResolvableType forClass(Class sourceClass) { - return new ResolvableType(sourceClass); + public static ResolvableType forClass(Class clazz) { + return new ResolvableType(clazz); } /** @@ -938,15 +938,15 @@ public static ResolvableType forClass(Class sourceClass) { * assignability checks against the raw class only (analogous to * {@link Class#isAssignableFrom}, which this serves as a wrapper for. * For example: {@code ResolvableType.forRawClass(List.class)}. - * @param sourceClass the source class ({@code null} is semantically + * @param clazz the class to introspect ({@code null} is semantically * equivalent to {@code Object.class} for typical use cases here} * @return a {@link ResolvableType} for the specified class * @since 4.2 * @see #forClass(Class) * @see #getRawClass() */ - public static ResolvableType forRawClass(Class sourceClass) { - return new ResolvableType(sourceClass) { + public static ResolvableType forRawClass(Class clazz) { + return new ResolvableType(clazz) { @Override public boolean isAssignableFrom(Class other) { return ClassUtils.isAssignable(getRawClass(), other); @@ -960,50 +960,50 @@ public boolean isAssignableFrom(ResolvableType other) { } /** - * Return a {@link ResolvableType} for the specified {@link Class} - * with a given implementation. + * Return a {@link ResolvableType} for the specified base type + * (interface or base class) with a given implementation class. * For example: {@code ResolvableType.forClass(List.class, MyArrayList.class)}. - * @param sourceClass the source class (must not be {@code null} + * @param baseType the base type (must not be {@code null}) * @param implementationClass the implementation class - * @return a {@link ResolvableType} for the specified class backed by the given - * implementation class + * @return a {@link ResolvableType} for the specified base type backed by the + * given implementation class * @see #forClass(Class) * @see #forClassWithGenerics(Class, Class...) */ - public static ResolvableType forClass(Class sourceClass, Class implementationClass) { - Assert.notNull(sourceClass, "Source class must not be null"); - ResolvableType asType = forType(implementationClass).as(sourceClass); - return (asType == NONE ? forType(sourceClass) : asType); + public static ResolvableType forClass(Class baseType, Class implementationClass) { + Assert.notNull(baseType, "Base type must not be null"); + ResolvableType asType = forType(implementationClass).as(baseType); + return (asType == NONE ? forType(baseType) : asType); } /** * Return a {@link ResolvableType} for the specified {@link Class} with pre-declared generics. - * @param sourceClass the source class + * @param clazz the class (or interface) to introspect * @param generics the generics of the class * @return a {@link ResolvableType} for the specific class and generics * @see #forClassWithGenerics(Class, ResolvableType...) */ - public static ResolvableType forClassWithGenerics(Class sourceClass, Class... generics) { - Assert.notNull(sourceClass, "Source class must not be null"); - Assert.notNull(generics, "Generics must not be null"); + public static ResolvableType forClassWithGenerics(Class clazz, Class... generics) { + Assert.notNull(clazz, "Class must not be null"); + Assert.notNull(generics, "Generics array must not be null"); ResolvableType[] resolvableGenerics = new ResolvableType[generics.length]; for (int i = 0; i < generics.length; i++) { resolvableGenerics[i] = forClass(generics[i]); } - return forClassWithGenerics(sourceClass, resolvableGenerics); + return forClassWithGenerics(clazz, resolvableGenerics); } /** * Return a {@link ResolvableType} for the specified {@link Class} with pre-declared generics. - * @param sourceClass the source class + * @param clazz the class (or interface) to introspect * @param generics the generics of the class * @return a {@link ResolvableType} for the specific class and generics * @see #forClassWithGenerics(Class, Class...) */ - public static ResolvableType forClassWithGenerics(Class sourceClass, ResolvableType... generics) { - Assert.notNull(sourceClass, "Source class must not be null"); - Assert.notNull(generics, "Generics must not be null"); - TypeVariable[] variables = sourceClass.getTypeParameters(); + public static ResolvableType forClassWithGenerics(Class clazz, ResolvableType... generics) { + Assert.notNull(clazz, "Class must not be null"); + Assert.notNull(generics, "Generics array must not be null"); + TypeVariable[] variables = clazz.getTypeParameters(); Assert.isTrue(variables.length == generics.length, "Mismatched number of generics specified"); Type[] arguments = new Type[generics.length]; @@ -1013,7 +1013,7 @@ public static ResolvableType forClassWithGenerics(Class sourceClass, Resolvab arguments[i] = (argument != null ? argument : variables[i]); } - ParameterizedType syntheticType = new SyntheticParameterizedType(sourceClass, arguments); + ParameterizedType syntheticType = new SyntheticParameterizedType(clazz, arguments); return forType(syntheticType, new TypeVariablesVariableResolver(variables, generics)); } @@ -1077,8 +1077,8 @@ public static ResolvableType forField(Field field, Class implementationClass) */ public static ResolvableType forField(Field field, ResolvableType implementationType) { Assert.notNull(field, "Field must not be null"); - implementationType = (implementationType == null ? NONE : implementationType); - ResolvableType owner = implementationType.as(field.getDeclaringClass()); + ResolvableType owner = (implementationType != null ? implementationType : NONE); + owner = owner.as(field.getDeclaringClass()); return forType(null, new FieldTypeProvider(field), owner.asVariableResolver()); } From c732f38b85c8be6d33903c4412fbd11ef9445385 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 9 Dec 2016 15:01:21 +0100 Subject: [PATCH 332/344] ImportRegistry properly tracks excluded superclasses Issue: SPR-14972 (cherry picked from commit dd3c370) --- ...onfigurationClassBeanDefinitionReader.java | 2 +- .../annotation/ConfigurationClassParser.java | 17 +++-- .../annotation/ConfigurationCondition.java | 4 +- .../context/annotation/ImportRegistry.java | 4 +- .../EnableTransactionManagementTests.java | 75 +++++++++++++++++-- 5 files changed, 81 insertions(+), 21 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java index 9031872e5b..9868877986 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java @@ -129,7 +129,7 @@ private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configC if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) { this.registry.removeBeanDefinition(beanName); } - this.importRegistry.removeImportingClassFor(configClass.getMetadata().getClassName()); + this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName()); return; } diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java index eebd2e3f11..cfadafd95f 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java @@ -652,22 +652,23 @@ public void registerImport(AnnotationMetadata importingClass, String importedCla } @Override - public void removeImportingClassFor(String importedClass) { + public AnnotationMetadata getImportingClassFor(String importedClass) { + List list = this.imports.get(importedClass); + return (!CollectionUtils.isEmpty(list) ? list.get(list.size() - 1) : null); + } + + @Override + public void removeImportingClass(String importingClass) { for (List list : this.imports.values()) { for (Iterator iterator = list.iterator(); iterator.hasNext();) { - if (iterator.next().getClassName().equals(importedClass)) { + if (iterator.next().getClassName().equals(importingClass)) { iterator.remove(); + break; } } } } - @Override - public AnnotationMetadata getImportingClassFor(String importedClass) { - List list = this.imports.get(importedClass); - return (!CollectionUtils.isEmpty(list) ? list.get(list.size() - 1) : null); - } - /** * Given a stack containing (in order) *

      diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationCondition.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationCondition.java index 8bd3d044b3..c46f8a030b 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationCondition.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,7 +38,7 @@ public interface ConfigurationCondition extends Condition { /** * The various configuration phases where the condition could be evaluated. */ - public static enum ConfigurationPhase { + enum ConfigurationPhase { /** * The {@link Condition} should be evaluated as a {@code @Configuration} diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ImportRegistry.java b/spring-context/src/main/java/org/springframework/context/annotation/ImportRegistry.java index 2d20ab17d7..c614d55d8d 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ImportRegistry.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ImportRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,6 +26,6 @@ interface ImportRegistry { AnnotationMetadata getImportingClassFor(String importedClass); - void removeImportingClassFor(String importedClass); + void removeImportingClass(String importingClass); } diff --git a/spring-tx/src/test/java/org/springframework/transaction/annotation/EnableTransactionManagementTests.java b/spring-tx/src/test/java/org/springframework/transaction/annotation/EnableTransactionManagementTests.java index d413269df8..e696b8a7aa 100644 --- a/spring-tx/src/test/java/org/springframework/transaction/annotation/EnableTransactionManagementTests.java +++ b/spring-tx/src/test/java/org/springframework/transaction/annotation/EnableTransactionManagementTests.java @@ -28,7 +28,11 @@ import org.springframework.context.annotation.AdviceMode; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.ConfigurationCondition; +import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.stereotype.Service; import org.springframework.tests.transaction.CallCountingTransactionManager; import org.springframework.transaction.PlatformTransactionManager; @@ -51,7 +55,8 @@ public class EnableTransactionManagementTests { @Test public void transactionProxyIsCreated() { - AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(EnableTxConfig.class, TxManagerConfig.class); + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext( + EnableTxConfig.class, TxManagerConfig.class); TransactionalTestBean bean = ctx.getBean(TransactionalTestBean.class); assertTrue("testBean is not a proxy", AopUtils.isAopProxy(bean)); Map services = ctx.getBeansWithAnnotation(Service.class); @@ -61,7 +66,19 @@ public void transactionProxyIsCreated() { @Test public void transactionProxyIsCreatedWithEnableOnSuperclass() { - AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(InheritedEnableTxConfig.class, TxManagerConfig.class); + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext( + InheritedEnableTxConfig.class, TxManagerConfig.class); + TransactionalTestBean bean = ctx.getBean(TransactionalTestBean.class); + assertTrue("testBean is not a proxy", AopUtils.isAopProxy(bean)); + Map services = ctx.getBeansWithAnnotation(Service.class); + assertTrue("Stereotype annotation not visible", services.containsKey("testBean")); + ctx.close(); + } + + @Test + public void transactionProxyIsCreatedWithEnableOnExcludedSuperclass() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext( + ParentEnableTxConfig.class, ChildEnableTxConfig.class, TxManagerConfig.class); TransactionalTestBean bean = ctx.getBean(TransactionalTestBean.class); assertTrue("testBean is not a proxy", AopUtils.isAopProxy(bean)); Map services = ctx.getBeansWithAnnotation(Service.class); @@ -71,7 +88,8 @@ public void transactionProxyIsCreatedWithEnableOnSuperclass() { @Test public void txManagerIsResolvedOnInvocationOfTransactionalMethod() { - AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(EnableTxConfig.class, TxManagerConfig.class); + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext( + EnableTxConfig.class, TxManagerConfig.class); TransactionalTestBean bean = ctx.getBean(TransactionalTestBean.class); // invoke a transactional method, causing the PlatformTransactionManager bean to be resolved. @@ -81,7 +99,8 @@ public void txManagerIsResolvedOnInvocationOfTransactionalMethod() { @Test public void txManagerIsResolvedCorrectlyWhenMultipleManagersArePresent() { - AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(EnableTxConfig.class, MultiTxManagerConfig.class); + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext( + EnableTxConfig.class, MultiTxManagerConfig.class); TransactionalTestBean bean = ctx.getBean(TransactionalTestBean.class); // invoke a transactional method, causing the PlatformTransactionManager bean to be resolved. @@ -96,7 +115,7 @@ public void txManagerIsResolvedCorrectlyWhenMultipleManagersArePresent() { @Test public void proxyTypeAspectJCausesRegistrationOfAnnotationTransactionAspect() { try { - new AnnotationConfigApplicationContext(EnableAspectJTxConfig.class, TxManagerConfig.class); + new AnnotationConfigApplicationContext(EnableAspectjTxConfig.class, TxManagerConfig.class); fail("should have thrown CNFE when trying to load AnnotationTransactionAspect. " + "Do you actually have org.springframework.aspects on the classpath?"); } @@ -138,15 +157,54 @@ public void spr11915() { static class EnableTxConfig { } + @Configuration static class InheritedEnableTxConfig extends EnableTxConfig { } + + @Configuration + @EnableTransactionManagement + @Conditional(NeverCondition.class) + static class ParentEnableTxConfig { + + @Bean + Object someBean() { + return new Object(); + } + } + + + @Configuration + static class ChildEnableTxConfig extends ParentEnableTxConfig { + + @Override + Object someBean() { + return "X"; + } + } + + + private static class NeverCondition implements ConfigurationCondition { + + @Override + public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { + return false; + } + + @Override + public ConfigurationPhase getConfigurationPhase() { + return ConfigurationPhase.REGISTER_BEAN; + } + } + + @Configuration - @EnableTransactionManagement(mode=AdviceMode.ASPECTJ) - static class EnableAspectJTxConfig { + @EnableTransactionManagement(mode = AdviceMode.ASPECTJ) + static class EnableAspectjTxConfig { } + @Configuration @EnableTransactionManagement static class Spr11915Config { @@ -166,6 +224,7 @@ public TransactionalTestBean testBean() { } } + @Configuration static class TxManagerConfig { @@ -178,9 +237,9 @@ public TransactionalTestBean testBean() { public PlatformTransactionManager txManager() { return new CallCountingTransactionManager(); } - } + @Configuration static class MultiTxManagerConfig extends TxManagerConfig implements TransactionManagementConfigurer { From 2de488e7f54bba760ba353ff720a6e795a0a357b Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 9 Dec 2016 15:05:20 +0100 Subject: [PATCH 333/344] Stronger explanation of default rollback rules Issue: SPR-14994 (cherry picked from commit 0296c7c) --- .../transaction/annotation/Transactional.java | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) 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 3b26813b25..024e376ada 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,7 +36,8 @@ * does not have to know about annotations. If no rules are relevant to the exception, * it will be treated like * {@link org.springframework.transaction.interceptor.DefaultTransactionAttribute} - * (rolling back on runtime exceptions). + * (rolling back on {@link RuntimeException} and {@link Error} but not on checked + * exceptions). * *

      For specific information about the semantics of this annotation's attributes, * consult the {@link org.springframework.transaction.TransactionDefinition} and @@ -102,7 +103,8 @@ *

      This just serves as a hint for the actual transaction subsystem; * it will not necessarily cause failure of write access attempts. * A transaction manager which cannot interpret the read-only hint will - * not throw an exception when asked for a read-only transaction. + * not throw an exception when asked for a read-only transaction + * but rather silently ignore the hint. * @see org.springframework.transaction.interceptor.TransactionAttribute#isReadOnly() */ boolean readOnly() default false; @@ -111,10 +113,15 @@ * Defines zero (0) or more exception {@link Class classes}, which must be * subclasses of {@link Throwable}, indicating which exception types must cause * a transaction rollback. + *

      By default, a transaction will be rolling back on {@link RuntimeException} + * and {@link Error} but not on checked exceptions (business exceptions). See + * {@link org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)} + * for a detailed explanation. *

      This is the preferred way to construct a rollback rule (in contrast to * {@link #rollbackForClassName}), matching the exception class and its subclasses. - *

      Similar to {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(Class clazz)} + *

      Similar to {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(Class clazz)}. * @see #rollbackForClassName + * @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable) */ Class[] rollbackFor() default {}; @@ -124,7 +131,7 @@ * a transaction rollback. *

      This can be a substring of a fully qualified class name, with no wildcard * support at present. For example, a value of {@code "ServletException"} would - * match {@link javax.servlet.ServletException} and its subclasses. + * match {@code javax.servlet.ServletException} and its subclasses. *

      NB: Consider carefully how specific the pattern is and whether * to include package information (which isn't mandatory). For example, * {@code "Exception"} will match nearly anything and will probably hide other @@ -132,8 +139,9 @@ * were meant to define a rule for all checked exceptions. With more unusual * {@link Exception} names such as {@code "BaseBusinessException"} there is no * need to use a FQN. - *

      Similar to {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(String exceptionName)} + *

      Similar to {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(String exceptionName)}. * @see #rollbackFor + * @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable) */ String[] rollbackForClassName() default {}; @@ -144,8 +152,9 @@ *

      This is the preferred way to construct a rollback rule (in contrast * to {@link #noRollbackForClassName}), matching the exception class and * its subclasses. - *

      Similar to {@link org.springframework.transaction.interceptor.NoRollbackRuleAttribute#NoRollbackRuleAttribute(Class clazz)} + *

      Similar to {@link org.springframework.transaction.interceptor.NoRollbackRuleAttribute#NoRollbackRuleAttribute(Class clazz)}. * @see #noRollbackForClassName + * @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable) */ Class[] noRollbackFor() default {}; @@ -155,8 +164,9 @@ * cause a transaction rollback. *

      See the description of {@link #rollbackForClassName} for further * information on how the specified names are treated. - *

      Similar to {@link org.springframework.transaction.interceptor.NoRollbackRuleAttribute#NoRollbackRuleAttribute(String exceptionName)} + *

      Similar to {@link org.springframework.transaction.interceptor.NoRollbackRuleAttribute#NoRollbackRuleAttribute(String exceptionName)}. * @see #noRollbackFor + * @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable) */ String[] noRollbackForClassName() default {}; From bcdda917e738d75287e34eff24652f0239a7a16e Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 12 Dec 2016 22:28:48 +0100 Subject: [PATCH 334/344] TypeDescriptor properly narrows ResolvableType for non-typed collection elements Issue: SPR-14971 (cherry picked from commit 442d8a6) --- .../core/convert/TypeDescriptor.java | 7 ++++-- .../DefaultConversionServiceTests.java | 24 ++++++++++++++++++- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java b/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java index ec016f04f5..9134b50c0e 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java +++ b/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java @@ -191,7 +191,7 @@ public TypeDescriptor narrow(Object value) { return this; } ResolvableType narrowed = ResolvableType.forType(value.getClass(), this.resolvableType); - return new TypeDescriptor(narrowed, null, this.annotations); + return new TypeDescriptor(narrowed, value.getClass(), this.annotations); } /** @@ -445,7 +445,10 @@ private TypeDescriptor narrow(Object value, TypeDescriptor typeDescriptor) { if (typeDescriptor != null) { return typeDescriptor.narrow(value); } - return (value != null ? new TypeDescriptor(this.resolvableType, value.getClass(), this.annotations) : null); + if (value != null) { + return narrow(value); + } + return null; } @Override diff --git a/spring-core/src/test/java/org/springframework/core/convert/support/DefaultConversionServiceTests.java b/spring-core/src/test/java/org/springframework/core/convert/support/DefaultConversionServiceTests.java index 5059738cab..4feec099c8 100644 --- a/spring-core/src/test/java/org/springframework/core/convert/support/DefaultConversionServiceTests.java +++ b/spring-core/src/test/java/org/springframework/core/convert/support/DefaultConversionServiceTests.java @@ -608,13 +608,35 @@ public void convertArrayToArrayAssignable() { assertEquals(3, result[2]); } + @Test + public void convertListOfNonStringifiable() { + List list = Arrays.asList(new TestEntity(1L), new TestEntity(2L)); + assertTrue(conversionService.canConvert(list.getClass(), String.class)); + try { + conversionService.convert(list, String.class); + } + catch (ConversionFailedException ex) { + assertTrue(ex.getMessage().contains(list.getClass().getName())); + assertTrue(ex.getCause() instanceof ConverterNotFoundException); + assertTrue(ex.getCause().getMessage().contains(TestEntity.class.getName())); + } + } + + @Test + public void convertListOfStringToString() { + List list = Arrays.asList("Foo", "Bar"); + assertTrue(conversionService.canConvert(list.getClass(), String.class)); + String result = conversionService.convert(list, String.class); + assertEquals("Foo,Bar", result); + } + @Test public void convertListOfListToString() { List list1 = Arrays.asList("Foo", "Bar"); List list2 = Arrays.asList("Baz", "Boop"); List> list = Arrays.asList(list1, list2); + assertTrue(conversionService.canConvert(list.getClass(), String.class)); String result = conversionService.convert(list, String.class); - assertNotNull(result); assertEquals("Foo,Bar,Baz,Boop", result); } From ee775248bc27ca24c03ad97b7a09da82faedf6ee Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 14 Dec 2016 22:06:13 +0100 Subject: [PATCH 335/344] Defensively catch IllegalStateException from match attempts (for compatibility with AspectJ 1.8.10) Issue: SPR-15019 (cherry picked from commit f0c3d50) --- .../aspectj/AspectJExpressionPointcut.java | 53 +++++++++++-------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJExpressionPointcut.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJExpressionPointcut.java index 3cb2613689..a4fdf6bb07 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJExpressionPointcut.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJExpressionPointcut.java @@ -261,6 +261,10 @@ public boolean matches(Class targetClass) { catch (BCException ex) { logger.debug("PointcutExpression matching rejected target class", ex); } + catch (IllegalStateException ex) { + // AspectJ 1.8.10: encountered invalid signature + logger.debug("PointcutExpression matching rejected target class", ex); + } return false; } @@ -413,39 +417,46 @@ private ShadowMatch getShadowMatch(Method targetMethod, Method originalMethod) { shadowMatch = this.shadowMatchCache.get(targetMethod); if (shadowMatch == null) { try { - shadowMatch = this.pointcutExpression.matchesMethodExecution(methodToMatch); - } - catch (ReflectionWorldException ex) { - // Failed to introspect target method, probably because it has been loaded - // in a special ClassLoader. Let's try the declaring ClassLoader instead... - try { - fallbackExpression = getFallbackPointcutExpression(methodToMatch.getDeclaringClass()); - if (fallbackExpression != null) { - shadowMatch = fallbackExpression.matchesMethodExecution(methodToMatch); - } - } - catch (ReflectionWorldException ex2) { - fallbackExpression = null; - } - } - if (shadowMatch == null && targetMethod != originalMethod) { - methodToMatch = originalMethod; try { shadowMatch = this.pointcutExpression.matchesMethodExecution(methodToMatch); } - catch (ReflectionWorldException ex3) { - // Could neither introspect the target class nor the proxy class -> - // let's try the original method's declaring class before we give up... + catch (ReflectionWorldException ex) { + // Failed to introspect target method, probably because it has been loaded + // in a special ClassLoader. Let's try the declaring ClassLoader instead... try { fallbackExpression = getFallbackPointcutExpression(methodToMatch.getDeclaringClass()); if (fallbackExpression != null) { shadowMatch = fallbackExpression.matchesMethodExecution(methodToMatch); } } - catch (ReflectionWorldException ex4) { + catch (ReflectionWorldException ex2) { fallbackExpression = null; } } + if (shadowMatch == null && targetMethod != originalMethod) { + methodToMatch = originalMethod; + try { + shadowMatch = this.pointcutExpression.matchesMethodExecution(methodToMatch); + } + catch (ReflectionWorldException ex3) { + // Could neither introspect the target class nor the proxy class -> + // let's try the original method's declaring class before we give up... + try { + fallbackExpression = getFallbackPointcutExpression(methodToMatch.getDeclaringClass()); + if (fallbackExpression != null) { + shadowMatch = fallbackExpression.matchesMethodExecution(methodToMatch); + } + } + catch (ReflectionWorldException ex4) { + fallbackExpression = null; + } + } + } + } + catch (IllegalStateException ex) { + // AspectJ 1.8.10: encountered invalid signature + logger.debug("PointcutExpression matching rejected target method", ex); + fallbackExpression = null; } if (shadowMatch == null) { shadowMatch = new ShadowMatchImpl(org.aspectj.util.FuzzyBoolean.NO, null, null, null); From 86614afb1ade97cefd265fbb50cd6a99e3ef3e53 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 15 Dec 2016 15:43:47 +0100 Subject: [PATCH 336/344] Consistent DeferredResultHandler invocation outside of result lock Issue: SPR-14978 (cherry picked from commit faab4f9) --- .../context/request/async/DeferredResult.java | 93 ++++++++++++++----- 1 file changed, 68 insertions(+), 25 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/context/request/async/DeferredResult.java b/spring-web/src/main/java/org/springframework/web/context/request/async/DeferredResult.java index 5ce9871491..ba78426614 100644 --- a/spring-web/src/main/java/org/springframework/web/context/request/async/DeferredResult.java +++ b/spring-web/src/main/java/org/springframework/web/context/request/async/DeferredResult.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,6 +43,7 @@ * is added to a {@link PriorityQueue} it is handled in the correct order. * * @author Rossen Stoyanchev + * @author Juergen Hoeller * @author Rob Winch * @since 3.2 */ @@ -65,7 +66,7 @@ public class DeferredResult { private volatile Object result = RESULT_NONE; - private volatile boolean expired; + private volatile boolean expired = false; /** @@ -164,24 +165,39 @@ public void onCompletion(Runnable callback) { */ public final void setResultHandler(DeferredResultHandler resultHandler) { Assert.notNull(resultHandler, "DeferredResultHandler is required"); + // Immediate expiration check outside of the result lock + if (this.expired) { + return; + } + Object resultToHandle; synchronized (this) { - this.resultHandler = resultHandler; - if (this.result != RESULT_NONE && !this.expired) { - try { - this.resultHandler.handleResult(this.result); - } - catch (Throwable ex) { - logger.trace("DeferredResult not handled", ex); - } + // Got the lock in the meantime: double-check expiration status + if (this.expired) { + return; } + resultToHandle = this.result; + if (resultToHandle == RESULT_NONE) { + // No result yet: store handler for processing once it comes in + this.resultHandler = resultHandler; + return; + } + } + // If we get here, we need to process an existing result object immediately. + // The decision is made within the result lock; just the handle call outside + // of it, avoiding any deadlock potential with Servlet container locks. + try { + resultHandler.handleResult(resultToHandle); + } + catch (Throwable ex) { + logger.debug("Failed to handle existing result", ex); } } /** * Set the value for the DeferredResult and handle it. * @param result the value to set - * @return "true" if the result was set and passed on for handling; - * "false" if the result was already set or the async request expired + * @return {@code true} if the result was set and passed on for handling; + * {@code false} if the result was already set or the async request expired * @see #isSetOrExpired() */ public boolean setResult(T result) { @@ -189,15 +205,32 @@ public boolean setResult(T result) { } private boolean setResultInternal(Object result) { + // Immediate expiration check outside of the result lock + if (isSetOrExpired()) { + return false; + } + DeferredResultHandler resultHandlerToUse; synchronized (this) { + // Got the lock in the meantime: double-check expiration status if (isSetOrExpired()) { return false; } + // At this point, we got a new result to process this.result = result; + resultHandlerToUse = this.resultHandler; + if (resultHandlerToUse == null) { + // No result handler set yet -> let the setResultHandler implementation + // pick up the result object and invoke the result handler for it. + return true; + } + // Result handler available -> let's clear the stored reference since + // we don't need it anymore. + this.resultHandler = null; } - if (this.resultHandler != null) { - this.resultHandler.handleResult(this.result); - } + // If we get here, we need to process an existing result object immediately. + // The decision is made within the result lock; just the handle call outside + // of it, avoiding any deadlock potential with Servlet container locks. + resultHandlerToUse.handleResult(result); return true; } @@ -206,8 +239,9 @@ private boolean setResultInternal(Object result) { * The value may be an {@link Exception} or {@link Throwable} in which case * it will be processed as if a handler raised the exception. * @param result the error result value - * @return "true" if the result was set to the error value and passed on for - * handling; "false" if the result was already set or the async request expired + * @return {@code true} if the result was set to the error value and passed on + * for handling; {@code false} if the result was already set or the async + * request expired * @see #isSetOrExpired() */ public boolean setErrorResult(Object result) { @@ -219,19 +253,28 @@ final DeferredResultProcessingInterceptor getInterceptor() { return new DeferredResultProcessingInterceptorAdapter() { @Override public boolean handleTimeout(NativeWebRequest request, DeferredResult deferredResult) { - if (timeoutCallback != null) { - timeoutCallback.run(); + boolean continueProcessing = true; + try { + if (timeoutCallback != null) { + timeoutCallback.run(); + } } - if (DeferredResult.this.timeoutResult != RESULT_NONE) { - setResultInternal(timeoutResult); + finally { + if (timeoutResult != RESULT_NONE) { + continueProcessing = false; + try { + setResultInternal(timeoutResult); + } + catch (Throwable ex) { + logger.debug("Failed to handle timeout result", ex); + } + } } - return true; + return continueProcessing; } @Override public void afterCompletion(NativeWebRequest request, DeferredResult deferredResult) { - synchronized (DeferredResult.this) { - expired = true; - } + expired = true; if (completionCallback != null) { completionCallback.run(); } From b592dd7e2fe882e10ae0dbccaea3ed5071ba2ddf Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 19 Dec 2016 01:51:57 +0100 Subject: [PATCH 337/344] Polishing (cherry picked from commit b5f2e56) --- .../context/i18n/LocaleContextHolder.java | 8 +- .../org/springframework/core/Conventions.java | 2 +- .../support/CollectionToStringConverter.java | 12 ++- .../support/GenericConversionService.java | 30 +++---- .../support/ObjectToOptionalConverter.java | 10 +-- .../util/xml/AbstractStaxHandlerTestCase.java | 12 ++- .../util/xml/StaxEventHandlerTests.java | 5 +- .../util/xml/StaxStreamHandlerTests.java | 5 +- .../org/springframework/oxm/Marshaller.java | 6 +- .../org/springframework/oxm/Unmarshaller.java | 6 +- .../web/client/AsyncRestOperations.java | 52 +++++++----- .../web/client/AsyncRestTemplate.java | 6 +- .../web/method/annotation/ModelFactory.java | 84 +++++++++---------- .../web/util/HierarchicalUriComponents.java | 4 +- ...stractMessageConverterMethodProcessor.java | 7 +- ...questResponseBodyMethodProcessorTests.java | 13 +-- 16 files changed, 130 insertions(+), 132 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/i18n/LocaleContextHolder.java b/spring-context/src/main/java/org/springframework/context/i18n/LocaleContextHolder.java index 6803a29341..e2737744d0 100644 --- a/spring-context/src/main/java/org/springframework/context/i18n/LocaleContextHolder.java +++ b/spring-context/src/main/java/org/springframework/context/i18n/LocaleContextHolder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -154,7 +154,7 @@ else if (locale != null) { /** * Return the Locale associated with the current thread, if any, - * or the system default Locale else. This is effectively a + * or the system default Locale otherwise. This is effectively a * replacement for {@link java.util.Locale#getDefault()}, * able to optionally respect a user-level Locale setting. *

      Note: This method has a fallback to the system default Locale. @@ -219,10 +219,10 @@ else if (locale != null) { /** * Return the TimeZone associated with the current thread, if any, - * or the system default TimeZone else. This is effectively a + * or the system default TimeZone otherwise. This is effectively a * replacement for {@link java.util.TimeZone#getDefault()}, * able to optionally respect a user-level TimeZone setting. - *

      Note: This method has a fallback to the system default Locale. + *

      Note: This method has a fallback to the system default TimeZone. * If you'd like to check for the raw LocaleContext content * (which may indicate no specific time zone through {@code null}, use * {@link #getLocaleContext()} and call {@link TimeZoneAwareLocaleContext#getTimeZone()} diff --git a/spring-core/src/main/java/org/springframework/core/Conventions.java b/spring-core/src/main/java/org/springframework/core/Conventions.java index 86d125248d..13aec85f4d 100644 --- a/spring-core/src/main/java/org/springframework/core/Conventions.java +++ b/spring-core/src/main/java/org/springframework/core/Conventions.java @@ -281,7 +281,7 @@ private static String pluralize(String name) { /** * Retrieves the {@code Class} of an element in the {@code Collection}. - * The exact element for which the {@code Class} is retreived will depend + * The exact element for which the {@code Class} is retrieved will depend * on the concrete {@code Collection} implementation. */ private static E peekAhead(Collection collection) { diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/CollectionToStringConverter.java b/spring-core/src/main/java/org/springframework/core/convert/support/CollectionToStringConverter.java index 7537c1c3e0..1cd142fd61 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/support/CollectionToStringConverter.java +++ b/spring-core/src/main/java/org/springframework/core/convert/support/CollectionToStringConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,10 +36,12 @@ final class CollectionToStringConverter implements ConditionalGenericConverter { private final ConversionService conversionService; + public CollectionToStringConverter(ConversionService conversionService) { this.conversionService = conversionService; } + @Override public Set getConvertibleTypes() { return Collections.singleton(new ConvertiblePair(Collection.class, String.class)); @@ -47,7 +49,8 @@ public Set getConvertibleTypes() { @Override public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { - return ConversionUtils.canConvertElements(sourceType.getElementTypeDescriptor(), targetType, this.conversionService); + return ConversionUtils.canConvertElements( + sourceType.getElementTypeDescriptor(), targetType, this.conversionService); } @Override @@ -56,7 +59,7 @@ public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor t return null; } Collection sourceCollection = (Collection) source; - if (sourceCollection.size() == 0) { + if (sourceCollection.isEmpty()) { return ""; } StringBuilder sb = new StringBuilder(); @@ -65,7 +68,8 @@ public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor t if (i > 0) { sb.append(DELIMITER); } - Object targetElement = this.conversionService.convert(sourceElement, sourceType.elementTypeDescriptor(sourceElement), targetType); + Object targetElement = this.conversionService.convert( + sourceElement, sourceType.elementTypeDescriptor(sourceElement), targetType); sb.append(targetElement); i++; } diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java b/spring-core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java index e06b246dc9..8d03b35440 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java +++ b/spring-core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java @@ -138,14 +138,14 @@ public void removeConvertible(Class sourceType, Class targetType) { @Override public boolean canConvert(Class sourceType, Class targetType) { - Assert.notNull(targetType, "targetType to convert to cannot be null"); + Assert.notNull(targetType, "Target type to convert to cannot be null"); return canConvert((sourceType != null ? TypeDescriptor.valueOf(sourceType) : null), TypeDescriptor.valueOf(targetType)); } @Override public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) { - Assert.notNull(targetType, "targetType to convert to cannot be null"); + Assert.notNull(targetType, "Target type to convert to cannot be null"); if (sourceType == null) { return true; } @@ -154,9 +154,9 @@ public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) } /** - * Return whether conversion between the sourceType and targetType can be bypassed. + * Return whether conversion between the source type and the target type can be bypassed. *

      More precisely, this method will return true if objects of sourceType can be - * converted to the targetType by returning the source object unchanged. + * converted to the target type by returning the source object unchanged. * @param sourceType context about the source type to convert from * (may be {@code null} if source is {@code null}) * @param targetType context about the target type to convert to (required) @@ -165,7 +165,7 @@ public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) * @since 3.2 */ public boolean canBypassConvert(TypeDescriptor sourceType, TypeDescriptor targetType) { - Assert.notNull(targetType, "targetType to convert to cannot be null"); + Assert.notNull(targetType, "Target type to convert to cannot be null"); if (sourceType == null) { return true; } @@ -176,20 +176,20 @@ public boolean canBypassConvert(TypeDescriptor sourceType, TypeDescriptor target @Override @SuppressWarnings("unchecked") public T convert(Object source, Class targetType) { - Assert.notNull(targetType, "targetType to convert to cannot be null"); + Assert.notNull(targetType, "Target type to convert to cannot be null"); return (T) convert(source, TypeDescriptor.forObject(source), TypeDescriptor.valueOf(targetType)); } @Override public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { - Assert.notNull(targetType, "targetType to convert to cannot be null"); + Assert.notNull(targetType, "Target type to convert to cannot be null"); if (sourceType == null) { - Assert.isTrue(source == null, "source must be [null] if sourceType == [null]"); + Assert.isTrue(source == null, "Source must be [null] if source type == [null]"); return handleResult(null, targetType, convertNullSource(null, targetType)); } if (source != null && !sourceType.getObjectType().isInstance(source)) { - throw new IllegalArgumentException("source to convert from must be an instance of " + - sourceType + "; instead it was a " + source.getClass().getName()); + throw new IllegalArgumentException("Source to convert from must be an instance of [" + + sourceType + "]; instead it was a [" + source.getClass().getName() + "]"); } GenericConverter converter = getConverter(sourceType, targetType); if (converter != null) { @@ -201,9 +201,9 @@ public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor t /** * Convenience operation for converting a source object to the specified targetType, - * where the targetType is a descriptor that provides additional conversion context. + * where the target type is a descriptor that provides additional conversion context. * Simply delegates to {@link #convert(Object, TypeDescriptor, TypeDescriptor)} and - * encapsulates the construction of the sourceType descriptor using + * encapsulates the construction of the source type descriptor using * {@link TypeDescriptor#forObject(Object)}. * @param source the source object * @param targetType the target type @@ -230,8 +230,8 @@ public String toString() { * {@link java.util.Optional#empty()} instance if the target type is * {@code java.util.Optional}. Subclasses may override this to return * custom {@code null} objects for specific target types. - * @param sourceType the sourceType to convert from - * @param targetType the targetType to convert to + * @param sourceType the source type to convert from + * @param targetType the target type to convert to * @return the converted null object */ protected Object convertNullSource(TypeDescriptor sourceType, TypeDescriptor targetType) { @@ -275,7 +275,7 @@ protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescripto /** * Return the default converter if no converter is found for the given sourceType/targetType pair. - *

      Returns a NO_OP Converter if the sourceType is assignable to the targetType. + *

      Returns a NO_OP Converter if the source type is assignable to the target type. * Returns {@code null} otherwise, indicating no suitable converter could be found. * @param sourceType the source type to convert from * @param targetType the target type to convert to diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/ObjectToOptionalConverter.java b/spring-core/src/main/java/org/springframework/core/convert/support/ObjectToOptionalConverter.java index 8c70120110..6137012b93 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/support/ObjectToOptionalConverter.java +++ b/spring-core/src/main/java/org/springframework/core/convert/support/ObjectToOptionalConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -68,13 +68,13 @@ public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor t else if (source instanceof Optional) { return source; } - else if (targetType.getResolvableType() == null) { - return Optional.of(source); - } - else { + else if (targetType.getResolvableType() != null) { Object target = this.conversionService.convert(source, sourceType, new GenericTypeDescriptor(targetType)); return Optional.ofNullable(target); } + else { + return Optional.of(source); + } } diff --git a/spring-core/src/test/java/org/springframework/util/xml/AbstractStaxHandlerTestCase.java b/spring-core/src/test/java/org/springframework/util/xml/AbstractStaxHandlerTestCase.java index 8dd8243077..73385a2739 100644 --- a/spring-core/src/test/java/org/springframework/util/xml/AbstractStaxHandlerTestCase.java +++ b/spring-core/src/test/java/org/springframework/util/xml/AbstractStaxHandlerTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,6 @@ import java.io.StringReader; import java.io.StringWriter; import java.net.Socket; - import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.stream.XMLStreamException; @@ -30,7 +29,6 @@ import org.junit.Assume; import org.junit.Before; import org.junit.Test; - import org.w3c.dom.Document; import org.xml.sax.InputSource; import org.xml.sax.XMLReader; @@ -58,11 +56,13 @@ public abstract class AbstractStaxHandlerTestCase { private XMLReader xmlReader; + @Before public void createXMLReader() throws Exception { xmlReader = XMLReaderFactory.createXMLReader(); } + @Test public void noNamespacePrefixes() throws Exception { Assume.assumeTrue(wwwSpringframeworkOrgIsAccessible()); @@ -109,8 +109,7 @@ public void namespacePrefixes() throws Exception { @Test public void noNamespacePrefixesDom() throws Exception { - DocumentBuilderFactory documentBuilderFactory = - DocumentBuilderFactory.newInstance(); + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); documentBuilderFactory.setNamespaceAware(true); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); @@ -131,8 +130,7 @@ public void noNamespacePrefixesDom() throws Exception { @Test public void namespacePrefixesDom() throws Exception { - DocumentBuilderFactory documentBuilderFactory = - DocumentBuilderFactory.newInstance(); + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); documentBuilderFactory.setNamespaceAware(true); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); diff --git a/spring-core/src/test/java/org/springframework/util/xml/StaxEventHandlerTests.java b/spring-core/src/test/java/org/springframework/util/xml/StaxEventHandlerTests.java index d2c44d66b6..2ad1ae9a43 100644 --- a/spring-core/src/test/java/org/springframework/util/xml/StaxEventHandlerTests.java +++ b/spring-core/src/test/java/org/springframework/util/xml/StaxEventHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,8 +27,7 @@ public class StaxEventHandlerTests extends AbstractStaxHandlerTestCase { @Override - protected AbstractStaxHandler createStaxHandler(Result result) - throws XMLStreamException { + protected AbstractStaxHandler createStaxHandler(Result result) throws XMLStreamException { XMLOutputFactory outputFactory = XMLOutputFactory.newFactory(); XMLEventWriter eventWriter = outputFactory.createXMLEventWriter(result); return new StaxEventHandler(eventWriter); diff --git a/spring-core/src/test/java/org/springframework/util/xml/StaxStreamHandlerTests.java b/spring-core/src/test/java/org/springframework/util/xml/StaxStreamHandlerTests.java index a039826bed..116aec625e 100644 --- a/spring-core/src/test/java/org/springframework/util/xml/StaxStreamHandlerTests.java +++ b/spring-core/src/test/java/org/springframework/util/xml/StaxStreamHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,8 +27,7 @@ public class StaxStreamHandlerTests extends AbstractStaxHandlerTestCase { @Override - protected AbstractStaxHandler createStaxHandler(Result result) - throws XMLStreamException { + protected AbstractStaxHandler createStaxHandler(Result result) throws XMLStreamException { XMLOutputFactory outputFactory = XMLOutputFactory.newFactory(); XMLStreamWriter streamWriter = outputFactory.createXMLStreamWriter(result); return new StaxStreamHandler(streamWriter); diff --git a/spring-oxm/src/main/java/org/springframework/oxm/Marshaller.java b/spring-oxm/src/main/java/org/springframework/oxm/Marshaller.java index 9dfb7214d2..2fe594f2e8 100644 --- a/spring-oxm/src/main/java/org/springframework/oxm/Marshaller.java +++ b/spring-oxm/src/main/java/org/springframework/oxm/Marshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,7 +35,7 @@ public interface Marshaller { /** - * Indicates whether this marshaller can marshal instances of the supplied type. + * Indicate whether this marshaller can marshal instances of the supplied type. * @param clazz the class that this marshaller is being asked if it can marshal * @return {@code true} if this marshaller can indeed marshal instances of the supplied class; * {@code false} otherwise @@ -43,7 +43,7 @@ public interface Marshaller { boolean supports(Class clazz); /** - * Marshals the object graph with the given root into the provided {@link Result}. + * Marshal the object graph with the given root into the provided {@link Result}. * @param graph the root of the object graph to marshal * @param result the result to marshal to * @throws IOException if an I/O error occurs diff --git a/spring-oxm/src/main/java/org/springframework/oxm/Unmarshaller.java b/spring-oxm/src/main/java/org/springframework/oxm/Unmarshaller.java index e5fbb959ad..400cc9bc5d 100644 --- a/spring-oxm/src/main/java/org/springframework/oxm/Unmarshaller.java +++ b/spring-oxm/src/main/java/org/springframework/oxm/Unmarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,7 +30,7 @@ public interface Unmarshaller { /** - * Indicates whether this unmarshaller can unmarshal instances of the supplied type. + * Indicate whether this unmarshaller can unmarshal instances of the supplied type. * @param clazz the class that this unmarshaller is being asked if it can marshal * @return {@code true} if this unmarshaller can indeed unmarshal to the supplied class; * {@code false} otherwise @@ -38,7 +38,7 @@ public interface Unmarshaller { boolean supports(Class clazz); /** - * Unmarshals the given {@link Source} into an object graph. + * Unmarshal the given {@link Source} into an object graph. * @param source the source to marshal from * @return the object graph * @throws IOException if an I/O error occurs diff --git a/spring-web/src/main/java/org/springframework/web/client/AsyncRestOperations.java b/spring-web/src/main/java/org/springframework/web/client/AsyncRestOperations.java index 0c4420366e..19658f6bb6 100644 --- a/spring-web/src/main/java/org/springframework/web/client/AsyncRestOperations.java +++ b/spring-web/src/main/java/org/springframework/web/client/AsyncRestOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,12 +29,14 @@ import org.springframework.util.concurrent.ListenableFuture; /** - * Interface specifying a basic set of asynchronous RESTful operations. Implemented by - * {@link AsyncRestTemplate}. Not often used directly, but a useful option to enhance - * testability, as it can easily be mocked or stubbed. + * Interface specifying a basic set of asynchronous RESTful operations. + * Implemented by {@link AsyncRestTemplate}. Not often used directly, but a useful + * option to enhance testability, as it can easily be mocked or stubbed. * * @author Arjen Poutsma * @since 4.0 + * @see AsyncRestTemplate + * @see RestOperations */ public interface AsyncRestOperations { @@ -47,8 +49,8 @@ public interface AsyncRestOperations { // GET /** - * Asynchronously retrieve an entity by doing a GET on the specified URL. The response is - * converted and stored in an {@link ResponseEntity}. + * Asynchronously retrieve an entity by doing a GET on the specified URL. + * The response is converted and stored in an {@link ResponseEntity}. *

      URI Template variables are expanded using the given URI variables, if any. * @param url the URL * @param responseType the type of the return value @@ -59,8 +61,8 @@ ListenableFuture> getForEntity(String url, Class respon Object... uriVariables) throws RestClientException; /** - * Asynchronously retrieve a representation by doing a GET on the URI template. The - * response is converted and stored in an {@link ResponseEntity}. + * Asynchronously retrieve a representation by doing a GET on the URI template. + * The response is converted and stored in an {@link ResponseEntity}. *

      URI Template variables are expanded using the given map. * @param url the URL * @param responseType the type of the return value @@ -80,6 +82,7 @@ ListenableFuture> getForEntity(String url, Class respon ListenableFuture> getForEntity(URI url, Class responseType) throws RestClientException; + // HEAD /** @@ -109,6 +112,7 @@ ListenableFuture headForHeaders(String url, Map uriVaria */ ListenableFuture headForHeaders(URI url) throws RestClientException; + // POST /** @@ -117,7 +121,7 @@ ListenableFuture headForHeaders(String url, Map uriVaria * typically indicates where the new resource is stored. *

      URI Template variables are expanded using the given URI variables, if any. * @param url the URL - * @param request the Object to be POSTed, may be {@code null} + * @param request the Object to be POSTed (may be {@code null}) * @param uriVariables the variables to expand the template * @return the value for the {@code Location} header wrapped in a {@link Future} * @see org.springframework.http.HttpEntity @@ -131,7 +135,7 @@ ListenableFuture postForLocation(String url, HttpEntity request, Object. * typically indicates where the new resource is stored. *

      URI Template variables are expanded using the given map. * @param url the URL - * @param request the Object to be POSTed, may be {@code null} + * @param request the Object to be POSTed (may be {@code null}) * @param uriVariables the variables to expand the template * @return the value for the {@code Location} header wrapped in a {@link Future} * @see org.springframework.http.HttpEntity @@ -144,7 +148,7 @@ ListenableFuture postForLocation(String url, HttpEntity request, Map postForLocation(String url, HttpEntity request, MapURI Template variables are expanded using the given URI variables, if any. * @param url the URL - * @param request the Object to be POSTed, may be {@code null} + * @param request the Object to be POSTed (may be {@code null}) * @param uriVariables the variables to expand the template * @return the entity wrapped in a {@link Future} * @see org.springframework.http.HttpEntity @@ -168,26 +172,26 @@ ListenableFuture> postForEntity(String url, HttpEntity * and asynchronously returns the response as {@link ResponseEntity}. *

      URI Template variables are expanded using the given map. * @param url the URL - * @param request the Object to be POSTed, may be {@code null} + * @param request the Object to be POSTed (may be {@code null}) * @param uriVariables the variables to expand the template * @return the entity wrapped in a {@link Future} * @see org.springframework.http.HttpEntity */ ListenableFuture> postForEntity(String url, HttpEntity request, - Class responseType, Map uriVariables) - throws RestClientException; + Class responseType, Map uriVariables) throws RestClientException; /** * Create a new resource by POSTing the given object to the URL, * and asynchronously returns the response as {@link ResponseEntity}. * @param url the URL - * @param request the Object to be POSTed, may be {@code null} + * @param request the Object to be POSTed (may be {@code null}) * @return the entity wrapped in a {@link Future} * @see org.springframework.http.HttpEntity */ ListenableFuture> postForEntity(URI url, HttpEntity request, Class responseType) throws RestClientException; + // PUT /** @@ -195,7 +199,7 @@ ListenableFuture> postForEntity(URI url, HttpEntity req *

      URI Template variables are expanded using the given URI variables, if any. *

      The Future will return a {@code null} result upon completion. * @param url the URL - * @param request the Object to be PUT, may be {@code null} + * @param request the Object to be PUT (may be {@code null}) * @param uriVariables the variables to expand the template * @see HttpEntity */ @@ -207,7 +211,7 @@ ListenableFuture put(String url, HttpEntity request, Object... uriVariable *

      URI Template variables are expanded using the given map. *

      The Future will return a {@code null} result upon completion. * @param url the URL - * @param request the Object to be PUT, may be {@code null} + * @param request the Object to be PUT (may be {@code null}) * @param uriVariables the variables to expand the template * @see HttpEntity */ @@ -218,11 +222,12 @@ ListenableFuture put(String url, HttpEntity request, Map uriVar * Creates a new resource by PUTting the given object to URL. *

      The Future will return a {@code null} result upon completion. * @param url the URL - * @param request the Object to be PUT, may be {@code null} + * @param request the Object to be PUT (may be {@code null}) * @see HttpEntity */ ListenableFuture put(URI url, HttpEntity request) throws RestClientException; + // DELETE /** @@ -251,6 +256,7 @@ ListenableFuture put(String url, HttpEntity request, Map uriVar */ ListenableFuture delete(URI url) throws RestClientException; + // OPTIONS /** @@ -344,7 +350,7 @@ ListenableFuture> exchange(URI url, HttpMethod method, * @param url the URL * @param method the HTTP method (GET, POST, etc) * @param requestEntity the entity (headers and/or body) to write to the - * request, may be {@code null} + * request (may be {@code null}) * @param responseType the type of the return value * @param uriVariables the variables to expand in the template * @return the response as entity wrapped in a {@link Future} @@ -364,7 +370,8 @@ ListenableFuture> exchange(String url, HttpMethod method, * * @param url the URL * @param method the HTTP method (GET, POST, etc) - * @param requestEntity the entity (headers and/or body) to write to the request, may be {@code null} + * @param requestEntity the entity (headers and/or body) to write to the request + * (may be {@code null}) * @param responseType the type of the return value * @param uriVariables the variables to expand in the template * @return the response as entity wrapped in a {@link Future} @@ -384,7 +391,8 @@ ListenableFuture> exchange(String url, HttpMethod method, * * @param url the URL * @param method the HTTP method (GET, POST, etc) - * @param requestEntity the entity (headers and/or body) to write to the request, may be {@code null} + * @param requestEntity the entity (headers and/or body) to write to the request + * (may be {@code null}) * @param responseType the type of the return value * @return the response as entity wrapped in a {@link Future} */ diff --git a/spring-web/src/main/java/org/springframework/web/client/AsyncRestTemplate.java b/spring-web/src/main/java/org/springframework/web/client/AsyncRestTemplate.java index 4a4a5a57f3..396da2ae84 100644 --- a/spring-web/src/main/java/org/springframework/web/client/AsyncRestTemplate.java +++ b/spring-web/src/main/java/org/springframework/web/client/AsyncRestTemplate.java @@ -58,10 +58,8 @@ * wrappers as opposed to concrete results. * *

      The {@code AsyncRestTemplate} exposes a synchronous {@link RestTemplate} via the - * {@link #getRestOperations()} method, and it shares its - * {@linkplain #setErrorHandler(ResponseErrorHandler) error handler} and - * {@linkplain #setMessageConverters(List) message converters} with this - * {@code RestTemplate}. + * {@link #getRestOperations()} method and shares its {@linkplain #setErrorHandler error handler} + * and {@linkplain #setMessageConverters message converters} with that {@code RestTemplate}. * *

      Note: by default {@code AsyncRestTemplate} relies on * standard JDK facilities to establish HTTP connections. You can switch to use diff --git a/spring-web/src/main/java/org/springframework/web/method/annotation/ModelFactory.java b/spring-web/src/main/java/org/springframework/web/method/annotation/ModelFactory.java index 9eb31c0d36..ce9910ec18 100644 --- a/spring-web/src/main/java/org/springframework/web/method/annotation/ModelFactory.java +++ b/spring-web/src/main/java/org/springframework/web/method/annotation/ModelFactory.java @@ -179,44 +179,6 @@ private List findSessionAttributeArguments(HandlerMethod handlerMethod) return result; } - /** - * Derives the model attribute name for a method parameter based on: - *

        - *
      1. The parameter {@code @ModelAttribute} annotation value - *
      2. The parameter type - *
      - * @return the derived name; never {@code null} or an empty string - */ - public static String getNameForParameter(MethodParameter parameter) { - ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class); - String name = (ann != null ? ann.value() : null); - return StringUtils.hasText(name) ? name : Conventions.getVariableNameForParameter(parameter); - } - - /** - * Derive the model attribute name for the given return value using one of: - *
        - *
      1. The method {@code ModelAttribute} annotation value - *
      2. The declared return type if it is more specific than {@code Object} - *
      3. The actual return value type - *
      - * @param returnValue the value returned from a method invocation - * @param returnType the return type of the method - * @return the model name, never {@code null} nor empty - */ - public static String getNameForReturnValue(Object returnValue, MethodParameter returnType) { - ModelAttribute ann = returnType.getMethodAnnotation(ModelAttribute.class); - if (ann != null && StringUtils.hasText(ann.value())) { - return ann.value(); - } - else { - Method method = returnType.getMethod(); - Class containingClass = returnType.getContainingClass(); - Class resolvedType = GenericTypeResolver.resolveReturnType(method, containingClass); - return Conventions.getVariableNameForReturnType(method, resolvedType, returnValue); - } - } - /** * Promote model attributes listed as {@code @SessionAttributes} to the session. * Add {@link BindingResult} attributes where necessary. @@ -244,10 +206,8 @@ private void updateBindingResult(NativeWebRequest request, ModelMap model) throw List keyNames = new ArrayList(model.keySet()); for (String name : keyNames) { Object value = model.get(name); - if (isBindingCandidate(name, value)) { String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + name; - if (!model.containsAttribute(bindingResultKey)) { WebDataBinder dataBinder = this.dataBinderFactory.createBinder(request, value, name); model.put(bindingResultKey, dataBinder.getBindingResult()); @@ -264,7 +224,7 @@ private boolean isBindingCandidate(String attributeName, Object value) { return false; } - Class attrType = (value != null) ? value.getClass() : null; + Class attrType = (value != null ? value.getClass() : null); if (this.sessionAttributesHandler.isHandlerSessionAttribute(attributeName, attrType)) { return true; } @@ -274,13 +234,53 @@ private boolean isBindingCandidate(String attributeName, Object value) { } + /** + * Derive the model attribute name for a method parameter based on: + *
        + *
      1. the parameter {@code @ModelAttribute} annotation value + *
      2. the parameter type + *
      + * @param parameter a descriptor for the method parameter + * @return the derived name (never {@code null} or empty String) + */ + public static String getNameForParameter(MethodParameter parameter) { + ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class); + String name = (ann != null ? ann.value() : null); + return (StringUtils.hasText(name) ? name : Conventions.getVariableNameForParameter(parameter)); + } + + /** + * Derive the model attribute name for the given return value based on: + *
        + *
      1. the method {@code ModelAttribute} annotation value + *
      2. the declared return type if it is more specific than {@code Object} + *
      3. the actual return value type + *
      + * @param returnValue the value returned from a method invocation + * @param returnType a descriptor for the return type of the method + * @return the derived name (never {@code null} or empty String) + */ + public static String getNameForReturnValue(Object returnValue, MethodParameter returnType) { + ModelAttribute ann = returnType.getMethodAnnotation(ModelAttribute.class); + if (ann != null && StringUtils.hasText(ann.value())) { + return ann.value(); + } + else { + Method method = returnType.getMethod(); + Class containingClass = returnType.getContainingClass(); + Class resolvedType = GenericTypeResolver.resolveReturnType(method, containingClass); + return Conventions.getVariableNameForReturnType(method, resolvedType, returnValue); + } + } + + private static class ModelMethod { private final InvocableHandlerMethod handlerMethod; private final Set dependencies = new HashSet(); - private ModelMethod(InvocableHandlerMethod handlerMethod) { + public ModelMethod(InvocableHandlerMethod handlerMethod) { this.handlerMethod = handlerMethod; for (MethodParameter parameter : handlerMethod.getMethodParameters()) { if (parameter.hasParameterAnnotation(ModelAttribute.class)) { diff --git a/spring-web/src/main/java/org/springframework/web/util/HierarchicalUriComponents.java b/spring-web/src/main/java/org/springframework/web/util/HierarchicalUriComponents.java index e25466ecbd..df69e43b69 100644 --- a/spring-web/src/main/java/org/springframework/web/util/HierarchicalUriComponents.java +++ b/spring-web/src/main/java/org/springframework/web/util/HierarchicalUriComponents.java @@ -193,7 +193,7 @@ public HierarchicalUriComponents encode(String encoding) throws UnsupportedEncod String hostTo = encodeUriComponent(this.host, encoding, getHostType()); PathComponent pathTo = this.path.encode(encoding); MultiValueMap paramsTo = encodeQueryParams(encoding); - String fragmentTo = encodeUriComponent(this.getFragment(), encoding, Type.FRAGMENT); + String fragmentTo = encodeUriComponent(getFragment(), encoding, Type.FRAGMENT); return new HierarchicalUriComponents(schemeTo, userInfoTo, hostTo, this.port, pathTo, paramsTo, fragmentTo, true, false); } @@ -327,7 +327,7 @@ protected HierarchicalUriComponents expandInternal(UriTemplateVariables uriVaria String portTo = expandUriComponent(this.port, uriVariables); PathComponent pathTo = this.path.expand(uriVariables); MultiValueMap paramsTo = expandQueryParams(uriVariables); - String fragmentTo = expandUriComponent(this.getFragment(), uriVariables); + String fragmentTo = expandUriComponent(getFragment(), uriVariables); return new HierarchicalUriComponents(schemeTo, userInfoTo, hostTo, portTo, pathTo, paramsTo, fragmentTo, false, false); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodProcessor.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodProcessor.java index 296caaf8ce..6df76c61a3 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodProcessor.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodProcessor.java @@ -91,7 +91,6 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe private final Set safeExtensions = new HashSet(); - /** * Constructor with list of converters only. */ @@ -339,9 +338,7 @@ private MediaType getMostSpecificMediaType(MediaType acceptType, MediaType produ * header with a safe attachment file name ("f.txt") is added to prevent * RFD exploits. */ - private void addContentDispositionHeader(ServletServerHttpRequest request, - ServletServerHttpResponse response) { - + private void addContentDispositionHeader(ServletServerHttpRequest request, ServletServerHttpResponse response) { HttpHeaders headers = response.getHeaders(); if (headers.containsKey(HttpHeaders.CONTENT_DISPOSITION)) { return; @@ -354,7 +351,7 @@ private void addContentDispositionHeader(ServletServerHttpRequest request, } } catch (Throwable ex) { - // Ignore + // ignore } HttpServletRequest servletRequest = request.getServletRequest(); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessorTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessorTests.java index 32ddb8bdd9..f45f706ef5 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessorTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -69,17 +69,12 @@ import org.springframework.web.servlet.view.json.MappingJackson2JsonView; import org.springframework.web.util.WebUtils; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; /** * Test fixture for a {@link RequestResponseBodyMethodProcessor} with - * actual delegation to {@link HttpMessageConverter} instances. - * - *

      Also see {@link RequestResponseBodyMethodProcessorMockTests}. + * actual delegation to {@link HttpMessageConverter} instances. Also see + * {@link RequestResponseBodyMethodProcessorMockTests}. * * @author Rossen Stoyanchev * @author Sebastien Deleuze From e9def51e6f998bd2441131e58be4c8d896ad50f3 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 19 Dec 2016 16:02:59 +0100 Subject: [PATCH 338/344] MBeanExporter silently ignores null beans Issue: SPR-15031 (cherry picked from commit 1e58c80) --- .../jmx/export/MBeanExporter.java | 28 ++-- .../jmx/export/MBeanExporterTests.java | 129 +++++++++++++----- 2 files changed, 110 insertions(+), 47 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/jmx/export/MBeanExporter.java b/spring-context/src/main/java/org/springframework/jmx/export/MBeanExporter.java index 6fc0013ebc..bbb6ec2fbf 100644 --- a/spring-context/src/main/java/org/springframework/jmx/export/MBeanExporter.java +++ b/spring-context/src/main/java/org/springframework/jmx/export/MBeanExporter.java @@ -83,7 +83,7 @@ * via the {@link #setListeners(MBeanExporterListener[]) listeners} property, allowing * application code to be notified of MBean registration and unregistration events. * - *

      This exporter is compatible with MBeans and MXBeans on Java 6 and above. + *

      This exporter is compatible with MBeans as well as MXBeans. * * @author Rob Harrop * @author Juergen Hoeller @@ -466,7 +466,7 @@ public ObjectName registerManagedResource(Object managedResource) throws MBeanEx objectName = JmxUtils.appendIdentityToObjectName(objectName, managedResource); } } - catch (Exception ex) { + catch (Throwable ex) { throw new MBeanExportException("Unable to generate ObjectName for MBean [" + managedResource + "]", ex); } registerManagedResource(managedResource, objectName); @@ -570,7 +570,7 @@ protected boolean isBeanDefinitionLazyInit(ListableBeanFactory beanFactory, Stri * should be exposed to the {@code MBeanServer}. Specifically, if the * supplied {@code mapValue} is the name of a bean that is configured * for lazy initialization, then a proxy to the resource is registered with - * the {@code MBeanServer} so that the the lazy load behavior is + * the {@code MBeanServer} so that the lazy load behavior is * honored. If the bean is already an MBean then it will be registered * directly with the {@code MBeanServer} without any intervention. For * all other beans or bean names, the resource itself is registered with @@ -578,7 +578,8 @@ protected boolean isBeanDefinitionLazyInit(ListableBeanFactory beanFactory, Stri * @param mapValue the value configured for this bean in the beans map; * may be either the {@code String} name of a bean, or the bean itself * @param beanKey the key associated with this bean in the beans map - * @return the {@code ObjectName} under which the resource was registered + * @return the {@code ObjectName} under which the resource was registered, + * or {@code null} if the actual resource was {@code null} as well * @throws MBeanExportException if the export failed * @see #setBeans * @see #registerBeanInstance @@ -599,12 +600,14 @@ protected ObjectName registerBeanNameOrInstance(Object mapValue, String beanKey) } else { Object bean = this.beanFactory.getBean(beanName); - ObjectName objectName = registerBeanInstance(bean, beanKey); - replaceNotificationListenerBeanNameKeysIfNecessary(beanName, objectName); - return objectName; + if (bean != null) { + ObjectName objectName = registerBeanInstance(bean, beanKey); + replaceNotificationListenerBeanNameKeysIfNecessary(beanName, objectName); + return objectName; + } } } - else { + else if (mapValue != null) { // Plain bean instance -> register it directly. if (this.beanFactory != null) { Map beansOfSameType = @@ -621,10 +624,11 @@ protected ObjectName registerBeanNameOrInstance(Object mapValue, String beanKey) return registerBeanInstance(mapValue, beanKey); } } - catch (Exception ex) { + catch (Throwable ex) { throw new UnableToRegisterMBeanException( "Unable to register MBean [" + mapValue + "] with key '" + beanKey + "'", ex); } + return null; } /** @@ -816,7 +820,7 @@ protected ModelMBean createAndConfigureMBean(Object managedResource, String bean mbean.setManagedResource(managedResource, MR_TYPE_OBJECT_REFERENCE); return mbean; } - catch (Exception ex) { + catch (Throwable ex) { throw new MBeanExportException("Could not create ModelMBean for managed resource [" + managedResource + "] with key '" + beanKey + "'", ex); } @@ -984,7 +988,7 @@ private void registerNotificationListeners() throws MBeanExportException { } } } - catch (Exception ex) { + catch (Throwable ex) { throw new MBeanExportException("Unable to register NotificationListener", ex); } } @@ -1004,7 +1008,7 @@ private void unregisterNotificationListeners() { this.server.removeNotificationListener(mappedObjectName, bean.getNotificationListener(), bean.getNotificationFilter(), bean.getHandback()); } - catch (Exception ex) { + catch (Throwable ex) { if (logger.isDebugEnabled()) { logger.debug("Unable to unregister NotificationListener", ex); } diff --git a/spring-context/src/test/java/org/springframework/jmx/export/MBeanExporterTests.java b/spring-context/src/test/java/org/springframework/jmx/export/MBeanExporterTests.java index 718d03e865..e8e1224e13 100644 --- a/spring-context/src/test/java/org/springframework/jmx/export/MBeanExporterTests.java +++ b/spring-context/src/test/java/org/springframework/jmx/export/MBeanExporterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,8 +39,10 @@ import org.junit.rules.ExpectedException; import org.springframework.aop.framework.ProxyFactory; +import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.jmx.AbstractMBeanServerTests; @@ -68,8 +70,7 @@ * @author Sam Brannen * @author Stephane Nicoll */ -@SuppressWarnings("deprecation") -public final class MBeanExporterTests extends AbstractMBeanServerTests { +public class MBeanExporterTests extends AbstractMBeanServerTests { @Rule public final ExpectedException thrown = ExpectedException.none(); @@ -79,7 +80,7 @@ public final class MBeanExporterTests extends AbstractMBeanServerTests { @Test public void testRegisterNullNotificationListenerType() throws Exception { - Map listeners = new HashMap(); + Map listeners = new HashMap<>(); // put null in as a value... listeners.put("*", null); MBeanExporter exporter = new MBeanExporter(); @@ -90,7 +91,7 @@ public void testRegisterNullNotificationListenerType() throws Exception { @Test public void testRegisterNotificationListenerForNonExistentMBean() throws Exception { - Map listeners = new HashMap(); + Map listeners = new HashMap<>(); NotificationListener dummyListener = new NotificationListener() { @Override public void handleNotification(Notification notification, Object handback) { @@ -130,7 +131,7 @@ public void testWithSuppliedMBeanServer() throws Exception { @Test public void testUserCreatedMBeanRegWithDynamicMBean() throws Exception { - Map map = new HashMap(); + Map map = new HashMap<>(); map.put("spring:name=dynBean", new TestDynamicMBean()); InvokeDetectAssembler asm = new InvokeDetectAssembler(); @@ -235,7 +236,6 @@ public void testWithMBeanExporterListeners() throws Exception { assertListener(listener2); } - @Test public void testExportJdkProxy() throws Exception { JmxTestBean bean = new JmxTestBean(); @@ -249,7 +249,7 @@ public void testExportJdkProxy() throws Exception { IJmxTestBean proxy = (IJmxTestBean) factory.getProxy(); String name = "bean:mmm=whatever"; - Map beans = new HashMap(); + Map beans = new HashMap<>(); beans.put(name, proxy); MBeanExporter exporter = new MBeanExporter(); @@ -268,7 +268,7 @@ public void testSelfNaming() throws Exception { SelfNamingTestBean testBean = new SelfNamingTestBean(); testBean.setObjectName(objectName); - Map beans = new HashMap(); + Map beans = new HashMap<>(); beans.put("foo", testBean); MBeanExporter exporter = new MBeanExporter(); @@ -295,14 +295,14 @@ public void testRegisterIgnoreExisting() throws Exception { String objectName2 = "spring:test=equalBean"; - Map beans = new HashMap(); + Map beans = new HashMap<>(); beans.put(objectName.toString(), springRegistered); beans.put(objectName2, springRegistered); MBeanExporter exporter = new MBeanExporter(); exporter.setServer(server); exporter.setBeans(beans); - exporter.setRegistrationBehavior(MBeanExporter.REGISTRATION_IGNORE_EXISTING); + exporter.setRegistrationPolicy(RegistrationPolicy.IGNORE_EXISTING); start(exporter); @@ -327,7 +327,7 @@ public void testRegisterReplaceExisting() throws Exception { Person springRegistered = new Person(); springRegistered.setName("Sally Greenwood"); - Map beans = new HashMap(); + Map beans = new HashMap<>(); beans.put(objectName.toString(), springRegistered); MBeanExporter exporter = new MBeanExporter(); @@ -353,7 +353,7 @@ public void testWithExposeClassLoader() throws Exception { bean.setName(name); ObjectName objectName = ObjectNameManager.getInstance("spring:type=Test"); - Map beans = new HashMap(); + Map beans = new HashMap<>(); beans.put(objectName.toString(), bean); MBeanExporter exporter = new MBeanExporter(); @@ -383,7 +383,7 @@ public void testBonaFideMBeanIsNotExportedWhenAutodetectIsTotallyTurnedOff() thr factory.registerSingleton(exportedBeanName, new TestBean()); MBeanExporter exporter = new MBeanExporter(); - Map beansToExport = new HashMap(); + Map beansToExport = new HashMap<>(); beansToExport.put(OBJECT_NAME, exportedBeanName); exporter.setBeans(beansToExport); exporter.setServer(getServer()); @@ -470,7 +470,7 @@ public void testBonaFideMBeanExplicitlyExportedAndAutodetectionIsOn() throws Exc MBeanExporter exporter = new MBeanExporter(); exporter.setServer(getServer()); - Map beansToExport = new HashMap(); + Map beansToExport = new HashMap<>(); beansToExport.put(OBJECT_NAME, OBJECT_NAME); exporter.setBeans(beansToExport); exporter.setAssembler(new NamedBeanAutodetectCapableMBeanInfoAssemblerStub(OBJECT_NAME)); @@ -526,7 +526,7 @@ public void testSetAutodetectModeNameToARubbishValue() { @Test public void testNotRunningInBeanFactoryAndPassedBeanNameToExport() throws Exception { MBeanExporter exporter = new MBeanExporter(); - Map beans = new HashMap(); + Map beans = new HashMap<>(); beans.put(OBJECT_NAME, "beanName"); exporter.setBeans(beans); thrown.expect(MBeanExportException.class); @@ -541,10 +541,7 @@ public void testNotRunningInBeanFactoryAndAutodetectionIsOn() throws Exception { start(exporter); } - /** - * SPR-2158 - */ - @Test + @Test // SPR-2158 public void testMBeanIsNotUnregisteredSpuriouslyIfSomeExternalProcessHasUnregisteredMBean() throws Exception { MBeanExporter exporter = new MBeanExporter(); exporter.setBeans(getBeanMap()); @@ -561,10 +558,7 @@ public void testMBeanIsNotUnregisteredSpuriouslyIfSomeExternalProcessHasUnregist listener.getUnregistered().size()); } - /** - * SPR-3302 - */ - @Test + @Test // SPR-3302 public void testBeanNameCanBeUsedInNotificationListenersMap() throws Exception { String beanName = "charlesDexterWard"; BeanDefinitionBuilder testBean = BeanDefinitionBuilder.rootBeanDefinition(JmxTestBean.class); @@ -576,7 +570,7 @@ public void testBeanNameCanBeUsedInNotificationListenersMap() throws Exception { MBeanExporter exporter = new MBeanExporter(); exporter.setServer(getServer()); - Map beansToExport = new HashMap(); + Map beansToExport = new HashMap<>(); beansToExport.put("test:what=ever", testBeanInstance); exporter.setBeans(beansToExport); exporter.setBeanFactory(factory); @@ -598,7 +592,7 @@ public void testWildcardCanBeUsedInNotificationListenersMap() throws Exception { MBeanExporter exporter = new MBeanExporter(); exporter.setServer(getServer()); - Map beansToExport = new HashMap(); + Map beansToExport = new HashMap<>(); beansToExport.put("test:what=ever", testBeanInstance); exporter.setBeans(beansToExport); exporter.setBeanFactory(factory); @@ -608,10 +602,7 @@ public void testWildcardCanBeUsedInNotificationListenersMap() throws Exception { start(exporter); } - /* - * SPR-3625 - */ - @Test + @Test // SPR-3625 public void testMBeanIsUnregisteredForRuntimeExceptionDuringInitialization() throws Exception { BeanDefinitionBuilder builder1 = BeanDefinitionBuilder.rootBeanDefinition(Person.class); BeanDefinitionBuilder builder2 = BeanDefinitionBuilder @@ -626,7 +617,7 @@ public void testMBeanIsUnregisteredForRuntimeExceptionDuringInitialization() thr MBeanExporter exporter = new MBeanExporter(); exporter.setServer(getServer()); - Map beansToExport = new HashMap(); + Map beansToExport = new HashMap<>(); beansToExport.put(objectName1, objectName1); beansToExport.put(objectName2, objectName2); exporter.setBeans(beansToExport); @@ -667,12 +658,43 @@ public void testIgnoreBeanName() throws MalformedObjectNameException { ObjectNameManager.getInstance(secondBeanName)); } + @Test + public void testRegisterFactoryBean() throws MalformedObjectNameException { + DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); + factory.registerBeanDefinition("spring:type=FactoryBean", new RootBeanDefinition(ProperSomethingFactoryBean.class)); + + MBeanExporter exporter = new MBeanExporter(); + exporter.setServer(getServer()); + exporter.setBeanFactory(factory); + exporter.setAutodetectMode(MBeanExporter.AUTODETECT_ALL); + + start(exporter); + assertIsRegistered("Non-null FactoryBean object registered", + ObjectNameManager.getInstance("spring:type=FactoryBean")); + } + + @Test + public void testIgnoreNullObjectFromFactoryBean() throws MalformedObjectNameException { + DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); + factory.registerBeanDefinition("spring:type=FactoryBean", new RootBeanDefinition(NullSomethingFactoryBean.class)); + + MBeanExporter exporter = new MBeanExporter(); + exporter.setServer(getServer()); + exporter.setBeanFactory(factory); + exporter.setAutodetectMode(MBeanExporter.AUTODETECT_ALL); + + start(exporter); + assertIsNotRegistered("Null FactoryBean object not registered", + ObjectNameManager.getInstance("spring:type=FactoryBean")); + } + + private ConfigurableApplicationContext load(String context) { return new ClassPathXmlApplicationContext(context, getClass()); } private Map getBeanMap() { - Map map = new HashMap(); + Map map = new HashMap<>(); map.put(OBJECT_NAME, new JmxTestBean()); return map; } @@ -700,9 +722,9 @@ public ModelMBeanInfo getMBeanInfo(Object managedResource, String beanKey) throw private static class MockMBeanExporterListener implements MBeanExporterListener { - private List registered = new ArrayList(); + private List registered = new ArrayList<>(); - private List unregistered = new ArrayList(); + private List unregistered = new ArrayList<>(); @Override public void mbeanRegistered(ObjectName objectName) { @@ -762,7 +784,7 @@ public void setName(String name) { public static final class StubNotificationListener implements NotificationListener { - private List notifications = new ArrayList(); + private List notifications = new ArrayList<>(); @Override public void handleNotification(Notification notification, Object handback) { @@ -799,4 +821,41 @@ public boolean includeBean(Class beanClass, String beanName) { } } + + public interface SomethingMBean {} + + public static class Something implements SomethingMBean {} + + + public static class ProperSomethingFactoryBean implements FactoryBean { + + @Override public Something getObject() { + return new Something(); + } + + @Override public Class getObjectType() { + return Something.class; + } + + @Override public boolean isSingleton() { + return true; + } + } + + + public static class NullSomethingFactoryBean implements FactoryBean { + + @Override public Something getObject() { + return null; + } + + @Override public Class getObjectType() { + return Something.class; + } + + @Override public boolean isSingleton() { + return true; + } + } + } From 74a0aee2f6ddd0863ead87dec53cd5084dae16d9 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 20 Dec 2016 12:17:51 +0100 Subject: [PATCH 339/344] Defensively catch any exception from match attempts (for compatibility with AspectJ 1.8.10) Issue: SPR-15019 (cherry picked from commit 935671a) --- .../aop/aspectj/AspectJExpressionPointcut.java | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJExpressionPointcut.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJExpressionPointcut.java index a4fdf6bb07..8d8f6a9e86 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJExpressionPointcut.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJExpressionPointcut.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,6 @@ import org.aopalliance.intercept.MethodInvocation; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.aspectj.weaver.BCException; import org.aspectj.weaver.patterns.NamePattern; import org.aspectj.weaver.reflect.ReflectionWorld.ReflectionWorldException; import org.aspectj.weaver.reflect.ShadowMatchImpl; @@ -258,11 +257,7 @@ public boolean matches(Class targetClass) { } } } - catch (BCException ex) { - logger.debug("PointcutExpression matching rejected target class", ex); - } - catch (IllegalStateException ex) { - // AspectJ 1.8.10: encountered invalid signature + catch (Throwable ex) { logger.debug("PointcutExpression matching rejected target class", ex); } return false; @@ -330,7 +325,6 @@ public boolean matches(Method method, Class targetClass, Object... args) { } catch (IllegalStateException ex) { // No current invocation... - // TODO: Should we really proceed here? if (logger.isDebugEnabled()) { logger.debug("Could not access current invocation - matching with limited context: " + ex); } @@ -453,8 +447,8 @@ private ShadowMatch getShadowMatch(Method targetMethod, Method originalMethod) { } } } - catch (IllegalStateException ex) { - // AspectJ 1.8.10: encountered invalid signature + catch (Throwable ex) { + // Possibly AspectJ 1.8.10 encountering an invalid signature logger.debug("PointcutExpression matching rejected target method", ex); fallbackExpression = null; } From 5fb4103a2587ef98bafaef7a51e712250f9a753e Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 20 Dec 2016 12:35:17 +0100 Subject: [PATCH 340/344] Polishing --- .../ConfigurationClassPostProcessor.java | 12 ++++---- .../configuration/Spr10668Tests.java | 28 ++++++------------- 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java index 301256a6a8..6d08dfc71d 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassPostProcessor.java @@ -37,7 +37,6 @@ import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; @@ -91,12 +90,12 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor, PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware { - private static final String IMPORT_AWARE_PROCESSOR_BEAN_NAME = - ConfigurationClassPostProcessor.class.getName() + ".importAwareProcessor"; - private static final String IMPORT_REGISTRY_BEAN_NAME = ConfigurationClassPostProcessor.class.getName() + ".importRegistry"; + private static final String IMPORT_AWARE_PROCESSOR_BEAN_NAME = + ConfigurationClassPostProcessor.class.getName() + ".importAwareProcessor"; + private static final String ENHANCED_CONFIGURATION_PROCESSOR_BEAN_NAME = ConfigurationClassPostProcessor.class.getName() + ".enhancedConfigurationProcessor"; @@ -260,6 +259,7 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) // Simply call processConfigurationClasses lazily at this point then. processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory); } + enhanceConfigurationClasses(beanFactory); } @@ -455,10 +455,10 @@ public Object postProcessAfterInitialization(Object bean, String beanName) { /** * {@link InstantiationAwareBeanPostProcessorAdapter} that ensures * {@link EnhancedConfiguration} beans are injected with the {@link BeanFactory} - * before the {@link AutowiredAnnotationBeanPostProcessor} runs (SPR-10668). + * before the {@code AutowiredAnnotationBeanPostProcessor} runs (SPR-10668). */ private static class EnhancedConfigurationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter - implements PriorityOrdered, BeanFactoryAware { + implements BeanFactoryAware, PriorityOrdered { private BeanFactory beanFactory; diff --git a/spring-context/src/test/java/org/springframework/context/annotation/configuration/Spr10668Tests.java b/spring-context/src/test/java/org/springframework/context/annotation/configuration/Spr10668Tests.java index c00c2edb0f..f3868bea67 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/configuration/Spr10668Tests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/configuration/Spr10668Tests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,29 +38,20 @@ public class Spr10668Tests { @Test public void testSelfInjectHierarchy() throws Exception { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( - ChildConfig.class); + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ChildConfig.class); assertNotNull(context.getBean(MyComponent.class)); context.close(); } + @Configuration - public static class ParentConfig implements BeanFactoryAware { + public static class ParentConfig { @Autowired(required = false) MyComponent component; - - public ParentConfig() { - System.out.println("Parent " + getClass()); - } - - @Override - public void setBeanFactory(BeanFactory beanFactory) throws BeansException { - System.out.println("BFA " + getClass()); - } - } + @Configuration public static class ChildConfig extends ParentConfig { @@ -68,12 +59,11 @@ public static class ChildConfig extends ParentConfig { public MyComponentImpl myComponent() { return new MyComponentImpl(); } - } - public static interface MyComponent { - } - public static class MyComponentImpl implements MyComponent { - } + public interface MyComponent {} + + public static class MyComponentImpl implements MyComponent {} + } From 6e5b8736098f8c14c7d22b8cacf87d90c61f5361 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 20 Dec 2016 19:57:52 +0100 Subject: [PATCH 341/344] Polishing (cherry picked from commit 8662c61) --- .../support/BeanDefinitionReaderUtils.java | 35 ++++++++++--------- .../ConfigurableApplicationContext.java | 6 ++++ .../AnnotatedBeanDefinitionReader.java | 30 ++++++++++++++++ .../AnnotationConfigApplicationContext.java | 19 +++++----- .../support/GenericApplicationContext.java | 8 ++++- .../core/env/ConfigurableEnvironment.java | 23 ++++++------ .../springframework/core/env/Environment.java | 6 ++-- 7 files changed, 86 insertions(+), 41 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java index f56f0e527a..8a745dab2c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -69,6 +69,23 @@ public static AbstractBeanDefinition createBeanDefinition( return bd; } + /** + * Generate a bean name for the given top-level bean definition, + * unique within the given bean factory. + * @param beanDefinition the bean definition to generate a bean name for + * @param registry the bean factory that the definition is going to be + * registered with (to check for existing bean names) + * @return the generated bean name + * @throws BeanDefinitionStoreException if no unique name can be generated + * for the given bean definition + * @see #generateBeanName(BeanDefinition, BeanDefinitionRegistry, boolean) + */ + public static String generateBeanName(BeanDefinition beanDefinition, BeanDefinitionRegistry registry) + throws BeanDefinitionStoreException { + + return generateBeanName(beanDefinition, registry, false); + } + /** * Generate a bean name for the given bean definition, unique within the * given bean factory. @@ -117,22 +134,6 @@ else if (definition.getFactoryBeanName() != null) { return id; } - /** - * Generate a bean name for the given top-level bean definition, - * unique within the given bean factory. - * @param beanDefinition the bean definition to generate a bean name for - * @param registry the bean factory that the definition is going to be - * registered with (to check for existing bean names) - * @return the generated bean name - * @throws BeanDefinitionStoreException if no unique name can be generated - * for the given bean definition - */ - public static String generateBeanName(BeanDefinition beanDefinition, BeanDefinitionRegistry registry) - throws BeanDefinitionStoreException { - - return generateBeanName(beanDefinition, registry, false); - } - /** * Register the given bean definition with the given bean factory. * @param definitionHolder the bean definition including name and aliases diff --git a/spring-context/src/main/java/org/springframework/context/ConfigurableApplicationContext.java b/spring-context/src/main/java/org/springframework/context/ConfigurableApplicationContext.java index dfc3c2dbba..98befaf841 100644 --- a/spring-context/src/main/java/org/springframework/context/ConfigurableApplicationContext.java +++ b/spring-context/src/main/java/org/springframework/context/ConfigurableApplicationContext.java @@ -53,6 +53,7 @@ public interface ConfigurableApplicationContext extends ApplicationContext, Life * Name of the ConversionService bean in the factory. * If none is supplied, default conversion rules apply. * @see org.springframework.core.convert.ConversionService + * @since 3.0 */ String CONVERSION_SERVICE_BEAN_NAME = "conversionService"; @@ -60,12 +61,14 @@ public interface ConfigurableApplicationContext extends ApplicationContext, Life * Name of the LoadTimeWeaver bean in the factory. If such a bean is supplied, * the context will use a temporary ClassLoader for type matching, in order * to allow the LoadTimeWeaver to process all actual bean classes. + * @since 2.5 * @see org.springframework.instrument.classloading.LoadTimeWeaver */ String LOAD_TIME_WEAVER_BEAN_NAME = "loadTimeWeaver"; /** * Name of the {@link Environment} bean in the factory. + * @since 3.1 */ String ENVIRONMENT_BEAN_NAME = "environment"; @@ -84,6 +87,7 @@ public interface ConfigurableApplicationContext extends ApplicationContext, Life /** * Set the unique id of this application context. + * @since 3.0 */ void setId(String id); @@ -99,6 +103,7 @@ public interface ConfigurableApplicationContext extends ApplicationContext, Life /** * Return the Environment for this application context in configurable form. + * @since 3.1 */ @Override ConfigurableEnvironment getEnvironment(); @@ -106,6 +111,7 @@ public interface ConfigurableApplicationContext extends ApplicationContext, Life /** * Set the {@code Environment} for this application context. * @param environment the new environment + * @since 3.1 */ void setEnvironment(ConfigurableEnvironment environment); diff --git a/spring-context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java b/spring-context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java index 4c16bb0e64..da1fa78376 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/AnnotatedBeanDefinitionReader.java @@ -83,6 +83,7 @@ public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environmen AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); } + /** * Return the BeanDefinitionRegistry that this scanner operates on. */ @@ -117,21 +118,50 @@ public void setScopeMetadataResolver(ScopeMetadataResolver scopeMetadataResolver (scopeMetadataResolver != null ? scopeMetadataResolver : new AnnotationScopeMetadataResolver()); } + + /** + * Register one or more annotated classes to be processed. + *

      Calls to {@code register} are idempotent; adding the same + * annotated class more than once has no additional effect. + * @param annotatedClasses one or more annotated classes, + * e.g. {@link Configuration @Configuration} classes + */ public void register(Class... annotatedClasses) { for (Class annotatedClass : annotatedClasses) { registerBean(annotatedClass); } } + /** + * Register a bean from the given bean class, deriving its metadata from + * class-declared annotations. + * @param annotatedClass the class of the bean + */ + @SuppressWarnings("unchecked") public void registerBean(Class annotatedClass) { registerBean(annotatedClass, null, (Class[]) null); } + /** + * Register a bean from the given bean class, deriving its metadata from + * class-declared annotations. + * @param annotatedClass the class of the bean + * @param qualifiers specific qualifier annotations to consider, + * in addition to qualifiers at the bean class level + */ @SuppressWarnings("unchecked") public void registerBean(Class annotatedClass, Class... qualifiers) { registerBean(annotatedClass, null, qualifiers); } + /** + * Register a bean from the given bean class, deriving its metadata from + * class-declared annotations. + * @param annotatedClass the class of the bean + * @param name an explicit name for the bean + * @param qualifiers specific qualifier annotations to consider, + * in addition to qualifiers at the bean class level + */ @SuppressWarnings("unchecked") public void registerBean(Class annotatedClass, String name, Class... qualifiers) { AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass); diff --git a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java index 98ab303e29..4d3b74f551 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationConfigApplicationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -135,6 +135,16 @@ public void setScopeMetadataResolver(ScopeMetadataResolver scopeMetadataResolver this.scanner.setScopeMetadataResolver(scopeMetadataResolver); } + @Override + protected void prepareRefresh() { + this.scanner.clearCache(); + super.prepareRefresh(); + } + + + //--------------------------------------------------------------------- + // Implementation of AnnotationConfigRegistry + //--------------------------------------------------------------------- /** * Register one or more annotated classes to be processed. @@ -163,11 +173,4 @@ public void scan(String... basePackages) { this.scanner.scan(basePackages); } - - @Override - protected void prepareRefresh() { - this.scanner.clearCache(); - super.prepareRefresh(); - } - } diff --git a/spring-context/src/main/java/org/springframework/context/support/GenericApplicationContext.java b/spring-context/src/main/java/org/springframework/context/support/GenericApplicationContext.java index aff0017ef9..9f48c08615 100644 --- a/spring-context/src/main/java/org/springframework/context/support/GenericApplicationContext.java +++ b/spring-context/src/main/java/org/springframework/context/support/GenericApplicationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -158,6 +158,7 @@ public void setId(String id) { * Set whether it should be allowed to override bean definitions by registering * a different definition with the same name, automatically replacing the former. * If not, an exception will be thrown. Default is "true". + * @since 3.0 * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowBeanDefinitionOverriding */ public void setAllowBeanDefinitionOverriding(boolean allowBeanDefinitionOverriding) { @@ -169,6 +170,7 @@ public void setAllowBeanDefinitionOverriding(boolean allowBeanDefinitionOverridi * try to resolve them. *

      Default is "true". Turn this off to throw an exception when encountering * a circular reference, disallowing them completely. + * @since 3.0 * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowCircularReferences */ public void setAllowCircularReferences(boolean allowCircularReferences) { @@ -198,6 +200,10 @@ public void setResourceLoader(ResourceLoader resourceLoader) { } + //--------------------------------------------------------------------- + // ResourceLoader / ResourcePatternResolver override if necessary + //--------------------------------------------------------------------- + /** * This implementation delegates to this context's ResourceLoader if set, * falling back to the default superclass behavior else. diff --git a/spring-core/src/main/java/org/springframework/core/env/ConfigurableEnvironment.java b/spring-core/src/main/java/org/springframework/core/env/ConfigurableEnvironment.java index 9e7cb58e7d..5ba0763ac5 100644 --- a/spring-core/src/main/java/org/springframework/core/env/ConfigurableEnvironment.java +++ b/spring-core/src/main/java/org/springframework/core/env/ConfigurableEnvironment.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,24 +35,24 @@ * *

      Example: adding a new property source with highest search priority

      *
      - *   ConfigurableEnvironment environment = new StandardEnvironment();
      - *   MutablePropertySources propertySources = environment.getPropertySources();
      - *   Map myMap = new HashMap();
      - *   myMap.put("xyz", "myValue");
      - *   propertySources.addFirst(new MapPropertySource("MY_MAP", myMap));
      + * ConfigurableEnvironment environment = new StandardEnvironment();
      + * MutablePropertySources propertySources = environment.getPropertySources();
      + * Map myMap = new HashMap();
      + * myMap.put("xyz", "myValue");
      + * propertySources.addFirst(new MapPropertySource("MY_MAP", myMap));
        * 
      * *

      Example: removing the default system properties property source

      *
      - *   MutablePropertySources propertySources = environment.getPropertySources();
      - *   propertySources.remove(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME)
      + * MutablePropertySources propertySources = environment.getPropertySources();
      + * propertySources.remove(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME)
        * 
      * *

      Example: mocking the system environment for testing purposes

      *
      - *   MutablePropertySources propertySources = environment.getPropertySources();
      - *   MockPropertySource mockEnvVars = new MockPropertySource().withProperty("xyz", "myValue");
      - *   propertySources.replace(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, mockEnvVars);
      + * MutablePropertySources propertySources = environment.getPropertySources();
      + * MockPropertySource mockEnvVars = new MockPropertySource().withProperty("xyz", "myValue");
      + * propertySources.replace(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, mockEnvVars);
        * 
      * * When an {@link Environment} is being used by an {@code ApplicationContext}, it is @@ -64,7 +64,6 @@ * org.springframework.context.support.PropertySourcesPlaceholderConfigurer property * placeholder configurers}. * - * * @author Chris Beams * @since 3.1 * @see StandardEnvironment diff --git a/spring-core/src/main/java/org/springframework/core/env/Environment.java b/spring-core/src/main/java/org/springframework/core/env/Environment.java index 17767732f9..52c8717e94 100644 --- a/spring-core/src/main/java/org/springframework/core/env/Environment.java +++ b/spring-core/src/main/java/org/springframework/core/env/Environment.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -77,8 +77,8 @@ public interface Environment extends PropertyResolver { * activated by setting {@linkplain AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME * "spring.profiles.active"} as a system property or by calling * {@link ConfigurableEnvironment#setActiveProfiles(String...)}. - *

      If no profiles have explicitly been specified as active, then any {@linkplain - * #getDefaultProfiles() default profiles} will automatically be activated. + *

      If no profiles have explicitly been specified as active, then any + * {@linkplain #getDefaultProfiles() default profiles} will automatically be activated. * @see #getDefaultProfiles * @see ConfigurableEnvironment#setActiveProfiles * @see AbstractEnvironment#ACTIVE_PROFILES_PROPERTY_NAME From 7275fd2086363a5ab2becb7de5d6a71f07ae1388 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 20 Dec 2016 20:54:00 +0100 Subject: [PATCH 342/344] Polishing --- .../oxm/xstream/CatchAllConverter.java | 24 +++++++++---------- .../adapter/AbstractWebSocketSession.java | 7 +++--- .../adapter/jetty/JettyWebSocketSession.java | 11 ++++----- .../jetty/JettyRequestUpgradeStrategy.java | 17 ++++++------- 4 files changed, 29 insertions(+), 30 deletions(-) diff --git a/spring-oxm/src/main/java/org/springframework/oxm/xstream/CatchAllConverter.java b/spring-oxm/src/main/java/org/springframework/oxm/xstream/CatchAllConverter.java index ee799f973f..d129529cc8 100644 --- a/spring-oxm/src/main/java/org/springframework/oxm/xstream/CatchAllConverter.java +++ b/spring-oxm/src/main/java/org/springframework/oxm/xstream/CatchAllConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,27 +23,27 @@ import com.thoughtworks.xstream.io.HierarchicalStreamWriter; /** - * XStream {@link Converter} that supports all classes but throws exceptions - * for (un)marshalling. + * XStream {@link Converter} that supports all classes, but throws exceptions for + * (un)marshalling. * - *

      Main purpose of this class is to - * {@linkplain com.thoughtworks.xstream.XStream#registerConverter(Converter, int) register} - * this converter as a catch-all converter with a + *

      The main purpose of this class is to + * {@linkplain com.thoughtworks.xstream.XStream#registerConverter(com.thoughtworks.xstream.converters.Converter, int) register} + * this converter as a catch-all last converter with a * {@linkplain com.thoughtworks.xstream.XStream#PRIORITY_NORMAL normal} - * or higher priority, in addition to converters that explicitly support the domain - * classes that should be supported. As a result, default XStream converters with lower - * priorities and possible security vulnerabilities do not get invoked. + * or higher priority, in addition to converters that explicitly handle the domain + * classes that should be supported. As a result, default XStream converters with + * lower priorities and possible security vulnerabilities do not get invoked. * - *

      For instance:

      + *

      For instance: *

        * XStreamMarshaller unmarshaller = new XStreamMarshaller();
        * unmarshaller.getXStream().registerConverter(new MyDomainClassConverter(), XStream.PRIORITY_VERY_HIGH);
        * unmarshaller.getXStream().registerConverter(new CatchAllConverter(), XStream.PRIORITY_NORMAL);
      - * MyDomainClass o = unmarshaller.unmarshal(source);
      + * MyDomainClass myObject = unmarshaller.unmarshal(source);
        * 
      implements NativeWebSocketSess protected static final Log logger = LogFactory.getLog(NativeWebSocketSession.class); + private final Map attributes = new ConcurrentHashMap(); private T nativeSession; - private final Map attributes = new ConcurrentHashMap(); - /** * Create a new instance and associate the given attributes with it. - * * @param attributes attributes from the HTTP handshake to associate with the WebSocket * session; the provided attributes are copied, the original map is not used. */ @@ -83,7 +81,7 @@ public R getNativeSession(Class requiredType) { } public void initializeNativeSession(T session) { - Assert.notNull(session, "session must not be null"); + Assert.notNull(session, "WebSocket session must not be null"); this.nativeSession = session; } @@ -125,6 +123,7 @@ else if (message instanceof PongMessage) { protected abstract void sendPongMessage(PongMessage message) throws IOException; + @Override public final void close() throws IOException { close(CloseStatus.NORMAL); diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/adapter/jetty/JettyWebSocketSession.java b/spring-websocket/src/main/java/org/springframework/web/socket/adapter/jetty/JettyWebSocketSession.java index cfb0aec6d9..c4a98187db 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/adapter/jetty/JettyWebSocketSession.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/adapter/jetty/JettyWebSocketSession.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -62,7 +62,6 @@ public class JettyWebSocketSession extends AbstractWebSocketSession { /** * Create a new {@link JettyWebSocketSession} instance. - * * @param attributes attributes from the HTTP handshake to associate with the WebSocket session */ public JettyWebSocketSession(Map attributes) { @@ -71,11 +70,10 @@ public JettyWebSocketSession(Map attributes) { /** * Create a new {@link JettyWebSocketSession} instance associated with the given user. - * * @param attributes attributes from the HTTP handshake to associate with the WebSocket * session; the provided attributes are copied, the original map is not used. - * @param user the user associated with the session; if {@code null} we'll fallback on the user - * available via {@link org.eclipse.jetty.websocket.api.Session#getUpgradeRequest()} + * @param user the user associated with the session; if {@code null} we'll fallback on the + * user available via {@link org.eclipse.jetty.websocket.api.Session#getUpgradeRequest()} */ public JettyWebSocketSession(Map attributes, Principal user) { super(attributes); @@ -156,9 +154,10 @@ public int getBinaryMessageSizeLimit() { @Override public boolean isOpen() { - return ((getNativeSession() != null) && getNativeSession().isOpen()); + return (getNativeSession() != null && getNativeSession().isOpen()); } + @Override public void initializeNativeSession(Session session) { super.initializeNativeSession(session); diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/jetty/JettyRequestUpgradeStrategy.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/jetty/JettyRequestUpgradeStrategy.java index 51c5aaeac0..f89e0f7831 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/server/jetty/JettyRequestUpgradeStrategy.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/jetty/JettyRequestUpgradeStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; - import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -75,12 +74,12 @@ public class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy, Life private final WebSocketServerFactory factory; - private volatile List supportedExtensions; - private ServletContext servletContext; private volatile boolean running = false; + private volatile List supportedExtensions; + /** * Default constructor that creates {@link WebSocketServerFactory} through @@ -215,16 +214,18 @@ private static class WebSocketHandlerContainer { private final List extensionConfigs; - public WebSocketHandlerContainer(JettyWebSocketHandlerAdapter handler, String protocol, List extensions) { + public WebSocketHandlerContainer( + JettyWebSocketHandlerAdapter handler, String protocol, List extensions) { + this.handler = handler; this.selectedProtocol = protocol; if (CollectionUtils.isEmpty(extensions)) { this.extensionConfigs = null; } else { - this.extensionConfigs = new ArrayList(); - for (WebSocketExtension e : extensions) { - this.extensionConfigs.add(new WebSocketToJettyExtensionConfigAdapter(e)); + this.extensionConfigs = new ArrayList(extensions.size()); + for (WebSocketExtension extension : extensions) { + this.extensionConfigs.add(new WebSocketToJettyExtensionConfigAdapter(extension)); } } } From 43bf008fbcd0d7945e2fcd5e30039bc4d74c7a98 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Wed, 21 Dec 2016 11:02:29 +0100 Subject: [PATCH 343/344] Normalize resource URL in ResourceServlet Issue: SPR-14946 --- .../web/servlet/ResourceServlet.java | 10 +-- .../web/servlet/ResourceServletTests.java | 61 +++++++++++++++++++ 2 files changed, 66 insertions(+), 5 deletions(-) create mode 100644 spring-webmvc/src/test/java/org/springframework/web/servlet/ResourceServletTests.java diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/ResourceServlet.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/ResourceServlet.java index 446c2bf1fc..d0a3ba7921 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/ResourceServlet.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/ResourceServlet.java @@ -270,18 +270,18 @@ private void doInclude(HttpServletRequest request, HttpServletResponse response, if (this.contentType != null) { response.setContentType(this.contentType); } - String[] resourceUrls = StringUtils.tokenizeToStringArray(resourceUrl, RESOURCE_URL_DELIMITERS); for (String url : resourceUrls) { + String path = StringUtils.cleanPath(url); // Check whether URL matches allowed resources - if (this.allowedResources != null && !this.pathMatcher.match(this.allowedResources, url)) { - throw new ServletException("Resource [" + url + + if (this.allowedResources != null && !this.pathMatcher.match(this.allowedResources, path)) { + throw new ServletException("Resource [" + path + "] does not match allowed pattern [" + this.allowedResources + "]"); } if (logger.isDebugEnabled()) { - logger.debug("Including resource [" + url + "]"); + logger.debug("Including resource [" + path + "]"); } - RequestDispatcher rd = request.getRequestDispatcher(url); + RequestDispatcher rd = request.getRequestDispatcher(path); rd.include(request, response); } } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/ResourceServletTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/ResourceServletTests.java new file mode 100644 index 0000000000..2ba08e26b6 --- /dev/null +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/ResourceServletTests.java @@ -0,0 +1,61 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.web.servlet; + +import java.io.IOException; +import javax.servlet.ServletException; + +import org.junit.Test; + +import org.springframework.mock.web.test.MockHttpServletRequest; +import org.springframework.mock.web.test.MockHttpServletResponse; +import org.springframework.mock.web.test.MockServletConfig; + +/** + * @author Rossen Stoyanchev + */ +public class ResourceServletTests { + + @Test(expected = ServletException.class) + public void example1() throws Exception { + testInvalidResourceUrl("/resources/**", "/resources/../WEB-INF/web.xml"); + } + + @Test(expected = ServletException.class) + public void example2() throws Exception { + testInvalidResourceUrl("/resources/*", "/resources/..\\WEB-INF\\web.xml"); + } + + @Test(expected = ServletException.class) + public void example3() throws Exception { + testInvalidResourceUrl("/resources/*", "/resources/..\\Servlet2?param=111"); + } + + private void testInvalidResourceUrl(String allowedResources, String resourceParam) + throws ServletException, IOException { + + ResourceServlet servlet = new ResourceServlet(); + servlet.setAllowedResources(allowedResources); + servlet.init(new MockServletConfig()); + + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/"); + request.addParameter("resource", resourceParam); + MockHttpServletResponse response = new MockHttpServletResponse(); + + servlet.service(request, response); + } + +} From 9c97585f3d0ed26cf7fa51723629fc47fb4fa75c Mon Sep 17 00:00:00 2001 From: Spring Buildmaster Date: Wed, 21 Dec 2016 12:20:25 +0000 Subject: [PATCH 344/344] Next Development Version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 140c68c4ae..a09ae3bc47 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=4.2.9.BUILD-SNAPSHOT +version=4.2.10.BUILD-SNAPSHOT