Compare commits

...

156 Commits

Author SHA1 Message Date
Spring Buildmaster 0f95a01131 Release version 5.1.15.RELEASE 2020-04-28 07:29:27 +00:00
Juergen Hoeller a4cc16051c Polishing 2020-04-27 14:29:56 +02:00
Juergen Hoeller c8b49346b1 Store source in index-derived ScannedGenericBeanDefinition as well
Includes consistent constructor-level storage of derived resource in ScannedGenericBeanDefinition and ConfigurationClassBeanDefinition.

See gh-24978
2020-04-27 14:29:41 +02:00
Rossen Stoyanchev 34b9ca310d Ugrade to Reactor Californium-SR18
Closes gh-24893
2020-04-27 13:07:18 +01:00
Juergen Hoeller ea7b0103c5 Upgrade to Tomcat 9.0.34, Jetty 9.4.28, Netty 4.1.49, BeanShell 2.0b6, Checkstyle 8.31 2020-04-25 17:15:03 +02:00
Juergen Hoeller e3175a2dc9 Polishing 2020-04-25 17:13:44 +02:00
Juergen Hoeller 91316dc11b Caching of autowire candidate metadata for injection point matching
Closes gh-24904
2020-04-25 17:01:22 +02:00
Juergen Hoeller ac8d1df3ab Enforce limit for storing suppressed exceptions
Closes gh-24902
2020-04-25 17:01:09 +02:00
Sébastien Deleuze f023df703a Remove potentially confusing kotlin-reflect related log message
Closes gh-24939
2020-04-20 10:39:57 +02:00
Sam Brannen 9ab7cde00e Retain brackets for IPV6 address in MockHttpServletRequest
According to the Javadoc for ServletRequest's getServerName() method,
when the `Host` header is set, the server name is "the value of the
part before ':' in the Host header value ...". For a value representing
an IPV6 address such as `[::ffff:abcd:abcd]`, the enclosing square
brackets should therefore not be stripped from the enclosed IPV6
address.

However, the changes made in conjunction with gh-16704 introduced a
regression in Spring Framework 4.1 for the getServerName() method in
MockHttpServletRequest by stripping the enclosing brackets from the
IPV6 address in the `Host` header. Similarly, the changes made in
conjunction with gh-20686 introduced a regression in Spring Framework
4.3.13 and 5.0.2 in the getRequestURL() method in
MockHttpServletRequest by delegating to the getServerName() method
which strips the enclosing brackets.

This commit fixes the implementation of getServerName() so that the
enclosing brackets are no longer stripped from an IPV6 address in the
`Host` header. The implementation of getRequestURL() is therefore also
fixed.

In addition, in order to avoid a NullPointerException, the
implementations of getServerName() and getServerPort() now assert that
an IPV6 address present in the `Host` header correctly contains an
opening and closing bracket and throw an IllegalStateException if that
is not the case.

Closes gh-24916
2020-04-17 16:22:51 +02:00
Stephane Nicoll fad9b509ab Polish 2020-04-10 17:19:40 +02:00
Stephane Nicoll 5ddbefecea Start building against Reactor Californium-SR18 snapshots
See gh-24893
2020-04-10 17:18:51 +02:00
Juergen Hoeller a4bba6aff2 Honor overridden AcceptHeaderLocaleContextResolver.getDefaultLocale()
Closes gh-24871
2020-04-09 12:03:29 +02:00
Juergen Hoeller 906583c40a Upgrade to Tomcat 9.0.33, Hibernate ORM 5.3.16, Hibernate Validator 6.0.19 2020-04-03 21:50:28 +02:00
Juergen Hoeller 3689b3fdd5 Recursively copy directory with symbolic link
Closes gh-24823
2020-04-03 21:46:07 +02:00
Juergen Hoeller 4a5bac97b5 Close-suppressing Connection proxy exposes target isClosed() state
Closes gh-24853
2020-04-03 21:43:25 +02:00
Juergen Hoeller 45a270b71f Clear by-type cache in case of no pre-existing bean definition as well
Closes gh-24852
2020-04-03 21:43:18 +02:00
Rossen Stoyanchev ef013f7158 UriComponentsBuilder.cloneBuilder copies uriVariables
Closes gh-24772
2020-03-26 16:41:21 +00:00
Juergen Hoeller 818d18aa09 Expose context class for interface-based CGLIB proxies as well
Closes gh-24657
2020-03-26 17:31:31 +01:00
Juergen Hoeller 4715ad242e Upgrade to Netty 4.1.48 and Checkstyle 8.30 2020-03-24 01:00:41 +01:00
Juergen Hoeller e51f660286 Polishing 2020-03-24 00:56:36 +01:00
Juergen Hoeller 9f71c98871 Expose parameter annotations from interfaces across entire class hierarchy
Closes gh-24127
2020-03-24 00:53:40 +01:00
Juergen Hoeller dbb09334f3 Thread-safe access to WebSocketServerFactory and WebSocketExtensions
Closes gh-24745
2020-03-24 00:53:32 +01:00
Juergen Hoeller 5953d99d1c Mark MediaTypeNotSupportedStatusException as deprecated
Closes gh-24743
2020-03-24 00:25:53 +01:00
Rossen Stoyanchev c9be4f6874 Upgrade to Reactor Californium-SR17
Closes gh-24726
2020-03-23 15:47:27 +00:00
Rossen Stoyanchev fbc63b0e32 Switch to Reactor Californium snapshots
See gh-24726
2020-03-18 17:30:27 +00:00
Juergen Hoeller 5f7df0d14f Latest dependency updates (RxJava 2.2.19, Undertow 2.0.30, Netty 4.1.47, Jetty 9.4.27, POI 4.1.2, OkHttp 3.14.7) 2020-03-13 23:42:30 +01:00
Juergen Hoeller ee832206dc Polishing 2020-03-13 23:41:42 +01:00
Juergen Hoeller 1e295c7bbb Create ScannedGenericBeanDefinitions for index-derived components as well
Closes gh-24638
2020-03-13 23:41:31 +01:00
Arjen Poutsma 4fbba931a5 Improve RouterFunction composition
This commit changes the way two RouterFunctions are composed in
WebFlux.fn. Prior to this commit, two were composed with
`switchIfEmpty()`, switching from the first to the second route if the
first did not provide an element.
After this commit, two router functions are compose using `concat`,
which results in a smaller stack trace.

See gh-24652
2020-03-13 16:26:15 +01:00
Spring Buildmaster 86f19f6cb6 Next Development Version 2020-02-25 16:23:19 +00:00
Juergen Hoeller df60919bb0 Polishing 2020-02-25 15:32:05 +01:00
Juergen Hoeller 2f1f36eeda Upgrade to Groovy 2.5.9, RxJava 2.2.18, Tomcat 9.0.31, Jetty 9.4.26, Netty 4.1.45, Checkstyle 8.29 2020-02-24 19:44:25 +01:00
Juergen Hoeller 1c0a4f8d78 Polishing 2020-02-24 19:34:15 +01:00
Juergen Hoeller cc376ac5ed Clarify setCacheMillis/setCacheSeconds vs java.util.ResourceBundle
Closes gh-24563
2020-02-24 19:10:25 +01:00
Rossen Stoyanchev 10c9d2fcec Upgrade to Reactor Calfiornium-SR16
Closes gh-24527
2020-02-24 14:57:34 +00:00
Arjen Poutsma 439ffe2e8a Convert non-UTF-8 JSON
Jackson's asynchronous parser does not support any encoding except UTF-8
(or ASCII). This commit converts non-UTF-8/ASCII encoded JSON to UTF-8.

Closes gh-24489
2020-02-20 11:21:00 +01:00
Rossen Stoyanchev 4e55262521 Upgrade to Reactor Californium snapshots
See gh-24527
2020-02-19 16:26:17 +00:00
Juergen Hoeller ebd2ec57fc Polishing 2020-02-12 17:58:26 +01:00
Rossen Stoyanchev f536819c5d Update advice on RestTemplate
Closes gh-24503
2020-02-12 17:19:57 +01:00
Juergen Hoeller af75c6db2f Consistent ROLE_INFRASTRUCTURE declarations for configuration classes
Closes gh-24509
2020-02-12 17:18:59 +01:00
Juergen Hoeller d12619cf1f Raise log level for exceptions from EntityManager close call
Closes gh-24501
2020-02-12 17:10:32 +01:00
Sam Brannen 4c1d227776 Polish documentation format
Closes gh-24462
2020-02-12 15:47:17 +01:00
Sam Brannen da02b7a141 Support SpEL compilation of interface methods again
Spring Framework 5.1.8 introduced a regression for the compilation of
SpEL expressions referencing a method declared in an interface. An
attempt to compile such an expression resulted in a
SpelEvaluationException caused by an IncompatibleClassChangeError.

This commit fixes this regression by adding explicit support in
ReflectivePropertyAccessor.OptimalPropertyAccessor.generateCode() for
methods declared in interfaces.

Closes gh-24357
2020-02-12 15:40:17 +01:00
Arjen Poutsma 17117bd21a Force TokenBuffer to use BigDecimal if necessary
This commit makes the Jackson2Tokenizer enable
TokenBuffer.forceUseOfBigDecimal if the element type given to the
Decoder is BigDecimal. Previous to this commit, values would be
converted to floats.

Closes gh-24479
2020-02-07 14:45:58 +01:00
Arjen Poutsma 0d7494ac52 Honour ObjectMapper feature in Jackson2Tokenizer
After this commit, Jackson2Tokenizer honours ObjectMapper's
DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS feature when creating
TokenBuffers.

Closes gh-24479
2020-02-06 17:12:05 +01:00
Christoph Dreis a59a33846e Polish documentation format
See gh-24462
2020-01-31 08:57:30 +01:00
Juergen Hoeller 51b8ba334a Polishing 2020-01-28 21:40:16 +01:00
Juergen Hoeller abc2269d97 Use local LoggerContext in Log4jLog when static field not initialized yet
Closes gh-24440
2020-01-28 21:21:30 +01:00
Juergen Hoeller ff385aaf7b Add Informix to supported database products for procedure calls
Closes gh-24443
2020-01-28 21:20:46 +01:00
Juergen Hoeller 1b50ca6bd3 Solve ReflectPermission issue in sandbox security policy model for CGLIB
Closes gh-24420
2020-01-28 21:19:15 +01:00
Arjen Poutsma 6bae759349 Use Jackson SequenceWriter for streaming
Before this commit, the AbstractJackson2Encoder instantiated a
ObjectWriter per value. This is not an issue for single values or
non-streaming scenarios (which effectively are the same, because in the
latter values are collected into a list until offered to Jackson).
However, this does create a problem for SMILE, because it allows for
shared references that do not match up when writing each value with a
new ObjectWriter, resulting in errors parsing the result.

This commit uses Jackson's SequenceWriter for streaming scenarios,
allowing Jackson to reuse the same context for writing multiple values,
fixing the issue described above.

Closes gh-24198
2020-01-22 10:12:05 +01:00
Brian Clozel e1e8c165db Avoid setting special Content-* response headers for Tomcat
As of gh-21783, Spring WebFlux uses a `TomcatHeadersAdapter`
implementation to directly address the native headers used by the
server.

In the case of Tomcat, "Content-Length" and "Content-Type" headers are
processed separately and should not be added to the native headers map.

This commit improves the `HandlerAdapter` implementation for Tomcat and
removes those headers, if previously set in the map. The adapter
already has a section that handles the Tomcat-specific calls for such
headers.

Fixes gh-24387
2020-01-17 15:15:14 +01:00
Sam Brannen 6699833121 Introduce regression test for gh-24375 2020-01-16 16:34:21 +01:00
Sam Brannen 095acefd7e Include Objenesis NOTICE file contents in binary distributions
Closes gh-24326
2020-01-16 10:54:23 +01:00
Spring Buildmaster b62e066b7d Next Development Version 2020-01-14 07:37:59 +00:00
Rossen Stoyanchev 9969cb6d83 Improve limit handling in StringDecoder
The case of one data buffer containing multiple lines can could cause
a buffer leak due to a suspected issue in concatMapIterable. This
commit adds workarounds for that until the underlying issue is
addressed.

Closes gh-24346
2020-01-13 20:46:51 +00:00
Rossen Stoyanchev 04b3f5a247 Upgrade to Reactor Californium SR15
Closes gh-24345
2020-01-13 17:09:38 +00:00
Sam Brannen c91b47fded Document Objenesis license in license.txt
Closes gh-24340
2020-01-13 16:08:14 +01:00
Sam Brannen f2c364b491 Update ASM and CBLIB versions in license.txt 2020-01-13 13:22:19 +01:00
Juergen Hoeller 170adccf80 Upgrade to RxJava 2.2.17, OkHttp 3.14.6, Hibernate ORM 5.3.15 2020-01-13 10:58:51 +01:00
Brian Clozel 98390e1e5e Copy strategiesConfigurers when cloning WebClient.Builder
This commit fixes the missing `strategiesConfigurers` copy when the
`WebClient.Builder` is cloned.

Fixes gh-24330
See gh-24329
2020-01-10 16:08:04 +01:00
Juergen Hoeller 6df8c2678b Upgrade to SLF4J 1.7.30 and Checkstyle 8.28 2020-01-09 17:13:16 +01:00
Juergen Hoeller 7f49abb6d1 Upgrade to Netty 4.1.44, Jetty 9.4.25, Undertow 2.0.29, OkHttp 3.14.5 2020-01-09 16:17:33 +01:00
Juergen Hoeller b9bf56e41d Polishing 2020-01-09 16:16:36 +01:00
Juergen Hoeller d0e3e2acfc Thread-safe compiled expression evaluation in SpelExpression
Closes gh-24265
2020-01-09 16:16:01 +01:00
Rossen Stoyanchev 18d983c686 Multi-value headers in ResponseStatusException
Closes gh-24284
2020-01-06 22:05:29 +00:00
Rossen Stoyanchev d8abbc501e CorsInterceptor skips async dispatch
Closes gh-24223
2020-01-06 21:24:02 +00:00
Rossen Stoyanchev 6ce19ff861 Escape quotes in filename
Also sync up with master on refactorings in ContentDisposition and
ContentDispositionTests.

Closes gh-24224
2020-01-06 18:18:56 +00:00
Juergen Hoeller c8ef49cc8e Upgrade to RxJava 2.2.16 2019-12-16 17:06:08 +01:00
Juergen Hoeller f61983d908 Polishing 2019-12-16 17:05:48 +01:00
Juergen Hoeller 7615e0b036 Un-deprecate PathResource (for NIO Path resolution in createRelative)
Includes aligned createRelative signature and dedicated java.io.File test.

Closes gh-24211
2019-12-16 17:05:28 +01:00
Juergen Hoeller afe22b84c2 ConcurrentReferenceHashMap cache for getInterfaceMethodIfPossible results
Closes gh-24206
2019-12-16 16:55:10 +01:00
Rossen Stoyanchev 634aba4ab6 Fix cloning issue in CodecConfigurer for multipart writers
Backport of b23617637d

Closes gh-24194
2019-12-13 06:38:49 +00:00
Rossen Stoyanchev 26a2d3875f Expose ClientCodecConfigurer in WebClient.Builder
Using Consumer<ClientCodecConfigurer> instead of
Consumer<ExchangeStrategies> eliminates one level of nesting that is
also unnecessary since codecs are the only strategy at present.

Backport of dd9b6287b4

Closes gh-24201
2019-12-13 06:38:49 +00:00
Rossen Stoyanchev 802d083df7 Add register methods to CodecConfigurer.CustomCodecs
The new register methods replace the now deprecated
encoder, decoder, reader, and writer methods, and also offer a choice
to opt into default properties such maxInMemorySize, if configured.

Backport of 11e321b8e7

See gh-24201
2019-12-13 06:38:43 +00:00
Rossen Stoyanchev f5b43a264a CodecConfigurer implementation refactoring
Backport of 9d65830133

See gh-24201
2019-12-13 06:23:40 +00:00
Rossen Stoyanchev 59e4755562 Correct WebFlux docs on BindingResult with @RequestBody
Backport of 70a0c93d69

Closes gh-22997
2019-12-13 05:54:22 +00:00
Rossen Stoyanchev 2576aa4063 ContentDisposition trims charset in filename
Backport of c8bce9686f

Closes gh-24112
2019-12-13 05:46:09 +00:00
Juergen Hoeller 3fbe762832 Consistent use of annotation-api dependency instead of tomcat-embed-core 2019-12-12 17:12:27 +01:00
Juergen Hoeller 611bb0b92e Explicit test dependency on javax.annotation.Priority 2019-12-12 17:01:18 +01:00
Juergen Hoeller 32a8b9b25a Polishing 2019-12-12 16:48:25 +01:00
Juergen Hoeller 97cad6ca8e Upgrade to Tomcat 9.0.30 2019-12-12 16:48:11 +01:00
Juergen Hoeller 015f7d8ce1 Polishing 2019-12-11 17:13:02 +01:00
Juergen Hoeller da4e2710b4 Upgrade to Joda-Time 2.10.5 and Commons Pool 2.6.2 2019-12-10 00:38:21 +01:00
Juergen Hoeller a368040fd6 Polishing 2019-12-09 15:54:40 +01:00
Juergen Hoeller 5dbd3b0bbf Avoid ByteArrayOutputStream for source values without the need to be encoded
Closes gh-24154
2019-12-09 13:56:50 +01:00
Juergen Hoeller 197dbffe20 Support variable resolution of wildcard types
Includes cleanup of "varaible" typos in ResolvableTypeTests.

Closes gh-24150
2019-12-09 13:56:25 +01:00
Sam Brannen 5341e77796 Introduce regression tests for prototype/request-scoped @ControllerAdvice
See gh-24157
2019-12-07 19:02:06 +01:00
Sam Brannen f049a6ec16 Add integration test for gh-24110 2019-12-03 17:12:14 +01:00
Spring Buildmaster 7203da7133 Next Development Version 2019-12-03 08:06:03 +00:00
Brian Clozel a21df0cc6d Provide default codecs config callback to custom codecs
As a follow-up of gh-23961, this change provides a way for custom codecs
to align with the default codecs' behavior on common features like
buffer size limits and logging request details.

Closes gh-24119
Co-authored-by: Rossen Stoyanchev <rstoyanchev@pivotal.io>
2019-12-02 23:16:49 +01:00
Brian Clozel 83683a13bb Allow ExchangeStrategies customizations in WebClient
Backport of d4209392 and acfeb77d

Closes gh-23961
2019-12-02 17:10:29 +00:00
Juergen Hoeller 59165dd526 Upgrade to AspectJ 1.9.5 and Checkstyle 8.27 2019-12-02 13:10:07 +01:00
Brian Clozel 25554d0b21 Revert "Allow ExchangeStrategies customizations in WebClient"
This reverts commit 43e047c523.
2019-12-02 10:29:24 +01:00
Juergen Hoeller 4f86282b14 Polishing 2019-12-01 01:55:26 +01:00
Juergen Hoeller b3237f3eb6 Polishing 2019-12-01 00:41:53 +01:00
Juergen Hoeller fd68fb115d Backport of recent ExtendedBeanInfo refinements from master
Closes gh-24095
2019-12-01 00:40:57 +01:00
Brian Clozel 43e047c523 Allow ExchangeStrategies customizations in WebClient
Prior to this commit, developers could configure their WebClient to use
their custom `ExchangeStrategies`, by providing it in the
`WebClient.Builder` chain.
Once created, an `ExchangeStrategies` instance is not mutable, which
makes it hard for further customizations by other components. In the
case of the reported issue, other components would override the default
configuration for the codecs maxInMemorySize.

This commit makes the `ExchangeStrategies` mutable and uses that fact to
further customize them with a new `WebClient.Builder#exchangeStrategies`
`Consumer` variant. This commit is also deprecating those mutating
variants in favor of a new `WebClient.Builder#exchangeStrategies` that
takes a `ExchangeStrategies#Builder` directly and avoids mutation issues
altogether.

Closes gh-24106
2019-11-29 23:35:20 +01:00
Sam Brannen 7fdf775394 Test status quo for @Inherited annotations in AnnotationMetadata
See gh-24077
2019-11-28 18:19:44 +01:00
Sam Brannen 59084c6b73 Test status quo for AnnotatedTypeMetadata.getAnnotationAttributes()
See gh-24077
2019-11-28 13:46:35 +01:00
Stephane Nicoll 4fcc09a02c Upgrade to Reactor Californium-SR14
Closes gh-24038
2019-11-27 11:51:27 +01:00
Arjen Poutsma f5b082d3b3 Remove println 2019-11-25 18:27:13 +01:00
Arjen Poutsma c164759c41 Fix NullPointerException in Jackson2SmileDecoder
Fix uncommon case in Jackson2SmileDecoder, where a null token,
incicating a document separator in streaming mode, is followed by
NOT_AVAILABLE.

Closes gh-24009

(cherry picked from commit 5f3c7ca559)
2019-11-25 16:37:36 +01:00
Juergen Hoeller 2179b67706 Upgrade to Tomcat 9.0.29, Jetty 9.4.24, RxJava 2.2.15 2019-11-24 21:47:25 +01:00
Rossen Stoyanchev fbde98f36e Add missing verify() in Jackson2TokenizerTests
Closes gh-24056
2019-11-22 16:39:14 +00:00
Rossen Stoyanchev 529f8ba786 Extra isReady-onWritePossible after last write
Closes gh-24050
2019-11-22 15:58:25 +00:00
Juergen Hoeller f4676bb41c Restore short-circuiting in equals implementation
Closes gh-24048
2019-11-21 18:25:37 +01:00
Juergen Hoeller 262332a7c9 Upgrade to Jetty 9.4.23 and Woodstox 5.3 2019-11-21 13:48:55 +01:00
Juergen Hoeller 8b1709b8f3 Upgrade to Hibernate Validator 6.0.18 and Undertow 2.0.28 2019-11-20 17:45:18 +01:00
Juergen Hoeller aee33d8b4b Mark SqlRowSet accessor methods as nullable (for alignment with JDBC)
Closes gh-24042
2019-11-20 17:42:11 +01:00
Stephane Nicoll 83f03976ea Start building against Reactor Californium-SR14 snapshots
See gh-24038
2019-11-20 11:02:08 +01:00
Sam Brannen ffcd83e3a8 Ignore scoped proxy targets for @ControllerAdvice beans
Prior to this commit, methods in a @ControllerAdvice bean were
registered and invoked twice if the advice was a scoped bean (e.g.,
request or session scoped). In other words, both the proxy bean and the
target bean were wrapped in ControllerAdviceBean instances.

This commit fixes this bug by modifying the findAnnotatedBeans() method
in ControllerAdviceBean so that it filters out targets of scoped
proxies.

Closes gh-24017
2019-11-19 15:21:48 +01:00
Sam Brannen 9a522946a5 Backport tests for gh-23985 2019-11-19 15:21:48 +01:00
Juergen Hoeller 268d029584 Avoid substring allocation in StringUtils.replace
Closes gh-24023
2019-11-19 15:04:15 +01:00
Juergen Hoeller 85471d0587 Revise getElementTypeDescriptor javadoc (no IllegalStateException)
Closes gh-23996
2019-11-14 18:34:19 +01:00
Sam Brannen 96a1a0dec5 Convert InstantFormatterTests to JUnit 4
See gh-23895
2019-11-14 12:01:11 +01:00
Sam Brannen 40ac055d11 Polish contribution
See gh-23895
2019-11-14 12:01:11 +01:00
monosoul c9a6f4282a Inverse condition to fix ISO-formatted Instant parsing
Prior to this commit, InstantFormatter was able to properly serialize
an Instant that is far in the future (or in the past), but it could not
properly deserialize it, because in such scenarios an ISO-formatted
Instant starts with a +/- sign.

This commit fixes this issue, while maintaining the previous contract,
and also introduces tests for InstantFormatter.

Closes gh-23895
2019-11-14 10:10:43 +01:00
Juergen Hoeller 6ed6c08ace Polishing 2019-11-13 23:49:33 +01:00
Juergen Hoeller e4c57a9b61 Minor internal refinements (backported from master) 2019-11-13 16:53:23 +01:00
Juergen Hoeller b0d8a667a1 Upgrade to Hibernate ORM 5.3.14 2019-11-13 16:51:47 +01:00
Juergen Hoeller 9c9ee22c2b Note on injecting results from local @Bean methods (self references)
Closes gh-23934
2019-11-13 16:50:47 +01:00
Juergen Hoeller 639dce6282 Support for new MySQL 8 error code 3572
Closes gh-23972
2019-11-13 16:25:30 +01:00
YuDongYing 8e65834c44 Fix schemaZip Gradle task on MS Windows
Prior to this commit, the schemaZip Gradle task failed to find Spring
schema files on MS Windows due to path separators hard coded to forward
slashes that are not compatible with the Windows operating system.

Consequently, a full build failed on Windows since the distZip task was
not able to locate the zipped schema archive that the schemaZip task
failed to create.

This commit fixes this by updating the schemaZip task to search for
schema files using backslashes as well as forward slashes.

Closes gh-23933
2019-11-13 15:02:49 +01:00
Rossen Stoyanchev 64db939e4a Javadoc update for content negotiation
Closes gh-23409
2019-11-11 12:08:05 +00:00
Sam Brannen 3d83f869d9 Fix Javadoc for SmartLifecycle.DEFAULT_PHASE regarding ordering
Closes gh-23956
2019-11-09 18:03:43 +01:00
Juergen Hoeller 088a653318 Upgrade to RxJava 2.2.14, OkHttp 3.14.4, Awaitility 3.1.6 2019-11-07 22:35:30 +01:00
Juergen Hoeller bdb1a81a39 Use Method.getParameterCount() when full type array is never needed 2019-11-07 22:35:21 +01:00
Arjen Poutsma c2e7b6341c Polishing 2019-11-07 15:03:28 +01:00
Arjen Poutsma e75556bc7b Polishing 2019-11-07 15:02:34 +01:00
Arjen Poutsma d3d40983d6 Corrupt multipart should not hang SynchronossPartHttpMessageReader
This commit notifies the Synchronoss listener that the buffer stream
has ended.

Closes gh-23768

(cherry picked from commit bf36f49dc5)
2019-11-07 14:48:18 +01:00
Arjen Poutsma 32adf77b22 Corrupt multipart should not hang SynchronossPartHttpMessageReader
This commit notifies the Synchronoss listener that the buffer stream
has ended.

See gh-23768
2019-11-07 09:42:36 +01:00
Rossen Stoyanchev 1301c7e95d Update WebMvcConfigurer#addInterceptors Javadoc
Closes gh-23908
2019-11-06 21:38:32 +00:00
Rossen Stoyanchev 1a057654b2 Defer ExchangeFilterFunction to subscription time
Previously fixed in 5.2 via d46359. Now also backported to 5.1.x.

Closes gh-23909
2019-11-06 21:38:32 +00:00
Sam Brannen ca3440cb42 Re-enable support for invalid Expires attributes in MockCookie
Changes introduced in commit 9b2087618b
caused a regression for Cookie support in MockHttpServletResponse.
Specifically, an Expires attribute that cannot be parsed using
`ZonedDateTime.parse()` now results in an exception; whereas,
previously an entry such as `Expires=0` was allowed.

This commit fixes this issue in MockCookie by catching and ignoring any
DateTimeException thrown while attempting to parse an Expires attribute.

Closes gh-23911
2019-11-06 22:23:20 +01:00
Rossen Stoyanchev 48b22292ff Fix issue with path matching options
Closes gh-23907
2019-11-06 18:13:10 +00:00
Rossen Stoyanchev e0faaa4807 Relax domain name checks in ResponseCookie
Closes gh-23924
2019-11-06 18:05:44 +00:00
Rossen Stoyanchev 2e4944198d Fix condition in ServletInvocableHandlerMethod
Closes gh-23775
2019-11-06 18:02:28 +00:00
Spring Buildmaster 38a1caefb8 Next Development Version 2019-11-02 07:17:14 +00:00
Rossen Stoyanchev e731a0a164 Use int for maxParts instead of long 2019-10-31 14:16:48 +00:00
Brian Clozel 0f2efdbe97 Polish
See gh-23884
2019-10-30 19:58:06 +01:00
Brian Clozel feeeab1761 Reorder date formatting converter in registrar
Prior to this commit, the `DateFormatterRegistrar` would register the
annotation-based formatter before the pattern-based formatter. This
would create an issue when an application tries to convert a `String` to
an annotated `@DateTimeFormat Date`: since the converters are considered
in reversed order of registration in
`GenericConversionServicei#ConvertersForPair`, the pattern-based variant
would always be considered before the annotation-based variant,
overriding the developer's opinion.

This commit aligns the `DateFormatterRegistrar` with the
`DateTimeFormatterRegistrar` and registers the annotation-based variant
last.

Closes gh-23896
2019-10-30 17:34:49 +01:00
Juergen Hoeller b4cf471021 Polishing 2019-10-30 16:45:35 +01:00
Juergen Hoeller 82751141ac Revise concurrent JAXBContext creation towards computeIfAbsent
Closes gh-23879
2019-10-30 16:26:13 +01:00
Sam Brannen 9f43ee3304 Treat InvalidPathException like an IOException in MockServletContext
Prior to this commit, if MockServletContext was configured with a
FileSystemResourceLoader, invocations of the following methods on a
Microsoft Windows operating system resulted in an InvalidPathException
if the supplied path contained a colon (such as "C:\\temp"). This is
inconsistent with the behavior on non-Windows operating systems. In
addition, for comparable errors resulting in an IOException, those
methods (except getRealPath()) return null instead of throwing the
exception.

- getResourcePaths()
- getResource()
- getResourceAsStream()
- getRealPath()

This commit makes handling of InvalidPathException and IOException
consistent for these methods: both exceptions now result in null be
returned by these methods.

Closes gh-23717
2019-10-30 16:19:40 +01:00
Rossen Stoyanchev 614c7b0f8e ResponseStatusException associated headers
A ResponseStatus exception now exposes extra method to return headers
for the response. This is used in ResponseStatusExceptionHandler to
apply the headers to the response.

Closes gh-23741
2019-10-30 12:34:24 +00:00
Rossen Stoyanchev cc84533d85 Logging decorator for WebSocketStompClient handler
Closes gh-23793
2019-10-30 12:16:45 +00:00
Juergen Hoeller 6faf61ba40 Latest applicable dependency updates (Tomcat 9.0.27, Undertow 2.0.27, RxJava 2.2.13, Hibernate ORM 5.3.13, POI 4.1.1, Checkstyle 8.26) 2019-10-30 11:07:39 +01:00
Juergen Hoeller 80a5019534 Verify read-only propagation in DataSourceTransactionManagerTests
See gh-23747
2019-10-30 11:07:07 +01:00
Juergen Hoeller 03bd02a627 Expose primary flag on BeanDefinitionBuilder
Closes gh-23794
2019-10-30 10:30:43 +01:00
Juergen Hoeller f16aa4a9b5 Nullability refinements 2019-10-30 10:30:36 +01:00
Sam Brannen a3c9e8d4fc Polish contribution
See gh-23769
2019-10-29 13:50:38 +01:00
Vedran Pavic 3814f12b67 Preserve expires attribute in MockCookie
At present, MockCookie doesn't preserve expires attribute. This has a
consequence that a cookie value set using
MockHttpServletResponse#addHeader containing an expires attribute will
not match the cookie value obtained from
MockHttpServletResponse#getHeader, since the expires attribute will get
calculated based on current time.

This commit enhances MockCookie to preserve the expires attribute.

Closes gh-23769
2019-10-29 13:50:38 +01:00
Sam Brannen ceb881ab05 Ignore projects not in this branch 2019-10-29 13:50:38 +01:00
Rossen Stoyanchev 871464811c Limits on input stream in codecs
- Add maxInMemorySize property to Decoder and HttpMessageReader
  implementations that aggregate input to trigger
  DataBufferLimitException when reached.

- For codecs that call DataBufferUtils#join, there is now an overloaded
  variant with a maxInMemorySize extra argument. Internally, a custom
  LimitedDataBufferList is used to count and enforce the limit.

- Jackson2Tokenizer and XmlEventDecoder support those limits per
  streamed JSON object.

- Configurable limits for multipart requests with Synchronoss NIO.

- Centralized maxInMemorySize exposed via CodecConfigurer along with
  ability to plug in an instance of MultipartHttpMessageWrite.

Closes gh-23884
2019-10-29 12:36:36 +00:00
235 changed files with 5471 additions and 1548 deletions
+4
View File
@@ -24,6 +24,10 @@ buildSrc/build
/src/asciidoc/build
target/
# Projects not in this branch
integration-tests/
spring-core/kotlin-coroutines/
# Eclipse artifacts, including WTP generated manifests
.classpath
.project
+12 -12
View File
@@ -28,24 +28,24 @@ ext {
!it.name.equals("spring-build-src") && !it.name.equals("spring-framework-bom")
}
aspectjVersion = "1.9.4"
aspectjVersion = "1.9.5"
freemarkerVersion = "2.3.28"
groovyVersion = "2.5.8"
groovyVersion = "2.5.9"
hsqldbVersion = "2.4.1"
jackson2Version = "2.9.9"
jettyVersion = "9.4.21.v20190926"
jettyVersion = "9.4.28.v20200408"
junit5Version = "5.3.2"
kotlinVersion = "1.2.71"
log4jVersion = "2.11.2"
nettyVersion = "4.1.43.Final"
reactorVersion = "Californium-SR13"
nettyVersion = "4.1.49.Final"
reactorVersion = "Californium-SR18"
rxjavaVersion = "1.3.8"
rxjavaAdapterVersion = "1.2.1"
rxjava2Version = "2.2.12"
slf4jVersion = "1.7.28" // spring-jcl + consistent 3rd party deps
rxjava2Version = "2.2.19"
slf4jVersion = "1.7.30" // spring-jcl + consistent 3rd party deps
tiles3Version = "3.0.8"
tomcatVersion = "9.0.26"
undertowVersion = "2.0.26.Final"
tomcatVersion = "9.0.34"
undertowVersion = "2.0.30.Final"
gradleScriptDir = "${rootProject.projectDir}/gradle"
withoutJclOverSlf4j = {
@@ -142,13 +142,13 @@ configure(allprojects) { project ->
}
checkstyle {
toolVersion = "8.24"
toolVersion = "8.31"
configDir = rootProject.file("src/checkstyle")
}
repositories {
mavenCentral()
maven { url "https://repo.spring.io/libs-release" }
mavenLocal()
}
dependencies {
@@ -184,7 +184,7 @@ configure(allprojects) { project ->
"https://tiles.apache.org/tiles-request/apidocs/",
"https://tiles.apache.org/framework/apidocs/",
"https://www.eclipse.org/aspectj/doc/released/aspectj5rt-api/",
"https://www.ehcache.org/apidocs/2.10.4",
"https://www.ehcache.org/apidocs/2.10.4/",
"https://www.quartz-scheduler.org/api/2.3.0/",
"https://fasterxml.github.io/jackson-core/javadoc/2.9/",
"https://fasterxml.github.io/jackson-databind/javadoc/2.9/",
+1 -1
View File
@@ -1 +1 @@
version=5.1.11.BUILD-SNAPSHOT
version=5.1.15.RELEASE
+2 -2
View File
@@ -149,14 +149,14 @@ task schemaZip(type: Zip) {
def Properties schemas = new Properties();
subproject.sourceSets.main.resources.find {
it.path.endsWith("META-INF/spring.schemas")
(it.path.endsWith("META-INF/spring.schemas") || it.path.endsWith("META-INF\\spring.schemas"))
}?.withInputStream { schemas.load(it) }
for (def key : schemas.keySet()) {
def shortName = key.replaceAll(/http.*schema.(.*).spring-.*/, '$1')
assert shortName != key
File xsdFile = subproject.sourceSets.main.resources.find {
it.path.endsWith(schemas.get(key))
(it.path.endsWith(schemas.get(key)) || it.path.endsWith(schemas.get(key).replaceAll('\\/','\\\\')))
}
assert xsdFile != null
into (shortName) {
+1 -1
View File
@@ -4,6 +4,6 @@ dependencies {
compile(project(":spring-beans"))
compile(project(":spring-core"))
optional("org.aspectj:aspectjweaver:${aspectjVersion}")
optional("org.apache.commons:commons-pool2:2.6.0")
optional("org.apache.commons:commons-pool2:2.6.2")
optional("com.jamonapi:jamon:2.81")
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -258,15 +258,17 @@ class CglibAopProxy implements AopProxy, Serializable {
int mod = method.getModifiers();
if (!Modifier.isStatic(mod) && !Modifier.isPrivate(mod)) {
if (Modifier.isFinal(mod)) {
if (implementsInterface(method, ifcs)) {
if (logger.isInfoEnabled() && implementsInterface(method, ifcs)) {
logger.info("Unable to proxy interface-implementing method [" + method + "] because " +
"it is marked as final: Consider using interface-based JDK proxies instead!");
}
logger.debug("Final method [" + method + "] cannot get proxied via CGLIB: " +
"Calls to this method will NOT be routed to the target instance and " +
"might lead to NPEs against uninitialized fields in the proxy instance.");
if (logger.isDebugEnabled()) {
logger.debug("Final method [" + method + "] cannot get proxied via CGLIB: " +
"Calls to this method will NOT be routed to the target instance and " +
"might lead to NPEs against uninitialized fields in the proxy instance.");
}
}
else if (!Modifier.isPublic(mod) && !Modifier.isProtected(mod) &&
else if (logger.isDebugEnabled() && !Modifier.isPublic(mod) && !Modifier.isProtected(mod) &&
proxyClassLoader != null && proxySuperClass.getClassLoader() != proxyClassLoader) {
logger.debug("Method [" + method + "] is package-visible across different ClassLoaders " +
"and cannot get proxied via CGLIB: Declare this method as public or protected " +
@@ -526,7 +528,7 @@ class CglibAopProxy implements AopProxy, Serializable {
private static class StaticDispatcher implements Dispatcher, Serializable {
@Nullable
private Object target;
private final Object target;
public StaticDispatcher(@Nullable Object target) {
this.target = target;
@@ -552,7 +554,7 @@ class CglibAopProxy implements AopProxy, Serializable {
}
@Override
public Object loadObject() throws Exception {
public Object loadObject() {
return this.advised;
}
}
@@ -940,11 +942,11 @@ class CglibAopProxy implements AopProxy, Serializable {
return true;
}
private boolean equalsAdviceClasses(Advisor a, Advisor b) {
private static boolean equalsAdviceClasses(Advisor a, Advisor b) {
return (a.getAdvice().getClass() == b.getAdvice().getClass());
}
private boolean equalsPointcuts(Advisor a, Advisor b) {
private static boolean equalsPointcuts(Advisor a, Advisor b) {
// If only one of the advisor (but not both) is PointcutAdvisor, then it is a mismatch.
// Takes care of the situations where an IntroductionAdvisor is used (see SPR-3959).
return (!(a instanceof PointcutAdvisor) ||
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2020 the original author 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,9 +21,7 @@ import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.Interceptor;
@@ -342,11 +340,8 @@ public class ProxyFactoryBean extends ProxyCreatorSupport
// an independent instance of the configuration.
// In this case, no proxy will have an instance of this object's configuration,
// but will have an independent copy.
if (logger.isTraceEnabled()) {
logger.trace("Creating copy of prototype ProxyFactoryBean config: " + this);
}
ProxyCreatorSupport copy = new ProxyCreatorSupport(getAopProxyFactory());
// The copy needs a fresh advisor chain, and a fresh TargetSource.
TargetSource targetSource = freshTargetSource();
copy.copyConfigurationFrom(this, targetSource, freshAdvisorChain());
@@ -359,9 +354,6 @@ public class ProxyFactoryBean extends ProxyCreatorSupport
}
copy.setFrozen(this.freezeProxy);
if (logger.isTraceEnabled()) {
logger.trace("Using ProxyCreatorSupport copy: " + copy);
}
return getProxy(copy.createAopProxy());
}
@@ -395,9 +387,7 @@ public class ProxyFactoryBean extends ProxyCreatorSupport
logger.debug("Bean with name '" + finalName + "' concluding interceptor chain " +
"is not an advisor class: treating it as a target or TargetSource");
}
String[] newNames = new String[this.interceptorNames.length - 1];
System.arraycopy(this.interceptorNames, 0, newNames, 0, newNames.length);
this.interceptorNames = newNames;
this.interceptorNames = Arrays.copyOf(this.interceptorNames, this.interceptorNames.length - 1);
}
}
}
@@ -449,16 +439,12 @@ public class ProxyFactoryBean extends ProxyCreatorSupport
// Materialize interceptor chain from bean names.
for (String name : this.interceptorNames) {
if (logger.isTraceEnabled()) {
logger.trace("Configuring advisor or advice '" + name + "'");
}
if (name.endsWith(GLOBAL_SUFFIX)) {
if (!(this.beanFactory instanceof ListableBeanFactory)) {
throw new AopConfigException(
"Can only use global advisors or interceptors with a ListableBeanFactory");
}
addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
addGlobalAdvisors((ListableBeanFactory) this.beanFactory,
name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
}
@@ -475,7 +461,7 @@ public class ProxyFactoryBean extends ProxyCreatorSupport
// Avoid unnecessary creation of prototype bean just for advisor chain initialization.
advice = new PrototypePlaceholderAdvisor(name);
}
addAdvisorOnChainCreation(advice, name);
addAdvisorOnChainCreation(advice);
}
}
}
@@ -498,11 +484,10 @@ public class ProxyFactoryBean extends ProxyCreatorSupport
if (logger.isDebugEnabled()) {
logger.debug("Refreshing bean named '" + pa.getBeanName() + "'");
}
// Replace the placeholder with a fresh prototype instance resulting
// from a getBean() lookup
// Replace the placeholder with a fresh prototype instance resulting from a getBean lookup
if (this.beanFactory == null) {
throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) " +
"- cannot resolve prototype advisor '" + pa.getBeanName() + "'");
throw new IllegalStateException("No BeanFactory available anymore (probably due to " +
"serialization) - cannot resolve prototype advisor '" + pa.getBeanName() + "'");
}
Object bean = this.beanFactory.getBean(pa.getBeanName());
Advisor refreshedAdvisor = namedBeanToAdvisor(bean);
@@ -519,28 +504,26 @@ public class ProxyFactoryBean extends ProxyCreatorSupport
/**
* Add all global interceptors and pointcuts.
*/
private void addGlobalAdvisor(ListableBeanFactory beanFactory, String prefix) {
private void addGlobalAdvisors(ListableBeanFactory beanFactory, String prefix) {
String[] globalAdvisorNames =
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, Advisor.class);
String[] globalInterceptorNames =
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, Interceptor.class);
List<Object> beans = new ArrayList<>(globalAdvisorNames.length + globalInterceptorNames.length);
Map<Object, String> names = new HashMap<>(beans.size());
for (String name : globalAdvisorNames) {
Object bean = beanFactory.getBean(name);
beans.add(bean);
names.put(bean, name);
}
for (String name : globalInterceptorNames) {
Object bean = beanFactory.getBean(name);
beans.add(bean);
names.put(bean, name);
}
AnnotationAwareOrderComparator.sort(beans);
for (Object bean : beans) {
String name = names.get(bean);
if (name.startsWith(prefix)) {
addAdvisorOnChainCreation(bean, name);
if (globalAdvisorNames.length > 0 || globalInterceptorNames.length > 0) {
List<Object> beans = new ArrayList<>(globalAdvisorNames.length + globalInterceptorNames.length);
for (String name : globalAdvisorNames) {
if (name.startsWith(prefix)) {
beans.add(beanFactory.getBean(name));
}
}
for (String name : globalInterceptorNames) {
if (name.startsWith(prefix)) {
beans.add(beanFactory.getBean(name));
}
}
AnnotationAwareOrderComparator.sort(beans);
for (Object bean : beans) {
addAdvisorOnChainCreation(bean);
}
}
}
@@ -551,17 +534,11 @@ public class ProxyFactoryBean extends ProxyCreatorSupport
* Because of these three possibilities, we can't type the signature
* more strongly.
* @param next advice, advisor or target object
* @param name bean name from which we obtained this object in our owning
* bean factory
*/
private void addAdvisorOnChainCreation(Object next, String name) {
private void addAdvisorOnChainCreation(Object next) {
// We need to convert to an Advisor if necessary so that our source reference
// matches what we find from superclass interceptors.
Advisor advisor = namedBeanToAdvisor(next);
if (logger.isTraceEnabled()) {
logger.trace("Adding advisor with name '" + name + "'");
}
addAdvisor(advisor);
addAdvisor(namedBeanToAdvisor(next));
}
/**
@@ -572,9 +549,7 @@ public class ProxyFactoryBean extends ProxyCreatorSupport
*/
private TargetSource freshTargetSource() {
if (this.targetName == null) {
if (logger.isTraceEnabled()) {
logger.trace("Not refreshing target: Bean name not specified in 'interceptorNames'.");
}
// Not refreshing target: bean name not specified in 'interceptorNames'
return this.targetSource;
}
else {
@@ -602,8 +577,8 @@ public class ProxyFactoryBean extends ProxyCreatorSupport
// We expected this to be an Advisor or Advice,
// but it wasn't. This is a configuration error.
throw new AopConfigException("Unknown advisor type " + next.getClass() +
"; Can only include Advisor or Advice type beans in interceptorNames chain except for last entry," +
"which may also be target or TargetSource", ex);
"; can only include Advisor or Advice type beans in interceptorNames chain " +
"except for last entry which may also be target instance or TargetSource", ex);
}
}
@@ -614,7 +589,7 @@ public class ProxyFactoryBean extends ProxyCreatorSupport
protected void adviceChanged() {
super.adviceChanged();
if (this.singleton) {
logger.debug("Advice has changed; recaching singleton instance");
logger.debug("Advice has changed; re-caching singleton instance");
synchronized (this) {
this.singletonInstance = null;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,6 +37,7 @@ import org.springframework.scheduling.config.TaskManagementConfigUtils;
* @see org.springframework.scheduling.annotation.ProxyAsyncConfiguration
*/
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class AspectJAsyncConfiguration extends AbstractAsyncConfiguration {
@Bean(name = TaskManagementConfigUtils.ASYNC_EXECUTION_ASPECT_BEAN_NAME)
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2020 the original author 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,7 @@ import org.springframework.transaction.config.TransactionManagementConfigUtils;
* @see TransactionManagementConfigurationSelector
*/
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class AspectJJtaTransactionManagementConfiguration extends AspectJTransactionManagementConfiguration {
@Bean(name = TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_BEAN_NAME)
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2020 the original author 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 @@ import org.springframework.transaction.config.TransactionManagementConfigUtils;
* @see AspectJJtaTransactionManagementConfiguration
*/
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class AspectJTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ASPECT_BEAN_NAME)
+1 -1
View File
@@ -9,7 +9,7 @@ dependencies {
optional("org.codehaus.groovy:groovy-xml:${groovyVersion}")
optional("org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}")
optional("org.jetbrains.kotlin:kotlin-stdlib:${kotlinVersion}")
testCompile("org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}")
testCompile("javax.annotation:javax.annotation-api:1.3.2")
}
// This module does joint compilation for Java and Groovy code with the compileGroovy task.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author 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,9 @@ import org.springframework.util.StringUtils;
* Internal class that caches JavaBeans {@link java.beans.PropertyDescriptor}
* information for a Java class. Not intended for direct use by application code.
*
* <p>Necessary for own caching of descriptors within the application's
* ClassLoader, rather than rely on the JDK's system-wide BeanInfo cache
* (in order to avoid leaks on ClassLoader shutdown).
* <p>Necessary for Spring's own caching of bean descriptors within the application
* {@link ClassLoader}, rather than relying on the JDK's system-wide {@link BeanInfo}
* cache (in order to avoid leaks on individual application shutdown in a shared JVM).
*
* <p>Information is cached statically, so we don't need to create new
* objects of this class for every JavaBean we manipulate. Hence, this class
@@ -163,7 +163,6 @@ public final class CachedIntrospectionResults {
* @return the corresponding CachedIntrospectionResults
* @throws BeansException in case of introspection failure
*/
@SuppressWarnings("unchecked")
static CachedIntrospectionResults forClass(Class<?> beanClass) throws BeansException {
CachedIntrospectionResults results = strongClassCache.get(beanClass);
if (results != null) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author 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,8 +41,8 @@ import org.springframework.util.ObjectUtils;
/**
* Decorator for a standard {@link BeanInfo} object, e.g. as created by
* {@link Introspector#getBeanInfo(Class)}, designed to discover and register static
* and/or non-void returning setter methods. For example:
* {@link Introspector#getBeanInfo(Class)}, designed to discover and register
* static and/or non-void returning setter methods. For example:
*
* <pre class="code">
* public class Bean {
@@ -145,11 +145,10 @@ class ExtendedBeanInfo implements BeanInfo {
public static boolean isCandidateWriteMethod(Method method) {
String methodName = method.getName();
Class<?>[] parameterTypes = method.getParameterTypes();
int nParams = parameterTypes.length;
int nParams = method.getParameterCount();
return (methodName.length() > 3 && methodName.startsWith("set") && Modifier.isPublic(method.getModifiers()) &&
(!void.class.isAssignableFrom(method.getReturnType()) || Modifier.isStatic(method.getModifiers())) &&
(nParams == 1 || (nParams == 2 && int.class == parameterTypes[0])));
(nParams == 1 || (nParams == 2 && int.class == method.getParameterTypes()[0])));
}
private void handleCandidateWriteMethod(Method method) throws IntrospectionException {
@@ -209,7 +208,7 @@ class ExtendedBeanInfo implements BeanInfo {
}
private String propertyNameFor(Method method) {
return Introspector.decapitalize(method.getName().substring(3, method.getName().length()));
return Introspector.decapitalize(method.getName().substring(3));
}
@@ -488,7 +487,7 @@ class ExtendedBeanInfo implements BeanInfo {
}
/*
* See java.beans.IndexedPropertyDescriptor#equals(java.lang.Object)
* See java.beans.IndexedPropertyDescriptor#equals
*/
@Override
public boolean equals(Object other) {
@@ -535,11 +534,13 @@ class ExtendedBeanInfo implements BeanInfo {
public int compare(PropertyDescriptor desc1, PropertyDescriptor desc2) {
String left = desc1.getName();
String right = desc2.getName();
byte[] leftBytes = left.getBytes();
byte[] rightBytes = right.getBytes();
for (int i = 0; i < left.length(); i++) {
if (right.length() == i) {
return 1;
}
int result = left.getBytes()[i] - right.getBytes()[i];
int result = leftBytes[i] - rightBytes[i];
if (result != 0) {
return result;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author 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 @@ abstract class PropertyDescriptorUtils {
Class<?> propertyType = null;
if (readMethod != null) {
Class<?>[] params = readMethod.getParameterTypes();
if (params.length != 0) {
if (readMethod.getParameterCount() != 0) {
throw new IntrospectionException("Bad read method arg count: " + readMethod);
}
propertyType = readMethod.getReturnType();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2020 the original author 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,10 +39,16 @@ import org.springframework.lang.Nullable;
*
* <p><b>{@code FactoryBean} is a programmatic contract. Implementations are not
* supposed to rely on annotation-driven injection or other reflective facilities.</b>
* {@link #getObjectType()} {@link #getObject()} invocations may arrive early in
* the bootstrap process, even ahead of any post-processor setup. If you need access
* {@link #getObjectType()} {@link #getObject()} invocations may arrive early in the
* bootstrap process, even ahead of any post-processor setup. If you need access to
* other beans, implement {@link BeanFactoryAware} and obtain them programmatically.
*
* <p><b>The container is only responsible for managing the lifecycle of the FactoryBean
* instance, not the lifecycle of the objects created by the FactoryBean.</b> Therefore,
* a destroy method on an exposed bean object (such as {@link java.io.Closeable#close()}
* will <i>not</i> be called automatically. Instead, a FactoryBean should implement
* {@link DisposableBean} and delegate any such close call to the underlying object.
*
* <p>Finally, FactoryBean objects participate in the containing BeanFactory's
* synchronization of bean creation. There is usually no need for internal
* synchronization other than for purposes of lazy initialization within the
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -373,7 +373,7 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable
/**
* Determine the name of the wrapped parameter/field.
* @return the declared name (never {@code null})
* @return the declared name (may be {@code null} if unresolvable)
*/
@Nullable
public String getDependencyName() {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author 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,10 +182,12 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
}
@Override
public void setMetaClass(MetaClass metaClass) {
this.metaClass = metaClass;
}
@Override
public MetaClass getMetaClass() {
return this.metaClass;
}
@@ -216,6 +218,7 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
*/
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
@@ -240,10 +243,11 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
logger.trace("Loading Groovy bean definitions from " + encodedResource);
}
Closure beans = new Closure(this) {
@SuppressWarnings("serial")
Closure<Object> beans = new Closure<Object>(this) {
@Override
public Object call(Object[] args) {
invokeBeanDefiningClosure((Closure) args[0]);
public Object call(Object... args) {
invokeBeanDefiningClosure((Closure<?>) args[0]);
return null;
}
};
@@ -285,7 +289,7 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
* @param closure the block or closure
* @return this {@code GroovyBeanDefinitionReader} instance
*/
public GroovyBeanDefinitionReader beans(Closure closure) {
public GroovyBeanDefinitionReader beans(Closure<?> closure) {
return invokeBeanDefiningClosure(closure);
}
@@ -309,25 +313,22 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
public AbstractBeanDefinition bean(Class<?> type, Object...args) {
GroovyBeanDefinitionWrapper current = this.currentBeanDefinition;
try {
Closure callable = null;
Collection constructorArgs = null;
Closure<?> callable = null;
Collection<Object> constructorArgs = null;
if (!ObjectUtils.isEmpty(args)) {
int index = args.length;
Object lastArg = args[index - 1];
if (lastArg instanceof Closure) {
callable = (Closure) lastArg;
if (lastArg instanceof Closure<?>) {
callable = (Closure<?>) lastArg;
index--;
}
if (index > -1) {
constructorArgs = resolveConstructorArguments(args, 0, index);
}
constructorArgs = resolveConstructorArguments(args, 0, index);
}
this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(null, type, constructorArgs);
if (callable != null) {
callable.call(this.currentBeanDefinition);
}
return this.currentBeanDefinition.getBeanDefinition();
}
finally {
this.currentBeanDefinition = current;
@@ -373,10 +374,11 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
* This method overrides method invocation to create beans for each method name that
* takes a class argument.
*/
@Override
public Object invokeMethod(String name, Object arg) {
Object[] args = (Object[])arg;
if ("beans".equals(name) && args.length == 1 && args[0] instanceof Closure) {
return beans((Closure) args[0]);
return beans((Closure<?>) args[0]);
}
else if ("ref".equals(name)) {
String refName;
@@ -429,10 +431,10 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
private void finalizeDeferredProperties() {
for (DeferredProperty dp : this.deferredProperties.values()) {
if (dp.value instanceof List) {
dp.value = manageListIfNecessary((List) dp.value);
dp.value = manageListIfNecessary((List<?>) dp.value);
}
else if (dp.value instanceof Map) {
dp.value = manageMapIfNecessary((Map) dp.value);
dp.value = manageMapIfNecessary((Map<?, ?>) dp.value);
}
dp.apply();
}
@@ -444,7 +446,7 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
* @param callable the closure argument
* @return this {@code GroovyBeanDefinitionReader} instance
*/
protected GroovyBeanDefinitionReader invokeBeanDefiningClosure(Closure callable) {
protected GroovyBeanDefinitionReader invokeBeanDefiningClosure(Closure<?> callable) {
callable.setDelegate(this);
callable.call();
finalizeDeferredProperties();
@@ -483,9 +485,10 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
else if (args[0] instanceof Map) {
// named constructor arguments
if (args.length > 1 && args[1] instanceof Class) {
List constructorArgs = resolveConstructorArguments(args, 2, hasClosureArgument ? args.length - 1 : args.length);
this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName, (Class)args[1], constructorArgs);
Map namedArgs = (Map)args[0];
List<Object> constructorArgs =
resolveConstructorArguments(args, 2, hasClosureArgument ? args.length - 1 : args.length);
this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName, (Class<?>) args[1], constructorArgs);
Map<?, ?> namedArgs = (Map<?, ?>) args[0];
for (Object o : namedArgs.keySet()) {
String propName = (String) o;
setProperty(propName, namedArgs.get(propName));
@@ -494,8 +497,8 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
// factory method syntax
else {
this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName);
//First arg is the map containing factoryBean : factoryMethod
Map.Entry factoryBeanEntry = (Map.Entry) ((Map) args[0]).entrySet().iterator().next();
// First arg is the map containing factoryBean : factoryMethod
Map.Entry<?, ?> factoryBeanEntry = ((Map<?, ?>) args[0]).entrySet().iterator().next();
// If we have a closure body, that will be the last argument.
// In between are the constructor args
int constructorArgsTest = (hasClosureArgument ? 2 : 1);
@@ -519,12 +522,13 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
this.currentBeanDefinition.getBeanDefinition().setAbstract(true);
}
else {
List constructorArgs = resolveConstructorArguments(args, 0, hasClosureArgument ? args.length - 1 : args.length);
List<Object> constructorArgs =
resolveConstructorArguments(args, 0, hasClosureArgument ? args.length - 1 : args.length);
this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName, null, constructorArgs);
}
if (hasClosureArgument) {
Closure callable = (Closure) args[args.length - 1];
Closure<?> callable = (Closure<?>) args[args.length - 1];
callable.setDelegate(this);
callable.setResolveStrategy(Closure.DELEGATE_FIRST);
callable.call(this.currentBeanDefinition);
@@ -544,10 +548,10 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
constructorArgs[i] = constructorArgs[i].toString();
}
else if (constructorArgs[i] instanceof List) {
constructorArgs[i] = manageListIfNecessary((List) constructorArgs[i]);
constructorArgs[i] = manageListIfNecessary((List<?>) constructorArgs[i]);
}
else if (constructorArgs[i] instanceof Map){
constructorArgs[i] = manageMapIfNecessary((Map) constructorArgs[i]);
constructorArgs[i] = manageMapIfNecessary((Map<?, ?>) constructorArgs[i]);
}
}
return Arrays.asList(constructorArgs);
@@ -601,6 +605,7 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
* This method overrides property setting in the scope of the {@code GroovyBeanDefinitionReader}
* to set properties on the current bean definition.
*/
@Override
public void setProperty(String name, Object value) {
if (this.currentBeanDefinition != null) {
applyPropertyToBeanDefinition(name, value);
@@ -617,7 +622,7 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
else if (value instanceof Closure) {
GroovyBeanDefinitionWrapper current = this.currentBeanDefinition;
try {
Closure callable = (Closure) value;
Closure<?> callable = (Closure<?>) value;
Class<?> parameterType = callable.getParameterTypes()[0];
if (Object.class == parameterType) {
this.currentBeanDefinition = new GroovyBeanDefinitionWrapper("");
@@ -647,6 +652,7 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
* properties from the {@code GroovyBeanDefinitionReader} itself
* </ul>
*/
@Override
public Object getProperty(String name) {
Binding binding = getBinding();
if (binding != null && binding.hasVariable(name)) {
@@ -690,8 +696,8 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
}
private GroovyDynamicElementReader createDynamicElementReader(String namespace) {
XmlReaderContext readerContext = this.groovyDslXmlBeanDefinitionReader.createReaderContext(new DescriptiveResource(
"Groovy"));
XmlReaderContext readerContext = this.groovyDslXmlBeanDefinitionReader.createReaderContext(
new DescriptiveResource("Groovy"));
BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
boolean decorating = (this.currentBeanDefinition != null);
if (!decorating) {
@@ -749,10 +755,12 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
this.metaClass = InvokerHelper.getMetaClass(this);
}
@Override
public MetaClass getMetaClass() {
return this.metaClass;
}
@Override
public Object getProperty(String property) {
if (property.equals("beanName")) {
return getBeanName();
@@ -769,14 +777,17 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
}
}
@Override
public Object invokeMethod(String name, Object args) {
return this.metaClass.invokeMethod(this, name, args);
}
@Override
public void setMetaClass(MetaClass metaClass) {
this.metaClass = metaClass;
}
@Override
public void setProperty(String property, Object newValue) {
if (!addDeferredProperty(property, newValue)) {
this.beanDefinition.getBeanDefinition().getPropertyValues().add(property, newValue);
@@ -785,7 +796,7 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
/**
* Wraps a bean definition property an ensures that any RuntimeBeanReference
* Wraps a bean definition property and ensures that any RuntimeBeanReference
* additions to it are deferred for resolution later.
*/
private class GroovyPropertyValue extends GroovyObjectSupport {
@@ -799,18 +810,21 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
this.propertyValue = propertyValue;
}
@SuppressWarnings("unused")
public void leftShift(Object value) {
InvokerHelper.invokeMethod(this.propertyValue, "leftShift", value);
updateDeferredProperties(value);
}
@SuppressWarnings("unused")
public boolean add(Object value) {
boolean retVal = (Boolean) InvokerHelper.invokeMethod(this.propertyValue, "add", value);
updateDeferredProperties(value);
return retVal;
}
public boolean addAll(Collection values) {
@SuppressWarnings("unused")
public boolean addAll(Collection<?> values) {
boolean retVal = (Boolean) InvokerHelper.invokeMethod(this.propertyValue, "addAll", values);
for (Object value : values) {
updateDeferredProperties(value);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author 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,7 +196,7 @@ class GroovyBeanDefinitionWrapper extends GroovyObjectSupport {
// constructorArgs
else if (CONSTRUCTOR_ARGS.equals(property) && newValue instanceof List) {
ConstructorArgumentValues cav = new ConstructorArgumentValues();
List args = (List) newValue;
List<?> args = (List<?>) newValue;
for (Object arg : args) {
cav.addGenericArgumentValue(arg);
}
@@ -645,7 +645,6 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
@Nullable
protected Class<?> predictBeanType(String beanName, RootBeanDefinition mbd, Class<?>... typesToMatch) {
Class<?> targetType = determineTargetType(beanName, mbd, typesToMatch);
// Apply SmartInstantiationAwareBeanPostProcessors to predict the
// eventual type after a before-instantiation shortcut.
if (targetType != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
@@ -1364,34 +1363,28 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
// state of the bean before properties are set. This can be used, for example,
// to support styles of field injection.
boolean continueWithPropertyPopulation = true;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
continueWithPropertyPopulation = false;
break;
return;
}
}
}
}
if (!continueWithPropertyPopulation) {
return;
}
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
int resolvedAutowireMode = mbd.getResolvedAutowireMode();
if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// Add property values based on autowire by name if applicable.
if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME) {
if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
// Add property values based on autowire by type if applicable.
if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
@@ -1495,7 +1488,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
if (Object.class != pd.getPropertyType()) {
MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
// Do not allow eager init for type matching in case of a prioritized post-processor.
boolean eager = !PriorityOrdered.class.isInstance(bw.getWrappedInstance());
boolean eager = !(bw.getWrappedInstance() instanceof PriorityOrdered);
DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
if (autowiredArgument != null) {
@@ -1127,30 +1127,30 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
return false;
}
AbstractBeanDefinition that = (AbstractBeanDefinition) other;
boolean rtn = ObjectUtils.nullSafeEquals(getBeanClassName(), that.getBeanClassName());
rtn = rtn &= ObjectUtils.nullSafeEquals(this.scope, that.scope);
rtn = rtn &= this.abstractFlag == that.abstractFlag;
rtn = rtn &= this.lazyInit == that.lazyInit;
rtn = rtn &= this.autowireMode == that.autowireMode;
rtn = rtn &= this.dependencyCheck == that.dependencyCheck;
rtn = rtn &= Arrays.equals(this.dependsOn, that.dependsOn);
rtn = rtn &= this.autowireCandidate == that.autowireCandidate;
rtn = rtn &= ObjectUtils.nullSafeEquals(this.qualifiers, that.qualifiers);
rtn = rtn &= this.primary == that.primary;
rtn = rtn &= this.nonPublicAccessAllowed == that.nonPublicAccessAllowed;
rtn = rtn &= this.lenientConstructorResolution == that.lenientConstructorResolution;
rtn = rtn &= ObjectUtils.nullSafeEquals(this.constructorArgumentValues, that.constructorArgumentValues);
rtn = rtn &= ObjectUtils.nullSafeEquals(this.propertyValues, that.propertyValues);
rtn = rtn &= ObjectUtils.nullSafeEquals(this.methodOverrides, that.methodOverrides);
rtn = rtn &= ObjectUtils.nullSafeEquals(this.factoryBeanName, that.factoryBeanName);
rtn = rtn &= ObjectUtils.nullSafeEquals(this.factoryMethodName, that.factoryMethodName);
rtn = rtn &= ObjectUtils.nullSafeEquals(this.initMethodName, that.initMethodName);
rtn = rtn &= this.enforceInitMethod == that.enforceInitMethod;
rtn = rtn &= ObjectUtils.nullSafeEquals(this.destroyMethodName, that.destroyMethodName);
rtn = rtn &= this.enforceDestroyMethod == that.enforceDestroyMethod;
rtn = rtn &= this.synthetic == that.synthetic;
rtn = rtn &= this.role == that.role;
return rtn && super.equals(other);
return (ObjectUtils.nullSafeEquals(getBeanClassName(), that.getBeanClassName()) &&
ObjectUtils.nullSafeEquals(this.scope, that.scope) &&
this.abstractFlag == that.abstractFlag &&
this.lazyInit == that.lazyInit &&
this.autowireMode == that.autowireMode &&
this.dependencyCheck == that.dependencyCheck &&
Arrays.equals(this.dependsOn, that.dependsOn) &&
this.autowireCandidate == that.autowireCandidate &&
ObjectUtils.nullSafeEquals(this.qualifiers, that.qualifiers) &&
this.primary == that.primary &&
this.nonPublicAccessAllowed == that.nonPublicAccessAllowed &&
this.lenientConstructorResolution == that.lenientConstructorResolution &&
ObjectUtils.nullSafeEquals(this.constructorArgumentValues, that.constructorArgumentValues) &&
ObjectUtils.nullSafeEquals(this.propertyValues, that.propertyValues) &&
ObjectUtils.nullSafeEquals(this.methodOverrides, that.methodOverrides) &&
ObjectUtils.nullSafeEquals(this.factoryBeanName, that.factoryBeanName) &&
ObjectUtils.nullSafeEquals(this.factoryMethodName, that.factoryMethodName) &&
ObjectUtils.nullSafeEquals(this.initMethodName, that.initMethodName) &&
this.enforceInitMethod == that.enforceInitMethod &&
ObjectUtils.nullSafeEquals(this.destroyMethodName, that.destroyMethodName) &&
this.enforceDestroyMethod == that.enforceDestroyMethod &&
this.synthetic == that.synthetic &&
this.role == that.role &&
super.equals(other));
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1283,7 +1283,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
else {
throw new NoSuchBeanDefinitionException(parentBeanName,
"Parent name '" + parentBeanName + "' is equal to bean name '" + beanName +
"': cannot be resolved without an AbstractBeanFactory parent");
"': cannot be resolved without a ConfigurableBeanFactory parent");
}
}
}
@@ -1298,7 +1298,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
// Set default singleton scope, if not configured before.
if (!StringUtils.hasLength(mbd.getScope())) {
mbd.setScope(RootBeanDefinition.SCOPE_SINGLETON);
mbd.setScope(SCOPE_SINGLETON);
}
// A bean contained in a non-singleton bean cannot be a singleton itself.
@@ -302,6 +302,15 @@ public final class BeanDefinitionBuilder {
return this;
}
/**
* Set whether this bean is a primary autowire candidate.
* @since 5.1.11
*/
public BeanDefinitionBuilder setPrimary(boolean primary) {
this.beanDefinition.setPrimary(primary);
return this;
}
/**
* Set the role of this definition.
*/
@@ -162,6 +162,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
/** Map of bean definition objects, keyed by bean name. */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
/** Map from bean name to merged BeanDefinitionHolder. */
private final Map<String, BeanDefinitionHolder> mergedBeanDefinitionHolders = new ConcurrentHashMap<>(256);
/** Map of singleton and non-singleton bean names, keyed by dependency type. */
private final Map<Class<?>, String[]> allBeanNamesByType = new ConcurrentHashMap<>(64);
@@ -759,8 +762,11 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
if (mbd.isFactoryMethodUnique && mbd.factoryMethodToIntrospect == null) {
new ConstructorResolver(this).resolveFactoryMethodIfPossible(mbd);
}
return resolver.isAutowireCandidate(
new BeanDefinitionHolder(mbd, beanName, getAliases(beanDefinitionName)), descriptor);
BeanDefinitionHolder holder = (beanName.equals(beanDefinitionName) ?
this.mergedBeanDefinitionHolders.computeIfAbsent(beanName,
key -> new BeanDefinitionHolder(mbd, beanName, getAliases(beanDefinitionName))) :
new BeanDefinitionHolder(mbd, beanName, getAliases(beanDefinitionName)));
return resolver.isAutowireCandidate(holder, descriptor);
}
@Override
@@ -783,9 +789,16 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
return iterator;
}
@Override
protected void clearMergedBeanDefinition(String beanName) {
super.clearMergedBeanDefinition(beanName);
this.mergedBeanDefinitionHolders.remove(beanName);
}
@Override
public void clearMetadataCache() {
super.clearMetadataCache();
this.mergedBeanDefinitionHolders.clear();
clearByTypeCache();
}
@@ -942,6 +955,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
if (existingDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
else if (isConfigurationFrozen()) {
clearByTypeCache();
}
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 the original author 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,10 @@ import org.springframework.util.StringUtils;
*/
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
/** Maximum number of suppressed exceptions to preserve. */
private static final int SUPPRESSED_EXCEPTIONS_LIMIT = 100;
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
@@ -90,7 +94,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
private final Set<String> inCreationCheckExclusions =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
/** List of suppressed Exceptions, available for associating related causes. */
/** Collection of suppressed Exceptions, available for associating related causes. */
@Nullable
private Set<Exception> suppressedExceptions;
@@ -253,13 +257,17 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
}
/**
* Register an Exception that happened to get suppressed during the creation of a
* Register an exception that happened to get suppressed during the creation of a
* singleton bean instance, e.g. a temporary circular reference resolution problem.
* <p>The default implementation preserves any given exception in this registry's
* collection of suppressed exceptions, up to a limit of 100 exceptions, adding
* them as related causes to an eventual top-level {@link BeanCreationException}.
* @param ex the Exception to register
* @see BeanCreationException#getRelatedCauses()
*/
protected void onSuppressedException(Exception ex) {
synchronized (this.singletonObjects) {
if (this.suppressedExceptions != null) {
if (this.suppressedExceptions != null && this.suppressedExceptions.size() < SUPPRESSED_EXCEPTIONS_LIMIT) {
this.suppressedExceptions.add(ex);
}
}
@@ -612,6 +620,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
* should <i>not</i> have their own mutexes involved in singleton creation,
* to avoid the potential for deadlocks in lazy-init situations.
*/
@Override
public final Object getSingletonMutex() {
return this.singletonObjects;
}
@@ -311,9 +311,9 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
* assuming a "force" parameter), else logging an error.
*/
private void invokeCustomDestroyMethod(final Method destroyMethod) {
Class<?>[] paramTypes = destroyMethod.getParameterTypes();
final Object[] args = new Object[paramTypes.length];
if (paramTypes.length == 1) {
int paramCount = destroyMethod.getParameterCount();
final Object[] args = new Object[paramCount];
if (paramCount == 1) {
args[0] = Boolean.TRUE;
}
if (logger.isTraceEnabled()) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -146,8 +146,9 @@ public class ArgumentConvertingMethodInvoker extends MethodInvoker {
for (Method candidate : candidates) {
if (candidate.getName().equals(targetMethod)) {
// Check if the inspected method has the correct number of parameters.
Class<?>[] paramTypes = candidate.getParameterTypes();
if (paramTypes.length == argCount) {
int parameterCount = candidate.getParameterCount();
if (parameterCount == argCount) {
Class<?>[] paramTypes = candidate.getParameterTypes();
Object[] convertedArguments = new Object[argCount];
boolean match = true;
for (int j = 0; j < argCount && match; j++) {
@@ -1399,6 +1399,39 @@ public class DefaultListableBeanFactoryTests {
lbf.getBean(TestBean.class);
}
@Test
public void testGetBeanByTypeWithLateRegistration() {
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
try {
lbf.getBean(TestBean.class);
fail("Should have thrown NoSuchBeanDefinitionException");
}
catch (NoSuchBeanDefinitionException ex) {
// expected
}
RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class);
lbf.registerBeanDefinition("bd1", bd1);
TestBean bean = lbf.getBean(TestBean.class);
assertThat(bean.getBeanName(), equalTo("bd1"));
}
@Test
public void testGetBeanByTypeWithLateRegistrationAgainstFrozen() {
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
lbf.freezeConfiguration();
try {
lbf.getBean(TestBean.class);
fail("Should have thrown NoSuchBeanDefinitionException");
}
catch (NoSuchBeanDefinitionException ex) {
// expected
}
RootBeanDefinition bd1 = new RootBeanDefinition(TestBean.class);
lbf.registerBeanDefinition("bd1", bd1);
TestBean bean = lbf.getBean(TestBean.class);
assertThat(bean.getBeanName(), equalTo("bd1"));
}
@Test
public void testGetBeanByTypeDefinedInParent() {
DefaultListableBeanFactory parent = new DefaultListableBeanFactory();
@@ -16,7 +16,7 @@ dependencies {
optional("org.freemarker:freemarker:${freemarkerVersion}")
testCompile(project(":spring-context"))
testCompile("org.hsqldb:hsqldb:${hsqldbVersion}")
testCompile("org.hibernate:hibernate-validator:6.0.17.Final")
testCompile("org.hibernate:hibernate-validator:6.0.19.Final")
testCompile("javax.annotation:javax.annotation-api:1.3.2")
testRuntime("org.ehcache:jcache:1.0.1")
testRuntime("org.ehcache:ehcache:3.4.0")
@@ -88,6 +88,7 @@ public class JCacheAspectSupport extends AbstractCacheInvoker implements Initial
return this.cacheOperationSource;
}
@Override
public void afterPropertiesSet() {
getCacheOperationSource();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -110,7 +110,7 @@ public class LocalDataSourceJobStore extends JobStoreCMT {
public void shutdown() {
// Do nothing - a Spring-managed DataSource has its own lifecycle.
}
/* Quartz 2.2 initialize method */
@Override
public void initialize() {
// Do nothing - a Spring-managed DataSource has its own lifecycle.
}
@@ -138,7 +138,7 @@ public class LocalDataSourceJobStore extends JobStoreCMT {
public void shutdown() {
// Do nothing - a Spring-managed DataSource has its own lifecycle.
}
/* Quartz 2.2 initialize method */
@Override
public void initialize() {
// Do nothing - a Spring-managed DataSource has its own lifecycle.
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2019 the original author 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 ResourceLoaderClassLoadHelper implements ClassLoadHelper {
}
@SuppressWarnings("unchecked")
@Override
public <T> Class<? extends T> loadClass(String name, Class<T> clazz) throws ClassNotFoundException {
return (Class<? extends T>) loadClass(name);
}
+4 -4
View File
@@ -18,17 +18,17 @@ dependencies {
optional("javax.xml.ws:jaxws-api:2.3.1")
optional("org.aspectj:aspectjweaver:${aspectjVersion}")
optional("org.codehaus.groovy:groovy:${groovyVersion}")
optional("org.beanshell:bsh:2.0b5")
optional("joda-time:joda-time:2.10.4")
optional("org.apache-extras.beanshell:bsh:2.0b6")
optional("joda-time:joda-time:2.10.5")
optional("org.hibernate:hibernate-validator:5.4.3.Final")
optional("org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}")
optional("org.jetbrains.kotlin:kotlin-stdlib:${kotlinVersion}")
testCompile("org.codehaus.groovy:groovy-jsr223:${groovyVersion}")
testCompile("org.codehaus.groovy:groovy-test:${groovyVersion}")
testCompile("org.codehaus.groovy:groovy-xml:${groovyVersion}")
testCompile("org.apache.commons:commons-pool2:2.6.0")
testCompile("org.apache.commons:commons-pool2:2.6.2")
testCompile("javax.inject:javax.inject-tck:1")
testCompile("org.awaitility:awaitility:3.1.3")
testCompile("org.awaitility:awaitility:3.1.6")
testRuntime("javax.xml.bind:jaxb-api:2.3.1")
testRuntime("org.glassfish:javax.el:3.0.1-b08")
testRuntime("org.javamoney:moneta:1.3")
@@ -32,7 +32,9 @@ import org.springframework.util.StringUtils;
@SuppressWarnings("serial")
public class SimpleKey implements Serializable {
/** An empty key. */
/**
* An empty key.
*/
public static final SimpleKey EMPTY = new SimpleKey();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author 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,19 +17,21 @@
package org.springframework.context;
/**
* An extension of the {@link Lifecycle} interface for those objects that require to
* be started upon ApplicationContext refresh and/or shutdown in a particular order.
* The {@link #isAutoStartup()} return value indicates whether this object should
* An extension of the {@link Lifecycle} interface for those objects that require
* to be started upon {@code ApplicationContext} refresh and/or shutdown in a
* particular order.
*
* <p>The {@link #isAutoStartup()} return value indicates whether this object should
* be started at the time of a context refresh. The callback-accepting
* {@link #stop(Runnable)} method is useful for objects that have an asynchronous
* shutdown process. Any implementation of this interface <i>must</i> invoke the
* callback's {@code run()} method upon shutdown completion to avoid unnecessary
* delays in the overall ApplicationContext shutdown.
* delays in the overall {@code ApplicationContext} shutdown.
*
* <p>This interface extends {@link Phased}, and the {@link #getPhase()} method's
* return value indicates the phase within which this Lifecycle component should
* be started and stopped. The startup process begins with the <i>lowest</i> phase
* value and ends with the <i>highest</i> phase value ({@code Integer.MIN_VALUE}
* return value indicates the phase within which this {@code Lifecycle} component
* should be started and stopped. The startup process begins with the <i>lowest</i>
* phase value and ends with the <i>highest</i> phase value ({@code Integer.MIN_VALUE}
* is the lowest possible, and {@code Integer.MAX_VALUE} is the highest possible).
* The shutdown process will apply the reverse order. Any components with the
* same value will be arbitrarily ordered within the same phase.
@@ -44,9 +46,11 @@ package org.springframework.context;
*
* <p>Any {@code Lifecycle} components within the context that do not also
* implement {@code SmartLifecycle} will be treated as if they have a phase
* value of 0. That way a {@code SmartLifecycle} implementation may start
* before those {@code Lifecycle} components if it has a negative phase value,
* or it may start after those components if it has a positive phase value.
* value of {@code 0}. This allows a {@code SmartLifecycle} component to start
* before those {@code Lifecycle} components if the {@code SmartLifecycle}
* component has a negative phase value, or the {@code SmartLifecycle} component
* may start after those {@code Lifecycle} components if the {@code SmartLifecycle}
* component has a positive phase value.
*
* <p>Note that, due to the auto-startup support in {@code SmartLifecycle}, a
* {@code SmartLifecycle} bean instance will usually get initialized on startup
@@ -55,6 +59,7 @@ package org.springframework.context;
*
* @author Mark Fisher
* @author Juergen Hoeller
* @author Sam Brannen
* @since 3.0
* @see LifecycleProcessor
* @see ConfigurableApplicationContext
@@ -63,9 +68,10 @@ public interface SmartLifecycle extends Lifecycle, Phased {
/**
* The default phase for {@code SmartLifecycle}: {@code Integer.MAX_VALUE}.
* <p>This is different from the common phase 0 associated with regular
* <p>This is different from the common phase {@code 0} associated with regular
* {@link Lifecycle} implementations, putting the typically auto-started
* {@code SmartLifecycle} beans into a separate later shutdown phase.
* {@code SmartLifecycle} beans into a later startup phase and an earlier
* shutdown phase.
* @since 5.1
* @see #getPhase()
* @see org.springframework.context.support.DefaultLifecycleProcessor#getPhase(Lifecycle)
@@ -115,7 +121,8 @@ public interface SmartLifecycle extends Lifecycle, Phased {
/**
* Return the phase that this lifecycle object is supposed to run in.
* <p>The default implementation returns {@link #DEFAULT_PHASE} in order to
* let stop callbacks execute after regular {@code Lifecycle} implementations.
* let {@code stop()} callbacks execute after regular {@code Lifecycle}
* implementations.
* @see #isAutoStartup()
* @see #start()
* @see #stop(Runnable)
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 the original author 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,8 @@ import org.springframework.core.type.AnnotationMetadata;
*
* @author Chris Beams
* @since 3.1
* @see EnableAspectJAutoProxy
* @see org.springframework.cache.annotation.EnableCaching
* @see org.springframework.transaction.annotation.EnableTransactionManagement
*/
public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 the original author 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,6 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
@@ -386,8 +385,8 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
for (String type : types) {
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(type);
if (isCandidateComponent(metadataReader)) {
AnnotatedGenericBeanDefinition sbd = new AnnotatedGenericBeanDefinition(
metadataReader.getAnnotationMetadata());
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setSource(metadataReader.getResource());
if (isCandidateComponent(sbd)) {
if (debugEnabled) {
logger.debug("Using candidate component class from index: " + type);
@@ -430,7 +429,6 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
if (isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
if (debugEnabled) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 the original author 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,9 +344,9 @@ import org.springframework.stereotype.Component;
*
* <p>By default, {@code @Bean} methods will be <em>eagerly instantiated</em> 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
* the class are by default lazily initialized. Note that {@code @Lazy} may be used on
* individual {@code @Bean} methods as well.
* the {@link Lazy @Lazy} annotation to indicate that all {@code @Bean} methods declared
* within the class are by default lazily initialized. Note that {@code @Lazy} may be used
* on individual {@code @Bean} methods as well.
*
* <h2>Testing support for {@code @Configuration} classes</h2>
*
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -209,7 +209,6 @@ class ConfigurationClassBeanDefinitionReader {
}
ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata);
beanDef.setResource(configClass.getResource());
beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));
if (metadata.isStatic()) {
@@ -381,6 +380,7 @@ class ConfigurationClassBeanDefinitionReader {
public ConfigurationClassBeanDefinition(ConfigurationClass configClass, MethodMetadata beanMethodMetadata) {
this.annotationMetadata = configClass.getMetadata();
this.factoryMethodMetadata = beanMethodMetadata;
setResource(configClass.getResource());
setLenientConstructorResolution(false);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 the original author 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 @@ class ConfigurationClassParser {
* @param importSelector the selector to handle
*/
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(
configClass, importSelector);
DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);
if (this.deferredImportSelectors == null) {
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
handler.register(holder);
@@ -775,7 +774,6 @@ class ConfigurationClassParser {
this.deferredImportSelectors = new ArrayList<>();
}
}
}
@@ -786,8 +784,7 @@ class ConfigurationClassParser {
private final Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>();
public void register(DeferredImportSelectorHolder deferredImport) {
Class<? extends Group> group = deferredImport.getImportSelector()
.getImportGroup();
Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
(group != null ? group : deferredImport),
key -> new DeferredImportSelectorGrouping(createGroup(group)));
@@ -799,8 +796,7 @@ class ConfigurationClassParser {
public void processGroupImports() {
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass = this.configurationClasses.get(
entry.getMetadata());
ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
try {
processImports(configurationClass, asSourceClass(configurationClass),
asSourceClasses(entry.getImportClassName()), false);
@@ -818,8 +814,7 @@ class ConfigurationClassParser {
}
private Group createGroup(@Nullable Class<? extends Group> type) {
Class<? extends Group> effectiveType = (type != null ? type
: DefaultDeferredImportSelectorGroup.class);
Class<? extends Group> effectiveType = (type != null ? type : DefaultDeferredImportSelectorGroup.class);
Group group = BeanUtils.instantiateClass(effectiveType);
ParserStrategyUtils.invokeAwareMethods(group,
ConfigurationClassParser.this.environment,
@@ -827,7 +822,6 @@ class ConfigurationClassParser {
ConfigurationClassParser.this.registry);
return group;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 the original author 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.context.annotation;
import java.util.Objects;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.lang.Nullable;
@@ -53,6 +51,7 @@ public interface DeferredImportSelector extends ImportSelector {
/**
* Interface used to group results from different import selectors.
* @since 5.0
*/
interface Group {
@@ -100,7 +99,7 @@ public interface DeferredImportSelector extends ImportSelector {
}
@Override
public boolean equals(Object other) {
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
@@ -108,13 +107,17 @@ public interface DeferredImportSelector extends ImportSelector {
return false;
}
Entry entry = (Entry) other;
return (Objects.equals(this.metadata, entry.metadata) &&
Objects.equals(this.importClassName, entry.importClassName));
return (this.metadata.equals(entry.metadata) && this.importClassName.equals(entry.importClassName));
}
@Override
public int hashCode() {
return Objects.hash(this.metadata, this.importClassName);
return (this.metadata.hashCode() * 31 + this.importClassName.hashCode());
}
@Override
public String toString() {
return this.importClassName;
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2020 the original author 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 interface ImportSelector {
/**
* Select and return the names of which class(es) should be imported based on
* the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
* @return the class names, or an empty array if none
*/
String[] selectImports(AnnotationMetadata importingClassMetadata);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2020 the original author 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 @@ import org.springframework.util.Assert;
* @see ConfigurableApplicationContext#LOAD_TIME_WEAVER_BEAN_NAME
*/
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class LoadTimeWeavingConfiguration implements ImportAware, BeanClassLoaderAware {
@Nullable
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2020 the original author 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.util.StringUtils;
* @see EnableMBeanExport
*/
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class MBeanExportConfiguration implements ImportAware, EnvironmentAware, BeanFactoryAware {
private static final String MBEAN_EXPORTER_BEAN_NAME = "mbeanExporter";
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -170,8 +170,11 @@ import org.springframework.core.io.support.PropertySourceFactory;
public @interface PropertySource {
/**
* Indicate the name of this property source. If omitted, a name will
* be generated based on the description of the underlying resource.
* Indicate the name of this property source. If omitted, the {@link #factory()}
* will generate a name based on the underlying resource (in the case of
* {@link org.springframework.core.io.support.DefaultPropertySourceFactory}:
* derived from the resource description through a corresponding name-less
* {@link org.springframework.core.io.support.ResourcePropertySource} constructor).
* @see org.springframework.core.env.PropertySource#getName()
* @see org.springframework.core.io.Resource#getDescription()
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2020 the original author 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,6 +60,7 @@ public class ScannedGenericBeanDefinition extends GenericBeanDefinition implemen
Assert.notNull(metadataReader, "MetadataReader must not be null");
this.metadata = metadataReader.getAnnotationMetadata();
setBeanClassName(this.metadata.getClassName());
setResource(metadataReader.getResource());
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -53,6 +53,6 @@ public enum ScopedProxyMode {
/**
* Create a class-based proxy (uses CGLIB).
*/
TARGET_CLASS;
TARGET_CLASS
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2020 the original author 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,19 @@ public class CandidateComponentsIndex {
this.index = parseIndex(content);
}
private static MultiValueMap<String, Entry> parseIndex(List<Properties> content) {
MultiValueMap<String, Entry> index = new LinkedMultiValueMap<>();
for (Properties entry : content) {
entry.forEach((type, values) -> {
String[] stereotypes = ((String) values).split(",");
for (String stereotype : stereotypes) {
index.add(stereotype, new Entry((String) type));
}
});
}
return index;
}
/**
* Return the candidate types that are associated with the specified stereotype.
@@ -76,21 +89,11 @@ public class CandidateComponentsIndex {
return Collections.emptySet();
}
private static MultiValueMap<String, Entry> parseIndex(List<Properties> content) {
MultiValueMap<String, Entry> index = new LinkedMultiValueMap<>();
for (Properties entry : content) {
entry.forEach((type, values) -> {
String[] stereotypes = ((String) values).split(",");
for (String stereotype : stereotypes) {
index.add(stereotype, new Entry((String) type));
}
});
}
return index;
}
private static class Entry {
private final String type;
private final String packageName;
Entry(String type) {
@@ -106,7 +109,6 @@ public class CandidateComponentsIndex {
return this.type.startsWith(basePackage);
}
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -160,8 +160,9 @@ public abstract class AbstractResourceBasedMessageSource extends AbstractMessage
/**
* Set the number of seconds to cache loaded properties files.
* <ul>
* <li>Default is "-1", indicating to cache forever (just like
* {@code java.util.ResourceBundle}).
* <li>Default is "-1", indicating to cache forever (matching the default behavior
* of {@code java.util.ResourceBundle}). Note that this constant follows Spring
* conventions, not {@link java.util.ResourceBundle.Control#getTimeToLive}.
* <li>A positive number will cache loaded properties files for the given
* number of seconds. This is essentially the interval between refresh checks.
* Note that a refresh attempt will first check the last-modified timestamp
@@ -184,8 +185,9 @@ public abstract class AbstractResourceBasedMessageSource extends AbstractMessage
* Set the number of milliseconds to cache loaded properties files.
* Note that it is common to set seconds instead: {@link #setCacheSeconds}.
* <ul>
* <li>Default is "-1", indicating to cache forever (just like
* {@code java.util.ResourceBundle}).
* <li>Default is "-1", indicating to cache forever (matching the default behavior
* of {@code java.util.ResourceBundle}). Note that this constant follows Spring
* conventions, not {@link java.util.ResourceBundle.Control#getTimeToLive}.
* <li>A positive number will cache loaded properties files for the given
* number of milliseconds. This is essentially the interval between refresh checks.
* Note that a refresh attempt will first check the last-modified timestamp
@@ -225,18 +225,22 @@ public class GenericGroovyApplicationContext extends GenericApplicationContext i
// Implementation of the GroovyObject interface
@Override
public void setMetaClass(MetaClass metaClass) {
this.metaClass = metaClass;
}
@Override
public MetaClass getMetaClass() {
return this.metaClass;
}
@Override
public Object invokeMethod(String name, Object args) {
return this.metaClass.invokeMethod(this, name, args);
}
@Override
public void setProperty(String property, Object newValue) {
if (newValue instanceof BeanDefinition) {
registerBeanDefinition(property, (BeanDefinition) newValue);
@@ -246,6 +250,7 @@ public class GenericGroovyApplicationContext extends GenericApplicationContext i
}
}
@Override
@Nullable
public Object getProperty(String property) {
if (containsBean(property)) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author 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,6 +237,7 @@ public class ReloadableResourceBundleMessageSource extends AbstractResourceBased
if (mergedHolder != null) {
return mergedHolder;
}
Properties mergedProps = newProperties();
long latestTimestamp = -1;
String[] basenames = StringUtils.toStringArray(getBasenameSet());
@@ -253,6 +254,7 @@ public class ReloadableResourceBundleMessageSource extends AbstractResourceBased
}
}
}
mergedHolder = new PropertiesHolder(mergedProps, latestTimestamp);
PropertiesHolder existing = this.cachedMergedProperties.putIfAbsent(locale, mergedHolder);
if (existing != null) {
@@ -279,18 +281,28 @@ public class ReloadableResourceBundleMessageSource extends AbstractResourceBased
return filenames;
}
}
// Filenames for given Locale
List<String> filenames = new ArrayList<>(7);
filenames.addAll(calculateFilenamesForLocale(basename, locale));
if (isFallbackToSystemLocale() && !locale.equals(Locale.getDefault())) {
List<String> fallbackFilenames = calculateFilenamesForLocale(basename, Locale.getDefault());
for (String fallbackFilename : fallbackFilenames) {
if (!filenames.contains(fallbackFilename)) {
// Entry for fallback locale that isn't already in filenames list.
filenames.add(fallbackFilename);
// Filenames for default Locale, if any
if (isFallbackToSystemLocale()) {
Locale defaultLocale = Locale.getDefault();
if (!locale.equals(defaultLocale)) {
List<String> fallbackFilenames = calculateFilenamesForLocale(basename, defaultLocale);
for (String fallbackFilename : fallbackFilenames) {
if (!filenames.contains(fallbackFilename)) {
// Entry for fallback locale that isn't already in filenames list.
filenames.add(fallbackFilename);
}
}
}
}
// Filename for default bundle file
filenames.add(basename);
if (localeMap == null) {
localeMap = new ConcurrentHashMap<>();
Map<Locale, List<String>> existing = this.cachedFilenames.putIfAbsent(basename, localeMap);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author 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 @@ public class StaticMessageSource extends AbstractMessageSource {
@Override
@Nullable
protected String resolveCodeWithoutArguments(String code, Locale locale) {
return this.messages.get(code + '_' + locale.toString());
}
@@ -61,14 +61,13 @@ public class DateFormatterRegistrar implements FormatterRegistrar {
@Override
public void registerFormatters(FormatterRegistry registry) {
addDateConverters(registry);
registry.addFormatterForFieldAnnotation(new DateTimeFormatAnnotationFormatterFactory());
// In order to retain back compatibility we only register Date/Calendar
// types when a user defined formatter is specified (see SPR-10105)
if (this.dateFormatter != null) {
registry.addFormatter(this.dateFormatter);
registry.addFormatterForFieldType(Calendar.class, this.dateFormatter);
}
registry.addFormatterForFieldAnnotation(new DateTimeFormatAnnotationFormatterFactory());
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2019 the original author 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 org.springframework.format.Formatter;
* (which is commonly used for HTTP date header values), as of Spring 4.3.
*
* @author Juergen Hoeller
* @author Andrei Nevedomskii
* @since 4.0
* @see java.time.Instant#parse
* @see java.time.format.DateTimeFormatter#ISO_INSTANT
@@ -40,14 +41,14 @@ public class InstantFormatter implements Formatter<Instant> {
@Override
public Instant parse(String text, Locale locale) throws ParseException {
if (text.length() > 0 && Character.isDigit(text.charAt(0))) {
// assuming UTC instant a la "2007-12-03T10:15:30.00Z"
return Instant.parse(text);
}
else {
if (text.length() > 0 && Character.isAlphabetic(text.charAt(0))) {
// assuming RFC-1123 value a la "Tue, 3 Jun 2008 11:05:30 GMT"
return Instant.from(DateTimeFormatter.RFC_1123_DATE_TIME.parse(text));
}
else {
// assuming UTC instant a la "2007-12-03T10:15:30.00Z"
return Instant.parse(text);
}
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -207,7 +207,7 @@ public class InterfaceBasedMBeanInfoAssembler extends AbstractConfigurableMBeanI
* configured interfaces and is public, otherwise {@code false}.
*/
private boolean isPublicInInterface(Method method, String beanKey) {
return ((method.getModifiers() & Modifier.PUBLIC) > 0) && isDeclaredInInterface(method, beanKey);
return Modifier.isPublic(method.getModifiers()) && isDeclaredInInterface(method, beanKey);
}
/**
@@ -231,6 +231,7 @@ public class InterfaceBasedMBeanInfoAssembler extends AbstractConfigurableMBeanI
for (Class<?> ifc : ifaces) {
for (Method ifcMethod : ifc.getMethods()) {
if (ifcMethod.getName().equals(method.getName()) &&
ifcMethod.getParameterCount() == method.getParameterCount() &&
Arrays.equals(ifcMethod.getParameterTypes(), method.getParameterTypes())) {
return true;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2020 the original author 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,6 @@ public enum RegistrationPolicy {
* Registration should replace the affected MBean when attempting to register an MBean
* under a name that already exists.
*/
REPLACE_EXISTING;
REPLACE_EXISTING
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2019 the original author 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,7 @@ public class CacheReproTests {
@Test
public void spr11124MultipleAnnotations() throws Exception {
public void spr11124MultipleAnnotations() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Spr11124Config.class);
Spr11124Service bean = context.getBean(Spr11124Service.class);
bean.single(2);
@@ -71,7 +71,7 @@ public class CacheReproTests {
}
@Test
public void spr11249PrimitiveVarargs() throws Exception {
public void spr11249PrimitiveVarargs() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Spr11249Config.class);
Spr11249Service bean = context.getBean(Spr11249Service.class);
Object result = bean.doSomething("op", 2, 3);
@@ -44,7 +44,7 @@ public class SimpleKeyGeneratorTests {
}
@Test
public void singleValue(){
public void singleValue() {
Object k1 = generateKey(new Object[] { "a" });
Object k2 = generateKey(new Object[] { "a" });
Object k3 = generateKey(new Object[] { "different" });
@@ -56,7 +56,7 @@ public class SimpleKeyGeneratorTests {
}
@Test
public void multipleValues() {
public void multipleValues() {
Object k1 = generateKey(new Object[] { "a", 1, "b" });
Object k2 = generateKey(new Object[] { "a", 1, "b" });
Object k3 = generateKey(new Object[] { "b", 1, "a" });
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,16 +28,18 @@ import org.springframework.beans.BeanInstantiationException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.support.StaticListableBeanFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation2.NamedStubDao2;
import org.springframework.context.index.CandidateComponentsTestClassLoader;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.stereotype.Component;
import org.springframework.tests.sample.beans.TestBean;
import static org.junit.Assert.*;
@@ -102,10 +104,66 @@ public class ClassPathBeanDefinitionScannerTests {
@Test
public void testDoubleScan() {
GenericApplicationContext context = new GenericApplicationContext();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
int beanCount = scanner.scan(BASE_PACKAGE);
assertEquals(12, beanCount);
scanner.scan(BASE_PACKAGE);
ClassPathBeanDefinitionScanner scanner2 = new ClassPathBeanDefinitionScanner(context) {
@Override
protected void postProcessBeanDefinition(AbstractBeanDefinition beanDefinition, String beanName) {
super.postProcessBeanDefinition(beanDefinition, beanName);
beanDefinition.setAttribute("someDifference", "someValue");
}
};
scanner2.scan(BASE_PACKAGE);
assertTrue(context.containsBean("serviceInvocationCounter"));
assertTrue(context.containsBean("fooServiceImpl"));
assertTrue(context.containsBean("stubFooDao"));
assertTrue(context.containsBean("myNamedComponent"));
assertTrue(context.containsBean("myNamedDao"));
assertTrue(context.containsBean("thoreau"));
}
@Test
public void testWithIndex() {
GenericApplicationContext context = new GenericApplicationContext();
context.setClassLoader(CandidateComponentsTestClassLoader.index(
ClassPathScanningCandidateComponentProviderTests.class.getClassLoader(),
new ClassPathResource("spring.components", FooServiceImpl.class)));
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
int beanCount = scanner.scan(BASE_PACKAGE);
assertEquals(12, beanCount);
assertTrue(context.containsBean("serviceInvocationCounter"));
assertTrue(context.containsBean("fooServiceImpl"));
assertTrue(context.containsBean("stubFooDao"));
assertTrue(context.containsBean("myNamedComponent"));
assertTrue(context.containsBean("myNamedDao"));
assertTrue(context.containsBean("thoreau"));
}
@Test
public void testDoubleScanWithIndex() {
GenericApplicationContext context = new GenericApplicationContext();
context.setClassLoader(CandidateComponentsTestClassLoader.index(
ClassPathScanningCandidateComponentProviderTests.class.getClassLoader(),
new ClassPathResource("spring.components", FooServiceImpl.class)));
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
int beanCount = scanner.scan(BASE_PACKAGE);
assertEquals(12, beanCount);
ClassPathBeanDefinitionScanner scanner2 = new ClassPathBeanDefinitionScanner(context) {
@Override
protected void postProcessBeanDefinition(AbstractBeanDefinition beanDefinition, String beanName) {
super.postProcessBeanDefinition(beanDefinition, beanName);
beanDefinition.setAttribute("someDifference", "someValue");
}
};
scanner2.scan(BASE_PACKAGE);
assertTrue(context.containsBean("serviceInvocationCounter"));
assertTrue(context.containsBean("fooServiceImpl"));
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2020 the original author 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 @@ import example.scannable.sub.BarComponent;
import org.aspectj.lang.annotation.Aspect;
import org.junit.Test;
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.componentscan.gh24375.MyComponent;
import org.springframework.context.index.CandidateComponentsTestClassLoader;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.StandardEnvironment;
@@ -80,18 +80,17 @@ public class ClassPathScanningCandidateComponentProviderTests {
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(true);
provider.setResourceLoader(new DefaultResourceLoader(
CandidateComponentsTestClassLoader.disableIndex(getClass().getClassLoader())));
testDefault(provider, ScannedGenericBeanDefinition.class);
testDefault(provider);
}
@Test
public void defaultsWithIndex() {
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(true);
provider.setResourceLoader(new DefaultResourceLoader(TEST_BASE_CLASSLOADER));
testDefault(provider, AnnotatedGenericBeanDefinition.class);
testDefault(provider);
}
private void testDefault(ClassPathScanningCandidateComponentProvider provider,
Class<? extends BeanDefinition> expectedBeanDefinitionType) {
private void testDefault(ClassPathScanningCandidateComponentProvider provider) {
Set<BeanDefinition> candidates = provider.findCandidateComponents(TEST_BASE_PACKAGE);
assertTrue(containsBeanClass(candidates, DefaultNamedComponent.class));
assertTrue(containsBeanClass(candidates, NamedComponent.class));
@@ -101,7 +100,7 @@ public class ClassPathScanningCandidateComponentProviderTests {
assertTrue(containsBeanClass(candidates, ServiceInvocationCounter.class));
assertTrue(containsBeanClass(candidates, BarComponent.class));
assertEquals(7, candidates.size());
assertBeanDefinitionType(candidates, expectedBeanDefinitionType);
assertBeanDefinitionType(candidates);
}
@Test
@@ -109,22 +108,21 @@ public class ClassPathScanningCandidateComponentProviderTests {
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(true);
provider.setResourceLoader(new DefaultResourceLoader(
CandidateComponentsTestClassLoader.disableIndex(getClass().getClassLoader())));
testAntStyle(provider, ScannedGenericBeanDefinition.class);
testAntStyle(provider);
}
@Test
public void antStylePackageWithIndex() {
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(true);
provider.setResourceLoader(new DefaultResourceLoader(TEST_BASE_CLASSLOADER));
testAntStyle(provider, AnnotatedGenericBeanDefinition.class);
testAntStyle(provider);
}
private void testAntStyle(ClassPathScanningCandidateComponentProvider provider,
Class<? extends BeanDefinition> expectedBeanDefinitionType) {
private void testAntStyle(ClassPathScanningCandidateComponentProvider provider) {
Set<BeanDefinition> candidates = provider.findCandidateComponents(TEST_BASE_PACKAGE + ".**.sub");
assertTrue(containsBeanClass(candidates, BarComponent.class));
assertEquals(1, candidates.size());
assertBeanDefinitionType(candidates, expectedBeanDefinitionType);
assertBeanDefinitionType(candidates);
}
@Test
@@ -151,7 +149,7 @@ public class ClassPathScanningCandidateComponentProviderTests {
provider.addIncludeFilter(new AnnotationTypeFilter(Component.class));
provider.resetFilters(true);
Set<BeanDefinition> candidates = provider.findCandidateComponents(TEST_BASE_PACKAGE);
assertBeanDefinitionType(candidates, AnnotatedGenericBeanDefinition.class);
assertBeanDefinitionType(candidates);
}
@Test
@@ -159,20 +157,19 @@ public class ClassPathScanningCandidateComponentProviderTests {
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
provider.setResourceLoader(new DefaultResourceLoader(
CandidateComponentsTestClassLoader.disableIndex(getClass().getClassLoader())));
testCustomAnnotationTypeIncludeFilter(provider, ScannedGenericBeanDefinition.class);
testCustomAnnotationTypeIncludeFilter(provider);
}
@Test
public void customAnnotationTypeIncludeFilterWithIndex() {
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
provider.setResourceLoader(new DefaultResourceLoader(TEST_BASE_CLASSLOADER));
testCustomAnnotationTypeIncludeFilter(provider, AnnotatedGenericBeanDefinition.class);
testCustomAnnotationTypeIncludeFilter(provider);
}
private void testCustomAnnotationTypeIncludeFilter(ClassPathScanningCandidateComponentProvider provider,
Class<? extends BeanDefinition> expectedBeanDefinitionType) {
private void testCustomAnnotationTypeIncludeFilter(ClassPathScanningCandidateComponentProvider provider) {
provider.addIncludeFilter(new AnnotationTypeFilter(Component.class));
testDefault(provider, expectedBeanDefinitionType);
testDefault(provider);
}
@Test
@@ -180,18 +177,17 @@ public class ClassPathScanningCandidateComponentProviderTests {
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
provider.setResourceLoader(new DefaultResourceLoader(
CandidateComponentsTestClassLoader.disableIndex(getClass().getClassLoader())));
testCustomAssignableTypeIncludeFilter(provider, ScannedGenericBeanDefinition.class);
testCustomAssignableTypeIncludeFilter(provider);
}
@Test
public void customAssignableTypeIncludeFilterWithIndex() {
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
provider.setResourceLoader(new DefaultResourceLoader(TEST_BASE_CLASSLOADER));
testCustomAssignableTypeIncludeFilter(provider, AnnotatedGenericBeanDefinition.class);
testCustomAssignableTypeIncludeFilter(provider);
}
private void testCustomAssignableTypeIncludeFilter(ClassPathScanningCandidateComponentProvider provider,
Class<? extends BeanDefinition> expectedBeanDefinitionType) {
private void testCustomAssignableTypeIncludeFilter(ClassPathScanningCandidateComponentProvider provider) {
provider.addIncludeFilter(new AssignableTypeFilter(FooService.class));
Set<BeanDefinition> candidates = provider.findCandidateComponents(TEST_BASE_PACKAGE);
// Interfaces/Abstract class are filtered out automatically.
@@ -199,7 +195,7 @@ public class ClassPathScanningCandidateComponentProviderTests {
assertTrue(containsBeanClass(candidates, FooServiceImpl.class));
assertTrue(containsBeanClass(candidates, ScopedProxyTestBean.class));
assertEquals(3, candidates.size());
assertBeanDefinitionType(candidates, expectedBeanDefinitionType);
assertBeanDefinitionType(candidates);
}
@Test
@@ -207,18 +203,17 @@ public class ClassPathScanningCandidateComponentProviderTests {
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
provider.setResourceLoader(new DefaultResourceLoader(
CandidateComponentsTestClassLoader.disableIndex(getClass().getClassLoader())));
testCustomSupportedIncludeAndExcludeFilter(provider, ScannedGenericBeanDefinition.class);
testCustomSupportedIncludeAndExcludeFilter(provider);
}
@Test
public void customSupportedIncludeAndExcludeFilterWithIndex() {
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
provider.setResourceLoader(new DefaultResourceLoader(TEST_BASE_CLASSLOADER));
testCustomSupportedIncludeAndExcludeFilter(provider, AnnotatedGenericBeanDefinition.class);
testCustomSupportedIncludeAndExcludeFilter(provider);
}
private void testCustomSupportedIncludeAndExcludeFilter(ClassPathScanningCandidateComponentProvider provider,
Class<? extends BeanDefinition> expectedBeanDefinitionType) {
private void testCustomSupportedIncludeAndExcludeFilter(ClassPathScanningCandidateComponentProvider provider) {
provider.addIncludeFilter(new AnnotationTypeFilter(Component.class));
provider.addExcludeFilter(new AnnotationTypeFilter(Service.class));
provider.addExcludeFilter(new AnnotationTypeFilter(Repository.class));
@@ -227,7 +222,7 @@ public class ClassPathScanningCandidateComponentProviderTests {
assertTrue(containsBeanClass(candidates, ServiceInvocationCounter.class));
assertTrue(containsBeanClass(candidates, BarComponent.class));
assertEquals(3, candidates.size());
assertBeanDefinitionType(candidates, expectedBeanDefinitionType);
assertBeanDefinitionType(candidates);
}
@Test
@@ -240,7 +235,7 @@ public class ClassPathScanningCandidateComponentProviderTests {
Set<BeanDefinition> candidates = provider.findCandidateComponents(TEST_BASE_PACKAGE);
assertTrue(containsBeanClass(candidates, DefaultNamedComponent.class));
assertEquals(1, candidates.size());
assertBeanDefinitionType(candidates, ScannedGenericBeanDefinition.class);
assertBeanDefinitionType(candidates);
}
@Test
@@ -251,7 +246,7 @@ public class ClassPathScanningCandidateComponentProviderTests {
Set<BeanDefinition> candidates = provider.findCandidateComponents(TEST_BASE_PACKAGE);
assertTrue(containsBeanClass(candidates, StubFooDao.class));
assertEquals(1, candidates.size());
assertBeanDefinitionType(candidates, ScannedGenericBeanDefinition.class);
assertBeanDefinitionType(candidates);
}
@Test
@@ -260,7 +255,7 @@ public class ClassPathScanningCandidateComponentProviderTests {
provider.setResourceLoader(new DefaultResourceLoader(
CandidateComponentsTestClassLoader.disableIndex(getClass().getClassLoader())));
provider.addExcludeFilter(new RegexPatternTypeFilter(Pattern.compile(TEST_BASE_PACKAGE + ".*Named.*")));
testExclude(provider, ScannedGenericBeanDefinition.class);
testExclude(provider);
}
@Test
@@ -268,18 +263,17 @@ public class ClassPathScanningCandidateComponentProviderTests {
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(true);
provider.setResourceLoader(new DefaultResourceLoader(TEST_BASE_CLASSLOADER));
provider.addExcludeFilter(new RegexPatternTypeFilter(Pattern.compile(TEST_BASE_PACKAGE + ".*Named.*")));
testExclude(provider, AnnotatedGenericBeanDefinition.class);
testExclude(provider);
}
private void testExclude(ClassPathScanningCandidateComponentProvider provider,
Class<? extends BeanDefinition> expectedBeanDefinitionType) {
private void testExclude(ClassPathScanningCandidateComponentProvider provider) {
Set<BeanDefinition> candidates = provider.findCandidateComponents(TEST_BASE_PACKAGE);
assertTrue(containsBeanClass(candidates, FooServiceImpl.class));
assertTrue(containsBeanClass(candidates, StubFooDao.class));
assertTrue(containsBeanClass(candidates, ServiceInvocationCounter.class));
assertTrue(containsBeanClass(candidates, BarComponent.class));
assertEquals(4, candidates.size());
assertBeanDefinitionType(candidates, expectedBeanDefinitionType);
assertBeanDefinitionType(candidates);
}
@Test
@@ -307,7 +301,7 @@ public class ClassPathScanningCandidateComponentProviderTests {
}
@Test
public void testWithAspectAnnotationOnly() throws Exception {
public void testWithAspectAnnotationOnly() {
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
provider.addIncludeFilter(new AnnotationTypeFilter(Aspect.class));
Set<BeanDefinition> candidates = provider.findCandidateComponents(TEST_BASE_PACKAGE);
@@ -501,6 +495,15 @@ public class ClassPathScanningCandidateComponentProviderTests {
}
}
@Test
public void gh24375() {
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(true);
Set<BeanDefinition> components = provider.findCandidateComponents(MyComponent.class.getPackage().getName());
assertEquals(1, components.size());
assertEquals(MyComponent.class.getName(), components.iterator().next().getBeanClassName());
}
private boolean containsBeanClass(Set<BeanDefinition> candidates, Class<?> beanClass) {
for (BeanDefinition candidate : candidates) {
if (beanClass.getName().equals(candidate.getBeanClassName())) {
@@ -510,10 +513,9 @@ public class ClassPathScanningCandidateComponentProviderTests {
return false;
}
private void assertBeanDefinitionType(Set<BeanDefinition> candidates,
Class<? extends BeanDefinition> expectedType) {
private void assertBeanDefinitionType(Set<BeanDefinition> candidates) {
candidates.forEach(c -> {
assertThat(c, is(instanceOf(expectedType)));
assertThat(c, is(instanceOf(ScannedGenericBeanDefinition.class)));
});
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -530,9 +530,10 @@ public class ImportSelectorTests {
static Map<String, List<String>> allImports() {
return TestImportGroup.imports.entrySet()
.stream()
.collect(Collectors.toMap((entry) -> entry.getKey().getClassName(),
.collect(Collectors.toMap(entry -> entry.getKey().getClassName(),
Map.Entry::getValue));
}
private final List<Entry> instanceImports = new ArrayList<>();
@Override
@@ -0,0 +1,35 @@
/*
* Copyright 2002-2020 the original author 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.context.annotation.componentscan.gh24375;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface A {
@AliasFor("value")
B other() default @B;
@AliasFor("other")
B value() default @B;
}
@@ -0,0 +1,29 @@
/*
* Copyright 2002-2020 the original author 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.context.annotation.componentscan.gh24375;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface B {
String name() default "";
}
@@ -0,0 +1,24 @@
/*
* Copyright 2002-2020 the original author 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.context.annotation.componentscan.gh24375;
import org.springframework.stereotype.Component;
@Component
@A(other = @B)
public class MyComponent {
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -794,6 +794,7 @@ public class AnnotationDrivenEventListenerTests {
@EventListener
@Async
@Override
public void handleAsync(AnotherTestEvent event) {
assertTrue(!Thread.currentThread().getName().equals(event.content));
this.eventCollector.addEvent(this, event);
@@ -820,6 +821,7 @@ public class AnnotationDrivenEventListenerTests {
@EventListener
@Async
@Override
public void handleAsync(AnotherTestEvent event) {
assertTrue(!Thread.currentThread().getName().equals(event.content));
this.eventCollector.addEvent(this, event);
@@ -902,7 +904,6 @@ public class AnnotationDrivenEventListenerTests {
}
@EventListener
@Retention(RetentionPolicy.RUNTIME)
public @interface ConditionalEvent {
@@ -934,18 +935,20 @@ public class AnnotationDrivenEventListenerTests {
super.handle(event);
}
@Override
@EventListener(condition = "#payload.startsWith('OK')")
@Override
public void handleString(String payload) {
super.handleString(payload);
}
@ConditionalEvent("#root.event.timestamp > #p0")
@Override
public void handleTimestamp(Long timestamp) {
collectEvent(timestamp);
}
@ConditionalEvent("@conditionEvaluator.valid(#p0)")
@Override
public void handleRatio(Double ratio) {
collectEvent(ratio);
}
@@ -92,7 +92,7 @@ public class CandidateComponentsIndexLoaderTests {
}
@Test
public void loadIndexNoEntry() throws IOException {
public void loadIndexNoEntry() {
CandidateComponentsIndex index = CandidateComponentsIndexLoader.loadIndex(
CandidateComponentsTestClassLoader.index(getClass().getClassLoader(),
new ClassPathResource("empty-spring.components", getClass())));
@@ -100,7 +100,7 @@ public class CandidateComponentsIndexLoaderTests {
}
@Test
public void loadIndexWithException() throws IOException {
public void loadIndexWithException() {
final IOException cause = new IOException("test exception");
this.thrown.expect(IllegalStateException.class);
this.thrown.expectMessage("Unable to load indexes");
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2019 the original author 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 StaticMessageSourceTests extends AbstractApplicationContextTests {
@Test
@Override
public void count() {
// These are only checked for current Ctx (not parent ctx)
assertCount(15);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author 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 @@ import static org.junit.Assert.*;
*/
public class DateFormattingTests {
private final FormattingConversionService conversionService = new FormattingConversionService();
private FormattingConversionService conversionService;
private DataBinder binder;
@@ -58,6 +58,7 @@ public class DateFormattingTests {
}
private void setup(DateFormatterRegistrar registrar) {
conversionService = new FormattingConversionService();
DefaultConversionService.addDefaultConverters(conversionService);
registrar.registerFormatters(conversionService);
@@ -148,6 +149,20 @@ public class DateFormattingTests {
assertEquals("10/31/09 1:05", binder.getBindingResult().getFieldValue("dateAnnotatedPattern"));
}
@Test
public void testBindDateAnnotatedPatternWithGlobalFormat() {
DateFormatterRegistrar registrar = new DateFormatterRegistrar();
DateFormatter dateFormatter = new DateFormatter();
dateFormatter.setIso(ISO.DATE_TIME);
registrar.setFormatter(dateFormatter);
setup(registrar);
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("dateAnnotatedPattern", "10/31/09 1:05");
binder.bind(propertyValues);
assertEquals(0, binder.getBindingResult().getErrorCount());
assertEquals("10/31/09 1:05", binder.getBindingResult().getFieldValue("dateAnnotatedPattern"));
}
@Test
public void testBindDateTimeOverflow() {
MutablePropertyValues propertyValues = new MutablePropertyValues();
@@ -0,0 +1,121 @@
/*
* Copyright 2002-2019 the original author 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
*
* https://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.format.datetime.standard;
import java.text.ParseException;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.Random;
import java.util.stream.Stream;
import org.junit.Test;
import static java.time.Instant.MAX;
import static java.time.Instant.MIN;
import static java.time.ZoneId.systemDefault;
import static org.junit.Assert.assertEquals;
/**
* Unit tests for {@link InstantFormatter}.
*
* @author Andrei Nevedomskii
* @author Sam Brannen
* @since 5.1.12
*/
public class InstantFormatterTests {
private final InstantFormatter instantFormatter = new InstantFormatter();
@Test
public void should_parse_an_ISO_formatted_string_representation_of_an_Instant() {
new ISOSerializedInstantProvider().provideArguments().forEach(input -> {
try {
Instant expected = DateTimeFormatter.ISO_INSTANT.parse(input, Instant::from);
Instant actual = instantFormatter.parse(input, null);
assertEquals(expected, actual);
}
catch (ParseException ex) {
throw new RuntimeException(ex);
}
});
}
@Test
public void should_parse_an_RFC1123_formatted_string_representation_of_an_Instant() {
new RFC1123SerializedInstantProvider().provideArguments().forEach(input -> {
try {
Instant expected = DateTimeFormatter.RFC_1123_DATE_TIME.parse(input, Instant::from);
Instant actual = instantFormatter.parse(input, null);
assertEquals(expected, actual);
}
catch (ParseException ex) {
throw new RuntimeException(ex);
}
});
}
@Test
public void should_serialize_an_Instant_using_ISO_format_and_ignoring_Locale() {
new RandomInstantProvider().randomInstantStream(MIN, MAX).forEach(instant -> {
String expected = DateTimeFormatter.ISO_INSTANT.format(instant);
String actual = instantFormatter.print(instant, null);
assertEquals(expected, actual);
});
}
private static class RandomInstantProvider {
private static final long DATA_SET_SIZE = 10;
private static final Random random = new Random();
Stream<Instant> randomInstantStream(Instant min, Instant max) {
return Stream.concat(Stream.of(Instant.now()), // make sure that the data set includes current instant
random.longs(min.getEpochSecond(), max.getEpochSecond()).limit(DATA_SET_SIZE).mapToObj(Instant::ofEpochSecond));
}
}
private static class ISOSerializedInstantProvider extends RandomInstantProvider {
Stream<String> provideArguments() {
return randomInstantStream(MIN, MAX).map(DateTimeFormatter.ISO_INSTANT::format);
}
}
private static class RFC1123SerializedInstantProvider extends RandomInstantProvider {
// RFC-1123 supports only 4-digit years
private static final Instant min = Instant.parse("0000-01-01T00:00:00.00Z");
private static final Instant max = Instant.parse("9999-12-31T23:59:59.99Z");
Stream<String> provideArguments() {
return randomInstantStream(min, max)
.map(DateTimeFormatter.RFC_1123_DATE_TIME.withZone(systemDefault())::format);
}
}
}
+2 -2
View File
@@ -79,11 +79,11 @@ dependencies {
optional("io.reactivex.rxjava2:rxjava:${rxjava2Version}")
optional("io.netty:netty-buffer")
testCompile("io.projectreactor:reactor-test")
testCompile("org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}")
testCompile("javax.annotation:javax.annotation-api:1.3.2")
testCompile("com.google.code.findbugs:jsr305:3.0.2")
testCompile("org.xmlunit:xmlunit-matchers:2.6.2")
testCompile("javax.xml.bind:jaxb-api:2.3.1")
testCompile("com.fasterxml.woodstox:woodstox-core:5.2.0") {
testCompile("com.fasterxml.woodstox:woodstox-core:5.3.0") {
exclude group: "stax", module: "stax-api"
}
}
@@ -336,7 +336,15 @@ public class ReflectUtils {
public static Constructor getConstructor(Class type, Class[] parameterTypes) {
try {
Constructor constructor = type.getDeclaredConstructor(parameterTypes);
constructor.setAccessible(true);
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
constructor.setAccessible(true);
return null;
});
}
else {
constructor.setAccessible(true);
}
return constructor;
}
catch (NoSuchMethodException e) {
@@ -259,6 +259,9 @@ public class Enhancer extends AbstractClassGenerator {
public void setSuperclass(Class superclass) {
if (superclass != null && superclass.isInterface()) {
setInterfaces(new Class[]{superclass});
// SPRING PATCH BEGIN
setContextClass(superclass);
// SPRING PATCH END
}
else if (superclass != null && superclass.equals(Object.class)) {
// affects choice of ClassLoader
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2020 the original author 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,11 +169,12 @@ public final class GenericTypeResolver {
ParameterizedType parameterizedType = (ParameterizedType) genericType;
Class<?>[] generics = new Class<?>[parameterizedType.getActualTypeArguments().length];
Type[] typeArguments = parameterizedType.getActualTypeArguments();
ResolvableType contextType = ResolvableType.forClass(contextClass);
for (int i = 0; i < typeArguments.length; i++) {
Type typeArgument = typeArguments[i];
if (typeArgument instanceof TypeVariable) {
ResolvableType resolvedTypeArgument = resolveVariable(
(TypeVariable<?>) typeArgument, ResolvableType.forClass(contextClass));
(TypeVariable<?>) typeArgument, contextType);
if (resolvedTypeArgument != ResolvableType.NONE) {
generics[i] = resolvedTypeArgument.resolve();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2020 the original author 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,9 +18,6 @@ package org.springframework.core;
import java.lang.annotation.Annotation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
@@ -34,8 +31,6 @@ import org.springframework.util.ClassUtils;
@SuppressWarnings("unchecked")
public abstract class KotlinDetector {
private static final Log logger = LogFactory.getLog(KotlinDetector.class);
@Nullable
private static final Class<? extends Annotation> kotlinMetadata;
@@ -53,9 +48,6 @@ public abstract class KotlinDetector {
}
kotlinMetadata = (Class<? extends Annotation>) metadata;
kotlinReflectPresent = ClassUtils.isPresent("kotlin.reflect.full.KClasses", classLoader);
if (kotlinMetadata != null && !kotlinReflectPresent) {
logger.info("Kotlin reflection implementation not found at runtime, related features won't be available.");
}
}
@@ -872,6 +872,12 @@ public class ResolvableType implements Serializable {
return forType(ownerType, this.variableResolver).resolveVariable(variable);
}
}
if (this.type instanceof WildcardType) {
ResolvableType resolved = resolveType().resolveVariable(variable);
if (resolved != null) {
return resolved;
}
}
if (this.variableResolver != null) {
return this.variableResolver.resolveVariable(variable);
}
@@ -982,7 +988,7 @@ public class ResolvableType implements Serializable {
* using the full generic type information for assignability checks.
* For example: {@code ResolvableType.forClass(MyArrayList.class)}.
* @param clazz the class to introspect ({@code null} is semantically
* equivalent to {@code Object.class} for typical use cases here}
* 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...)
@@ -997,7 +1003,7 @@ public class ResolvableType implements Serializable {
* {@link Class#isAssignableFrom}, which this serves as a wrapper for.
* For example: {@code ResolvableType.forRawClass(List.class)}.
* @param clazz the class to introspect ({@code null} is semantically
* equivalent to {@code Object.class} for typical use cases here}
* equivalent to {@code Object.class} for typical use cases here)
* @return a {@link ResolvableType} for the specified class
* @since 4.2
* @see #forClass(Class)
@@ -1485,10 +1491,10 @@ public class ResolvableType implements Serializable {
@Override
@Nullable
public ResolvableType resolveVariable(TypeVariable<?> variable) {
TypeVariable<?> variableToCompare = SerializableTypeWrapper.unwrap(variable);
for (int i = 0; i < this.variables.length; i++) {
TypeVariable<?> v1 = SerializableTypeWrapper.unwrap(this.variables[i]);
TypeVariable<?> v2 = SerializableTypeWrapper.unwrap(variable);
if (ObjectUtils.nullSafeEquals(v1, v2)) {
TypeVariable<?> resolvedVariable = SerializableTypeWrapper.unwrap(this.variables[i]);
if (ObjectUtils.nullSafeEquals(resolvedVariable, variableToCompare)) {
return this.generics[i];
}
}
@@ -47,12 +47,40 @@ import org.springframework.util.MimeType;
*/
public abstract class AbstractDataBufferDecoder<T> extends AbstractDecoder<T> {
private int maxInMemorySize = -1;
protected AbstractDataBufferDecoder(MimeType... supportedMimeTypes) {
super(supportedMimeTypes);
}
/**
* Configure a limit on the number of bytes that can be buffered whenever
* the input stream needs to be aggregated. This can be a result of
* decoding to a single {@code DataBuffer},
* {@link java.nio.ByteBuffer ByteBuffer}, {@code byte[]},
* {@link org.springframework.core.io.Resource Resource}, {@code String}, etc.
* It can also occur when splitting the input stream, e.g. delimited text,
* in which case the limit applies to data buffered between delimiters.
* <p>By default in 5.1 this is set to -1, unlimited. In 5.2 the default
* value for this limit is set to 256K.
* @param byteCount the max number of bytes to buffer, or -1 for unlimited
* @since 5.1.11
*/
public void setMaxInMemorySize(int byteCount) {
this.maxInMemorySize = byteCount;
}
/**
* Return the {@link #setMaxInMemorySize configured} byte count limit.
* @since 5.1.11
*/
public int getMaxInMemorySize() {
return this.maxInMemorySize;
}
@Override
public Flux<T> decode(Publisher<DataBuffer> input, ResolvableType elementType,
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
@@ -64,7 +92,7 @@ public abstract class AbstractDataBufferDecoder<T> extends AbstractDecoder<T> {
public Mono<T> decodeToMono(Publisher<DataBuffer> input, ResolvableType elementType,
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
return DataBufferUtils.join(input)
return DataBufferUtils.join(input, this.maxInMemorySize)
.map(buffer -> decodeDataBuffer(buffer, elementType, mimeType, hints));
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 the original author 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,14 +25,17 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Consumer;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import org.springframework.core.ResolvableType;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferLimitException;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.core.io.buffer.LimitedDataBufferList;
import org.springframework.core.io.buffer.PooledDataBuffer;
import org.springframework.core.log.LogFormatUtils;
import org.springframework.lang.Nullable;
@@ -92,11 +95,44 @@ public final class StringDecoder extends AbstractDataBufferDecoder<String> {
List<byte[]> delimiterBytes = getDelimiterBytes(mimeType);
Flux<DataBuffer> inputFlux = Flux.from(input)
.flatMapIterable(buffer -> splitOnDelimiter(buffer, delimiterBytes))
.bufferUntil(buffer -> buffer == END_FRAME)
.map(StringDecoder::joinUntilEndFrame)
.doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release);
Flux<DataBuffer> inputFlux = Flux.defer(() -> {
if (getMaxInMemorySize() != -1) {
// Passing limiter into endFrameAfterDelimiter helps to ensure that in case of one DataBuffer
// containing multiple lines, the limit is checked and raised immediately without accumulating
// subsequent lines. This is necessary because concatMapIterable doesn't respect doOnDiscard.
// When reactor-core#1925 is resolved, we could replace bufferUntil with:
// .windowUntil(buffer -> buffer instanceof EndFrameBuffer)
// .concatMap(fluxes -> fluxes.collect(() -> new LimitedDataBufferList(getMaxInMemorySize()), LimitedDataBufferList::add))
LimitedDataBufferList limiter = new LimitedDataBufferList(getMaxInMemorySize());
return Flux.from(input)
.concatMapIterable(buffer -> splitOnDelimiter(buffer, delimiterBytes, limiter))
.bufferUntil(buffer -> buffer == END_FRAME)
.map(StringDecoder::joinUntilEndFrame)
.doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release);
}
else {
// When the decoder is unlimited (-1), concatMapIterable will cache buffers that may not
// be released if cancel is signalled before they are turned into String lines
// (see test maxInMemoryLimitReleasesUnprocessedLinesWhenUnlimited).
// When reactor-core#1925 is resolved, the workaround can be removed and the entire
// else clause possibly dropped.
ConcatMapIterableDiscardWorkaroundCache cache = new ConcatMapIterableDiscardWorkaroundCache();
return Flux.from(input)
.concatMapIterable(buffer -> cache.addAll(splitOnDelimiter(buffer, delimiterBytes, null)))
.doOnNext(cache)
.doOnCancel(cache)
.bufferUntil(buffer -> buffer == END_FRAME)
.map(StringDecoder::joinUntilEndFrame)
.doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release);
}
});
return super.decode(inputFlux, elementType, mimeType, hints);
}
@@ -116,7 +152,9 @@ public final class StringDecoder extends AbstractDataBufferDecoder<String> {
* Split the given data buffer on delimiter boundaries.
* The returned Flux contains an {@link #END_FRAME} buffer after each delimiter.
*/
private List<DataBuffer> splitOnDelimiter(DataBuffer buffer, List<byte[]> delimiterBytes) {
private List<DataBuffer> splitOnDelimiter(
DataBuffer buffer, List<byte[]> delimiterBytes, @Nullable LimitedDataBufferList limiter) {
List<DataBuffer> frames = new ArrayList<>();
try {
do {
@@ -138,15 +176,28 @@ public final class StringDecoder extends AbstractDataBufferDecoder<String> {
buffer.readPosition(readPosition + length + matchingDelimiter.length);
frames.add(DataBufferUtils.retain(frame));
frames.add(END_FRAME);
if (limiter != null) {
limiter.add(frame); // enforce the limit
limiter.clear();
}
}
else {
frame = buffer.slice(readPosition, buffer.readableByteCount());
buffer.readPosition(readPosition + buffer.readableByteCount());
frames.add(DataBufferUtils.retain(frame));
if (limiter != null) {
limiter.add(frame);
}
}
}
while (buffer.readableByteCount() > 0);
}
catch (DataBufferLimitException ex) {
if (limiter != null) {
limiter.releaseAndClear();
}
throw ex;
}
catch (Throwable ex) {
for (DataBuffer frame : frames) {
DataBufferUtils.release(frame);
@@ -283,4 +334,33 @@ public final class StringDecoder extends AbstractDataBufferDecoder<String> {
new MimeType("text", "plain", DEFAULT_CHARSET), MimeTypeUtils.ALL);
}
private class ConcatMapIterableDiscardWorkaroundCache implements Consumer<DataBuffer>, Runnable {
private final List<DataBuffer> buffers = new ArrayList<>();
public List<DataBuffer> addAll(List<DataBuffer> buffersToAdd) {
this.buffers.addAll(buffersToAdd);
return buffersToAdd;
}
@Override
public void accept(DataBuffer dataBuffer) {
this.buffers.remove(dataBuffer);
}
@Override
public void run() {
this.buffers.forEach(buffer -> {
try {
DataBufferUtils.release(buffer);
}
catch (Throwable ex) {
// Keep going..
}
});
}
}
}
@@ -36,7 +36,8 @@ import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
/**
* Context about a type to convert from or to.
* Contextual descriptor about a type to convert from or to.
* Capable of representing arrays and generic collection types.
*
* @author Keith Donald
* @author Andy Clement
@@ -45,6 +46,8 @@ import org.springframework.util.ObjectUtils;
* @author Sam Brannen
* @author Stephane Nicoll
* @since 3.0
* @see ConversionService#canConvert(TypeDescriptor, TypeDescriptor)
* @see ConversionService#convert(Object, TypeDescriptor, TypeDescriptor)
*/
@SuppressWarnings("serial")
public class TypeDescriptor implements Serializable {
@@ -322,9 +325,9 @@ public class TypeDescriptor implements Serializable {
* If this type is a {@code Stream}, returns the stream's component type.
* If this type is a {@link Collection} and it is parameterized, returns the Collection's element type.
* If the Collection is not parameterized, returns {@code null} indicating the element type is not declared.
* @return the array component type or Collection element type, or {@code null} if this type is a
* Collection but its element type is not parameterized
* @throws IllegalStateException if this type is not a {@code java.util.Collection} or array type
* @return the array component type or Collection element type, or {@code null} if this type is not
* an array type or a {@code java.util.Collection} or if its element type is not parameterized
* @see #elementTypeDescriptor(Object)
*/
@Nullable
public TypeDescriptor getElementTypeDescriptor() {
@@ -351,8 +354,7 @@ public class TypeDescriptor implements Serializable {
* TypeDescriptor that is returned.
* @param element the collection or array element
* @return a element type descriptor, narrowed to the type of the provided element
* @throws IllegalStateException if this type is not a {@code java.util.Collection}
* or array type
* @see #getElementTypeDescriptor()
* @see #narrow(Object)
*/
@Nullable
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author 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,6 +49,7 @@ import org.springframework.util.StringUtils;
*
* @author Juergen Hoeller
* @since 28.12.2003
* @see #FileSystemResource(String)
* @see #FileSystemResource(File)
* @see #FileSystemResource(Path)
* @see java.io.File
@@ -108,6 +109,15 @@ public class FileSystemResource extends AbstractResource implements WritableReso
* <p>In contrast to {@link PathResource}, this variant strictly follows the
* general {@link FileSystemResource} conventions, in particular in terms of
* path cleaning and {@link #createRelative(String)} handling.
* <p>Note: When building relative resources via {@link #createRelative},
* the relative path will apply <i>at the same directory level</i>:
* e.g. Paths.get("C:/dir1"), relative path "dir2" -> "C:/dir2"!
* If you prefer to have relative paths built underneath the given root directory,
* use the {@link #FileSystemResource(String) constructor with a file path}
* to append a trailing slash to the root path: "C:/dir1/", which indicates
* this directory as root for all relative paths. Alternatively, consider
* using {@link PathResource#PathResource(Path)} for {@code java.nio.path.Path}
* resolution in {@code createRelative}, always nesting relative paths.
* @param filePath a Path handle to a file
* @since 5.1
* @see #FileSystemResource(File)
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author 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,15 +44,16 @@ import org.springframework.util.Assert;
* in {@link FileSystemResource#FileSystemResource(Path) FileSystemResource},
* applying Spring's standard String-based path transformations but
* performing all operations via the {@link java.nio.file.Files} API.
* This {@code PathResource} is effectively a pure {@code java.nio.path.Path}
* based alternative with different {@code createRelative} behavior.
*
* @author Philippe Marschall
* @author Juergen Hoeller
* @since 4.0
* @see java.nio.file.Path
* @see java.nio.file.Files
* @deprecated as of 5.1.1, in favor of {@link FileSystemResource#FileSystemResource(Path)}
* @see FileSystemResource
*/
@Deprecated
public class PathResource extends AbstractResource implements WritableResource {
private final Path path;
@@ -252,7 +253,7 @@ public class PathResource extends AbstractResource implements WritableResource {
* @see java.nio.file.Path#resolve(String)
*/
@Override
public Resource createRelative(String relativePath) throws IOException {
public Resource createRelative(String relativePath) {
return new PathResource(this.path.resolve(relativePath));
}
@@ -0,0 +1,37 @@
/*
* Copyright 2002-2019 the original author 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
*
* https://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.core.io.buffer;
/**
* Exception that indicates the cumulative number of bytes consumed from a
* stream of {@link DataBuffer DataBuffer}'s exceeded some pre-configured limit.
* This can be raised when data buffers are cached and aggregated, e.g.
* {@link DataBufferUtils#join}. Or it could also be raised when data buffers
* have been released but a parsed representation is being aggregated, e.g. async
* parsing with Jackson.
*
* @author Rossen Stoyanchev
* @since 5.1.11
*/
@SuppressWarnings("serial")
public class DataBufferLimitException extends IllegalStateException {
public DataBufferLimitException(String message) {
super(message);
}
}
@@ -437,14 +437,36 @@ public abstract class DataBufferUtils {
* @since 5.0.3
*/
public static Mono<DataBuffer> join(Publisher<DataBuffer> dataBuffers) {
Assert.notNull(dataBuffers, "'dataBuffers' must not be null");
return join(dataBuffers, -1);
}
return Flux.from(dataBuffers)
.collectList()
/**
* Variant of {@link #join(Publisher)} that behaves the same way up until
* the specified max number of bytes to buffer. Once the limit is exceeded,
* {@link DataBufferLimitException} is raised.
* @param buffers the data buffers that are to be composed
* @param maxByteCount the max number of bytes to buffer, or -1 for unlimited
* @return a buffer with the aggregated content, possibly an empty Mono if
* the max number of bytes to buffer is exceeded.
* @throws DataBufferLimitException if maxByteCount is exceeded
* @since 5.1.11
*/
@SuppressWarnings("unchecked")
public static Mono<DataBuffer> join(Publisher<? extends DataBuffer> buffers, int maxByteCount) {
Assert.notNull(buffers, "'dataBuffers' must not be null");
if (buffers instanceof Mono) {
return (Mono<DataBuffer>) buffers;
}
// TODO: Drop doOnDiscard(LimitedDataBufferList.class, ...) (reactor-core#1924)
return Flux.from(buffers)
.collect(() -> new LimitedDataBufferList(maxByteCount), LimitedDataBufferList::add)
.filter(list -> !list.isEmpty())
.map(list -> list.get(0).factory().join(list))
.doOnDiscard(LimitedDataBufferList.class, LimitedDataBufferList::releaseAndClear)
.doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release);
}
@@ -0,0 +1,157 @@
/*
* Copyright 2002-2019 the original author 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
*
* https://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.core.io.buffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Predicate;
import reactor.core.publisher.Flux;
/**
* Custom {@link List} to collect data buffers with and enforce a
* limit on the total number of bytes buffered. For use with "collect" or
* other buffering operators in declarative APIs, e.g. {@link Flux}.
*
* <p>Adding elements increases the byte count and if the limit is exceeded,
* {@link DataBufferLimitException} is raised. {@link #clear()} resets the
* count. Remove and set are not supported.
*
* <p><strong>Note:</strong> This class does not automatically release the
* buffers it contains. It is usually preferable to use hooks such as
* {@link Flux#doOnDiscard} that also take care of cancel and error signals,
* or otherwise {@link #releaseAndClear()} can be used.
*
* @author Rossen Stoyanchev
* @since 5.1.11
*/
@SuppressWarnings("serial")
public class LimitedDataBufferList extends ArrayList<DataBuffer> {
private final int maxByteCount;
private int byteCount;
public LimitedDataBufferList(int maxByteCount) {
this.maxByteCount = maxByteCount;
}
@Override
public boolean add(DataBuffer buffer) {
boolean result = super.add(buffer);
if (result) {
updateCount(buffer.readableByteCount());
}
return result;
}
@Override
public void add(int index, DataBuffer buffer) {
super.add(index, buffer);
updateCount(buffer.readableByteCount());
}
@Override
public boolean addAll(Collection<? extends DataBuffer> collection) {
boolean result = super.addAll(collection);
collection.forEach(buffer -> updateCount(buffer.readableByteCount()));
return result;
}
@Override
public boolean addAll(int index, Collection<? extends DataBuffer> collection) {
boolean result = super.addAll(index, collection);
collection.forEach(buffer -> updateCount(buffer.readableByteCount()));
return result;
}
private void updateCount(int bytesToAdd) {
if (this.maxByteCount < 0) {
return;
}
if (bytesToAdd > Integer.MAX_VALUE - this.byteCount) {
raiseLimitException();
}
else {
this.byteCount += bytesToAdd;
if (this.byteCount > this.maxByteCount) {
raiseLimitException();
}
}
}
private void raiseLimitException() {
// Do not release here, it's likely down via doOnDiscard..
throw new DataBufferLimitException(
"Exceeded limit on max bytes to buffer : " + this.maxByteCount);
}
@Override
public DataBuffer remove(int index) {
throw new UnsupportedOperationException();
}
@Override
public boolean remove(Object o) {
throw new UnsupportedOperationException();
}
@Override
protected void removeRange(int fromIndex, int toIndex) {
throw new UnsupportedOperationException();
}
@Override
public boolean removeAll(Collection<?> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean removeIf(Predicate<? super DataBuffer> filter) {
throw new UnsupportedOperationException();
}
@Override
public DataBuffer set(int index, DataBuffer element) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
this.byteCount = 0;
super.clear();
}
/**
* Shortcut to {@link DataBufferUtils#release release} all data buffers and
* then {@link #clear()}.
*/
public void releaseAndClear() {
forEach(buf -> {
try {
DataBufferUtils.release(buf);
}
catch (Throwable ex) {
// Keep going..
}
});
clear();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2020 the original author 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,8 @@ public interface PropertySourceFactory {
/**
* Create a {@link PropertySource} that wraps the given resource.
* @param name the name of the property source
* (can be {@code null} in which case the factory implementation
* will have to generate a name based on the given resource)
* @param resource the resource (potentially encoded) to wrap
* @return the new {@link PropertySource} (never {@code null})
* @throws IOException if resource resolution failed
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -149,9 +149,10 @@ public class MethodMetadataReadingVisitor extends MethodVisitor implements Metho
MultiValueMap<String, Object> allAttributes = new LinkedMultiValueMap<>();
List<AnnotationAttributes> attributesList = this.attributesMap.get(annotationName);
if (attributesList != null) {
String annotatedElement = "method '" + getMethodName() + '\'';
for (AnnotationAttributes annotationAttributes : attributesList) {
AnnotationAttributes convertedAttributes = AnnotationReadingVisitorUtils.convertClassValues(
"method '" + getMethodName() + "'", this.classLoader, annotationAttributes, classValuesAsString);
annotatedElement, this.classLoader, annotationAttributes, classValuesAsString);
convertedAttributes.forEach(allAttributes::add);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -110,6 +110,11 @@ public abstract class ClassUtils {
*/
private static final Set<Class<?>> javaLanguageInterfaces;
/**
* Cache for equivalent methods on an interface implemented by the declaring class.
*/
private static final Map<Method, Method> interfaceMethodCache = new ConcurrentReferenceHashMap<>(256);
static {
primitiveWrapperTypeMap.put(Boolean.class, boolean.class);
@@ -527,7 +532,7 @@ public abstract class ClassUtils {
* @param lhsType the target type
* @param rhsType the value type that should be assigned to the target type
* @return if the target type is assignable from the value type
* @see TypeUtils#isAssignable
* @see TypeUtils#isAssignable(java.lang.reflect.Type, java.lang.reflect.Type)
*/
public static boolean isAssignable(Class<?> lhsType, Class<?> rhsType) {
Assert.notNull(lhsType, "Left-hand side type must not be null");
@@ -537,17 +542,12 @@ public abstract class ClassUtils {
}
if (lhsType.isPrimitive()) {
Class<?> resolvedPrimitive = primitiveWrapperTypeMap.get(rhsType);
if (lhsType == resolvedPrimitive) {
return true;
}
return (lhsType == resolvedPrimitive);
}
else {
Class<?> resolvedWrapper = primitiveTypeToWrapperMap.get(rhsType);
if (resolvedWrapper != null && lhsType.isAssignableFrom(resolvedWrapper)) {
return true;
}
return (resolvedWrapper != null && lhsType.isAssignableFrom(resolvedWrapper));
}
return false;
}
/**
@@ -1059,7 +1059,7 @@ public abstract class ClassUtils {
* @param clazz the clazz to analyze
* @param paramTypes the parameter types of the method
* @return whether the class has a corresponding constructor
* @see Class#getMethod
* @see Class#getConstructor
*/
public static boolean hasConstructor(Class<?> clazz, Class<?>... paramTypes) {
return (getConstructorIfAvailable(clazz, paramTypes) != null);
@@ -1291,13 +1291,16 @@ public abstract class ClassUtils {
* @see #getMostSpecificMethod
*/
public static Method getInterfaceMethodIfPossible(Method method) {
if (Modifier.isPublic(method.getModifiers()) && !method.getDeclaringClass().isInterface()) {
Class<?> current = method.getDeclaringClass();
if (!Modifier.isPublic(method.getModifiers()) || method.getDeclaringClass().isInterface()) {
return method;
}
return interfaceMethodCache.computeIfAbsent(method, key -> {
Class<?> current = key.getDeclaringClass();
while (current != null && current != Object.class) {
Class<?>[] ifcs = current.getInterfaces();
for (Class<?> ifc : ifcs) {
try {
return ifc.getMethod(method.getName(), method.getParameterTypes());
return ifc.getMethod(key.getName(), key.getParameterTypes());
}
catch (NoSuchMethodException ex) {
// ignore
@@ -1305,8 +1308,8 @@ public abstract class ClassUtils {
}
current = current.getSuperclass();
}
}
return method;
return key;
});
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2020 the original author 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,9 +24,12 @@ import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.EnumSet;
import org.springframework.lang.Nullable;
import static java.nio.file.FileVisitOption.FOLLOW_LINKS;
/**
* Utility methods for working with the file system.
*
@@ -65,11 +68,11 @@ public abstract class FileSystemUtils {
}
/**
* Delete the supplied {@link File} - for directories,
* Delete the supplied {@link File} &mdash; for directories,
* recursively delete any nested directories or files as well.
* @param root the root {@code File} to delete
* @return {@code true} if the {@code File} existed and was deleted,
* or {@code false} it it did not exist
* or {@code false} if it did not exist
* @throws IOException in the case of I/O errors
* @since 5.0
*/
@@ -123,7 +126,7 @@ public abstract class FileSystemUtils {
BasicFileAttributes srcAttr = Files.readAttributes(src, BasicFileAttributes.class);
if (srcAttr.isDirectory()) {
Files.walkFileTree(src, new SimpleFileVisitor<Path>() {
Files.walkFileTree(src, EnumSet.of(FOLLOW_LINKS), Integer.MAX_VALUE, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
Files.createDirectories(dest.resolve(src.relativize(dir)));
@@ -417,14 +417,14 @@ public abstract class StringUtils {
int pos = 0; // our position in the old string
int patLen = oldPattern.length();
while (index >= 0) {
sb.append(inString.substring(pos, index));
sb.append(inString, pos, index);
sb.append(newPattern);
pos = index + patLen;
index = inString.indexOf(oldPattern, pos);
}
// append any characters to the right of a match
sb.append(inString.substring(pos));
sb.append(inString, pos, inString.length());
return sb.toString();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,38 +16,53 @@
package org.springframework.util.unit;
import java.util.Objects;
/**
* A standard set of data size units.
* A standard set of {@link DataSize} units.
*
* <p>The unit prefixes used in this class are
* <a href="https://en.wikipedia.org/wiki/Binary_prefix">binary prefixes</a>
* indicating multiplication by powers of 2. The following table displays the
* enum constants defined in this class and corresponding values.
*
* <p>
* <table border="1">
* <tr><th>Constant</th><th>Data Size</th><th>Power&nbsp;of&nbsp;2</th><th>Size in Bytes</th></tr>
* <tr><td>{@link #BYTES}</td><td>1B</td><td>2^0</td><td>1</td></tr>
* <tr><td>{@link #KILOBYTES}</td><td>1KB</td><td>2^10</td><td>1,024</td></tr>
* <tr><td>{@link #MEGABYTES}</td><td>1MB</td><td>2^20</td><td>1,048,576</td></tr>
* <tr><td>{@link #GIGABYTES}</td><td>1GB</td><td>2^30</td><td>1,073,741,824</td></tr>
* <tr><td>{@link #TERABYTES}</td><td>1TB</td><td>2^40</td><td>1,099,511,627,776</td></tr>
* </table>
*
* @author Stephane Nicoll
* @author Sam Brannen
* @since 5.1
* @see DataSize
*/
public enum DataUnit {
/**
* Bytes.
* Bytes, represented by suffix {@code B}.
*/
BYTES("B", DataSize.ofBytes(1)),
/**
* Kilobytes.
* Kilobytes, represented by suffix {@code KB}.
*/
KILOBYTES("KB", DataSize.ofKilobytes(1)),
/**
* Megabytes.
* Megabytes, represented by suffix {@code MB}.
*/
MEGABYTES("MB", DataSize.ofMegabytes(1)),
/**
* Gigabytes.
* Gigabytes, represented by suffix {@code GB}.
*/
GIGABYTES("GB", DataSize.ofGigabytes(1)),
/**
* Terabytes.
* Terabytes, represented by suffix {@code TB}.
*/
TERABYTES("TB", DataSize.ofTerabytes(1));
@@ -68,18 +83,18 @@ public enum DataUnit {
/**
* Return the {@link DataUnit} matching the specified {@code suffix}.
* @param suffix one of the standard suffix
* @param suffix one of the standard suffixes
* @return the {@link DataUnit} matching the specified {@code suffix}
* @throws IllegalArgumentException if the suffix does not match any
* of this enum's constants
* @throws IllegalArgumentException if the suffix does not match the suffix
* of any of this enum's constants
*/
public static DataUnit fromSuffix(String suffix) {
for (DataUnit candidate : values()) {
if (Objects.equals(candidate.suffix, suffix)) {
if (candidate.suffix.equals(suffix)) {
return candidate;
}
}
throw new IllegalArgumentException("Unknown unit '" + suffix + "'");
throw new IllegalArgumentException("Unknown data unit suffix '" + suffix + "'");
}
}
@@ -684,10 +684,16 @@ public class ResolvableTypeTests {
@Test
public void resolveBoundedTypeVariableResult() throws Exception {
ResolvableType type = ResolvableType.forMethodReturnType(Methods.class.getMethod("boundedTypeVaraibleResult"));
ResolvableType type = ResolvableType.forMethodReturnType(Methods.class.getMethod("boundedTypeVariableResult"));
assertThat(type.resolve(), equalTo((Class) CharSequence.class));
}
@Test
public void resolveBoundedTypeVariableWildcardResult() throws Exception {
ResolvableType type = ResolvableType.forMethodReturnType(Methods.class.getMethod("boundedTypeVariableWildcardResult"));
assertThat(type.getGeneric(1).asCollection().resolveGeneric(), equalTo((Class) CharSequence.class));
}
@Test
public void resolveVariableNotFound() throws Exception {
ResolvableType type = ResolvableType.forMethodReturnType(Methods.class.getMethod("typedReturn"));
@@ -695,27 +701,14 @@ public class ResolvableTypeTests {
}
@Test
public void resolveTypeVaraibleFromMethodReturn() throws Exception {
ResolvableType type = ResolvableType.forMethodReturnType(Methods.class.getMethod("typedReturn"));
assertThat(type.resolve(), nullValue());
}
@Test
public void resolveTypeVaraibleFromMethodReturnWithInstanceClass() throws Exception {
ResolvableType type = ResolvableType.forMethodReturnType(
Methods.class.getMethod("typedReturn"), TypedMethods.class);
assertThat(type.resolve(), equalTo((Class) String.class));
}
@Test
public void resolveTypeVaraibleFromSimpleInterfaceType() {
public void resolveTypeVariableFromSimpleInterfaceType() {
ResolvableType type = ResolvableType.forClass(
MySimpleInterfaceType.class).as(MyInterfaceType.class);
assertThat(type.resolveGeneric(), equalTo((Class) String.class));
}
@Test
public void resolveTypeVaraibleFromSimpleCollectionInterfaceType() {
public void resolveTypeVariableFromSimpleCollectionInterfaceType() {
ResolvableType type = ResolvableType.forClass(
MyCollectionInterfaceType.class).as(MyInterfaceType.class);
assertThat(type.resolveGeneric(), equalTo((Class) Collection.class));
@@ -723,14 +716,14 @@ public class ResolvableTypeTests {
}
@Test
public void resolveTypeVaraibleFromSimpleSuperclassType() {
public void resolveTypeVariableFromSimpleSuperclassType() {
ResolvableType type = ResolvableType.forClass(
MySimpleSuperclassType.class).as(MySuperclassType.class);
assertThat(type.resolveGeneric(), equalTo((Class) String.class));
}
@Test
public void resolveTypeVaraibleFromSimpleCollectionSuperclassType() {
public void resolveTypeVariableFromSimpleCollectionSuperclassType() {
ResolvableType type = ResolvableType.forClass(
MyCollectionSuperclassType.class).as(MySuperclassType.class);
assertThat(type.resolveGeneric(), equalTo((Class) Collection.class));
@@ -1459,7 +1452,9 @@ public class ResolvableTypeTests {
void charSequenceParameter(List<CharSequence> cs);
<R extends CharSequence & Serializable> R boundedTypeVaraibleResult();
<R extends CharSequence & Serializable> R boundedTypeVariableResult();
Map<String, ? extends List<? extends CharSequence>> boundedTypeVariableWildcardResult();
void nested(Map<Map<String, Integer>, Map<Byte, Long>> p);
@@ -488,6 +488,20 @@ public class AnnotatedElementUtilsTests {
assertTrue(isAnnotated(element, name));
}
@Test
public void getMergedAnnotationWithImplicitAliasesWithDefaultsInMetaAnnotationOnComposedAnnotation() {
Class<?> element = ImplicitAliasesWithDefaultsClass.class;
String name = AliasesWithDefaults.class.getName();
AliasesWithDefaults annotation = getMergedAnnotation(element, AliasesWithDefaults.class);
assertNotNull("Should find @AliasesWithDefaults on " + element.getSimpleName(), annotation);
assertEquals("a1", "ImplicitAliasesWithDefaults", annotation.a1());
assertEquals("a2", "ImplicitAliasesWithDefaults", annotation.a2());
// Verify contracts between utility methods:
assertTrue(isAnnotated(element, name));
}
@Test
public void getMergedAnnotationAttributesWithInvalidConventionBasedComposedAnnotation() {
Class<?> element = InvalidConventionBasedComposedContextConfigClass.class;
@@ -958,7 +972,6 @@ public class AnnotatedElementUtilsTests {
String[] xmlConfigFiles() default {};
}
@ContextConfig
@Retention(RetentionPolicy.RUNTIME)
@interface AliasedComposedContextConfig {
@@ -999,6 +1012,27 @@ public class AnnotatedElementUtilsTests {
@interface ComposedImplicitAliasesContextConfig {
}
@Retention(RetentionPolicy.RUNTIME)
@interface AliasesWithDefaults {
@AliasFor("a2")
String a1() default "AliasesWithDefaults";
@AliasFor("a1")
String a2() default "AliasesWithDefaults";
}
@Retention(RetentionPolicy.RUNTIME)
@AliasesWithDefaults
@interface ImplicitAliasesWithDefaults {
@AliasFor(annotation = AliasesWithDefaults.class, attribute = "a1")
String b1() default "ImplicitAliasesWithDefaults";
@AliasFor(annotation = AliasesWithDefaults.class, attribute = "a2")
String b2() default "ImplicitAliasesWithDefaults";
}
@ImplicitAliasesContextConfig
@Retention(RetentionPolicy.RUNTIME)
@interface TransitiveImplicitAliasesContextConfig {
@@ -1296,6 +1330,10 @@ public class AnnotatedElementUtilsTests {
static class ImplicitAliasesContextConfigClass3 {
}
@ImplicitAliasesWithDefaults
static class ImplicitAliasesWithDefaultsClass {
}
@TransitiveImplicitAliasesContextConfig(groovy = "test.groovy")
static class TransitiveImplicitAliasesContextConfigClass {
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 the original author 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 reactor.test.StepVerifier;
import org.springframework.core.ResolvableType;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferLimitException;
import org.springframework.util.MimeType;
import org.springframework.util.MimeTypeUtils;
@@ -126,6 +127,33 @@ public class StringDecoderTests extends AbstractDecoderTestCase<StringDecoder> {
.verify());
}
@Test
public void maxInMemoryLimit() {
Flux<DataBuffer> input = Flux.just(
stringBuffer("abc\n"), stringBuffer("defg\n"), stringBuffer("hijkl\n"));
this.decoder.setMaxInMemorySize(4);
testDecode(input, String.class, step ->
step.expectNext("abc", "defg").verifyError(DataBufferLimitException.class));
}
@Test // gh-24312
public void maxInMemoryLimitReleaseUnprocessedLinesFromCurrentBuffer() {
Flux<DataBuffer> input = Flux.just(
stringBuffer("TOO MUCH DATA\nanother line\n\nand another\n"));
this.decoder.setMaxInMemorySize(5);
testDecode(input, String.class, step -> step.verifyError(DataBufferLimitException.class));
}
@Test // gh-24339
public void maxInMemoryLimitReleaseUnprocessedLinesWhenUnlimited() {
Flux<DataBuffer> input = Flux.just(stringBuffer("Line 1\nLine 2\nLine 3\n"));
this.decoder.setMaxInMemorySize(-1);
testDecodeCancel(input, ResolvableType.forClass(String.class), null, Collections.emptyMap());
}
@Test
public void decodeNewLineIncludeDelimiters() {
this.decoder = StringDecoder.allMimeTypes(StringDecoder.DEFAULT_DELIMITERS, false);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@
package org.springframework.core.io;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
@@ -129,6 +130,14 @@ public class ResourceTests {
assertEquals(new FileSystemResource(file), resource);
}
@Test
public void fileSystemResourceWithFile() throws IOException {
File file = new File(getClass().getResource("Resource.class").getFile());
Resource resource = new FileSystemResource(file);
doTestResource(resource);
assertEquals(new FileSystemResource(file), resource);
}
@Test
public void testFileSystemResourceWithFilePath() throws Exception {
Path filePath = Paths.get(getClass().getResource("Resource.class").toURI());
@@ -48,11 +48,14 @@ import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.buffer.support.DataBufferTestUtils;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.anyLong;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.isA;
import static org.mockito.Mockito.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* @author Arjen Poutsma
@@ -716,14 +719,25 @@ public class DataBufferUtilsTests extends AbstractDataBufferAllocatingTestCase {
Mono<DataBuffer> result = DataBufferUtils.join(flux);
StepVerifier.create(result)
.consumeNextWith(dataBuffer -> {
assertEquals("foobarbaz",
DataBufferTestUtils.dumpString(dataBuffer, StandardCharsets.UTF_8));
release(dataBuffer);
.consumeNextWith(buf -> {
assertEquals("foobarbaz", DataBufferTestUtils.dumpString(buf, StandardCharsets.UTF_8));
release(buf);
})
.verifyComplete();
}
@Test
public void joinWithLimit() {
DataBuffer foo = stringBuffer("foo");
DataBuffer bar = stringBuffer("bar");
DataBuffer baz = stringBuffer("baz");
Flux<DataBuffer> flux = Flux.just(foo, bar, baz);
Mono<DataBuffer> result = DataBufferUtils.join(flux, 8);
StepVerifier.create(result)
.verifyError(DataBufferLimitException.class);
}
@Test
public void joinErrors() {
DataBuffer foo = stringBuffer("foo");
@@ -0,0 +1,63 @@
/*
* Copyright 2002-2019 the original author 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
*
* https://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.core.io.buffer;
import java.nio.charset.StandardCharsets;
import org.junit.Test;
import static org.junit.Assert.fail;
/**
* Unit tests for {@link LimitedDataBufferList}.
* @author Rossen Stoyanchev
* @since 5.1.11
*/
public class LimitedDataBufferListTests {
private final static DataBufferFactory bufferFactory = new DefaultDataBufferFactory();
@Test
public void limitEnforced() {
try {
new LimitedDataBufferList(5).add(toDataBuffer("123456"));
fail();
}
catch (DataBufferLimitException ex) {
// Expected
}
}
@Test
public void limitIgnored() {
new LimitedDataBufferList(-1).add(toDataBuffer("123456"));
}
@Test
public void clearResetsCount() {
LimitedDataBufferList list = new LimitedDataBufferList(5);
list.add(toDataBuffer("12345"));
list.clear();
list.add(toDataBuffer("12345"));
}
private static DataBuffer toDataBuffer(String value) {
return bufferFactory.wrap(value.getBytes(StandardCharsets.UTF_8));
}
}
@@ -20,6 +20,7 @@ import java.io.Serializable;
import java.lang.annotation.Annotation;
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;
@@ -37,7 +38,7 @@ import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import org.springframework.stereotype.Component;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
/**
@@ -70,7 +71,7 @@ public class AnnotationMetadataTests {
@Test
public void standardAnnotationMetadataForSubclass() {
AnnotationMetadata metadata = new StandardAnnotationMetadata(AnnotatedComponentSubClass.class, true);
doTestSubClassAnnotationInfo(metadata);
doTestSubClassAnnotationInfo(metadata, false);
}
@Test
@@ -78,10 +79,10 @@ public class AnnotationMetadataTests {
MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory();
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(AnnotatedComponentSubClass.class.getName());
AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
doTestSubClassAnnotationInfo(metadata);
doTestSubClassAnnotationInfo(metadata, true);
}
private void doTestSubClassAnnotationInfo(AnnotationMetadata metadata) {
private void doTestSubClassAnnotationInfo(AnnotationMetadata metadata, boolean asm) {
assertThat(metadata.getClassName(), is(AnnotatedComponentSubClass.class.getName()));
assertThat(metadata.isInterface(), is(false));
assertThat(metadata.isAnnotation(), is(false));
@@ -93,11 +94,26 @@ public class AnnotationMetadataTests {
assertThat(metadata.isAnnotated(Component.class.getName()), is(false));
assertThat(metadata.isAnnotated(Scope.class.getName()), is(false));
assertThat(metadata.isAnnotated(SpecialAttr.class.getName()), is(false));
if (asm) {
assertThat(metadata.isAnnotated(NamedComposedAnnotation.class.getName()), is(false));
assertThat(metadata.hasAnnotation(NamedComposedAnnotation.class.getName()), is(false));
assertThat(metadata.getAnnotationTypes(), is(emptyCollectionOf(String.class)));
}
else {
assertThat(metadata.isAnnotated(NamedComposedAnnotation.class.getName()), is(true));
assertThat(metadata.hasAnnotation(NamedComposedAnnotation.class.getName()), is(true));
assertThat(metadata.getAnnotationTypes(), containsInAnyOrder(NamedComposedAnnotation.class.getName()));
}
assertThat(metadata.hasAnnotation(Component.class.getName()), is(false));
assertThat(metadata.hasAnnotation(Scope.class.getName()), is(false));
assertThat(metadata.hasAnnotation(SpecialAttr.class.getName()), is(false));
assertThat(metadata.getAnnotationTypes().size(), is(0));
assertThat(metadata.hasMetaAnnotation(Component.class.getName()), is(false));
assertThat(metadata.hasMetaAnnotation(MetaAnnotation.class.getName()), is(false));
assertThat(metadata.getAnnotationAttributes(Component.class.getName()), nullValue());
assertThat(metadata.getAnnotationAttributes(MetaAnnotation.class.getName(), false), nullValue());
assertThat(metadata.getAnnotationAttributes(MetaAnnotation.class.getName(), true), nullValue());
assertThat(metadata.getAnnotatedMethods(DirectAnnotation.class.getName()).size(), equalTo(0));
assertThat(metadata.isAnnotated(IsAnnotatedAnnotation.class.getName()), equalTo(false));
assertThat(metadata.getAllAnnotationAttributes(DirectAnnotation.class.getName()), nullValue());
@@ -262,13 +278,18 @@ public class AnnotationMetadataTests {
assertThat(metadata.getInterfaceNames().length, is(1));
assertThat(metadata.getInterfaceNames()[0], is(Serializable.class.getName()));
assertThat(metadata.isAnnotated(NamedComposedAnnotation.class.getName()), is(true));
assertThat(metadata.isAnnotated(Component.class.getName()), is(true));
assertThat(metadata.hasAnnotation(Component.class.getName()), is(true));
assertThat(metadata.hasAnnotation(Scope.class.getName()), is(true));
assertThat(metadata.hasAnnotation(SpecialAttr.class.getName()), is(true));
assertThat(metadata.getAnnotationTypes().size(), is(6));
assertThat(metadata.getAnnotationTypes().contains(Component.class.getName()), is(true));
assertThat(metadata.getAnnotationTypes().contains(Scope.class.getName()), is(true));
assertThat(metadata.getAnnotationTypes().contains(SpecialAttr.class.getName()), is(true));
assertThat(metadata.hasAnnotation(NamedComposedAnnotation.class.getName()), is(true));
assertThat(metadata.getAnnotationTypes(),
containsInAnyOrder(Component.class.getName(), Scope.class.getName(),
SpecialAttr.class.getName(), DirectAnnotation.class.getName(),
MetaMetaAnnotation.class.getName(),
EnumSubclasses.class.getName(),
NamedComposedAnnotation.class.getName()));
AnnotationAttributes compAttrs = (AnnotationAttributes) metadata.getAnnotationAttributes(Component.class.getName());
assertThat(compAttrs.size(), is(1));
@@ -465,6 +486,7 @@ public class AnnotationMetadataTests {
@DirectAnnotation(value = "direct", additional = "", additionalArray = {})
@MetaMetaAnnotation
@EnumSubclasses({SubclassEnum.FOO, SubclassEnum.BAR})
@NamedComposedAnnotation
private static class AnnotatedComponent implements Serializable {
@TestAutowired
@@ -545,6 +567,7 @@ public class AnnotationMetadataTests {
@NamedAnnotation3(name = "name 3")
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
public @interface NamedComposedAnnotation {
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2020 the original author 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.expression.spel.standard;
import java.util.concurrent.atomic.AtomicInteger;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
@@ -65,15 +67,15 @@ public class SpelExpression implements Expression {
// Holds the compiled form of the expression (if it has been compiled)
@Nullable
private CompiledExpression compiledAst;
private volatile CompiledExpression compiledAst;
// Count of many times as the expression been interpreted - can trigger compilation
// when certain limit reached
private volatile int interpretedCount = 0;
private final AtomicInteger interpretedCount = new AtomicInteger(0);
// The number of times compilation was attempted and failed - enables us to eventually
// give up trying to compile it when it just doesn't seem to be possible.
private volatile int failedAttempts = 0;
private final AtomicInteger failedAttempts = new AtomicInteger(0);
/**
@@ -116,16 +118,17 @@ public class SpelExpression implements Expression {
@Override
@Nullable
public Object getValue() throws EvaluationException {
if (this.compiledAst != null) {
CompiledExpression compiledAst = this.compiledAst;
if (compiledAst != null) {
try {
EvaluationContext context = getEvaluationContext();
return this.compiledAst.getValue(context.getRootObject().getValue(), context);
return compiledAst.getValue(context.getRootObject().getValue(), context);
}
catch (Throwable ex) {
// If running in mixed mode, revert to interpreted
if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) {
this.interpretedCount = 0;
this.compiledAst = null;
this.interpretedCount.set(0);
}
else {
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
@@ -144,10 +147,11 @@ public class SpelExpression implements Expression {
@Override
@Nullable
public <T> T getValue(@Nullable Class<T> expectedResultType) throws EvaluationException {
if (this.compiledAst != null) {
CompiledExpression compiledAst = this.compiledAst;
if (compiledAst != null) {
try {
EvaluationContext context = getEvaluationContext();
Object result = this.compiledAst.getValue(context.getRootObject().getValue(), context);
Object result = compiledAst.getValue(context.getRootObject().getValue(), context);
if (expectedResultType == null) {
return (T) result;
}
@@ -159,8 +163,8 @@ public class SpelExpression implements Expression {
catch (Throwable ex) {
// If running in mixed mode, revert to interpreted
if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) {
this.interpretedCount = 0;
this.compiledAst = null;
this.interpretedCount.set(0);
}
else {
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
@@ -179,15 +183,16 @@ public class SpelExpression implements Expression {
@Override
@Nullable
public Object getValue(Object rootObject) throws EvaluationException {
if (this.compiledAst != null) {
CompiledExpression compiledAst = this.compiledAst;
if (compiledAst != null) {
try {
return this.compiledAst.getValue(rootObject, getEvaluationContext());
return compiledAst.getValue(rootObject, getEvaluationContext());
}
catch (Throwable ex) {
// If running in mixed mode, revert to interpreted
if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) {
this.interpretedCount = 0;
this.compiledAst = null;
this.interpretedCount.set(0);
}
else {
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
@@ -207,9 +212,10 @@ public class SpelExpression implements Expression {
@Override
@Nullable
public <T> T getValue(Object rootObject, @Nullable Class<T> expectedResultType) throws EvaluationException {
if (this.compiledAst != null) {
CompiledExpression compiledAst = this.compiledAst;
if (compiledAst != null) {
try {
Object result = this.compiledAst.getValue(rootObject, getEvaluationContext());
Object result = compiledAst.getValue(rootObject, getEvaluationContext());
if (expectedResultType == null) {
return (T)result;
}
@@ -221,8 +227,8 @@ public class SpelExpression implements Expression {
catch (Throwable ex) {
// If running in mixed mode, revert to interpreted
if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) {
this.interpretedCount = 0;
this.compiledAst = null;
this.interpretedCount.set(0);
}
else {
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
@@ -244,15 +250,16 @@ public class SpelExpression implements Expression {
public Object getValue(EvaluationContext context) throws EvaluationException {
Assert.notNull(context, "EvaluationContext is required");
if (this.compiledAst != null) {
CompiledExpression compiledAst = this.compiledAst;
if (compiledAst != null) {
try {
return this.compiledAst.getValue(context.getRootObject().getValue(), context);
return compiledAst.getValue(context.getRootObject().getValue(), context);
}
catch (Throwable ex) {
// If running in mixed mode, revert to interpreted
if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) {
this.interpretedCount = 0;
this.compiledAst = null;
this.interpretedCount.set(0);
}
else {
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
@@ -273,9 +280,10 @@ public class SpelExpression implements Expression {
public <T> T getValue(EvaluationContext context, @Nullable Class<T> expectedResultType) throws EvaluationException {
Assert.notNull(context, "EvaluationContext is required");
if (this.compiledAst != null) {
CompiledExpression compiledAst = this.compiledAst;
if (compiledAst != null) {
try {
Object result = this.compiledAst.getValue(context.getRootObject().getValue(), context);
Object result = compiledAst.getValue(context.getRootObject().getValue(), context);
if (expectedResultType != null) {
return ExpressionUtils.convertTypedValue(context, new TypedValue(result), expectedResultType);
}
@@ -286,8 +294,8 @@ public class SpelExpression implements Expression {
catch (Throwable ex) {
// If running in mixed mode, revert to interpreted
if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) {
this.interpretedCount = 0;
this.compiledAst = null;
this.interpretedCount.set(0);
}
else {
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
@@ -307,15 +315,16 @@ public class SpelExpression implements Expression {
public Object getValue(EvaluationContext context, Object rootObject) throws EvaluationException {
Assert.notNull(context, "EvaluationContext is required");
if (this.compiledAst != null) {
CompiledExpression compiledAst = this.compiledAst;
if (compiledAst != null) {
try {
return this.compiledAst.getValue(rootObject, context);
return compiledAst.getValue(rootObject, context);
}
catch (Throwable ex) {
// If running in mixed mode, revert to interpreted
if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) {
this.interpretedCount = 0;
this.compiledAst = null;
this.interpretedCount.set(0);
}
else {
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
@@ -338,9 +347,10 @@ public class SpelExpression implements Expression {
Assert.notNull(context, "EvaluationContext is required");
if (this.compiledAst != null) {
CompiledExpression compiledAst = this.compiledAst;
if (compiledAst != null) {
try {
Object result = this.compiledAst.getValue(rootObject, context);
Object result = compiledAst.getValue(rootObject, context);
if (expectedResultType != null) {
return ExpressionUtils.convertTypedValue(context, new TypedValue(result), expectedResultType);
}
@@ -351,8 +361,8 @@ public class SpelExpression implements Expression {
catch (Throwable ex) {
// If running in mixed mode, revert to interpreted
if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) {
this.interpretedCount = 0;
this.compiledAst = null;
this.interpretedCount.set(0);
}
else {
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
@@ -473,48 +483,58 @@ public class SpelExpression implements Expression {
* @param expressionState the expression state used to determine compilation mode
*/
private void checkCompile(ExpressionState expressionState) {
this.interpretedCount++;
this.interpretedCount.incrementAndGet();
SpelCompilerMode compilerMode = expressionState.getConfiguration().getCompilerMode();
if (compilerMode != SpelCompilerMode.OFF) {
if (compilerMode == SpelCompilerMode.IMMEDIATE) {
if (this.interpretedCount > 1) {
if (this.interpretedCount.get() > 1) {
compileExpression();
}
}
else {
// compilerMode = SpelCompilerMode.MIXED
if (this.interpretedCount > INTERPRETED_COUNT_THRESHOLD) {
if (this.interpretedCount.get() > INTERPRETED_COUNT_THRESHOLD) {
compileExpression();
}
}
}
}
/**
* Perform expression compilation. This will only succeed once exit descriptors for all nodes have
* been determined. If the compilation fails and has failed more than 100 times the expression is
* no longer considered suitable for compilation.
* Perform expression compilation. This will only succeed once exit descriptors for
* all nodes have been determined. If the compilation fails and has failed more than
* 100 times the expression is no longer considered suitable for compilation.
* @return whether this expression has been successfully compiled
*/
public boolean compileExpression() {
if (this.failedAttempts > FAILED_ATTEMPTS_THRESHOLD) {
CompiledExpression compiledAst = this.compiledAst;
if (compiledAst != null) {
// Previously compiled
return true;
}
if (this.failedAttempts.get() > FAILED_ATTEMPTS_THRESHOLD) {
// Don't try again
return false;
}
if (this.compiledAst == null) {
synchronized (this.expression) {
// Possibly compiled by another thread before this thread got into the sync block
if (this.compiledAst != null) {
return true;
}
SpelCompiler compiler = SpelCompiler.getCompiler(this.configuration.getCompilerClassLoader());
this.compiledAst = compiler.compile(this.ast);
if (this.compiledAst == null) {
this.failedAttempts++;
}
synchronized (this) {
if (this.compiledAst != null) {
// Compiled by another thread before this thread got into the sync block
return true;
}
SpelCompiler compiler = SpelCompiler.getCompiler(this.configuration.getCompilerClassLoader());
compiledAst = compiler.compile(this.ast);
if (compiledAst != null) {
// Successfully compiled
this.compiledAst = compiledAst;
return true;
}
else {
// Failed to compile
this.failedAttempts.incrementAndGet();
return false;
}
}
return (this.compiledAst != null);
}
/**
@@ -524,8 +544,8 @@ public class SpelExpression implements Expression {
*/
public void revertToInterpreted() {
this.compiledAst = null;
this.interpretedCount = 0;
this.failedAttempts = 0;
this.interpretedCount.set(0);
this.failedAttempts.set(0);
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 the original author 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,6 +55,7 @@ import org.springframework.util.StringUtils;
* @author Andy Clement
* @author Juergen Hoeller
* @author Phillip Webb
* @author Sam Brannen
* @since 3.0
* @see StandardEvaluationContext
* @see SimpleEvaluationContext
@@ -765,8 +766,11 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
}
if (this.member instanceof Method) {
mv.visitMethodInsn((isStatic ? INVOKESTATIC : INVOKEVIRTUAL), classDesc, this.member.getName(),
CodeFlow.createSignatureDescriptor((Method) this.member), false);
Method method = (Method) this.member;
boolean isInterface = method.getDeclaringClass().isInterface();
int opcode = (isStatic ? INVOKESTATIC : isInterface ? INVOKEINTERFACE : INVOKEVIRTUAL);
mv.visitMethodInsn(opcode, classDesc, method.getName(),
CodeFlow.createSignatureDescriptor(method), isInterface);
}
else {
mv.visitFieldInsn((isStatic ? GETSTATIC : GETFIELD), classDesc, this.member.getName(),
@@ -0,0 +1,59 @@
/*
* Copyright 2002-2020 the original author 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
*
* https://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.expression.spel.standard;
import java.util.stream.IntStream;
import org.junit.Test;
import org.springframework.core.Ordered;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.SpelCompilerMode;
import org.springframework.expression.spel.SpelParserConfiguration;
import static org.junit.Assert.assertEquals;
/**
* Tests for the {@link SpelCompiler}.
*
* @author Sam Brannen
* @since 5.1.14
*/
public class SpelCompilerTests {
@Test // gh-24357
public void expressionCompilesWhenMethodComesFromPublicInterface() {
SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, null);
SpelExpressionParser parser = new SpelExpressionParser(config);
OrderedComponent component = new OrderedComponent();
Expression expression = parser.parseExpression("order");
// Evaluate the expression multiple times to ensure that it gets compiled.
IntStream.rangeClosed(1, 5).forEach(i -> assertEquals(42, expression.getValue(component)));
}
static class OrderedComponent implements Ordered {
@Override
public int getOrder() {
return 42;
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -157,7 +157,12 @@ final class LogAdapter {
private final ExtendedLogger logger;
public Log4jLog(String name) {
this.logger = loggerContext.getLogger(name);
LoggerContext context = loggerContext;
if (context == null) {
// Circular call in early-init scenario -> static field not initialized yet
context = LogManager.getContext(Log4jLog.class.getClassLoader(), false);
}
this.logger = context.getLogger(name);
}
@Override
@@ -280,92 +285,110 @@ final class LogAdapter {
this.logger = logger;
}
@Override
public boolean isFatalEnabled() {
return isErrorEnabled();
}
@Override
public boolean isErrorEnabled() {
return this.logger.isErrorEnabled();
}
@Override
public boolean isWarnEnabled() {
return this.logger.isWarnEnabled();
}
@Override
public boolean isInfoEnabled() {
return this.logger.isInfoEnabled();
}
@Override
public boolean isDebugEnabled() {
return this.logger.isDebugEnabled();
}
@Override
public boolean isTraceEnabled() {
return this.logger.isTraceEnabled();
}
@Override
public void fatal(Object message) {
error(message);
}
@Override
public void fatal(Object message, Throwable exception) {
error(message, exception);
}
@Override
public void error(Object message) {
if (message instanceof String || this.logger.isErrorEnabled()) {
this.logger.error(String.valueOf(message));
}
}
@Override
public void error(Object message, Throwable exception) {
if (message instanceof String || this.logger.isErrorEnabled()) {
this.logger.error(String.valueOf(message), exception);
}
}
@Override
public void warn(Object message) {
if (message instanceof String || this.logger.isWarnEnabled()) {
this.logger.warn(String.valueOf(message));
}
}
@Override
public void warn(Object message, Throwable exception) {
if (message instanceof String || this.logger.isWarnEnabled()) {
this.logger.warn(String.valueOf(message), exception);
}
}
@Override
public void info(Object message) {
if (message instanceof String || this.logger.isInfoEnabled()) {
this.logger.info(String.valueOf(message));
}
}
@Override
public void info(Object message, Throwable exception) {
if (message instanceof String || this.logger.isInfoEnabled()) {
this.logger.info(String.valueOf(message), exception);
}
}
@Override
public void debug(Object message) {
if (message instanceof String || this.logger.isDebugEnabled()) {
this.logger.debug(String.valueOf(message));
}
}
@Override
public void debug(Object message, Throwable exception) {
if (message instanceof String || this.logger.isDebugEnabled()) {
this.logger.debug(String.valueOf(message), exception);
}
}
@Override
public void trace(Object message) {
if (message instanceof String || this.logger.isTraceEnabled()) {
this.logger.trace(String.valueOf(message));
}
}
@Override
public void trace(Object message, Throwable exception) {
if (message instanceof String || this.logger.isTraceEnabled()) {
this.logger.trace(String.valueOf(message), exception);
@@ -486,74 +509,92 @@ final class LogAdapter {
this.logger = java.util.logging.Logger.getLogger(name);
}
@Override
public boolean isFatalEnabled() {
return isErrorEnabled();
}
@Override
public boolean isErrorEnabled() {
return this.logger.isLoggable(java.util.logging.Level.SEVERE);
}
@Override
public boolean isWarnEnabled() {
return this.logger.isLoggable(java.util.logging.Level.WARNING);
}
@Override
public boolean isInfoEnabled() {
return this.logger.isLoggable(java.util.logging.Level.INFO);
}
@Override
public boolean isDebugEnabled() {
return this.logger.isLoggable(java.util.logging.Level.FINE);
}
@Override
public boolean isTraceEnabled() {
return this.logger.isLoggable(java.util.logging.Level.FINEST);
}
@Override
public void fatal(Object message) {
error(message);
}
@Override
public void fatal(Object message, Throwable exception) {
error(message, exception);
}
@Override
public void error(Object message) {
log(java.util.logging.Level.SEVERE, message, null);
}
@Override
public void error(Object message, Throwable exception) {
log(java.util.logging.Level.SEVERE, message, exception);
}
@Override
public void warn(Object message) {
log(java.util.logging.Level.WARNING, message, null);
}
@Override
public void warn(Object message, Throwable exception) {
log(java.util.logging.Level.WARNING, message, exception);
}
@Override
public void info(Object message) {
log(java.util.logging.Level.INFO, message, null);
}
@Override
public void info(Object message, Throwable exception) {
log(java.util.logging.Level.INFO, message, exception);
}
@Override
public void debug(Object message) {
log(java.util.logging.Level.FINE, message, null);
}
@Override
public void debug(Object message, Throwable exception) {
log(java.util.logging.Level.FINE, message, exception);
}
@Override
public void trace(Object message) {
log(java.util.logging.Level.FINEST, message, null);
}
@Override
public void trace(Object message, Throwable exception) {
log(java.util.logging.Level.FINEST, message, exception);
}
@@ -567,8 +608,8 @@ final class LogAdapter {
else {
rec = new LocationResolvingLogRecord(level, String.valueOf(message));
rec.setLoggerName(this.name);
rec.setResourceBundleName(logger.getResourceBundleName());
rec.setResourceBundle(logger.getResourceBundle());
rec.setResourceBundleName(this.logger.getResourceBundleName());
rec.setResourceBundle(this.logger.getResourceBundle());
rec.setThrown(exception);
}
logger.log(rec);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -220,6 +220,7 @@ public class SingleColumnRowMapper<T> implements RowMapper<T> {
* (with the required type specified only once).
* @param requiredType the type that each result object is expected to match
* @since 4.1
* @see #newInstance(Class, ConversionService)
*/
public static <T> SingleColumnRowMapper<T> newInstance(Class<T> requiredType) {
return new SingleColumnRowMapper<>(requiredType);
@@ -229,10 +230,15 @@ public class SingleColumnRowMapper<T> implements RowMapper<T> {
* Static factory method to create a new {@code SingleColumnRowMapper}
* (with the required type specified only once).
* @param requiredType the type that each result object is expected to match
* @param conversionService the {@link ConversionService} for converting a fetched value
* @param conversionService the {@link ConversionService} for converting a
* fetched value, or {@code null} for none
* @since 5.0.4
* @see #newInstance(Class)
* @see #setConversionService
*/
public static <T> SingleColumnRowMapper<T> newInstance(Class<T> requiredType, @Nullable ConversionService conversionService) {
public static <T> SingleColumnRowMapper<T> newInstance(
Class<T> requiredType, @Nullable ConversionService conversionService) {
SingleColumnRowMapper<T> rowMapper = newInstance(requiredType);
rowMapper.setConversionService(conversionService);
return rowMapper;

Some files were not shown because too many files have changed in this diff Show More