Compare commits

...

530 Commits

Author SHA1 Message Date
Spring Buildmaster 83c6d10095 Release v5.3.6 2021-04-13 11:08:55 +00:00
Juergen Hoeller f31933e67e Nullability refinements 2021-04-13 12:26:52 +02:00
Juergen Hoeller c7989c78c8 Polishing 2021-04-13 12:26:31 +02:00
Sam Brannen f03ccd5cc9 Fix Javadoc in AcceptHeaderLocaleResolver
Closes gh-26793
2021-04-13 11:36:28 +02:00
Sam Brannen bcc6171a72 Polish contribution
See gh-26785
2021-04-13 11:21:56 +02:00
Sviatoslav Hryb a80c5fcfaf Improve @EventListener documentation in reference manual
Closes gh-26785
2021-04-13 11:17:09 +02:00
Juergen Hoeller 04ce8e0ac4 Make URL path tests compatible with Windows
See gh-26775
2021-04-13 09:52:23 +02:00
Stephane Nicoll 74f7eb11be Polish "Upgrade to Reactor 2020.0.6"
See gh-26767
2021-04-13 08:12:02 +02:00
Juergen Hoeller e05b584623 Ignore testWindowsAbsoluteFilePath exception if not on Windows
See gh-26702
2021-04-12 23:46:27 +02:00
Juergen Hoeller 29955a2898 Polishing 2021-04-12 22:41:00 +02:00
Juergen Hoeller 4b6b12bf2f Bypass root path resolution for "file:" prefix only
Closes gh-26702
2021-04-12 22:40:53 +02:00
Rossen Stoyanchev ab0e8f0617 Upgrade to Reactor 2020.0.6
Closes gh-26767
2021-04-12 20:22:38 +01:00
Rossen Stoyanchev 0a6a6d48e2 Avoid CI failures with UTF-8 chars in test filenames
See gh-26775
2021-04-12 20:14:48 +01:00
Rossen Stoyanchev a08593b44b Correct matching of static resources with parsed patterns
Closes gh-26775
2021-04-12 19:54:20 +01:00
Rebwon 7954dc7a00 Polish tests
Closes gh-26708
2021-04-12 20:11:14 +02:00
Sam Brannen 22d9012081 Retain root cause for parsing patterns in @DateTimeFormat
The support for fallback parsing patterns in @DateTimeFormat introduced
in gh-20292 introduced a regression in that the original cause of the
parsing exception was no longer retained.

This commit ensures that the original DateTimeParseException is set as
the cause for any newly created DateTimeParseException, thereby
retaining the original exception as the root cause.

Closes gh-26777
2021-04-12 19:55:43 +02:00
Arjen Poutsma 327e761536 Recreate file storage directory when removed in DefaultPartHttpMessageReader
Some operating systems delete temp files not just when booting up, but
also during operation. This commit makes sure that the
DefaultPartHttpMessageReader recreates the directory used to store
files in, if it's not there.

Closes gh-26790
2021-04-12 16:27:10 +02:00
Juergen Hoeller 4e42b5dfbc Upgrade to Tomcat 9.0.45, Jetty 9.4.39, Undertow 2.2.7, Netty 4.1.63, RxJava 3.0.12 2021-04-12 14:15:45 +02:00
Brandon Fergerson 432c720dd0 Allow spring-expression to be more easily repackaged
This commit allows the spring-expression module to be more easily repackaged
for embedding in third-party JARs -- for example, via the Shadow Gradle plugin.

Closes gh-26779
2021-04-09 17:29:29 +02:00
Sviatoslav Hryb f7606ba51b Update ref docs regarding RequiredAnnotationBPP registration
The RequiredAnnotationBeanPostProcessor is no longer automatically
registered via XML namespace elements.

Closes gh-26783
2021-04-09 16:59:48 +02:00
Sam Brannen 0f54585f05 Polish contribution
See gh-26782
2021-04-09 16:54:49 +02:00
Stephane Nicoll d3d6e100da Merge pull request #26781 from runningcode
* pr/26781:
  Update to Gradle Enterprise Gradle Plugin 3.6.1

Closes gh-26781
2021-04-09 16:47:04 +02:00
Nelson Osacky a48c5f6a0d Update to Gradle Enterprise Gradle Plugin 3.6.1
See gh-26781
2021-04-09 16:46:53 +02:00
Sviatoslav Hryb 35ea33e13a Update documentation for <context:annotation-config/>
Closes gh-26782
2021-04-09 16:32:17 +02:00
Arjen Poutsma a2d91a562d Support "Accept-Patch" for unsupported media type
This commit introduces support in both servlet and webflux for the
"Accept-Patch" header, which is sent when the client sends unsupported
data in PATCH requests.
See  section 2.2 of RFC 5789.

Closes gh-26759
2021-04-08 14:46:59 +02:00
Arjen Poutsma 97f3846971 Support "Accept-Patch" for OPTIONS requests
This commit introduces support in both servlet and webflux for the
"Accept-Patch" header in OPTIONS requests, as defined in section 3.1 of
RFC 5789.

See gh-26759
2021-04-08 14:43:49 +02:00
Stephane Nicoll 44e1d6d1bf Merge pull request #26776 from KENNYSOFT
* pr/26776:
  Update copyright year of changed file
  Fix javadoc link syntax

Closes gh-26776
2021-04-08 14:24:39 +02:00
Stephane Nicoll 7adac25e7e Update copyright year of changed file
See gh-26776
2021-04-08 14:24:18 +02:00
Hyeonmin Park 7dca6a36a6 Fix javadoc link syntax
See gh-26776
2021-04-08 14:21:11 +02:00
Rossen Stoyanchev c5ac8e8ab6 Use createUnresolved for forwarded header address
Avoid DNS resolution.

See gh-26748
2021-04-07 12:50:07 +01:00
Stephane Nicoll fa4c62cab2 Start building against Reactor 2020.0.6 snapshots
See gh-26767
2021-04-07 09:34:43 +02:00
Rossen Stoyanchev 8a1182a678 Additional validation for forwarded header address value
Closes gh-26748
2021-04-06 19:43:13 +01:00
Stephane Nicoll 01bea34569 Merge pull request #26755 from izeye
* pr/26755:
  Add since to WebClient.ResponseSpec.awaitBodyOrNull()

Closes gh-26755
2021-04-02 17:46:13 +02:00
Johnny Lim aec151e014 Add since to WebClient.ResponseSpec.awaitBodyOrNull()
See gh-26755
2021-04-02 17:45:57 +02:00
Valentin 58e40d1aa2 Add awaitBodyOrNull to WebClient.ResponseSpec
Such variant is already provided for ClientResponse but not
for WebClient.ResponseSpec.

Closes gh-26731
2021-04-01 17:06:48 +02:00
Sébastien Deleuze 85dbd2a4c3 Upgrade Kotlin to 1.4.32 2021-04-01 16:06:14 +02:00
Sébastien Deleuze eca87b87de Improve Kotlin support in BridgeMethodResolver#findBridgedMethod
This commit adds support for Kotlin non-nullable type which resolves
to primitive Java types in BridgeMethodResolver#findBridgedMethod.

Closes gh-26585
2021-04-01 15:39:53 +02:00
Rossen Stoyanchev 5e1e29a2dd Fix failing tests
See gh-26746
2021-03-31 12:25:39 +01:00
Rossen Stoyanchev 7dc3a55648 Deferred buffer allocation in SSE message writer
See gh-26746
2021-03-31 11:47:22 +01:00
Arjen Poutsma f982fd99d7 Fix timezone offset in CronExpressionTests
Closes gh-26744
2021-03-30 16:37:26 +02:00
Arjen Poutsma ab18ab6025 Fix CronExpression issue with ZonedDateTime & DST
This commit fixes an issue with CronExpression when used in combination
with ZonedDateTime and daylight saving time.

Closes gh-26744
2021-03-30 15:20:59 +02:00
Arjen Poutsma d83fb09914 Change header encoding to UTF8 in DefaultPartHttpMessageReader
This commit changes the encoding used to parse multipart headers from
ISO-8859-1 to UTF-8, in accordance with RFC 7578.

Closes gh-26736
2021-03-30 10:51:19 +02:00
Rossen Stoyanchev b651c10e83 Fix error messages to match commit #1a9556
See gh-26679
2021-03-29 18:30:49 +01:00
Rossen Stoyanchev 5651695fd1 Fix typo in Javadoc
Closes gh-26740
2021-03-29 17:59:19 +01:00
Rossen Stoyanchev 1a9556925d missingAfterConversion flag for missing request values
Closes gh-26679
2021-03-29 15:23:29 +01:00
nara 2dc203ffdc Polish web docs example 2021-03-29 11:40:56 +01:00
Brian Clozel 610de3ae78 Upgrade to Concourse Release Scripts 0.3.2 2021-03-23 13:59:53 +01:00
Juergen Hoeller 5b2ace5742 Upgrade to Hibernate ORM 5.4.30, Tomcat 9.0.44, FreeMarker 2.3.31, Log4J 2.14.1, TestNG 7.4, HtmlUnit 2.48 2021-03-22 23:34:13 +01:00
Juergen Hoeller 5b227e5677 Upgrade to Objenesis 3.2
Closes gh-26714
2021-03-22 23:11:30 +01:00
Rossen Stoyanchev 41d5048280 Add BlockHoundIntegration for spring-web
Closes gh-26712
2021-03-22 21:18:48 +00:00
Rossen Stoyanchev a931ff12f6 MappedInterceptor falls back on PathMatcher
Closes gh-26690
2021-03-22 07:12:10 +00:00
Sam Brannen e4f753e3e3 Honor class-level @DirtiesContext if test class is disabled via SpEL
Prior to this commit, if a test class annotated with @DirtiesContext
and @EnabledIf/@DisabledIf with `loadContext = true` was disabled due
to the evaluated SpEL expression, the ApplicationContext would not be
marked as dirty and closed.

The reason is that @EnabledIf/@DisabledIf are implemented via JUnit
Jupiter's ExecutionCondition extension API which results in the entire
test class (as well as any associated extension callbacks) being
skipped if the condition evaluates to `disabled`. This effectively
prevents any of Spring's TestExecutionListener APIs from being invoked.
Consequently, the DirtiesContextTestExecutionListener does not get a
chance to honor the class-level @DirtiesContext declaration.

This commit fixes this by implementing part of the logic of
DirtiesContextTestExecutionListener in
AbstractExpressionEvaluatingCondition (i.e., the base class for
@EnabledIf/@DisabledIf support). Specifically, if the test class for an
eagerly loaded ApplicationContext is disabled,
AbstractExpressionEvaluatingCondition will now mark the test
ApplicationContext as dirty if the test class is annotated with
@DirtiesContext.

Closes gh-26694
2021-03-18 13:51:32 +01:00
Rossen Stoyanchev 4982b5fcb9 Improve docs on SSE tests for Spring MVC
Closes gh-26687
2021-03-16 17:55:43 +00:00
Sam Brannen f7678cdcdd Polish core-beans.adoc 2021-03-16 13:09:36 +01:00
Brian Clozel 3ab39eda08 Fix Gradle Toolchain configuration
See gh-25787
2021-03-16 13:04:11 +01:00
Brian Clozel 14a6909c4a Fix release pipeline for Maven Central publication
This commit fixes the missing pieces in our Maven Central publication
pipeline. Our first attempt at releasing with it showed a few problems:

* the promote task did not have the artifacts downladed with the
  artifactory repository
* we applied the wrong Sonatype credentials to the task
* the github changelog task would fail because of docker rate limiting
  since we were not using the right type of resource, which is
  configured with the proper caching mechanism

See gh-26654
2021-03-16 13:04:11 +01:00
Rebwon fd17738f76 Polish TestContextAnnotationsUtilsTests
Closes gh-26684
2021-03-16 12:15:00 +01:00
Spring Buildmaster f674652dd0 Next development version (v5.3.6-SNAPSHOT) 2021-03-16 08:12:35 +00:00
Rossen Stoyanchev 5476b2edff Polishing contribution
Closes gh-26650
2021-03-16 07:19:28 +00:00
Sébastien Deleuze c0fee67c6d Upgrade Kotlin serialization to 1.0.1
Let's wait Spring Boot 2.5 to upgrade to 1.1.x
2021-03-16 06:59:41 +01:00
Sébastien Deleuze f229fa4b72 Upgrade Kotlin Coroutines to 1.4.3 2021-03-16 06:53:52 +01:00
Sébastien Deleuze 684939d5f8 Upgrade Kotlin to 1.4.31 2021-03-16 06:53:22 +01:00
Rossen Stoyanchev b8d75c3139 Polishing contribution
Closes gh-26674
2021-03-15 18:07:13 +00:00
sokomishalov d92c74d923 Add cookies to the WebSocket HandshakeInfo
See gh-26674
2021-03-15 17:36:47 +00:00
Juergen Hoeller 4f14291e2f Add since tag
See gh-19647
2021-03-15 18:00:54 +01:00
Juergen Hoeller 7d3f42b27b Expose endpoint id as listener container name (for transaction/thread name)
Closes gh-26683
2021-03-15 17:58:43 +01:00
Brian Clozel a8d553218c Introduce Gradle Toolchain support in build
Prior to this commit, the Spring Framework build would rely on
setting a custom Java HOME for building all sources and tests
with that JDK.

This approach is not flexible enough, since we would be testing
the source compatibility against a recent JDK, but not a common
case experienced by the community: compiling and running
application code with a recent JDK and the official, JDK8-based
Framework artifacts.
This method is also limiting our choice of JDKs to the ones
currently supported by Gradle itself.

This commit introduces the support of Gradle JVM Toolchains in
the Spring Framework build.

We can now select a specific JDK for compiling the main
SourceSets (Java, Groovy and Kotlin) and another one for
compiling and running the test SourceSets:

`./gradlew check -PmainToolChain=8 -PtestToolchain=15`

Gradle will automatically find the JDKs present on the host or
download one automcatically. You can find out about the ones
installed on your host using:

`./gradlew -q javaToolchains`

Finally, this commit also refactors the CI infrastructure to:

* only have a single CI image (with all the supported JDKs)
* use this new feature to compile with JDK8 but test it
against JDK11 and JDK15.

Closes gh-25787
2021-03-15 14:33:41 +01:00
Rebwon 7f422f206c Polishing
Closes gh-26682
2021-03-15 13:47:13 +01:00
Sam Brannen 70f0895f96 Polish HeaderWebSessionIdResolverTests
See gh-26675
2021-03-15 12:12:53 +01:00
MaengSol 2728a9b931 Polishing
Closes gh-26675
2021-03-15 12:02:40 +01:00
drgnchan bc261fd995 Remove duplicate word in Javadoc
Closes gh-26676
2021-03-15 11:47:17 +01:00
Mark Paluch 0eaf6d12eb Add support for Oracle bind marker scheme using R2DBC
We now support Oracle's bind marker scheme that identifies dynamic
parameters using names that are prefixed with a colon such as
`:P0_myparam`.

Closes gh-26680
2021-03-15 11:26:15 +01:00
Rossen Stoyanchev 6e264f9bdd Add test for global Consumer<ExchangeResult> in WebTestClient
See gh-26662
2021-03-15 10:01:41 +00:00
Rossen Stoyanchev 6214ff153f Public method to register MessagingAdviceBean
Closes gh-26636
2021-03-15 09:56:21 +00:00
Rossen Stoyanchev c6b271f1b6 Support global Consumer<ExchangeResult> in WebTestClient
Closes gh-26662
2021-03-15 09:56:17 +00:00
Stephane Nicoll 8bf16ee1f4 Merge pull request #26678 from 1993heqiang
* pr/26678:
  Polish "Fix Commons FileUpload URL in reference guide"
  Fix Commons FileUpload URL in reference guide

Closes gh-26678
2021-03-15 08:46:17 +01:00
Stephane Nicoll a3451f9908 Polish "Fix Commons FileUpload URL in reference guide"
See gh-26678
2021-03-15 08:45:51 +01:00
heqiang 5c38b89f4e Fix Commons FileUpload URL in reference guide
See gh-26678
2021-03-15 08:44:48 +01:00
Stephane Nicoll 9c030bacb8 Merge pull request #26673 from Rebwon
* pr/26673:
  Update copyright year of changed files
  Polish

Closes gh-26673
2021-03-13 18:03:34 +01:00
Stephane Nicoll 12f8cdd715 Update copyright year of changed files
See gh-26673
2021-03-13 18:03:26 +01:00
Rebwon 2daefedf5e Polish
See gh-26673
2021-03-13 18:02:54 +01:00
Juergen Hoeller 6670db9b58 Polishing 2021-03-12 15:31:42 +01:00
Juergen Hoeller 7b6cac2c93 Polishing 2021-03-12 15:17:54 +01:00
Juergen Hoeller 6ffeee3e6f Basic integration tests with various listener container settings
See gh-26442
2021-03-12 15:17:41 +01:00
Juergen Hoeller 1b458aeafc Upgrade to ActiveMQ 5.16.1 2021-03-12 15:17:32 +01:00
Juergen Hoeller 0503cf2937 Revised checks for maxMessagesPerTask and idleReceivesPerTaskLimit
See gh-26442
2021-03-12 11:56:53 +01:00
Koen Serneels 14c802f979 Introduced 'idleReceivesPerTaskLimit': also mark task idle when idle receives per task threshold is reached
Closes GH-26195
2021-03-12 11:15:44 +01:00
Juergen Hoeller c1b1940dd2 Polishing 2021-03-12 11:02:35 +01:00
Juergen Hoeller 97b3aa4b13 Polishing 2021-03-12 00:14:04 +01:00
Michal Stehlik 79b5710386 StatusAssertion value methods fail when used with custom status code 2021-03-11 15:57:32 +01:00
Sam Brannen b2bcb0f93a Support multiple parsing patterns in @DateTimeFormat
Prior to this commit, @DateTimeFormat only supported a single format
for parsing date time values via the style, iso, and pattern attributes.

This commit introduces a new fallbackPatterns attribute that can be
used to configure multiple fallback patterns for parsing date time
values. This allows applications to accept multiple input formats for
date time values.

For example, if you wish to use the ISO date format for parsing and
printing but allow for lenient parsing of user input for various
additional date formats, you could annotate a field or method parameter
with configuration similar to the following.

    @DateTimeFormat(
        iso = ISO.DATE,
        fallbackPatterns = { "M/d/yy", "dd.MM.yyyy" }
    )

Closes gh-20292
2021-03-11 11:30:30 +01:00
Sam Brannen 5593e95e89 Do not assert thread count in ParallelApplicationEventsIntegrationTests 2021-03-11 10:14:39 +01:00
Rossen Stoyanchev 6d4c0091b5 Add application/*+xml to Jaxb2XmlEncoder
Closes gh-26655
2021-03-10 17:33:04 +00:00
Rossen Stoyanchev 55aa8e914e Expose id from ClientHttpResponse
Closes gh-26656
2021-03-10 17:33:04 +00:00
Juergen Hoeller 2b017fe540 Apply original ClassLoader in AbstractAdvisingBeanPostProcessor as well
See gh-26601
2021-03-10 16:37:31 +01:00
Stephane Nicoll 61cdd647b6 Fix image name for JDK 15 build 2021-03-10 14:17:37 +01:00
Stephane Nicoll f6d3d9d6e6 Polish CI pipeline 2021-03-10 13:59:30 +01:00
Brian Clozel ed71fe0460 Publish directly to Maven Central
This commit skips the Bintray-related tasks in our CI pipeline and
instead relies on Maven Central for publishing Spring Framework
artifacts.

This commit also updates the CI pipeline to sign the artifacts directly
with the `artifactory-resource`.

Closes gh-26654
2021-03-09 15:30:06 +01:00
Juergen Hoeller 89f70bdc46 Upgrade to Netty 4.1.60, RxJava 3.0.11, JsonPath 2.5 2021-03-09 13:45:31 +01:00
Juergen Hoeller 3c9bd3177e Move getListenerId method to Smart/GenericApplicationListener
See gh-26638
2021-03-09 12:30:52 +01:00
Stephane Nicoll 4b80ef21b6 Merge pull request #26651 from MichelTenVoorde
* pr/26651:
  Polish "Update reference to deprecated CronSequenceGenerator"
  Update reference to deprecated CronSequenceGenerator

Closes gh-26651
2021-03-09 08:16:48 +01:00
Stephane Nicoll d308985edc Polish "Update reference to deprecated CronSequenceGenerator"
See gh-26651
2021-03-09 08:14:22 +01:00
Michel ten Voorde 5aef87f120 Update reference to deprecated CronSequenceGenerator
See gh-26651
2021-03-09 08:10:52 +01:00
Juergen Hoeller 70c0e104b5 Upgrade to Protobuf 3.15.5, Undertow 2.2.5, Woodstox 6.2.4 2021-03-09 00:29:02 +01:00
Juergen Hoeller 01bf1c9021 Polishing 2021-03-09 00:08:03 +01:00
Juergen Hoeller 530fb0808d Polishing 2021-03-08 23:45:56 +01:00
Juergen Hoeller 48688b7b04 Pass getListenerId call on to delegate
See gh-26638
2021-03-08 23:45:47 +01:00
Rossen Stoyanchev 1ef8cad7bc Make use of Reactor Netty's ChannelOperationsId
Closes gh-26649
2021-03-08 21:06:49 +00:00
Juergen Hoeller 86902d27b2 Expose id/getListenerId in base EventListener/ApplicationListener (pulled up from tx)
Includes removeApplicationListeners(Predicate) method in ApplicationEventMulticaster.

Closes gh-26638
2021-03-08 19:31:56 +01:00
Qimiao Chen 9877a9e6b7 Reduce memory consumption 2021-03-08 18:56:46 +01:00
Qimiao Chen 75b6540bd8 Specify 20 to the initial size of the ArrayList 2021-03-08 18:56:22 +01:00
Oleksandr Kravchuk 21d77dd7c7 Fixed typo in ref docs 2021-03-05 14:07:23 +00:00
Rossen Stoyanchev 8bdc53ac5c Fix log message
See gh-gh-26434
2021-03-04 22:13:52 +00:00
Rossen Stoyanchev acb638f828 WriteResultPublisher propagates cancel upstream
Closes gh-26642
2021-03-04 20:58:18 +00:00
Juergen Hoeller c7e8989f16 Upgrade to Hibernate ORM 5.4.29, Jackson 2.12.2, Jetty 9.4.38, Jetty Reactive HttpClient 1.1.6, R2DBC Arabba-SR9, JRuby 9.2.16, SnakeYAML 1.28, Checkstyle 8.41 2021-03-04 16:54:04 +01:00
Rossen Stoyanchev 78f65c4ca9 Consistent handling on Servlet non-blocking error callbacks
See gh-26434
2021-03-04 13:58:21 +00:00
Rossen Stoyanchev 42e4ca1e2d Improve logging for Servlet / Reactive Streams adapters
See gh-26434
2021-03-04 13:58:21 +00:00
Rossen Stoyanchev d23a108e76 Consolidate AsyncListener registration
Previously we registered 3 AsyncListener's from the request, from the
response, and from the Servlet adapter.

After this change, only the Servlet adapter registers a listener and
the others are delegated to. This consolidates the handling of
AsyncListener events so that it's easier to discover, trace, and
enforce the order of handling.

See gh-26434
2021-03-04 13:58:21 +00:00
Taemin Shin b9a612b637 Add RouterFunctionMapping to Javadoc for WebMvcConfigurationSupport
Closes gh-26635
2021-03-03 14:44:40 +01:00
Sam Brannen 1ee9223348 Add section of CI builds 2021-03-03 11:42:35 +01:00
Sam Brannen 926c4aae52 Improve formatting and link to 5.3.2 build pipeline 2021-03-03 11:36:09 +01:00
Arjen Poutsma b61643b71a Support UTF-16/UTF-32 in Jackson message converter
This commit makes sure that we let Jackson deal with all the encodings
that it supports, including UTF-16 and UTF-32.

Closes gh-26627
2021-03-02 14:45:48 +01:00
Sam Brannen 7efb4eade6 Delete unnecessary @SuppressWarnings declaration 2021-03-02 14:10:40 +01:00
Sam Brannen e89cc9934f Delete unused method 2021-03-02 14:00:36 +01:00
Sam Brannen 010b7375fc Fix typo in Javadoc 2021-03-02 14:00:21 +01:00
Sam Brannen 01c2e12fef Polish contribution
See gh-26619
2021-03-02 13:54:01 +01:00
GungnirLaevatain 90de1ab6d1 Ensure local @CrossOrigin maxAge overrides global value
Prior to this commit, a method-level @CrossOrigin maxAge value did not
override a class-level @CrossOrigin maxAge value. This contradicts the
Javadoc for @CrossOrgin which states the following.

    For those attributes where only a single value can be accepted such
    as allowCredentials and maxAge, the local overrides the global
    value.

This commit ensures that a method-level @CrossOrigin maxAge value
overrides a class-level @CrossOrigin maxAge value.

Closes gh-26619
2021-03-02 13:21:35 +01:00
Brian Clozel 5a11569790 Allow ServerHttpRequest content-type mutation
Prior to this commit, `ServerHttpRequest.mutate()` would not reflect
changes made on the "Accept" and "Content-Type" HTTP headers.
This was due to the fact that the instantiation of a new request based
on the mutated values would not use the writable HTTP headers used
during the mutation, but rather a read-only view of the headers backed
by `ReadOnlyHttpHeaders`.

`ReadOnlyHttpHeaders` caches those values for performance reasons, so
getting those from the new request would not reflect the changes made
during the mutation phase.

This commit ensures that the new request uses the mutated headers.

Fixes gh-26615
2021-03-01 20:55:03 +01:00
Oleksandr Kravchuk 0087578469 Fix @EventListener example in reference manual
Closes gh-26622
2021-03-01 20:22:58 +01:00
Arjen Poutsma 1a79c54b01 Support quoted boundary in DefaultPartHttpMessageReader
This commit makes sure that quoted boundary parameters are supported in
the DefaultPartHttpMessageReader.

Closes gh-26616
2021-03-01 14:07:20 +01:00
Juergen Hoeller 4af7a6863b Polishing 2021-02-26 17:49:10 +01:00
Juergen Hoeller c084936870 Upgrade to Jetty 9.4.37, Caffeine 2.9, Mockito 3.8, Checkstyle 8.40 2021-02-26 17:31:30 +01:00
Juergen Hoeller 48bd81341f Add javadoc for body parameter
See gh-26613
2021-02-26 17:31:14 +01:00
k3v1n 118d8133d3 Add missing nullable annotation to ResponseEntity ok convenience method 2021-02-26 17:00:32 +01:00
Rossen Stoyanchev c2137a0d2f Cancel handling onError/Timeout in ServletHttpHandlerAdapter
This commit ensures handling is cancelled in case of onError/Timeout
callback from the Servlet container.

Separately we detect the same in ServletServerHttpRequest and
ServletServerHttpResponse, which signal onError to the read publisher
and cancel writing, but if the onError/Timeout arrives after reading
is done and before writing has started (e.g. longer handling), then
neither will reach handling.

See gh-26434, gh-26407
2021-02-26 14:29:01 +00:00
Rossen Stoyanchev 8c9c59eba0 Fix typo
See gh-24539
2021-02-25 18:33:35 +00:00
Rossen Stoyanchev 8ed7f4703b Improve Javadoc for responseType on RestTemplate#exchange
Closes gh-26599
2021-02-25 18:29:05 +00:00
Rossen Stoyanchev e04269cadd Consistently check DispatcherType to avoid NPE in tests
Closes gh-26602
2021-02-25 18:20:34 +00:00
Rossen Stoyanchev aa73f6733e Lenient treatment of malformed Accept header for @ExceptionHandler
Closes gh-24539
2021-02-25 17:58:32 +00:00
Juergen Hoeller b5147a034c Polishing
See gh-26574
See gh-26575
2021-02-25 18:08:04 +01:00
Craig Andrews ebf6fff312 Fix handling of file: paths to non-existent files
For setAsText, if the text argument is a file: URL for a path that does not exist, Paths.get(text) is called where text is a file: URL, which doesn't work - the result is an InvalidPathException.

To fix this issue, also check that the resource isn't a file before calling Paths.get(). That way, resources that are files skip to the other branch.
2021-02-25 17:55:55 +01:00
Craig Andrews dee12db50a getResource can throw IllegalArgumentException
Class.getResource, ClassLoader.getResource, and ClassLoader.getSystemResource will throw IllegalArgumentException if a malformed URL is provided to them.

According to its javadoc, resolveURL should return null if not resolvable, so catch the IllegalArgumentException and return null.
2021-02-25 17:55:24 +01:00
Juergen Hoeller e53cce0778 Allow AOP proxies to be created using the original ClassLoader
Closes gh-26601
2021-02-25 17:48:01 +01:00
Sam Brannen 8baf404893 Polish contribution
See gh-26600
2021-02-25 11:41:11 +01:00
nullzl a33eac3ec0 Correctly set auto-growing array's element
Prior to this commit, the implementation of processKeyedProperty() in
AbstractNestablePropertyAccessor resulted in a
`java.lang.IllegalArgumentException: array element type mismatch` when
the property expression had more than one property key and the last key
should cause the array to grow automatically.

For example, given a property `int[][] multiArray` and property
expression `multiArray[1][3]`, the `processKeyedProperty()` method
created a new array object and assigned it to `multiArray`; whereas,
the new array object should have be assigned to `multiArray[1]`.

This commit fixes this issue.

Closes gh-26600
2021-02-25 11:25:54 +01:00
Arjen Poutsma eb68e6a62a Support SUN as minimum of range in CronExpression
This commit makes sure that SUN can be used at the beginning of a range,
 like SUN-FRI.

Closes gh-26598
2021-02-25 10:50:34 +01:00
Sam Brannen a9240e0bac Document all supported SpringProperties keys in the reference manual
Closes gh-26554
2021-02-24 17:18:04 +01:00
Sam Brannen 262b7d7c27 Polishing 2021-02-24 17:14:10 +01:00
Sam Brannen 31d9f31806 Polish Javadoc for @DateTimeFormat 2021-02-24 11:38:07 +01:00
Rossen Stoyanchev 0fd774e69f Add allowedOriginPatterns to WebSocketHandlerRegistration
Closes gh-26593
2021-02-24 09:37:20 +00:00
Rossen Stoyanchev ec5774e748 Polishing
Closes gh-26577
2021-02-24 09:37:20 +00:00
fengyuanwei 962ec38a61 Improve Javadoc of ExchangeFunction
See gh-26577
2021-02-24 09:37:20 +00:00
Rossen Stoyanchev 3ec0452fed Dedicated, "_"-prefixed log category for request mappings
Closes gh-26539
2021-02-24 09:36:43 +00:00
ShaoqiangLu cb3af52df2 Add vscode folder to .gitignore
Closes gh-26595
2021-02-23 17:43:39 +01:00
Sam Brannen 5b114b7208 Cross reference DataClassRowMapper from BeanPropertyRowMapper 2021-02-23 17:30:11 +01:00
Sam Brannen f7cef2ab20 Remove section on RequiredAnnotationBeanPostProcessor in ref docs
In order to reduce the focus on the deprecated @Required support, this
commit removes the RequiredAnnotationBeanPostProcessor section and
replaces it with a TIP and an updated NOTE.

See gh-26578
2021-02-23 16:21:07 +01:00
Brian Clozel 77a658f51b Fix registration of ApplicationStartupAware
Closes gh-26576
2021-02-23 11:45:53 +01:00
Johnny Lim cf4e77907f Fix MockHttpServletRequest reference in Javadoc
Closes gh-26592
2021-02-23 10:43:38 +01:00
Sam Brannen 42a223b9cd Upgrade to Gradle 6.8.3 2021-02-22 23:00:49 +01:00
Sam Brannen 6d2fec383b Reduce focus on RequiredAnnotationBeanPostProcessor in ref docs
See gh-26578
2021-02-22 18:41:50 +01:00
Oleksandr Kravchuk 90ee22f2af Update reference manual regarding deprecated @Required annotation
Closes gh-26578
2021-02-22 17:54:53 +01:00
Sam Brannen db5be44ff4 Polishing 2021-02-22 17:02:17 +01:00
Sam Brannen a78c12b8d3 Fix Javadoc for PlaceholderConfigurerSupport 2021-02-22 17:02:01 +01:00
Johnny Lim dfc8406917 Polish contribution
See gh-26462
Closes gh-26586
2021-02-22 11:05:25 +01:00
Sam Brannen 313e754a84 Support load-time weaving for @Component classes again
Since Spring Framework 5.2, the LoadTimeWeaver no longer weaves bean
classes annotated with @Component. This is a regression caused by the
changes in 40c62139ae, stemming from the fact that any class annotated
or meta-annotated with @Component is considered to be a candidate
configuration class in 'configuration lite' mode (i.e., a class without
the @Configuration annotation and without any @Bean methods) and
therefore now has its class eagerly loaded. This results in the class
being loaded before the LoadTimeWeaver has a chance to weave it.

This commit fixes this regression by explicitly avoiding eager class
loading for any 'lite' @Configuration or @Component class without @Bean
methods.

Closes gh-26199
2021-02-22 09:59:22 +01:00
Sam Brannen 207ee9e307 Polishing 2021-02-21 19:14:05 +01:00
Sam Brannen 3215880d08 Remove Javadoc link to private method 2021-02-21 19:13:02 +01:00
Rossen Stoyanchev 274db2f7a9 Undo HttpServletMapping support in MockHttpServletRequest
Closes gh-26555
2021-02-19 12:07:05 +00:00
Rossen Stoyanchev 1dd7d53de0 More precise mapping for WebSocket handshake requests
Closes gh-26565
2021-02-19 11:49:44 +00:00
Sam Brannen 8535193df3 Update copyright date for DynamicIntroductionAdvice
See gh-26568
2021-02-18 13:02:10 +01:00
heqiang a78701cc4b Fix Javadoc link in DynamicIntroductionAdvice
Closes gh-26568
2021-02-18 12:54:15 +01:00
Brian Clozel 947387b4cd Fix ResourceUrlProvider handler auto-detection
Prior to this commit, `ResourceUrlProvider` would listen and consider
all `ContextRefreshedEvent` and use the given context to detect
`SimpleUrlHandlerMapping`.

This could lead to situations where a `ResourceUrlProvider` uses another
application context than its own (in a parent/child context setup) and
detect the wrong set of handlers.

Because `ResourceUrlProvider` locks itself once the auto-detection is
done, we need to ensure that it considers only events sent by its
application context.

Fixes gh-26561
2021-02-17 22:04:39 +01:00
Sam Brannen 49ccd7ad9c Polish MockHttpServletResponseTests
See gh-26558
2021-02-17 14:32:39 +01:00
Sam Brannen 5b97c47fc4 Sync changes in MockHttpServletResponse test fixture
See gh-26558
2021-02-17 14:32:39 +01:00
Koos Gadellaa 40661d62c1 Support cookie w/ only Expires attribute in MockHttpServletResponse
Prior to this commit, MockHttpServletResponse only included the Expires
attribute in the generated Cookie header if the Max-Age attribute had
also been set.

This commit supports including the Expires attribute in the generated
Cookie Header even when the Max-Age attribute has not been set.

Closes gh-26558
2021-02-17 14:32:39 +01:00
Sam Brannen e17ca9a4e9 Implement toString() in MockCookie 2021-02-17 14:32:38 +01:00
Sam Brannen 87761397b0 Document @Value support for Resource/Resource[] injection in ref docs
See gh-26447
2021-02-17 12:55:53 +01:00
Sam Brannen ac58614be8 Polish Resources section of the reference manual
See gh-26447
2021-02-16 18:56:29 +01:00
Sam Brannen 4ebd8d77b7 Update link to AsciiDoc Tooling 2021-02-16 18:28:19 +01:00
Taemin Shin 7ca47cfe6b Fix build output dir for ref docs in CONTRIBUTING.md
Closes gh-26556
2021-02-16 18:21:59 +01:00
Sam Brannen fd49c8f598 Polishing 2021-02-16 16:54:49 +01:00
Spring Buildmaster ff234568d2 Next development version (v5.3.5-SNAPSHOT) 2021-02-16 10:52:26 +00:00
Sam Brannen efc335e198 Remove remaining Kotlin "translations" of Java APIs in the reference manual 2021-02-16 11:16:14 +01:00
Sam Brannen 1e57e572dd Link to all built-in Resource implementations
See gh-26447
2021-02-16 10:29:41 +01:00
Sam Brannen 829000d511 Remove Kotlin "translations" of Java APIs in the reference manual
See gh-26447
2021-02-16 10:24:24 +01:00
Sam Brannen a315abfd32 Polish ResourcePatternResolver docuementation in the reference manual
See gh-26447
2021-02-16 10:18:13 +01:00
Sam Brannen 36d133ce53 Escape URLs in literals in reference manual
This prevents the URLs from being rendered as clickable links in the
generated HTML.
2021-02-16 09:59:44 +01:00
Juergen Hoeller 9417975f66 Nullability refinements
See gh-26462
2021-02-16 08:51:42 +01:00
Juergen Hoeller ed9ec58caa Document ASM version as 9.1 in license file 2021-02-16 00:18:14 +01:00
Juergen Hoeller f48c3d9b67 Document ASM version as 9.x (effectively 9.1 at present) 2021-02-16 00:09:10 +01:00
Sam Brannen 6b705443a6 Document ResourcePatternResolver in the reference manual
Closes gh-26447
2021-02-15 22:09:33 +01:00
Sam Brannen c26087e848 Document PathResource as a built-in Resource in the reference manual
See gh-26447
2021-02-15 21:04:37 +01:00
Sam Brannen 7225238ad5 Update Resource interface in the reference manual
See gh-26447
2021-02-15 20:40:40 +01:00
Sam Brannen ff43731347 Polish Resources section of the reference manual
See gh-26447
2021-02-15 20:20:21 +01:00
Taemin Shin 29fb3f27be Fix typo in @Configurable example in reference manual
Closes gh-26551
2021-02-15 19:40:45 +01:00
Rossen Stoyanchev 63db250ebe Upgrade to Reactor 2020.0.4
Closes gh-26548
2021-02-15 17:25:38 +00:00
gaerfield b00c7075a5 Introduce WebClient.ResponseSpec.awaitBodilessEntity()
This commit also allows awaitBody<Unit>() to be used.

Closes gh-26504
2021-02-15 17:46:18 +01:00
Juergen Hoeller 3718c8d0e0 Upgrade to Hibernate ORM 5.4.28 and OpenPDF 1.3.25 2021-02-15 17:25:14 +01:00
Juergen Hoeller ca8261cbf8 Polishing 2021-02-15 17:18:01 +01:00
Carsten Dimmek 8de10e9199 Accept nullable values in MapSqlParameterSource set operators
Closes gh-26288
2021-02-15 17:15:58 +01:00
Stephane Nicoll 7536b7f42a Upgrade to Gradle 6.8.2
Closes gh-26552
2021-02-15 16:57:49 +01:00
Stephane Nicoll bc71cc1a76 Merge branch 'gh-26462'
Closes gh-26462
2021-02-15 16:53:43 +01:00
Stephane Nicoll 582559dd9e Polish "Allow customer resolver and property sources"
See gh-26462
2021-02-15 16:53:18 +01:00
Phillip Webb d4c609f2f0 Allow customer resolver and property sources
Add factory methods to `AbstractEnvironment` that allow a custom
`ConfigurablePropertyResolver` and `MutablePropertySources` instance
to be used.

See gh-26462
2021-02-15 16:53:18 +01:00
Sébastien Deleuze 3524401bf1 Introduce a NativeDetector utility class
With the introduction of the -H:+InlineBeforeAnalysis native image
compiler flag in GraalVM 21.0.0, it is now possible to use an utility method and get code
removal at build time.

This flag will be enabled as of Spring Native 0.9.0.

closes gh-25795
2021-02-15 16:36:23 +01:00
Stephane Nicoll 86423c91f7 Merge branch 'gh-26461'
Closes gh-26461
2021-02-15 16:30:06 +01:00
Stephane Nicoll 8807f94ad8 Polish "Allow bypass of active/default properties"
See gh-26461
2021-02-15 16:28:18 +01:00
Phillip Webb da3ff29e88 Allow bypass of active/default properties
Introduce protected methods that can be used to bypass or change the
way that active and default property values are read.

See gh-26461
2021-02-15 16:24:22 +01:00
Sébastien Deleuze 9c6b1b645d Prevent kotlinx.serialization usage on collection of interfaces
Closes gh-26371
2021-02-15 14:44:54 +01:00
Rossen Stoyanchev 0fc8bf654b Expose DispatcherHandler as PreFlightRequestHandler
Closes gh-26257
2021-02-15 12:59:03 +00:00
Rossen Stoyanchev 729535d36c Ensure presence of cached lookupPath
Closes gh-26546
2021-02-15 12:24:07 +00:00
Juergen Hoeller 634c7710f9 Remove trailing whitespace (Checkstyle)
See gh-26471
2021-02-15 11:34:38 +01:00
Yanming Zhou 2fbfd8a5db Avoid unnecessary wrapping for SqlParameterValue
Fix https://github.com/spring-projects/spring-framework/issues/26467
2021-02-15 11:15:37 +01:00
Sébastien Deleuze 42c15be8d3 Upgrade to Kotlin 1.4.30
Closes gh-26545
2021-02-15 10:47:27 +01:00
Juergen Hoeller 3baa44c5ef Polishing 2021-02-14 18:53:53 +01:00
Juergen Hoeller 4b86a1a755 Upgrade to Netty 4.1.59, Tomcat 9.0.43, Undertow 2.2.4, Mockito 3.7.7, HtmlUnit 2.47.1, Joda-Time 2.10.10, RxJava 2.2.21 and 3.0.10 2021-02-14 17:58:03 +01:00
Juergen Hoeller df977a2fd2 Nullability refinements and related polishing 2021-02-14 17:57:32 +01:00
Juergen Hoeller 99a1388bbd Re-resolve cached arguments in case of NoSuchBeanDefinitionException
Closes gh-26517
2021-02-14 17:57:22 +01:00
Juergen Hoeller 809813dd52 Preserve resolved destroy method name in RootBeanDefinition
Closes gh-26498
2021-02-14 17:57:05 +01:00
Juergen Hoeller d5e5dcb7e1 Consider non-initialized holders as equal to empty holders
Closes gh-26433
2021-02-14 17:56:52 +01:00
Juergen Hoeller defc2466b0 Fail early FactoryBean instantiation for LinkageError
Closes gh-26425
2021-02-14 17:56:38 +01:00
fengyuanwei a991c23db5 Close mapping streams after the ValidatorFactory has been built 2021-02-14 17:29:08 +01:00
Sam Brannen b928ed8f37 Upgrade to JUnit 4.13.2
Closes gh-26543
2021-02-13 19:27:09 +01:00
Sam Brannen 5d70aaacef Improve diagnostics for flaky ParallelApplicationEventsIntegrationTests 2021-02-12 12:02:37 +01:00
Sam Brannen 566ff54782 Polishing
See gh-26540
2021-02-12 11:29:00 +01:00
Oleksandr Kravchuk 77b7e49fb2 Update ref docs for Inner class names
This commit updates the reference manual to point out that one may use
`.` instead of `$` as the nested class separator when providing a fully
qualified class name for a nested class in XML configuration.

This commit also updates the test for ClassUtils#forName to assert
support for `.` instead of `$`.

Closes gh-26540
2021-02-12 10:54:55 +01:00
Rossen Stoyanchev 1c6bab23ea Polishing
See gh-26434
2021-02-11 21:26:37 +00:00
Rossen Stoyanchev 3e502d4739 Document removal of duplicate slashes in UriComponentsBuilder
Closes gh-26457
2021-02-11 20:57:03 +00:00
Sam Brannen 667256adf9 Ignore generics when Proxy is supplied to BeanUtils.copyProperties()
gh-24281 introduced support to honor generic type information in
BeanUtils.copyProperties(), but that introduced a regression.
Specifically, if the supplied source or target object lacked generic
type information for the return type of the read-method or the
parameter type of the write-method for a given property, respectively,
the two properties would be considered a mismatch and ignored. This can
occur if the source or target object is a java.lang.reflect.Proxy since
the dynamically generated class for the proxy loses the generic type
information from interfaces that the proxy implements.

This commit fixes this regression by ignoring generic type information
if either the source or target property is lacking generic type
information.

Closes gh-26531
2021-02-11 18:18:57 +01:00
Rossen Stoyanchev 8791928f61 Update variable detection in UriComponentsBuilder#encode
This commit better aligns how URI variable placeholders are detected
in UriComponentsBuilder#encode (i.e. the pre-encoding of the literal
parts of a URI template) and how they are expanded later on.
The latter relies on a pattern that stops at the first closing '}'
which excludes the possibility for well-formed, nested placeholders
other than variables with regex syntax, e.g. "{year:\d{1,4}}".

UriComponentsBuilder#encode now also stops at the first closing '}' and
further ensures the placeholder is not empty and that it has '{' before
deciding to treat it as a URI variable.

Closes gh-26466
2021-02-11 08:34:06 +00:00
Rossen Stoyanchev c9147c4281 Add RSocket to "Web Reactive" subtext
Closes gh-26534
2021-02-11 08:31:20 +00:00
Stephane Nicoll d6f363c634 Merge pull request #26538 from izeye
* pr/26538:
  Update copyright of change file
  Polish

Closes gh-26538
2021-02-11 08:59:24 +01:00
Stephane Nicoll 87c0e9b48a Update copyright of change file
See gh-26538
2021-02-11 08:58:50 +01:00
izeye 9a4164c4a8 Polish
See gh-26538
2021-02-11 08:57:32 +01:00
Sam Brannen c36b80bd75 Update copyright date in reference manual 2021-02-10 17:56:22 +01:00
Sam Brannen bc90512309 Fix Checkstyle violation 2021-02-10 14:03:33 +01:00
Sam Brannen edd15646af Check available processors in ParallelApplicationEventsIntegrationTests
This commit changes the condition in the if-block to check the number of
available processors instead of currently active threads with the hope
that doing so will prove more reliable on the CI server.
2021-02-10 12:02:32 +01:00
Rossen Stoyanchev 53cafe728c Support to customize rather than replace default codecs
See gh-26212
2021-02-09 18:40:00 +00:00
Brian Clozel 58e9b187fe Enforce standard Java types in YamlProcessor
`spring-beans` ships a `YamlProcessor` that's used as a base class by
`YamlMapFactoryBean` and `YamlPropertiesFactoryBean`. These
implementations have a clear use case: mapping application-internal Yaml
documents for configuration or infrastructure purposes.

Since this use case rarely requires extended types support from the
underlying library, and since we're offering ways to list custom types
(since #25152), we'll restrict to java standard types only by default.
This simplifies the setup and focuses the abstract class on the core
use cases.

Closes gh-26530
2021-02-09 15:05:53 +01:00
Arjen Poutsma 9b0c2cca95 Ensure StringDecoder supports multiline delimiters
This commit makes sure the StringDecoder supports stripping off
multi-line delimiters, such as \r\n. Specifically, we ensure that the
delimiter is stripped from the joined buffer.

Closes gh-26511
2021-02-09 14:31:49 +01:00
Rossen Stoyanchev 26000ee29f Polishing contribution
Closes gh-26502
2021-02-08 17:35:59 +00:00
Christophe Maillard 0c2c66b38b Add HttpHandlerDecoratorFactory
See gh-26502
2021-02-08 15:47:21 +00:00
heqiang bd8e682c51 Simplify BeanUtils.findPrimaryConstructor()
Closes gh-26519
2021-02-08 13:45:40 +01:00
Rossen Stoyanchev f7c952cb3e Polishing contribution
Closes gh-26512
2021-02-05 13:28:19 +00:00
ascopes 6bc7e12bcd Fix typos in ResponseEntity Javadoc
See gh-26512
2021-02-05 13:28:19 +00:00
Rossen Stoyanchev ab94c7c016 Update WebMvcConfigurer Javadoc for message converters
Closes gh-26388
2021-02-05 13:04:44 +00:00
Rossen Stoyanchev 8fb9796d1d Polishing contribution
Closes gh-26463
2021-02-05 12:16:59 +00:00
Yusuke Yamamoto a264013d7a Content-Disposition can parse BASE64 encoded filename
See gh-26463
2021-02-05 12:16:59 +00:00
Rossen Stoyanchev cd80b6b4ac Message broker skips messages with user destination
When a broker message handler is not configured with any prefixes, it will
process all messages by default, but user destination messages should be
pre-processed by the userDestinationMessageHandler first. This change
protects against that.

Closes gh-26474
2021-02-05 11:53:46 +00:00
Rossen Stoyanchev 164b48e25f Polishing STOMP/WebSocket config 2021-02-05 11:53:46 +00:00
Rossen Stoyanchev eb7b206142 Expose ChannelId from ReactorNettyWebSocketSession
Closes gh-26485
2021-02-05 11:53:46 +00:00
Sam Brannen 42061d27bd Upgrade to JUnit 5.7.1
Closes gh-26510
2021-02-04 21:47:21 +01:00
Rossen Stoyanchev 0d16c9100a MIME types by Class for Encoder, Decoder, HttpMessageReader|Writer
Closes gh-26212
2021-02-04 16:12:57 +00:00
Rossen Stoyanchev 7cdaaa22bd Extra ObjectMapper registrations in Jackson2CodecSupport
See gh-26212
2021-02-04 16:12:57 +00:00
Rossen Stoyanchev f4c9f6b860 Media types by Class for HttpMessageConverter
See gh-26212
2021-02-04 16:12:57 +00:00
Rossen Stoyanchev 1721b0b8d7 Extra ObjectMapper registrations in HttpMessageConverter
See gh-26212
2021-02-04 16:12:57 +00:00
Sam Brannen 836976d732 Increase fudge factor 2021-02-04 13:41:47 +01:00
Sam Brannen 59818ceca9 Fix StoredProcedure documentation in reference manual
This commit aligns the documentation in the reference manual with the
actual source code for StoredProcedure with regard to public execute()
methods.

Closes gh-26505
2021-02-04 11:10:50 +01:00
Sam Brannen 08ce6baedf Polishing 2021-02-04 11:08:25 +01:00
Rossen Stoyanchev c52526ad42 Fix in MockMultipartHttpServletRequest#getMultipartHeaders
Previously this method returned headers only when a Content-Type part header
was present. Now it is guaranteed to return headers (possibly empty) as long
as there is a MultipartFile or Part with the given name.

Closes gh-26501
2021-02-03 21:55:42 +00:00
Sam Brannen 7a329eba5b Do not retain partial column metadata in SimpleJdbcInsert
Prior to this commit, if an SQLException was thrown while retrieving
column metadata from the database, SimpleJdbcInsert would generate an
INSERT statement that was syntactically valid but missing columns,
which could lead to data silently missing in the database (for nullable
columns).

This commit fixes this by clearing all collected column metadata if an
SQLException is thrown while processing the metadata. The result is
that an InvalidDataAccessApiUsageException will be thrown later while
generating the INSERT statement. The exception message now contains an
additional hint to make use of SimpleJdbcInsert#usingColumns() in order
to ensure that all required columns are included in the generated
INSERT statement.

SimpleJdbcCall can also encounter an SQLException while retrieving
column metadata for a stored procedure/function, but an exception is
not thrown since a later invocation of the stored procedure/function
will likely fail anyway due to missing arguments. Consequently, this
commit only improves the warning level log message by including a hint
to make use of SimpleJdbcCall#addDeclaredParameter().

Closes gh-26486
2021-02-03 18:47:19 +01:00
Sam Brannen 91d542e406 Polishing 2021-02-03 16:27:28 +01:00
Rossen Stoyanchev 6fde28be98 Fix checkstyle violations 2021-02-03 12:24:45 +00:00
Rossen Stoyanchev 1e481cd14c Fix checkstyle violation 2021-02-03 12:12:52 +00:00
Rossen Stoyanchev df4ba742cb Apply global CORS settings to static resources in WebFlux
Closes gh-26495
2021-02-03 11:17:59 +00:00
Rossen Stoyanchev 996c86f448 Suppress PREFLIGHT_AMBIGUOUS_MATCH if matches have no CORS config
Closes gh-26490
2021-02-03 10:43:08 +00:00
Toshiaki Maki 0575e6637c Protobuf Codec supports application/vnd.google.protobuf 2021-02-02 16:55:10 +00:00
Sam Brannen ad8775ef95 Link to Objenesis property name constant in SpringProperties 2021-02-02 17:42:45 +01:00
Sam Brannen 8589804012 Link to known property name constants in SpringProperties 2021-02-02 15:20:41 +01:00
Sam Brannen c649b74235 Use api-spring-framework attribute for links to SpringProperties
See gh-26492
2021-02-02 14:01:51 +01:00
Sam Brannen bef5d59e43 Link to SpringProperties Javadoc from the Testing chapter
Closes gh-26492
2021-02-02 13:48:11 +01:00
Sam Brannen 99bd8115dd Clean up deprecation warnings in Gradle build output 2021-02-02 11:37:30 +01:00
Sam Brannen 1a3ff83157 Remove obsolete code in HeaderValueHolder 2021-02-02 11:28:32 +01:00
Sam Brannen c19b2851f1 Ignore null Locale in MockHttpServletResponse
Prior to this commit, calls to setLocale() MockHttpServletResponse
would result in a NullPointerException if the supplied Locale was null.

Although the Javadoc for setLocale(Locale) and addHeader(String, String)
in javax.servlet.ServletResponse does not specify how a null
Locale should be handled, both Tomcat and Jetty simply ignore a null
value.

This commit therefore updates MockHttpServletResponse to silently
ignore a null Locale passed to setLocale().

Closes gh-26493
2021-02-02 11:18:34 +01:00
Sam Brannen 3b4a3431d4 Ignore null header value in MockHttpServletResponse
Prior to this commit, calls to setHeader() and addHeader() in
MockHttpServletResponse would result in an IllegalArgumentException or
NullPointerException if the supplied header value was null.

Although the Javadoc for setHeader(String, String) and
addHeader(String, String) in javax.servlet.http.HttpServletResponse
does not specify how a null header value should be handled, both Tomcat
and Jetty simply ignore a null value. Furthermore,
org.springframework.http.HttpHeaders.add(String, String) declares the
headerValue parameter as @Nullable.

This commit therefore updates MockHttpServletResponse to silently
ignore null header values passed to setHeader() and addHeader().

Closes gh-26488
2021-02-02 11:18:34 +01:00
fengyuanwei c82a445094 Fix Javadoc in StandardServletAsyncWebRequest 2021-02-01 08:27:40 +00:00
Rossen Stoyanchev 95752ef1c9 Improve handling for pre-flight requests
1. Update the HandlerMapping contract to state that CORS checks are expected
to be applied before returning a handler.

2. DispatcherHandler checks explicitly for pre-flight requests or CORS failed
requests and skips handling for both. Technically no change since
AbstractHandlerMapping already returns a NO_OP_HANDLER for those cases.
The purpose however is for the DispatcherHandler to also guarantee more
explicitly that no such handling can take place for such cases.

As one consequence, this makes it possible to invoke the DispatcherHandler from
anywhere in the WebFilter chain in order to "handle" a pre-flight request, and
then skip the rest of the WebFilter chain.

See gh-26257
2021-01-29 22:12:10 +00:00
Rossen Stoyanchev 0ff50d6d9e Improve Javadoc of ServerHttpRequest#getPath
Closes gh-26469
2021-01-29 20:40:13 +00:00
Rossen Stoyanchev 30cdeb23fe Fix order of headers in DefaultHandlerExceptionResolver
Closes gh-26470
2021-01-29 20:30:55 +00:00
Rossen Stoyanchev ecf5113e0e Handle forwarded header parse issues
Closes gh-26459
2021-01-28 20:45:26 +00:00
Gediminas Rimša 80c4e6bb95 Fix ContentCachingResponseWrapper comment typo 2021-01-28 17:53:38 +00:00
Rossen Stoyanchev 5e5d8e4a23 Handle flushingFailed() for Servlet containers
Closes gh-26434
2021-01-28 17:03:35 +00:00
Brian Clozel d7e05aa48f Upgrade CI images to ubuntu:focal-20210119 2021-01-28 11:50:19 +01:00
Rossen Stoyanchev cf52246319 WebSocketExtension#equals matches sub-classes too
Closes gh-26449
2021-01-27 20:39:44 +00:00
Sam Brannen c5284009a1 Fix UriComponentsBuilder examples in ref docs
Closes gh-26453
2021-01-27 15:16:15 +01:00
Sam Brannen 2d29fcd0bb Polish Javadoc for TransactionSynchronizationUtils 2021-01-27 14:03:38 +01:00
Sam Brannen 5e56162394 Fix Checkstyle violation
The @since tag must come before @see tags.

See gh-26450
2021-01-27 11:53:20 +01:00
Johnny Lim 45650f2b27 Add @since tag to TransactionSynchronizationUtils.unwrapResourceIfNecessary()
Closes gh-26450
2021-01-27 11:23:22 +01:00
Rossen Stoyanchev 3e1b1638ef Update link for reporting security issues 2021-01-26 21:39:29 +00:00
Rossen Stoyanchev e80a23d6ad Fix CheckStyle violations 2021-01-26 17:51:52 +00:00
Rossen Stoyanchev 8aeae49f40 Support for servletPath prefix in ServletRequestPathUtils
Closes gh-26445
2021-01-26 17:30:16 +00:00
Rossen Stoyanchev f22e2ac578 Add HttpServletMapping support to MockHttpServletRequest
See gh-26428
2021-01-26 17:30:16 +00:00
izeye 355aca7665 Fix DelegatingWebMvcConfigurationTests.configurePathPatternParser() 2021-01-26 16:15:46 +00:00
Rossen Stoyanchev ec5f99f981 Add mutate() to RequestMappingInfo
Closes gh-26428
2021-01-26 11:44:20 +00:00
Sam Brannen 03f1b65084 Polish Javadoc for ResourceLoader APIs 2021-01-26 12:20:28 +01:00
Juergen Hoeller 0de2833894 Upgrade to Jetty 9.4.36, EclipseLink 2.7.8, XStream 1.4.15, Apache Johnzon 1.2.10 2021-01-25 17:02:27 +01:00
Sam Brannen cd9d562129 Check thread count in ParallelApplicationEventsIntegrationTests 2021-01-25 11:02:36 +01:00
Brian Clozel 080ba5d448 Upgrade JDK versions in CI pipeline 2021-01-25 10:51:47 +01:00
daisuzz d96448fde9 Fix typo in URI Encoding section of reference docs
Closes gh-26436
2021-01-24 14:38:33 +01:00
Sam Brannen 84eed00260 Upgrade to Gradle 6.8.1 2021-01-24 12:48:35 +01:00
Sam Brannen cee51b248a Upgrade to AssertJ 3.19.0 2021-01-24 12:11:17 +01:00
Rossen Stoyanchev 5a640bb635 Expose mvcPatternParser bean in WebMvcConfigurationSupport
See gh-26427
2021-01-22 17:31:59 +00:00
Rossen Stoyanchev 5c1cbb769c Check that PathPatternParser is not set too late
This commit ensures the PathPatternParser cannot be set after request mappings
have been initialized when it is too late.

See gh-26427
2021-01-22 17:31:59 +00:00
Sam Brannen 4a7a2258f9 Unwrap proxied DataSources in SqlScriptsTestExecutionListener
Prior to this commit, if the DataSource in the
DataSourceFromTransactionManager was wrapped in a proxy implementing
InfrastructureProxy, SqlScriptsTestExecutionListener would throw an
exception stating that the DataSource in the ApplicationContext and the
DataSource in the DataSourceFromTransactionManager were not the same.

This commit unwraps both data sources and compares the underlying
target instances to check for equality.

In addition, this commit makes the unwrapResourceIfNecessary() method in
TransactionSynchronizationUtils public.

Closes gh-26422
2021-01-22 18:22:00 +01:00
Rossen Stoyanchev bad8954e65 Append unique number to log prefix for Undertow
Closes gh-26430
2021-01-22 15:26:48 +00:00
Rossen Stoyanchev e4566c11b8 Apply PathPatternParser also to BeanNameUrlHandlerMapping
Closes gh-26414
2021-01-22 15:22:51 +00:00
Sam Brannen 9d8910d869 Improve inline documentation for PostProcessorRegistrationDelegate
This commit introduces warnings in invokeBeanFactoryPostProcessors()
and registerBeanPostProcessors() to deter people from submitting PRs
that result in breaking changes.

Closes gh-26401
2021-01-22 13:34:29 +01:00
Rossen Stoyanchev aa7584d252 Support for Jetty 10
Closes gh-26123
2021-01-21 20:16:21 +00:00
Arjen Poutsma e537844a09 Copy headers from part in MultipartBodyBuilder
This commit makes sure that Part.headers() is copied over when adding a
part in the MultipartBodyBuilder.

Closes gh-26410
2021-01-21 16:24:59 +01:00
Arjen Poutsma daa5465003 Checkstyle fix 2021-01-21 15:55:28 +01:00
Brian Clozel 5a4a677fbd Fix contextPath request matching with PathPatterns
Prior to this commit, `SimpleUrlHandlerMapping` and other
implementations of `AbstractUrlHandlerMapping` would not strip the
Servlet context path from the request path before performing path
matching with PathPattern instances.

This resulted in requests not matching as expected if the application
was configured with a Servlet context path.

This commit ensures that the `RequestPath` "path whithin the
application" is used for matching against path patterns.

Fixes gh-26411
2021-01-21 10:09:49 +01:00
Rossen Stoyanchev 584dabbb39 Add all non-file parts as request parameters
Closes gh-26400
2021-01-19 20:49:59 +00:00
Stephane Nicoll 4779323d33 Clarify behaviour of condition attribute of CachePut
Closes gh-26186
2021-01-19 15:14:26 +01:00
Arjen Poutsma 67112b211a Rollback to midnight in quartz expressions
This commit makes sure that the CronExpression rolls back the time to
midnight when dealing with Quartz expression fields (such as "L", "LW",
etc.).

Closes gh-26390
2021-01-19 15:06:40 +01:00
Juergen Hoeller 799885fd6d Introduce public defineClass variant for SmartClassLoader implementations
Closes gh-26403
2021-01-19 12:03:18 +01:00
Sam Brannen 3c959d3515 Fix grammar in Javadoc 2021-01-18 11:26:29 +01:00
Rossen Stoyanchev cfae40afb4 Return 415 for form data with @RequestBody in WebFlux
Closes gh-26386
2021-01-14 21:23:49 +00:00
Juergen Hoeller 1b1ae33efe Upgrade to Netty 4.1.58, Jackson 2.12.1, Apache Johnzon 1.2.9 2021-01-14 18:14:46 +01:00
Arjen Poutsma d80b08084e Document ordering of RouterFunction beans
Closes gh-26283
2021-01-14 16:18:22 +01:00
Arjen Poutsma d1a7fed16c Clean up content type in ContentNegotiatingViewResolver
This commit removes the quality value of the selected media type before
setting it in the request attribute.

Closes gh-26315
2021-01-14 14:30:07 +01:00
izeye 469eb8146e Add a missing space in exception message
Closes gh-26385
2021-01-14 12:01:04 +00:00
Rossen Stoyanchev 35225e5794 Add verify(Duration) to MockRestServiceServer
Closes gh-22618
2021-01-13 17:50:01 +00:00
Rossen Stoyanchev a7413ea76b Add recommendation for original multipart filename
See gh-26299
2021-01-12 17:28:44 +00:00
Rossen Stoyanchev c3c36ab498 Document choices for async ResponseEntity return values
Closes gh-22614
2021-01-12 14:37:01 +00:00
Nelson Osacky ea4fe7eaf7 Update Gradle Enterprise Plugin versions
Closes gh-26374
2021-01-12 13:46:14 +01:00
Sam Brannen 3112dc5f81 Improve JMH Jar configuration 2021-01-12 12:27:54 +01:00
Sam Brannen b07a6b3283 Upgrade Shadow plugin to version 6.1.0 2021-01-12 12:27:54 +01:00
Sam Brannen 6b9d5698ce Upgrade to JMH 1.25 2021-01-12 12:27:54 +01:00
Sam Brannen 72a9d0ff4b Upgrade JMH plugin to 0.5.2 2021-01-12 11:28:03 +01:00
Sam Brannen 872255a591 Fix ConcurrentBeanFactoryBenchmark 2021-01-12 10:59:57 +01:00
Brian Clozel 671270dae2 Fix optional deployments regex in release CI pipeline
This commit fixes the "optional-deployments" regex for ".zip" artifacts;
"\" do not need to be escaped in YML single-quoted strings.
2021-01-12 10:56:52 +01:00
Sam Brannen 6ea39fd122 Upgrade to Gradle 6.8
Closes gh-26368
2021-01-12 10:42:07 +01:00
Spring Buildmaster dadef9f5bc Next development version (v5.3.4-SNAPSHOT) 2021-01-12 06:34:25 +00:00
Rossen Stoyanchev fceceb480f Validate Resource type in ResourceHttpRequestHandler 2021-01-11 20:54:27 +00:00
Rossen Stoyanchev e5ab67bd71 ResourceHandlerRegistration exposes List<Resource> locations 2021-01-11 18:40:01 +00:00
Sébastien Deleuze 2d53570f4c Prevent kotlinx.serialization usage on interfaces
Remove support for open polymorphic serialization in
kotlinx.serialization web converters and codecs in order
to prevent serialization handling suitable for Jackson
or other general purpose Java JSON libraries.

This will probably need further refinements for collections
for example, and could ultimately be fixed when
kotlinx.serialization will provide a dedicated function to
evaluate upfront if a type can be serialized or not.

Closes gh-26298
2021-01-11 19:07:59 +01:00
Juergen Hoeller be5eb7037f Explicit notes on non-null enforcement and deep cause support in 5.3
Closes gh-26296
See gh-26317
2021-01-11 19:04:12 +01:00
Sam Brannen fff2291f2a Document context lifecycle & logging semantics for the TestContext framework
This commit documents ApplicationContext initialization and shutdown
logging semantics for console output triggered in conjunction with the
Spring TestContext Framework.

Closes gh-25385
2021-01-11 17:48:33 +01:00
Stephane Nicoll 917f3ad101 Upgrade to Reactor 2020.0.3
Closes gh-26367
2021-01-11 16:41:03 +01:00
Sam Brannen 2fd6e6e87c Polish Javadoc for base ExceptionHandlerMethodResolvers 2021-01-11 12:58:19 +01:00
Sam Brannen 570bdbd253 Avoid unnecessary sorting in base ExceptionHandlerMethodResolvers 2021-01-11 12:58:19 +01:00
Juergen Hoeller b587a16d46 Expose all exception causes as provided handler method arguments
Closes gh-26317
2021-01-11 11:10:07 +01:00
Rossen Stoyanchev dd8ea89bea Upgrade to Reactor 2020.0.3 snapshots
See gh-26367
2021-01-11 09:30:47 +00:00
Juergen Hoeller b0dea4d3f6 Upgrade to RxJava 2.2.20, OpenPDF 1.3.24, HtmlUnit 2.46 2021-01-11 09:45:14 +01:00
Juergen Hoeller 4ac27e4dab Suppress ClassCastException for payload type mismatch as well
Closes gh-26349
2021-01-11 09:44:38 +01:00
Nelson Osacky b0abdee012 Add Revved up by Gradle Enterprise badge to Readme
Similar to: https://github.com/spring-projects/spring-boot/pull/24640

Closes gh-26348
2021-01-11 09:40:00 +01:00
Brian Clozel e4dc863ad0 Fix headers keySet in WebFlux adapters
Prior to this commit, WebFlux native headers adapters would delegate the
`httpHeaders.keySet` to underlying implementations that do not honor the
`remove*` methods.

This commit fixes the `Set` implementation backing the
`httpHeaders.keySet` and ensures that headers can be safely removed from
the set.

Fixes gh-26361
2021-01-11 09:18:10 +01:00
Rossen Stoyanchev 689b5566bf Cache "no match" result from ExceptionHandler methods
Closes gh-26339
2021-01-08 19:25:11 +00:00
Arjen Poutsma 234b4719c6 Use default bounded elastic scheduler
Instead of using a new bounded elastic scheduler per
DefaultPartHttpMessageReader instance, which creates daemon threads that
are not shut down, we now use the shared bounded elastic scheduler.

Closes gh-26347
2021-01-08 14:32:49 +01:00
Arjen Poutsma ce1ae2f1b2 Only write non-default charset in FormHttpMessageConverter
This commit only writes the 'charset' parameter in the written headers
if it is non-default (not UTF-8), since RFC7578 states that the only
allowed parameter is 'boundary'.

See gh-25885
Closes gh-26290
2021-01-08 14:03:58 +01:00
Arjen Poutsma 69ce7d33b9 Polishing 2021-01-08 12:02:13 +01:00
Rossen Stoyanchev e36d4162c2 Filter empty buffers PayloadMethodArgumentResolver
An empty buffer for RSocket is an empty message and while some codecs
such as StringDecoder may be able turn that into something such as an
empty String, for other formats such as JSON it is invalid input.

Closes gh-26344
2021-01-07 17:26:00 +00:00
Arjen Poutsma d387d9ae1e Allow quartz expression in cron expression lists
This commit introduces support for lists of quartz cron fields, such
as "1L, LW" or "TUE#1, TUE#3, TUE#5".

Closes gh-26289
2021-01-07 14:27:07 +01:00
Rossen Stoyanchev 138f6bfd84 Update Javadoc of FilePart#filename
See gh-26299
2021-01-07 11:26:00 +00:00
Rossen Stoyanchev a1cf6bbc37 Avoid wrapping of AssertionError in ExchangeResult
Closes gh-26303
2021-01-06 21:56:14 +00:00
Rossen Stoyanchev fb040479eb Update Javadoc of MultipartFile#getOriginalFilename
Closes gh-26299
2021-01-06 21:56:14 +00:00
Rossen Stoyanchev d50375da8e Add more static imports to MockMvc snippets
Closes gh-26311
2021-01-06 21:56:14 +00:00
Rossen Stoyanchev fb2e53276c Assert WebFlux present for WebTestClient
Closes gh-26308
2021-01-06 21:56:14 +00:00
Rossen Stoyanchev c040cd7b05 Parsed RequestPath is recalculated on Forward
Closes gh-26318
2021-01-06 21:56:14 +00:00
Brian Clozel dcc8dcdff8 Set content length on ServletHttpResponse
Prior to this commit, the `ServletServerHttpResponse` would copy headers
from the `HttpHeaders` map and calls methods related to headers exposed
as properties (content-type, character encoding).

Unlike its reactive variant, this would not set the content length.
Depending on the Servlet container implementation, this could cause
duplicate Content-Length response headers in the actual HTTP response.

This commit aligns both implementations and ensures that the
`setContentLengthLong` method is called if necessary so that the Servlet
container can ensure a single header for that.

Fixes gh-26330
2021-01-06 20:38:45 +01:00
Paul Warren efa360cff7 Avoid writing content-length twice in resource handling
Prior to this commit, the `ResourceHttpRequestHandler` would write the
resource size to the "Content-Length" response header.
This is already done by the underlying `ResourceHttpMessageConverter`
and `ResourceRegionHttpMessageConverter`.

This commit avoid this duplicate operation and delegates instead to the
converters.

See gh-26330
Closes gh-26340
2021-01-06 20:38:45 +01:00
Juergen Hoeller 5804db6479 Upgrade to Mockito 3.7, Checkstyle 8.39, RxJava 3.0.9, Joda-Time 2.10.9 2021-01-05 19:33:01 +01:00
Rossen Stoyanchev faa749934b Minor refactoring
- Remove unused clone() code left-over from previous way of cloning.
- Lazy instantiation of ServerCodecConfigurer in
  HttpWebHandlerAdapter since in most cases the configurer instance
  is set externally.

Closes gh-26263
2021-01-05 17:20:26 +00:00
Rossen Stoyanchev e1385090e4 Cached codec instances in DefaultCodecs
Closes gh-26263
2021-01-05 17:20:26 +00:00
Sébastien Deleuze 3113917c55 Update ReactiveAdapterRegistry to do classpath checks during init
To improve GraalVM native image footprint and compatibility.

Closes gh-26295
2021-01-05 09:17:34 +01:00
Rossen Stoyanchev 994a35d691 Mutated ServerHttpRequest returns native request correctly
Closes gh-26304
2021-01-04 21:40:25 +00:00
Juergen Hoeller 8095ba4cd2 Upgrade to Hibernate Validator 6.2 (and Hibernate ORM 5.4.27)
Closes gh-26255
2021-01-04 19:10:46 +01:00
Mattison chao bd2edf26df Fix implementation of isOpen() in ReactorNettyWebSocketSession
See gh-26332 for details.

Closes gh-26341
2021-01-04 17:13:39 +01:00
Arjen Poutsma 4f6d26b25c Parse list with range delta in CronExpression
Closes gh-26313
2021-01-04 16:02:28 +01:00
Sébastien Deleuze 6d85a8a0cc Fix JdbcOperationsExtensions
This commit reverts some changes done in 2d5f9723fa
in order to fix JdbcOperations.queryForObject and
JdbcOperations.queryForList.

It is not possible to provide a vararg variant in Kotlin
for now due to a signature clash, but that will probably
make sense in Spring Framework 6 timeframe along to array
based variant removal for both Java and Kotlin.

Closes gh-26312
2021-01-04 14:57:31 +01:00
Sam Brannen 44b29b6ecf Remove obsolete commandName attribute in spring-form.tld
Since support for the commandName attribute was removed from the
implementation of FormTag in Spring Framework 5.0, the presence of the
commandName attribute in the spring-form.tld file is no longer valid
and can lead one to assume that the commandName attribute is still
supported -- for example when using code completion in a JSP editor.

This commit therefore removes the obsolete commandName attribute in
spring-form.tld.

Closes gh-26337
2021-01-03 17:46:20 +01:00
Sam Brannen 91a6254187 Test status quo for repeatable @PropertySource on composed annotation
This commit introduces tests that verify support for using
@PropertySource as a repeatable annotation without the
@PropertySources container, both locally on an @Configuration class
and on a custom composed annotation.

See gh-26329
2021-01-02 16:21:53 +01:00
Sam Brannen c8ad762b40 Polishing 2021-01-02 16:21:53 +01:00
Sam Brannen 5c1176786c Test status quo for list support in CronExpression
See gh-26289
2020-12-30 14:38:58 +01:00
diguage eab61f692f Merge source and substitution configuration in reference docs
Closes gh-25545
2020-12-28 20:49:36 +01:00
Sam Brannen ee41ebc1ab Polish MockMvcReuseTests 2020-12-28 16:03:16 +01:00
Sam Brannen 885f6dbab9 Ensure ParallelApplicationEventsIntegrationTests passes on CI server
See gh-25616
2020-12-23 10:16:13 +01:00
Juergen Hoeller b0f404722e Upgrade to ASM master (including early support for Java 17 bytecode)
Closes gh-26307
2020-12-21 19:33:33 +01:00
Juergen Hoeller cd7b0b11f3 Upgrade to Hibernate ORM 5.4.26, Netty 4.1.56, XMLUnit 2.8.2 2020-12-21 19:10:17 +01:00
Sam Brannen 1565f4b83e Introduce ApplicationEvents to assert events published during tests
This commit introduces a new feature in the Spring TestContext
Framework (TCF) that provides support for recording application events
published in the ApplicationContext so that assertions can be performed
against those events within tests. All events published during the
execution of a single test are made available via the ApplicationEvents
API which allows one to process the events as a java.util.Stream.

The following example demonstrates usage of this new feature.

@SpringJUnitConfig(/* ... */)
@RecordApplicationEvents
class OrderServiceTests {

    @Autowired
    OrderService orderService;

    @Autowired
    ApplicationEvents events;

    @Test
    void submitOrder() {
        // Invoke method in OrderService that publishes an event
        orderService.submitOrder(new Order(/* ... */));
        // Verify that an OrderSubmitted event was published
        int numEvents = events.stream(OrderSubmitted.class).count();
        assertThat(numEvents).isEqualTo(1);
    }
}

To enable the feature, a test class must be annotated or meta-annotated
with @RecordApplicationEvents. Behind the scenes, a new
ApplicationEventsTestExecutionListener manages the registration of
ApplicationEvents for the current thread at various points within the
test execution lifecycle and makes the current instance of
ApplicationEvents available to tests via an @Autowired field in the
test class. The latter is made possible by a custom ObjectFactory that
is registered as a "resolvable dependency". Thanks to the magic of
ObjectFactoryDelegatingInvocationHandler in the spring-beans module,
the ApplicationEvents instance injected into test classes is
effectively a scoped-proxy that always accesses the ApplicationEvents
managed for the current test.

The current ApplicationEvents instance is stored in a ThreadLocal
variable which is made available in the ApplicationEventsHolder.
Although this class is public, it is only intended for use within the
TCF or in the implementation of third-party extensions.

ApplicationEventsApplicationListener is responsible for listening to
all application events and recording them in the current
ApplicationEvents instance. A single
ApplicationEventsApplicationListener is registered with the test's
ApplicationContext by the ApplicationEventsTestExecutionListener.

The SpringExtension has also been updated to support parameters of type
ApplicationEvents via the JUnit Jupiter ParameterResolver extension
API. This allows JUnit Jupiter based tests to receive access to the
current ApplicationEvents via test and lifecycle method parameters as
an alternative to @Autowired fields in the test class.

Closes gh-25616
2020-12-20 23:46:19 +01:00
Sam Brannen 65a395ef0e Fix syntax in Kotlin example 2020-12-19 15:29:40 +01:00
Juergen Hoeller 947255e377 Always propagate checked exceptions from Kotlin code behind CGLIB proxies
Closes gh-23844
2020-12-18 11:55:20 +01:00
Rossen Stoyanchev 499be70a71 Update async dispatch check in OncePerRequestFilter
We no longer need to rely on an indirect check since Servlet 3.0 is expected
so we can just check the DispatcherType of the request.

Closes gh-26282
2020-12-17 17:33:39 +00:00
Rossen Stoyanchev 0cf5005a3d Apply abortOnCancel in JettyClientHttpConnector
This new option allows a cancel signal to abort the request, which is
how we expect a connection to be aborted in a reactive chain that
involves the WebClient.

Closes gh-26287
2020-12-17 17:18:38 +00:00
Rossen Stoyanchev f07fc76cf3 Limit scheme/host check in fromUriString to HTTP URLs
Closes gh-26258
2020-12-17 14:49:57 +00:00
Juergen Hoeller d3e1c54354 Upgrade to Hibernate Validator 6.1.7, Jetty Reactive HttpClient 1.1.5, Joda-Time 2.10.8, XMLUnit 2.8.1 2020-12-17 10:46:13 +01:00
Juergen Hoeller a109b4c31a Translate PostgreSQL code 21000 (cardinality_violation)
Closes gh-26276
2020-12-16 22:27:57 +01:00
Juergen Hoeller 00b56c026a Consistent handling of NullBean instances in resolveNamedBean
Closes gh-26271
2020-12-16 22:27:41 +01:00
Juergen Hoeller fbd2ffdd23 Consistent declarations and assertions in MockMultipartFile
See gh-26261
2020-12-16 22:27:33 +01:00
Sam Brannen b3a47c76f8 Introduce TestContextAnnotationUtils.hasAnnotation() 2020-12-15 22:48:33 +01:00
Sam Brannen 1292947f78 Introduce computeAttribute() in AttributeAccessor
This commit introduces computeAttribute() as an interface default method
in the AttributeAccessor API. This serves as a convenience analogous to
the computeIfAbsent() method in java.util.Map.

Closes gh-26281
2020-12-15 22:47:02 +01:00
Sam Brannen ef6a582c78 Clean up warnings in Gradle build 2020-12-15 22:47:02 +01:00
Rossen Stoyanchev a11d1c8510 Enrich WebSocketHandler context
Closes gh-26210
2020-12-15 21:34:16 +00:00
Brian Clozel 83c19cd60e Fix NPE when calling NettyHeadersAdapter.add()
Prior to this commit, the `NettyHeadersAdapter` would directly delegate
the `add()` and `set()` calls to the adapted
`io.netty.handler.codec.http.HttpHeaders`. This implementation rejects
`null` values with exceptions.

This commit aligns the behavior here with other implementations, by not
rejecting null values but simply ignoring them.

Fixes gh-26274
2020-12-15 13:23:53 +01:00
Rossen Stoyanchev bcfbde9848 Parse parts in MockMultipartHttpServletRequestBuilder
Closes gh-26261
2020-12-14 21:13:29 +00:00
izeye 17e6cf1cc1 Replace AtomicReference<Boolean> with AtomicBoolean in AbstractServerHttpResponse.writeWith() 2020-12-12 10:40:05 +01:00
Juergen Hoeller ec33b4241a Upgrade to Netty 4.1.55, Tomcat 9.0.41, Caffeine 2.8.8 2020-12-11 11:53:53 +01:00
Brian Clozel 657641ebaa Remove JDK14 CI variant from build pipeline 2020-12-11 11:08:33 +01:00
Brian Clozel 4597e9b547 Upgrade CI container images to Ubuntu Focal 2020-12-11 09:46:00 +01:00
Brian Clozel 721dacca5a Upgrade JDK8, JDK11 and JDK15 versions in CI build 2020-12-11 09:45:21 +01:00
alexscari 9d124fffcb Fix typo in Javadoc for AbstractJdbcCall
Closes gh-26254
2020-12-10 17:32:58 +01:00
Juergen Hoeller 2a47751fcd Defensively handle loadClass null result in BeanUtils.findEditorByConvention
Closes gh-26252
2020-12-10 16:24:32 +01:00
Spring Buildmaster 06e352822a Next development version (v5.3.3-SNAPSHOT) 2020-12-09 06:11:23 +00:00
Rossen Stoyanchev 25101fb034 Additional fixes for discarding data buffers
Closes gh-26232
2020-12-08 21:33:50 +00:00
Rossen Stoyanchev cb44ae62e9 Additional DataBuffer hints
See gh-26230
2020-12-08 18:43:54 +00:00
Juergen Hoeller 01fb4dbeba Polishing
See gh-26237
2020-12-08 14:59:10 +01:00
fengyuanwei 07fadae27d Remove duplicate "property" in PropertyCacheKey.toString() 2020-12-08 14:49:33 +01:00
Arjen Poutsma 2b77c08e09 Clear path pattern in HandlerMapping
This commit refactors cb2b141d31 to move
the cleaning code from a DeferredResultProcessingInterceptor to the
RouterFunctionMapping.

See gh-26239
2020-12-08 13:06:45 +01:00
Arjen Poutsma cb2b141d31 Clear path pattern after async result
This commit makes sure that the matching pattern attributes is cleared
when an async result has been obtained, so that the subsequent
dispatch starts from scratch.

Closes gh-26239
2020-12-08 12:08:56 +01:00
Brian Clozel 3b92d45984 Upgrade reference docs dependencies
This commit upgrades the spring-doc-resources dependency to 0.2.5
and the spring-asciidoctor-extensions-block-switch to 0.5.0
2020-12-08 11:38:39 +01:00
Juergen Hoeller 1195b3a0b0 Polishing 2020-12-08 10:39:56 +01:00
Sébastien Deleuze 56721669d1 Upgrade to Kotlin 1.4.21
See gh-26132
2020-12-08 08:29:21 +01:00
Sébastien Deleuze e87e03c539 Refine ConfigurationClassPostProcessor behavior in native images
This commit refines ConfigurationClassPostProcessor behavior in
native images by skipping configuration classes enhancement
instead of raising an error.

See spring-projects-experimental/spring-graalvm-native#248 for
more details.

Closes gh-26236
2020-12-08 00:12:02 +01:00
Rossen Stoyanchev 194cebd730 Upgrade to Reactor 2020.0.2
Closes gh-26176
2020-12-07 22:59:49 +00:00
Rossen Stoyanchev 7418c4b7b7 Fix buffer leak in AbstractServerHttpResponse
See gh-26232
2020-12-07 22:52:08 +00:00
Rossen Stoyanchev ad42010785 Correlate data buffers to request log messages
HttpMessageWriter implementations now attach the request log prefix
as a hint to created data buffers when the logger associated with
the writer is at DEBUG level.

Closes gh-26230
2020-12-07 22:29:07 +00:00
Juergen Hoeller 10f6a22315 Upgrade to Undertow 2.2.3 and Apache HttpCore Reactive 5.0.3 2020-12-07 22:08:50 +01:00
Juergen Hoeller c970c318f4 Polishing 2020-12-07 22:08:01 +01:00
Juergen Hoeller 834032df1f Clarify intended advice execution behavior (includes related polishing)
Closes gh-26202
2020-12-07 22:06:22 +01:00
Brian Clozel 5efa4ad442 Pull images with registry-image resource in build 2020-12-07 20:38:07 +01:00
Rossen Stoyanchev 7ef3257b03 Correctly determine HttpServletMapping for INCLUDE
Closes gh-26216
2020-12-07 17:44:22 +00:00
Sébastien Deleuze 0172424635 Avoid CGLIB proxies on websocket/messaging configurations
This commit updates websocket and messaging configurations in order
to not use CGLIB proxies anymore. The goal here is to allow support
in native executables and to increase the consistency across the
portfolio.

Closes gh-26227
2020-12-07 12:18:29 +01:00
Sébastien Deleuze 994ec708fc Upgrade to Kotlin Coroutines 1.4.2
Closes gh-26226
2020-12-07 10:49:12 +01:00
Stephane Nicoll da15fe4c94 Upgrade to Gradle 6.7.1
Closes gh-26225
2020-12-07 10:43:22 +01:00
mplain a1d134b086 Add missing Kotlin extensions for WebClient.ResponseSpec
Closes gh-26030
2020-12-07 10:34:39 +01:00
Juergen Hoeller 3c85c263d2 Upgrade to Groovy 3.0.7, RxJava 3.0.8, Mockito 3.6.28 2020-12-04 23:29:21 +01:00
Sam Brannen 7b3f53de50 Polish SpelParserTests 2020-12-04 18:49:05 +01:00
VonUniGE ec7425c1f4 Fix typo in javadoc for ResponseExtractor 2020-12-04 16:18:59 +00:00
limo520 ef05eb3729 Fix assertions in SpelParserTests.generalExpressions
Closes gh-26211

Co-authored-by: fengyuanwei <fengyuanwei@wondersgroup.com>
2020-12-04 13:59:16 +01:00
Rossen Stoyanchev 8ac39a50fe ServletServerHttpResponse reflects Content-Type override
Closes gh-25490
2020-12-03 18:41:18 +00:00
Rossen Stoyanchev d82cb15439 Ensure both mock files and parts can be provided
Closes gh-26166
2020-12-03 17:53:26 +00:00
Rossen Stoyanchev 8c1d06e0c4 Polishing contribution
Closes gh-25927
2020-12-03 17:16:27 +00:00
shevtsiv 01892c6524 Optimization in ResourceArrayPropertyEditor
The previous implementation uses ArrayList for storing resolved
resources and ArrayList has O(n) time complexity for the contains
operation. By switching to the HashSet for storing resolved
resources we improve the time complexity of this operation to be O(1).

See gh-25927
2020-12-03 17:16:27 +00:00
Brian Clozel 9776929a9d Mention security considerations in Forwarded filters
This commit improves the Javadoc for the `ForwardedHeaderFilter`
(Servlet Filter) and `ForwardedHeaderTransformer` (reactive variant) so
as to mention security considerations linked to Forwarded HTTP headers.

Closes gh-26081
2020-12-03 15:40:31 +01:00
Brian Clozel 4337d8465c Remove spring.event.invoke-listener startup event
Prior to this commit, the `SimpleApplicationEventMulticaster` would be
instrumented with the `ApplicationStartup` and start/stop events for
invoking event listeners (`spring.event.invoke-listener`).

This feature was already limited to single-threaded event publishers,
but is still flawed since several types of events can happen
concurrently. Due to the single-threaded nature of the startup sequence,
our implementation should not produce startup events concurrently.

This can cause issues like gh-26057, where concurrent events lead to
inconcistencies when tracking parent/child relationships.

This commit removes the `spring.event.invoke-listener` startup event as
a result.

Fixes gh-26057
2020-12-03 15:40:31 +01:00
Arjen Poutsma 8f0ad73bfd Merge pull request #26133 from alexfeigin/master
* gh-26133:
  Expose future response in new AsyncServerResponse
2020-12-03 14:51:49 +01:00
Alex Feigin 3714d0e401 Expose future response in new AsyncServerResponse
This commit introduces AsyncServerResponse, an extension of
ServerResponse that is returned from ServerResponse.async and that
allows users to get the future response by calling the block method.
This is particularly useful for testing purposes.
2020-12-03 14:45:56 +01:00
Juergen Hoeller 227d85a6b4 Upgrade to Jackson 2.12, Hibernate ORM 5.4.25, Checkstyle 8.38
See gh-25907
2020-12-02 17:22:22 +01:00
Arjen Poutsma a1320cd450 Add SSE support to WebMvc.fn
This commit adds support for sending Server-Sent Events in WebMvc.fn,
through the ServerResponse.sse method that takes a SseBuilder DSL.
It also includes reference documentation.

Closes gh-25920
2020-12-02 15:14:47 +01:00
Johnny Lim c7f2f50c15 Polish ConfigurationClassPostProcessorTests
Closes gh-26197
2020-12-02 15:07:15 +01:00
Juergen Hoeller 396fb0cd51 Support for multi-threaded addConverter calls
Closes gh-26183
2020-12-02 12:25:37 +01:00
Rossen Stoyanchev 5328184f3a ContentCachingResponseWrapper skips contentLength for chunked responses
Closes gh-26182
2020-12-01 17:46:12 +00:00
Rossen Stoyanchev 1e19e51c9c Fix exchangeToMono sample in reference
Closes gh-26189
2020-12-01 17:46:12 +00:00
Arjen Poutsma 69aed83504 Use nested path variables instead of Servlet's
Remove the overriden pathVariable method from the nested request
wrapper,so that the merged attributes are used instead of the ones
provided by servlet.

Closes gh-26163
2020-12-01 11:24:45 +01:00
Arjen Poutsma 0978cc629a Polishing 2020-12-01 11:19:10 +01:00
Rossen Stoyanchev 0c825621b8 Avoid use of Optional wrapper to get List<MediaType>
See gh-26170
2020-11-30 17:30:45 +00:00
Rossen Stoyanchev e7b0b65244 Support for MediaType mappings in ResourceWebHandler
Closes gh-26170
2020-11-30 17:16:55 +00:00
Stephane Nicoll e9caee1b6e Start building against Reactor 2020.0.2 snapshots
See gh-26176
2020-11-30 07:21:51 +01:00
Juergen Hoeller 622d0831a3 Polishing 2020-11-27 21:46:45 +01:00
Juergen Hoeller 56dbe06d9b Register @Bean definitions as dependent on containing configuration class
Closes gh-26167
2020-11-27 17:00:16 +01:00
Rossen Stoyanchev 11f6c4e7ab Matching update for copyHeadersIfAbsent
The change to copyHeaders from d46091 also applied to copyHeadersIfPresent.

Closes gh-26155
2020-11-26 21:51:53 +00:00
Rossen Stoyanchev d46091565b MessageHeaderAccessor handle self-copy correctly
1. Revert changes in setHeader from 5.2.9 that caused regression on self-copy.
2. Update copyHeaders to ensure it still gets a copy of native headers.
3. Exit if source and target are the same instance, as an optimization.

Closes gh-26155
2020-11-26 16:14:51 +00:00
Сергей Цыпанов 42216b77df Remove unused package-private class o.s.w.u.p.SubSequence 2020-11-26 15:31:21 +01:00
Sébastien Deleuze 43faa439ab Refine kotlinx.serialization support
This commit introduces the following changes:
 - Converters/codecs are now used based on generic type info.
 - On WebMvc and WebFlux, kotlinx.serialization is enabled along
   to Jackson because it only serializes Kotlin @Serializable classes
   which is not enough for error or actuator endpoints in Boot as
   described on spring-projects/spring-boot#24238.

TODO: leverage Kotlin/kotlinx.serialization#1164 when fixed.

Closes gh-26147
2020-11-26 12:36:35 +01:00
Juergen Hoeller 1f701d9bad Upgrade to Jetty 9.4.35 2020-11-25 11:41:06 +01:00
Juergen Hoeller 73e1f24ac1 Restore HttpHeaders-based constructor for binary compatibility
Closes gh-26151
2020-11-25 11:36:12 +01:00
Juergen Hoeller 86f9716fef Remove misleading default note on ISO.DATE_TIME
Closes gh-26134
2020-11-25 11:35:06 +01:00
Rossen Stoyanchev 80701082cd Set favorPathExtension to false by default
Applies a change that was intended in #23915 but wasn't.

Closes gh-26119
2020-11-24 17:31:28 +00:00
Arjen Poutsma 3612990344 Polishing 2020-11-24 14:41:39 +01:00
Arjen Poutsma dea2029e94 Only write non-default charset in MultipartWriterSupport
This commit only writes the 'charset' parameter in the written headers
if it is non-default (not UTF-8), since RFC7578 states that the only
allowed parameter is 'boundary'.

Closes gh-25885
2020-11-24 14:41:39 +01:00
Sam Brannen 77d6f8bc00 Polishing 2020-11-24 14:04:33 +01:00
Rossen Stoyanchev 23006d417b Deprecate context method in WebClient
See gh-25710
2020-11-23 21:46:14 +00:00
Rossen Stoyanchev d8dafbc49d Add DEBUG log message in MetadataExtractor
Closes gh-26130
2020-11-23 17:22:09 +00:00
Rossen Stoyanchev cc4a1af091 Ensure AsyncSupportConfigurer is created once
Now that we have two adapters that need access to the configurer, we need
to save it in a field like others.

See gh-25931
2020-11-23 17:22:09 +00:00
Sam Brannen fd70a9e95d Fix comment
See gh-26140
2020-11-23 17:42:49 +01:00
Sam Brannen 298cb22bfe Ensure that projects can be imported into Eclipse IDE
Recently the Spring Framework projects could no longer be imported into
Eclipse IDE without compilation errors in JMH sources.

This commit addresses this issue by applying workarounds for bugs in
Gradle and the JMH plugin for Gradle.

Gradle bug: https://github.com/gradle/gradle/issues/14932

JMH plugin bug: https://github.com/melix/jmh-gradle-plugin/issues/157

The Gradle bug has already been fixed in Gradle 6.8 RC1; however, the
issue for the JMH plugin bug seems not to have been triaged yet.

Closes gh-26140
2020-11-23 17:24:57 +01:00
Sam Brannen 4ed645e710 Fix broken links to XSD schemas in ref docs
Closes gh-26129
2020-11-23 14:49:43 +01:00
Arjen Poutsma 7c47f554c0 Remove unnecessary check in RequestMappingHandlerAdapter 2020-11-23 13:33:00 +01:00
Sébastien Deleuze 642d47ef8d Upgrade to Kotlin 1.4.20
Closes gh-26132
2020-11-23 11:24:37 +01:00
Juergen Hoeller 42e492ef2a Polishing 2020-11-20 19:00:06 +01:00
Juergen Hoeller 4fb5d59c64 Declare resolvedCharset as transient (restoring serializability)
Closes gh-26127
2020-11-20 18:40:10 +01:00
Arjen Poutsma e6324bd578 Using WebAsyncManager in WebMvc.fn
This commit removed the existing code for dealing with asynchronous
results, and replaced it with code using the WebAsyncManager.

Closes gh-25931
2020-11-20 15:50:58 +01:00
Sam Brannen 45a9227047 Remove TODO from AbstractAspectJAdvice
Despite the code duplication, we will not delegate to
AopUtils.invokeJoinpointUsingReflection() from
AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs().

The rationale is that the exception message in
invokeAdviceMethodWithGivenArgs() provides additional context via the
pointcut expression, and we would lose that additional context if we
simply delegate to AopUtils.invokeJoinpointUsingReflection(). We could
introduce an overloaded variant of invokeJoinpointUsingReflection() that
accepts an additional argument to provide the additional context for the
exception message, but we don't think that would be the best solution
for this particular use case.

In light of that, we are simply removing the TODO.

Closes gh-26126
2020-11-20 12:39:28 +01:00
Rossen Stoyanchev f1af8a6ae9 Fix checkstyle violation 2020-11-19 21:41:39 +00:00
Rossen Stoyanchev c4d7e6ff46 Fix checkstyle violations
See gh-26117
2020-11-19 21:25:21 +00:00
Spindler, Justin 922d5d271a Add ResponseSpec#toEntityFlux overload with BodyExtractor
See gh-26114
2020-11-19 21:12:56 +00:00
Rossen Stoyanchev 05e3f271b6 Consistently apply PrincipalMethodArgumentResolver
Closes gh-26117
2020-11-19 21:11:37 +00:00
Juergen Hoeller a7cce64e5d Add assertion check to FieldFilter.and(FieldFilter) method as well
See gh-26121
2020-11-19 21:05:24 +01:00
Juergen Hoeller a0544e78ea Replace early SpringProperties logger usage with System.err
Closes gh-26120
2020-11-19 18:47:31 +01:00
Сергей Цыпанов c92dccea1b Simplify XMLEventStreamWriter.writeEndElement() 2020-11-19 18:32:05 +01:00
Сергей Цыпанов 9ec96f6141 Fail MethodFilter.and() immediately when null is passed 2020-11-19 18:31:39 +01:00
Juergen Hoeller 135682e073 Upgrade to Hibernate ORM 5.4.24 2020-11-19 16:02:23 +01:00
Juergen Hoeller 6a0377b1a2 Upgrade to Tomcat 9.0.40 and Apache HttpClient 4.5.13 2020-11-19 15:09:30 +01:00
Rossen Stoyanchev 8c3a05bbcf Allow "*" for Access-Control-Expose-Headers
Closes gh-26113
2020-11-19 09:20:55 +00:00
Rossen Stoyanchev 684e695b08 Expose allowedOriginPatterns in SocketJS XML config
Closes gh-26108
2020-11-18 21:20:38 +00:00
Rossen Stoyanchev 8130bf505f Apply allowedOriginPatterns in SockJsService
See gh-26108
2020-11-18 20:57:49 +00:00
Rossen Stoyanchev 9beca06404 Polishing contribution
See gh-26108
2020-11-18 20:25:39 +00:00
Benjamin Faal ae75db2657 Add allowedOriginPatterns to SockJS config
See gh-26108
2020-11-18 19:41:32 +00:00
Juergen Hoeller 4cc831238c Revise Servlet 4 HttpServletMapping check
Closes gh-26112
2020-11-18 15:30:20 +01:00
Marten Deinum 41835ba5a4 Re-use the isVariableName method
Prior to this change the checks for isVariableName were duplicated
in 2 different locations. The logic has been moved to AspectJProxyUtils
to allow for re-use in those places and so that it benefits from any
optimizations that are done.
2020-11-17 15:58:53 +01:00
Brian Clozel de0b5bc5a1 Polish Kotlin code snippet in reference docs 2020-11-17 15:21:10 +01:00
Juergen Hoeller b29723623b Encode hash symbol in jar file path (for compatibility with JDK 11+)
Closes gh-26104
2020-11-17 14:45:45 +01:00
Juergen Hoeller 2ee231dee2 Document that @Transactional does not propagate to new threads
Closes gh-25439
2020-11-17 14:43:17 +01:00
Juergen Hoeller b56dbd2aa8 Explicitly mention Jakarta Mail 1.6 next to JavaMail
See gh-25855
2020-11-17 14:42:41 +01:00
Marten Deinum c9b27af64f Reduce overhead of char[] creation
There are more locations which could benefit from not using a
toCharArray on a String, but rather use the charAt method from
the String itself. This to prevent an additional copy of the
char[] being created.
2020-11-17 11:57:09 +01:00
thanus 9139cb85bd Update javax.mail reference to jakarta.mail 2020-11-17 11:49:01 +01:00
Stephane Nicoll 367fbbb981 Merge pull request #26100 from izeye
* pr/26100:
  Avoid char array creation in AbstractAspectJAdvice.isVariableName()

Closes gh-26100
2020-11-17 07:55:38 +01:00
izeye 1b1ba47912 Avoid char array creation in AbstractAspectJAdvice.isVariableName()
See gh-26100
2020-11-17 07:54:06 +01:00
Juergen Hoeller 041bff4e56 Upgrade to Log4J 2.14, Reactor 2020.0.1, Netty 4.1.54, Protobuf 3.14, XStream 1.4.14, OpenPDF 1.3.23, AssertJ 3.18.1, MockK 1.10.2, HtmlUnit 2.45, Checkstyle 8.37 2020-11-16 20:36:29 +01:00
Juergen Hoeller bc5a10c70d Document that Hibernate Search 5.11.6 is required for Spring JPA compatibility
Closes gh-26090
2020-11-16 19:51:50 +01:00
Brian Clozel 3d31750acc Don't swallow logs during promotion job in CI
Prior to this commit, the promote task in the CI release pipeline would
write the "concourse-release-scripts" CONSOLE logs to /dev/null; this
commit ensures that we can read those while promoting artifacts.
2020-11-16 18:20:19 +01:00
Rossen Stoyanchev c22a483c3d Update section on type conversion for web method arguments
Closes gh-26088
2020-11-16 17:01:57 +00:00
Juergen Hoeller 238354a081 Polishing 2020-11-16 17:42:22 +01:00
Juergen Hoeller 51b1306d70 Move coroutines invocation decision to invokeWithinTransaction
See gh-26092
2020-11-16 17:41:09 +01:00
Juergen Hoeller 7206a23d33 Consistent attribute value spelling for PATH_ATTRIBUTE
See gh-24945
2020-11-16 17:40:39 +01:00
Сергей Цыпанов 83996b12cc Simplify AstUtils.getPropertyAccessorsToTry() 2020-11-16 16:11:31 +01:00
Sébastien Deleuze 0fd6c100a6 Polishing
See gh-24103
2020-11-16 14:33:21 +01:00
Sam Brannen 0819a9fcc9 Discover @DynamicPropertySource methods on enclosing test classes
gh-19930 introduced support for finding class-level test configuration
annotations on enclosing classes when using JUnit Jupiter @Nested test
classes, but support for @DynamicPropertySource methods got overlooked
since they are method-level annotations.

This commit addresses this shortcoming by introducing full support for
@NestedTestConfiguration semantics for @DynamicPropertySource methods
on enclosing classes.

Closes gh-26091
2020-11-15 20:59:15 +01:00
Juergen Hoeller 0b580d194d Early log entry for async EntityManagerFactory initialization failure
Closes gh-26093
2020-11-13 17:52:51 +01:00
Juergen Hoeller 500b54b86f Expose plain Hibernate Session(Factory) interface by default again
See gh-26090
2020-11-13 17:50:51 +01:00
Juergen Hoeller 942400ae47 Expose Hibernate Session(Factory)Implementor interface by default
Closes gh-26090
2020-11-13 11:35:34 +01:00
Rossen Stoyanchev b92d249f45 AntPathMatcher allows newline in URI template variables
Closes gh-23252
2020-11-12 22:00:47 +00:00
Rossen Stoyanchev 204a7fe91f UrlPathHelper.removeJsessionid correctly appends remainder
Closes gh-26079
2020-11-12 21:28:18 +00:00
Rossen Stoyanchev 42d3bc47c9 toEntityFlux methods apply error status handling
Closes gh-26069
2020-11-12 18:43:36 +00:00
Rossen Stoyanchev 94fcb37d30 Re-order methods in DefaultResponseSpec
See gh-26069
2020-11-12 18:43:36 +00:00
Rossen Stoyanchev ba9325446c Optimize WebClientUtils
Use constant Predicate for exception wrapping.
Use ResponseEntity constructor instead of builder.

See gh-26069
2020-11-12 18:43:36 +00:00
Arjen Poutsma 5338d8b5e9 Add cron expression documentation
This commit adds a section about the cron expression format supported by
 Spring.

Closes gh-26067
2020-11-12 15:57:14 +01:00
Sam Brannen 1b0be5862d Polishing 2020-11-12 15:32:41 +01:00
Sam Brannen 48af36c6fa Ensure test() conditions in JUnit TestKit match method names
Prior to this commit, the test("test") conditions used in
AutowiredConfigurationErrorsIntegrationTests inadvertently asserted
that the invoked test methods reside in an org.springframework.test
subpackage, which is always the case for any test method in the
`spring-test` module. In other words, "test" is always a substring of
"org.springframework.test...", which is not a meaningful assertion.

This commit ensures that the JUnit Platform Test Kit is asserting the
actual names of test methods.
2020-11-12 15:11:54 +01:00
Sam Brannen bd4e915abf Assert same instance returned for cached merged BeanDefinition 2020-11-12 14:57:05 +01:00
Sam Brannen c419ea7ba7 Use MethodFilter.and() in ReflectiveAspectJAdvisorFactory 2020-11-12 14:35:28 +01:00
Juergen Hoeller 66292cd7a1 Individually apply the SQL type from each SqlParameterSource argument
Closes gh-26071
2020-11-12 14:05:31 +01:00
fengyuanwei 6eec1acdac Make tests meaningful in DefaultListableBeanFactoryTests 2020-11-12 13:50:30 +01:00
izeye bc32d513d9 Polish Javadoc for InjectionMetadata.forElements() 2020-11-12 13:50:00 +01:00
Sam Brannen 3851b291da Use MethodFilter.and() in TransactionalTestExecutionListener 2020-11-11 11:11:34 +01:00
Rossen Stoyanchev 2b1f229998 LimitedDataBufferList adds or raises error
Closes gh-26060
2020-11-10 19:59:36 +00:00
Sam Brannen fc5e3c335f Introduce and() methods in MethodFilter & FieldFilter for composition
This commit introduces `and()` default methods in the MethodFilter and
FieldFilter functional interfaces in ReflectionUtils in order to simplify
uses cases that need to compose filter logic.

Closes gh-26063
2020-11-10 18:00:24 +01:00
Сергей Цыпанов daf9a82e8c Simplify AbstractAspectJAdvice.isVariableName() 2020-11-10 17:30:00 +01:00
Sam Brannen b019f30a15 Validate that test & lifecycle methods are not @Autowired
Prior to this commit, a developer may have accidentally annotated a
JUnit Jupiter test method or lifecycle method with @Autowired, and that
would have potentially resulted in an exception that was hard to
understand. This is because the Spring container considers any
@Autowired method to be a "configuration method" when autowiring the
test class instance. Consequently, such an @Autowired method would be
invoked twice: once by Spring while attempting to autowire the test
instance and another time by JUnit Jupiter when invoking the test or
lifecycle method. The autowiring invocation of the method often leads
to an exception, either because Spring cannot satisfy a dependency (such
as JUnit Jupiter's TestInfo) or because the body of the method fails due
to test setup that has not yet been invoked.

This commit introduces validation for @Autowired test and lifecycle
methods in the SpringExtension that will throw an IllegalStateException
if any @Autowired method in a test class is also annotated with any of
the following JUnit Jupiter annotations.

- @Test
- @TestFactory
- @TestTemplate
- @RepeatedTest
- @ParameterizedTest
- @BeforeAll
- @AfterAll
- @BeforeEach
- @AfterEach

Closes gh-25966
2020-11-10 15:28:34 +01:00
Spring Buildmaster d5b5e894c9 Next development version (v5.3.2-SNAPSHOT) 2020-11-10 09:02:35 +00:00
669 changed files with 19508 additions and 6468 deletions
+2 -2
View File
@@ -1,5 +1,5 @@
<!--
!!! For Security Vulnerabilities, please go to https://pivotal.io/security !!!
!!! For Security Vulnerabilities, please go to https://spring.io/security-policy !!!
-->
**Affects:** \<Spring Framework version>
@@ -14,4 +14,4 @@ Thanks for taking the time to create an issue. Please read the following:
Issue or Pull Request? Create only one, not both. GitHub treats them as the same.
If unsure, start with an issue, and if you submit a pull request later, the
issue will be closed as superseded.
-->
-->
+3
View File
@@ -41,3 +41,6 @@ out
test-output
atlassian-ide-plugin.xml
.gradletasknamecache
# VS Code
.vscode/
+3 -6
View File
@@ -125,10 +125,7 @@ The reference documentation is in the [src/docs/asciidoc](src/docs/asciidoc) dir
edit source files, and submit directly from GitHub.
When making changes locally, execute `./gradlew asciidoctor` and then browse the result under
`build/asciidoc/html5/index.html`.
`build/docs/ref-docs/html5/index.html`.
Asciidoctor also supports live editing. For more details read
[Editing AsciiDoc with Live Preview](https://asciidoctor.org/docs/editing-asciidoc-with-live-preview/).
Note that if you choose the
[System Monitor](https://asciidoctor.org/docs/editing-asciidoc-with-live-preview/#using-a-system-monitor)
option, you can find a Guardfile under `src/docs/asciidoc`.
Asciidoctor also supports live editing. For more details see
[AsciiDoc Tooling](https://docs.asciidoctor.org/asciidoctor/latest/tooling/).
+5 -1
View File
@@ -1,4 +1,4 @@
# <img src="src/docs/spring-framework.png" width="80" height="80"> Spring Framework [![Build Status](https://ci.spring.io/api/v1/teams/spring-framework/pipelines/spring-framework-5.3.x/jobs/build/badge)](https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-5.3.x?groups=Build")
# <img src="src/docs/spring-framework.png" width="80" height="80"> Spring Framework [![Build Status](https://ci.spring.io/api/v1/teams/spring-framework/pipelines/spring-framework-5.3.x/jobs/build/badge)](https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-5.3.x?groups=Build") [![Revved up by Gradle Enterprise](https://img.shields.io/badge/Revved%20up%20by-Gradle%20Enterprise-06A0CE?logo=Gradle&labelColor=02303A)](https://ge.spring.io/scans?search.rootProjectNames=spring)
This is the home of the Spring Framework: the foundation for all [Spring projects](https://spring.io/projects). Collectively the Spring Framework and the family of Spring projects are often referred to simply as "Spring".
@@ -25,6 +25,10 @@ See the [Micro-Benchmarks](https://github.com/spring-projects/spring-framework/w
See the [Build from Source](https://github.com/spring-projects/spring-framework/wiki/Build-from-Source) Wiki page and the [CONTRIBUTING.md](CONTRIBUTING.md) file.
## Continuous Integration Builds
Information regarding CI builds can be found in the [Spring Framework Concourse pipeline](ci/README.adoc) documentation.
## Stay in Touch
Follow [@SpringCentral](https://twitter.com/springcentral), [@SpringFramework](https://twitter.com/springframework), and its [team members](https://twitter.com/springframework/lists/team/members) on Twitter. In-depth articles can be found at [The Spring Blog](https://spring.io/blog/), and releases are announced via our [news feed](https://spring.io/blog/category/news).
+50 -51
View File
@@ -1,15 +1,16 @@
plugins {
id 'io.spring.dependency-management' version '1.0.9.RELEASE' apply false
id 'io.spring.nohttp' version '0.0.5.RELEASE'
id 'org.jetbrains.kotlin.jvm' version '1.4.10' apply false
id 'org.jetbrains.kotlin.jvm' version '1.4.32' apply false
id 'org.jetbrains.dokka' version '0.10.1' apply false
id 'org.asciidoctor.jvm.convert' version '3.1.0'
id 'org.asciidoctor.jvm.pdf' version '3.1.0'
id 'de.undercouch.download' version '4.1.1'
id "io.freefair.aspectj" version '5.1.1' apply false
id "com.github.ben-manes.versions" version '0.28.0'
id "me.champeau.gradle.jmh" version "0.5.0" apply false
id "org.jetbrains.kotlin.plugin.serialization" version "1.4.10" apply false
id "com.github.johnrengelman.shadow" version "6.1.0" apply false
id "me.champeau.gradle.jmh" version "0.5.2" apply false
id "org.jetbrains.kotlin.plugin.serialization" version "1.4.32" apply false
}
ext {
@@ -25,18 +26,18 @@ configure(allprojects) { project ->
dependencyManagement {
imports {
mavenBom "com.fasterxml.jackson:jackson-bom:2.11.3"
mavenBom "io.netty:netty-bom:4.1.53.Final"
mavenBom "io.projectreactor:reactor-bom:2020.0.0"
mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR8"
mavenBom "com.fasterxml.jackson:jackson-bom:2.12.2"
mavenBom "io.netty:netty-bom:4.1.63.Final"
mavenBom "io.projectreactor:reactor-bom:2020.0.6"
mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR9"
mavenBom "io.rsocket:rsocket-bom:1.1.0"
mavenBom "org.eclipse.jetty:jetty-bom:9.4.34.v20201102"
mavenBom "org.jetbrains.kotlin:kotlin-bom:1.4.10"
mavenBom "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.4.1"
mavenBom "org.junit:junit-bom:5.7.0"
mavenBom "org.eclipse.jetty:jetty-bom:9.4.39.v20210325"
mavenBom "org.jetbrains.kotlin:kotlin-bom:1.4.32"
mavenBom "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.4.3"
mavenBom "org.junit:junit-bom:5.7.1"
}
dependencies {
dependencySet(group: 'org.apache.logging.log4j', version: '2.13.3') {
dependencySet(group: 'org.apache.logging.log4j', version: '2.14.1') {
entry 'log4j-api'
entry 'log4j-core'
entry 'log4j-jul'
@@ -53,7 +54,7 @@ configure(allprojects) { project ->
entry 'aspectjtools'
entry 'aspectjweaver'
}
dependencySet(group: 'org.codehaus.groovy', version: '3.0.6') {
dependencySet(group: 'org.codehaus.groovy', version: '3.0.7') {
entry 'groovy'
entry 'groovy-jsr223'
entry 'groovy-templates' // requires findbugs for warning-free compilation
@@ -63,23 +64,23 @@ configure(allprojects) { project ->
dependency "io.reactivex:rxjava:1.3.8"
dependency "io.reactivex:rxjava-reactive-streams:1.2.1"
dependency "io.reactivex.rxjava2:rxjava:2.2.19"
dependency "io.reactivex.rxjava3:rxjava:3.0.7"
dependency "io.reactivex.rxjava2:rxjava:2.2.21"
dependency "io.reactivex.rxjava3:rxjava:3.0.12"
dependency "io.projectreactor.tools:blockhound:1.0.4.RELEASE"
dependency "com.caucho:hessian:4.0.63"
dependency "com.fasterxml:aalto-xml:1.2.2"
dependency("com.fasterxml.woodstox:woodstox-core:6.2.3") {
dependency("com.fasterxml.woodstox:woodstox-core:6.2.4") {
exclude group: "stax", name: "stax-api"
}
dependency "com.google.code.gson:gson:2.8.6"
dependency "com.google.protobuf:protobuf-java-util:3.13.0"
dependency "com.google.protobuf:protobuf-java-util:3.15.5"
dependency "com.googlecode.protobuf-java-format:protobuf-java-format:1.4"
dependency("com.thoughtworks.xstream:xstream:1.4.13") {
dependency("com.thoughtworks.xstream:xstream:1.4.15") {
exclude group: "xpp3", name: "xpp3_min"
exclude group: "xmlpull", name: "xmlpull"
}
dependency "org.apache.johnzon:johnzon-jsonb:1.2.8"
dependency "org.apache.johnzon:johnzon-jsonb:1.2.10"
dependency("org.codehaus.jettison:jettison:1.3.8") {
exclude group: "stax", name: "stax-api"
}
@@ -88,20 +89,20 @@ configure(allprojects) { project ->
entry 'jibx-run'
}
dependency "org.ogce:xpp3:1.1.6"
dependency "org.yaml:snakeyaml:1.27"
dependencySet(group: 'org.jetbrains.kotlinx', version: '1.0.0') {
dependency "org.yaml:snakeyaml:1.28"
dependencySet(group: 'org.jetbrains.kotlinx', version: '1.0.1') {
entry 'kotlinx-serialization-core'
entry 'kotlinx-serialization-json'
}
dependency "com.h2database:h2:1.4.200"
dependency "com.github.ben-manes.caffeine:caffeine:2.8.6"
dependency "com.github.librepdf:openpdf:1.3.22"
dependency "com.github.ben-manes.caffeine:caffeine:2.9.0"
dependency "com.github.librepdf:openpdf:1.3.25"
dependency "com.rometools:rome:1.15.0"
dependency "commons-io:commons-io:2.5"
dependency "io.vavr:vavr:0.10.3"
dependency "net.sf.jopt-simple:jopt-simple:5.0.4"
dependencySet(group: 'org.apache.activemq', version: '5.16.0') {
dependencySet(group: 'org.apache.activemq', version: '5.16.1') {
entry 'activemq-broker'
entry('activemq-kahadb-store') {
exclude group: "org.springframework", name: "spring-context"
@@ -116,30 +117,30 @@ configure(allprojects) { project ->
}
dependency "org.apache.poi:poi-ooxml:4.1.2"
dependency "org.apache-extras.beanshell:bsh:2.0b6"
dependency "org.freemarker:freemarker:2.3.30"
dependency "org.freemarker:freemarker:2.3.31"
dependency "org.hsqldb:hsqldb:2.5.1"
dependency "org.quartz-scheduler:quartz:2.3.2"
dependency "org.codehaus.fabric3.api:commonj:1.1.0"
dependency "net.sf.ehcache:ehcache:2.10.6"
dependency "org.ehcache:jcache:1.0.1"
dependency "org.ehcache:ehcache:3.4.0"
dependency "org.hibernate:hibernate-core:5.4.23.Final"
dependency "org.hibernate:hibernate-validator:6.1.6.Final"
dependency "org.hibernate:hibernate-core:5.4.30.Final"
dependency "org.hibernate:hibernate-validator:6.2.0.Final"
dependency "org.webjars:webjars-locator-core:0.46"
dependency "org.webjars:underscorejs:1.8.3"
dependencySet(group: 'org.apache.tomcat', version: '9.0.39') {
dependencySet(group: 'org.apache.tomcat', version: '9.0.45') {
entry 'tomcat-util'
entry('tomcat-websocket') {
exclude group: "org.apache.tomcat", name: "tomcat-websocket-api"
exclude group: "org.apache.tomcat", name: "tomcat-servlet-api"
}
}
dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.39') {
dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.45') {
entry 'tomcat-embed-core'
entry 'tomcat-embed-websocket'
}
dependencySet(group: 'io.undertow', version: '2.2.2.Final') {
dependencySet(group: 'io.undertow', version: '2.2.7.Final') {
entry 'undertow-core'
entry('undertow-websockets-jsr') {
exclude group: "org.jboss.spec.javax.websocket", name: "jboss-websocket-api_1.1_spec"
@@ -154,17 +155,17 @@ configure(allprojects) { project ->
entry 'okhttp'
entry 'mockwebserver'
}
dependency("org.apache.httpcomponents:httpclient:4.5.12") {
dependency("org.apache.httpcomponents:httpclient:4.5.13") {
exclude group: "commons-logging", name: "commons-logging"
}
dependency("org.apache.httpcomponents:httpasyncclient:4.1.4") {
exclude group: "commons-logging", name: "commons-logging"
}
dependency 'org.apache.httpcomponents.client5:httpclient5:5.0.3'
dependency 'org.apache.httpcomponents.core5:httpcore5-reactive:5.0.2'
dependency "org.eclipse.jetty:jetty-reactive-httpclient:1.1.4"
dependency 'org.apache.httpcomponents.core5:httpcore5-reactive:5.0.3'
dependency "org.eclipse.jetty:jetty-reactive-httpclient:1.1.6"
dependency "org.jruby:jruby:9.2.13.0"
dependency "org.jruby:jruby:9.2.16.0"
dependency "org.python:jython-standalone:2.7.1"
dependency "org.mozilla:rhino:1.7.11"
@@ -181,34 +182,34 @@ configure(allprojects) { project ->
exclude group: "dom4j", name: "dom4j"
}
dependency("junit:junit:4.13.1") {
dependency("junit:junit:4.13.2") {
exclude group: "org.hamcrest", name: "hamcrest-core"
}
dependency("de.bechte.junit:junit-hierarchicalcontextrunner:4.12.1") {
exclude group: "junit", name: "junit"
}
dependency "org.testng:testng:7.3.0"
dependency "org.testng:testng:7.4.0"
dependency "org.hamcrest:hamcrest:2.1"
dependency "org.awaitility:awaitility:3.1.6"
dependency "org.assertj:assertj-core:3.18.0"
dependencySet(group: 'org.xmlunit', version: '2.6.2') {
dependency "org.assertj:assertj-core:3.19.0"
dependencySet(group: 'org.xmlunit', version: '2.8.2') {
entry 'xmlunit-assertj'
entry('xmlunit-matchers') {
exclude group: "org.hamcrest", name: "hamcrest-core"
}
}
dependencySet(group: 'org.mockito', version: '3.6.0') {
dependencySet(group: 'org.mockito', version: '3.8.0') {
entry('mockito-core') {
exclude group: "org.hamcrest", name: "hamcrest-core"
}
entry 'mockito-junit-jupiter'
}
dependency "io.mockk:mockk:1.10.0"
dependency "io.mockk:mockk:1.10.2"
dependency("net.sourceforge.htmlunit:htmlunit:2.44.0") {
dependency("net.sourceforge.htmlunit:htmlunit:2.48.0") {
exclude group: "commons-logging", name: "commons-logging"
}
dependency("org.seleniumhq.selenium:htmlunit-driver:2.44.0") {
dependency("org.seleniumhq.selenium:htmlunit-driver:2.48.0") {
exclude group: "commons-logging", name: "commons-logging"
}
dependency("org.seleniumhq.selenium:selenium-java:3.141.59") {
@@ -216,7 +217,7 @@ configure(allprojects) { project ->
exclude group: "io.netty", name: "netty"
}
dependency "org.skyscreamer:jsonassert:1.5.0"
dependency "com.jayway.jsonpath:json-path:2.4.0"
dependency "com.jayway.jsonpath:json-path:2.5.0"
dependency "org.bouncycastle:bcpkix-jdk15on:1.66"
dependencySet(group: 'org.apache.tiles', version: '3.0.8') {
@@ -236,8 +237,8 @@ configure(allprojects) { project ->
dependency "com.ibm.websphere:uow:6.0.2.17"
dependency "com.jamonapi:jamon:2.82"
dependency "joda-time:joda-time:2.10.6"
dependency "org.eclipse.persistence:org.eclipse.persistence.jpa:2.7.7"
dependency "joda-time:joda-time:2.10.10"
dependency "org.eclipse.persistence:org.eclipse.persistence.jpa:2.7.8"
dependency "org.javamoney:moneta:1.3"
dependency "com.sun.activation:javax.activation:1.2.0"
@@ -308,14 +309,13 @@ configure([rootProject] + javaProjects) { project ->
apply plugin: "java-test-fixtures"
apply plugin: "checkstyle"
apply plugin: 'org.springframework.build.compile'
apply from: "${rootDir}/gradle/custom-java-home.gradle"
apply from: "${rootDir}/gradle/toolchains.gradle"
apply from: "${rootDir}/gradle/ide.gradle"
pluginManager.withPlugin("kotlin") {
apply plugin: "org.jetbrains.dokka"
compileKotlin {
kotlinOptions {
jvmTarget = "1.8"
languageVersion = "1.3"
apiVersion = "1.3"
freeCompilerArgs = ["-Xjsr305=strict"]
@@ -324,7 +324,6 @@ configure([rootProject] + javaProjects) { project ->
}
compileTestKotlin {
kotlinOptions {
jvmTarget = "1.8"
freeCompilerArgs = ["-Xjsr305=strict"]
}
}
@@ -339,7 +338,7 @@ configure([rootProject] + javaProjects) { project ->
}
checkstyle {
toolVersion = "8.36.2"
toolVersion = "8.41"
configDirectory.set(rootProject.file("src/checkstyle"))
}
@@ -380,8 +379,8 @@ configure([rootProject] + javaProjects) { project ->
"https://fasterxml.github.io/jackson-dataformat-xml/javadoc/2.10/",
"https://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/",
"https://projectreactor.io/docs/test/release/api/",
"https://junit.org/junit4/javadoc/4.13.1/",
"https://junit.org/junit5/docs/5.7.0/api/"
"https://junit.org/junit4/javadoc/4.13.2/",
"https://junit.org/junit5/docs/5.7.1/api/"
] as String[]
}
+2 -8
View File
@@ -8,16 +8,10 @@ They are declared in the `build.gradle` file in this folder.
### Compiler conventions
The `org.springframework.build.compile` plugin applies the Java compiler conventions to the build.
By default, the build compiles sources with Java `1.8` source and target compatibility.
You can test a different source compatibility version on the CLI with a project property like:
```
./gradlew test -PjavaSourceVersion=11
```
## Build Plugins
## Optional dependencies
### Optional dependencies
The `org.springframework.build.optional-dependencies` plugin creates a new `optional`
Gradle configuration - it adds the dependencies to the project's compile and runtime classpath
@@ -25,7 +19,7 @@ but doesn't affect the classpath of dependent projects.
This plugin does not provide a `provided` configuration, as the native `compileOnly` and `testCompileOnly`
configurations are preferred.
## API Diff
### API Diff
This plugin uses the [Gradle JApiCmp](https://github.com/melix/japicmp-gradle-plugin) plugin
to generate API Diff reports for each Spring Framework module. This plugin is applied once on the root
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,31 +20,21 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.gradle.api.JavaVersion;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.plugins.JavaLibraryPlugin;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.tasks.compile.JavaCompile;
/**
* {@link Plugin} that applies conventions for compiling Java sources in Spring Framework.
* <p>One can override the default Java source compatibility version
* with a dedicated property on the CLI: {@code "./gradlew test -PjavaSourceVersion=11"}.
*
* @author Brian Clozel
* @author Sam Brannen
*/
public class CompilerConventionsPlugin implements Plugin<Project> {
/**
* The project property that can be used to switch the Java source
* compatibility version for building source and test classes.
*/
public static final String JAVA_SOURCE_VERSION_PROPERTY = "javaSourceVersion";
public static final JavaVersion DEFAULT_COMPILER_VERSION = JavaVersion.VERSION_1_8;
private static final List<String> COMPILER_ARGS;
private static final List<String> TEST_COMPILER_ARGS;
@@ -69,7 +59,7 @@ public class CompilerConventionsPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
project.getPlugins().withType(JavaPlugin.class, javaPlugin -> applyJavaCompileConventions(project));
project.getPlugins().withType(JavaLibraryPlugin.class, javaPlugin -> applyJavaCompileConventions(project));
}
/**
@@ -79,15 +69,6 @@ public class CompilerConventionsPlugin implements Plugin<Project> {
*/
private void applyJavaCompileConventions(Project project) {
JavaPluginConvention java = project.getConvention().getPlugin(JavaPluginConvention.class);
if (project.hasProperty(JAVA_SOURCE_VERSION_PROPERTY)) {
JavaVersion javaSourceVersion = JavaVersion.toVersion(project.property(JAVA_SOURCE_VERSION_PROPERTY));
java.setSourceCompatibility(javaSourceVersion);
}
else {
java.setSourceCompatibility(DEFAULT_COMPILER_VERSION);
}
java.setTargetCompatibility(DEFAULT_COMPILER_VERSION);
project.getTasks().withType(JavaCompile.class)
.matching(compileTask -> compileTask.getName().equals(JavaPlugin.COMPILE_JAVA_TASK_NAME))
.forEach(compileTask -> {
+9 -3
View File
@@ -1,7 +1,8 @@
== Spring Framework Concourse pipeline
The Spring Framework is using https://concourse-ci.org/[Concourse] for its CI build and other automated tasks.
The Spring team has a dedicated Concourse instance available at https://ci.spring.io.
The Spring Framework uses https://concourse-ci.org/[Concourse] for its CI build and other automated tasks.
The Spring team has a dedicated Concourse instance available at https://ci.spring.io with a build pipeline
for https://ci.spring.io/teams/spring-framework/pipelines/spring-framework-5.3.x[Spring Framework 5.3.x].
=== Setting up your development environment
@@ -25,13 +26,17 @@ spring https://ci.spring.io spring-framework Wed, 25 Mar 20
----
=== Pipeline configuration and structure
The build pipelines are described in `pipeline.yml` file.
This file is listing Concourse resources, i.e. build inputs and outputs such as container images, artifact repositories, source repositories, notification services, etc.
It also describes jobs (a job is a sequence of inputs, tasks and outputs); jobs are organized by groups.
The `pipeline.yml` definition contains `((parameters))` which are loaded from the `parameters.yml` file or from our https://docs.cloudfoundry.org/credhub/[credhub instance].
You'll find in this folder the following resources:
* `pipeline.yml` the build pipeline
* `parameters.yml` the build parameters used for the pipeline
* `images/` holds the container images definitions used in this pipeline
@@ -41,6 +46,7 @@ You'll find in this folder the following resources:
=== Updating the build pipeline
Updating files on the repository is not enough to update the build pipeline, as changes need to be applied.
The pipeline can be deployed using the following command:
[source]
@@ -48,4 +54,4 @@ The pipeline can be deployed using the following command:
$ fly -t spring set-pipeline -p spring-framework-5.3.x -c ci/pipeline.yml -l ci/parameters.yml
----
NOTE: This assumes that you have credhub integration configured with the appropriate secrets.
NOTE: This assumes that you have credhub integration configured with the appropriate secrets.
+5 -4
View File
@@ -1,9 +1,10 @@
logging:
level:
io.spring.concourse: DEBUG
distribute:
optional-deployments:
- '.*\\.zip'
spring:
main:
banner-mode: off
banner-mode: off
sonatype:
exclude:
- 'build-info\.json'
- '.*\.zip'
+11
View File
@@ -0,0 +1,11 @@
FROM ubuntu:focal-20210119
ADD setup.sh /setup.sh
ADD get-jdk-url.sh /get-jdk-url.sh
RUN ./setup.sh java8
ENV JAVA_HOME /opt/openjdk/java8
ENV JDK11 /opt/openjdk/java11
ENV JDK15 /opt/openjdk/java15
ENV PATH $JAVA_HOME/bin:$PATH
+3 -6
View File
@@ -3,16 +3,13 @@ set -e
case "$1" in
java8)
echo "https://github.com/AdoptOpenJDK/openjdk8-binaries/releases/download/jdk8u265-b01/OpenJDK8U-jdk_x64_linux_hotspot_8u265b01.tar.gz"
echo "https://github.com/AdoptOpenJDK/openjdk8-binaries/releases/download/jdk8u282-b08/OpenJDK8U-jdk_x64_linux_hotspot_8u282b08.tar.gz"
;;
java11)
echo "https://github.com/AdoptOpenJDK/openjdk11-binaries/releases/download/jdk-11.0.8%2B10/OpenJDK11U-jdk_x64_linux_hotspot_11.0.8_10.tar.gz"
;;
java14)
echo "https://github.com/AdoptOpenJDK/openjdk14-binaries/releases/download/jdk-14.0.2%2B12/OpenJDK14U-jdk_x64_linux_hotspot_14.0.2_12.tar.gz"
echo "https://github.com/AdoptOpenJDK/openjdk11-binaries/releases/download/jdk-11.0.10%2B9/OpenJDK11U-jdk_x64_linux_hotspot_11.0.10_9.tar.gz"
;;
java15)
echo "https://github.com/AdoptOpenJDK/openjdk15-binaries/releases/download/jdk-15%2B36/OpenJDK15U-jdk_x64_linux_hotspot_15_36.tar.gz"
echo "https://github.com/AdoptOpenJDK/openjdk15-binaries/releases/download/jdk-15.0.2%2B7/OpenJDK15U-jdk_x64_linux_hotspot_15.0.2_7.tar.gz"
;;
*)
echo $"Unknown java version"
+18 -8
View File
@@ -5,24 +5,34 @@ set -ex
# UTILS
###########################################################
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install --no-install-recommends -y ca-certificates net-tools libxml2-utils git curl libudev1 libxml2-utils iptables iproute2 jq fontconfig
apt-get install --no-install-recommends -y tzdata ca-certificates net-tools libxml2-utils git curl libudev1 libxml2-utils iptables iproute2 jq fontconfig
ln -fs /usr/share/zoneinfo/UTC /etc/localtime
dpkg-reconfigure --frontend noninteractive tzdata
rm -rf /var/lib/apt/lists/*
curl https://raw.githubusercontent.com/spring-io/concourse-java-scripts/v0.0.3/concourse-java.sh > /opt/concourse-java.sh
curl https://raw.githubusercontent.com/spring-io/concourse-java-scripts/v0.0.4/concourse-java.sh > /opt/concourse-java.sh
curl --output /opt/concourse-release-scripts.jar https://repo.spring.io/release/io/spring/concourse/releasescripts/concourse-release-scripts/0.2.1/concourse-release-scripts-0.2.1.jar
curl --output /opt/concourse-release-scripts.jar https://repo.spring.io/release/io/spring/concourse/releasescripts/concourse-release-scripts/0.3.2/concourse-release-scripts-0.3.2.jar
###########################################################
# JAVA
###########################################################
JDK_URL=$( ./get-jdk-url.sh $1 )
mkdir -p /opt/openjdk
cd /opt/openjdk
curl -L ${JDK_URL} | tar zx --strip-components=1
test -f /opt/openjdk/bin/java
test -f /opt/openjdk/bin/javac
pushd /opt/openjdk > /dev/null
for jdk in java8 java11 java15
do
JDK_URL=$( /get-jdk-url.sh $jdk )
mkdir $jdk
pushd $jdk > /dev/null
curl -L ${JDK_URL} | tar zx --strip-components=1
test -f bin/java
test -f bin/javac
popd > /dev/null
done
popd
###########################################################
# GRADLE ENTERPRISE
@@ -1,8 +0,0 @@
FROM ubuntu:bionic-20200713
ADD setup.sh /setup.sh
ADD get-jdk-url.sh /get-jdk-url.sh
RUN ./setup.sh java8
ENV JAVA_HOME /opt/openjdk
ENV PATH $JAVA_HOME/bin:$PATH
@@ -1,8 +0,0 @@
FROM ubuntu:bionic-20200713
ADD setup.sh /setup.sh
ADD get-jdk-url.sh /get-jdk-url.sh
RUN ./setup.sh java11
ENV JAVA_HOME /opt/openjdk
ENV PATH $JAVA_HOME/bin:$PATH
@@ -1,8 +0,0 @@
FROM ubuntu:bionic-20200713
ADD setup.sh /setup.sh
ADD get-jdk-url.sh /get-jdk-url.sh
RUN ./setup.sh java14
ENV JAVA_HOME /opt/openjdk
ENV PATH $JAVA_HOME/bin:$PATH
@@ -1,8 +0,0 @@
FROM ubuntu:bionic-20200713
ADD setup.sh /setup.sh
ADD get-jdk-url.sh /get-jdk-url.sh
RUN ./setup.sh java15
ENV JAVA_HOME /opt/openjdk
ENV PATH $JAVA_HOME/bin:$PATH
+2 -3
View File
@@ -6,9 +6,8 @@ github-repo-name: "spring-projects/spring-framework"
docker-hub-organization: "springci"
artifactory-server: "https://repo.spring.io"
branch: "master"
milestone: "5.3.x"
build-name: "spring-framework"
pipeline-name: "spring-framework"
concourse-url: "https://ci.spring.io"
bintray-subject: "spring"
bintray-repo: "jars"
task-timeout: 1h00m
task-timeout: 1h00m
+71 -144
View File
@@ -1,21 +1,32 @@
anchors:
artifactory-task-params: &artifactory-task-params
ARTIFACTORY_SERVER: ((artifactory-server))
ARTIFACTORY_USERNAME: ((artifactory-username))
ARTIFACTORY_PASSWORD: ((artifactory-password))
bintray-task-params: &bintray-task-params
BINTRAY_SUBJECT: ((bintray-subject))
BINTRAY_REPO: ((bintray-repo))
BINTRAY_USERNAME: ((bintray-username))
BINTRAY_API_KEY: ((bintray-api-key))
docker-resource-source: &docker-resource-source
username: ((docker-hub-username))
password: ((docker-hub-password))
tag: 5.3.x
git-repo-resource-source: &git-repo-resource-source
uri: ((github-repo))
username: ((github-username))
password: ((github-password))
branch: ((branch))
gradle-enterprise-task-params: &gradle-enterprise-task-params
GRADLE_ENTERPRISE_ACCESS_KEY: ((gradle_enterprise_secret_access_key))
GRADLE_ENTERPRISE_CACHE_USERNAME: ((gradle_enterprise_cache_user.username))
GRADLE_ENTERPRISE_CACHE_PASSWORD: ((gradle_enterprise_cache_user.password))
sonatype-task-params: &sonatype-task-params
SONATYPE_USERNAME: ((sonatype-username))
SONATYPE_PASSWORD: ((sonatype-password))
SONATYPE_URL: ((sonatype-url))
SONATYPE_STAGING_PROFILE_ID: ((sonatype-staging-profile-id))
artifactory-task-params: &artifactory-task-params
ARTIFACTORY_SERVER: ((artifactory-server))
ARTIFACTORY_USERNAME: ((artifactory-username))
ARTIFACTORY_PASSWORD: ((artifactory-password))
build-project-task-params: &build-project-task-params
privileged: true
timeout: ((task-timeout))
params:
BRANCH: ((branch))
<<: *gradle-enterprise-task-params
docker-resource-source: &docker-resource-source
username: ((docker-hub-username))
password: ((docker-hub-password))
tag: ((milestone))
slack-fail-params: &slack-fail-params
text: >
:concourse-failed: <https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}|${BUILD_PIPELINE_NAME} ${BUILD_JOB_NAME} failed!>
@@ -24,9 +35,6 @@ anchors:
silent: true
icon_emoji: ":concourse:"
username: concourse-ci
sonatype-task-params: &sonatype-task-params
SONATYPE_USER_TOKEN: ((sonatype-user-token))
SONATYPE_PASSWORD_TOKEN: ((sonatype-user-token-password))
changelog-task-params: &changelog-task-params
name: generated-changelog/tag
tag: generated-changelog/tag
@@ -37,30 +45,26 @@ anchors:
resource_types:
- name: artifactory-resource
type: docker-image
type: registry-image
source:
repository: springio/artifactory-resource
tag: 0.0.12
tag: 0.0.13
- name: github-status-resource
type: docker-image
type: registry-image
source:
repository: dpb587/github-status-resource
tag: master
- name: slack-notification
type: docker-image
type: registry-image
source:
repository: cfcommunity/slack-notification-resource
tag: latest
resources:
- name: git-repo
type: git
icon: github
source:
uri: ((github-repo))
username: ((github-username))
password: ((github-password))
branch: ((branch))
<<: *git-repo-resource-source
- name: every-morning
type: time
icon: alarm
@@ -75,30 +79,12 @@ resources:
uri: ((github-repo))
branch: ((branch))
paths: ["ci/images/*"]
- name: spring-framework-ci-image
- name: ci-image
type: docker-image
icon: docker
source:
<<: *docker-resource-source
repository: ((docker-hub-organization))/spring-framework-ci-image
- name: spring-framework-jdk11-ci-image
type: docker-image
icon: docker
source:
<<: *docker-resource-source
repository: ((docker-hub-organization))/spring-framework-jdk11-ci-image
- name: spring-framework-jdk14-ci-image
type: docker-image
icon: docker
source:
<<: *docker-resource-source
repository: ((docker-hub-organization))/spring-framework-jdk14-ci-image
- name: spring-framework-jdk15-ci-image
type: docker-image
icon: docker
source:
<<: *docker-resource-source
repository: ((docker-hub-organization))/spring-framework-jdk15-ci-image
repository: ((docker-hub-organization))/spring-framework-ci
- name: artifactory-repo
type: artifactory-resource
icon: package-variant
@@ -123,14 +109,6 @@ resources:
access_token: ((github-ci-status-token))
branch: ((branch))
context: jdk11-build
- name: repo-status-jdk14-build
type: github-status-resource
icon: eye-check-outline
source:
repository: ((github-repo-name))
access_token: ((github-ci-status-token))
branch: ((branch))
context: jdk14-build
- name: repo-status-jdk15-build
type: github-status-resource
icon: eye-check-outline
@@ -161,47 +139,30 @@ resources:
repository: spring-framework
access_token: ((github-ci-release-token))
pre_release: false
jobs:
- name: build-spring-framework-ci-images
- name: build-ci-images
plan:
- get: ci-images-git-repo
trigger: true
- in_parallel:
- put: spring-framework-ci-image
- put: ci-image
params:
build: ci-images-git-repo/ci/images
dockerfile: ci-images-git-repo/ci/images/spring-framework-ci-image/Dockerfile
- put: spring-framework-jdk11-ci-image
params:
build: ci-images-git-repo/ci/images
dockerfile: ci-images-git-repo/ci/images/spring-framework-jdk11-ci-image/Dockerfile
- put: spring-framework-jdk14-ci-image
params:
build: ci-images-git-repo/ci/images
dockerfile: ci-images-git-repo/ci/images/spring-framework-jdk14-ci-image/Dockerfile
- put: spring-framework-jdk15-ci-image
params:
build: ci-images-git-repo/ci/images
dockerfile: ci-images-git-repo/ci/images/spring-framework-jdk15-ci-image/Dockerfile
dockerfile: ci-images-git-repo/ci/images/ci-image/Dockerfile
- name: build
serial: true
public: true
plan:
- get: spring-framework-ci-image
- get: ci-image
- get: git-repo
trigger: true
- put: repo-status-build
params: { state: "pending", commit: "git-repo" }
- do:
- task: build-project
privileged: true
timeout: ((task-timeout))
image: spring-framework-ci-image
image: ci-image
file: git-repo/ci/tasks/build-project.yml
params:
BRANCH: ((branch))
<<: *gradle-enterprise-task-params
<<: *build-project-task-params
on_failure:
do:
- put: repo-status-build
@@ -213,6 +174,8 @@ jobs:
params: { state: "success", commit: "git-repo" }
- put: artifactory-repo
params: &artifactory-params
signing_key: ((signing-key))
signing_passphrase: ((signing-passphrase))
repo: libs-snapshot-local
folder: distribution-repository
build_uri: "https://ci.spring.io/teams/${BUILD_TEAM_NAME}/pipelines/${BUILD_PIPELINE_NAME}/jobs/${BUILD_JOB_NAME}/builds/${BUILD_NAME}"
@@ -244,7 +207,7 @@ jobs:
serial: true
public: true
plan:
- get: spring-framework-jdk11-ci-image
- get: ci-image
- get: git-repo
- get: every-morning
trigger: true
@@ -252,13 +215,12 @@ jobs:
params: { state: "pending", commit: "git-repo" }
- do:
- task: check-project
privileged: true
timeout: ((task-timeout))
image: spring-framework-jdk11-ci-image
image: ci-image
file: git-repo/ci/tasks/check-project.yml
params:
BRANCH: ((branch))
<<: *gradle-enterprise-task-params
MAIN_TOOLCHAIN: 8
TEST_TOOLCHAIN: 11
<<: *build-project-task-params
on_failure:
do:
- put: repo-status-jdk11-build
@@ -268,53 +230,24 @@ jobs:
<<: *slack-fail-params
- put: repo-status-jdk11-build
params: { state: "success", commit: "git-repo" }
- name: jdk14-build
serial: true
public: true
plan:
- get: spring-framework-jdk14-ci-image
- get: git-repo
- get: every-morning
trigger: true
- put: repo-status-jdk14-build
params: { state: "pending", commit: "git-repo" }
- do:
- task: check-project
privileged: true
timeout: ((task-timeout))
image: spring-framework-jdk14-ci-image
file: git-repo/ci/tasks/check-project.yml
params:
BRANCH: ((branch))
<<: *gradle-enterprise-task-params
on_failure:
do:
- put: repo-status-jdk14-build
params: { state: "failure", commit: "git-repo" }
- put: slack-alert
params:
<<: *slack-fail-params
- put: repo-status-jdk14-build
params: { state: "success", commit: "git-repo" }
- name: jdk15-build
serial: true
public: true
plan:
- get: spring-framework-jdk15-ci-image
- get: ci-image
- get: git-repo
- get: every-morning
trigger: true
- put: repo-status-jdk15-build
params: { state: "pending", commit: "git-repo" }
- do:
- task: check-project
privileged: true
timeout: ((task-timeout))
image: spring-framework-jdk15-ci-image
file: git-repo/ci/tasks/check-project.yml
params:
BRANCH: ((branch))
<<: *gradle-enterprise-task-params
- task: check-project
image: ci-image
file: git-repo/ci/tasks/check-project.yml
params:
MAIN_TOOLCHAIN: 8
TEST_TOOLCHAIN: 15
<<: *build-project-task-params
on_failure:
do:
- put: repo-status-jdk15-build
@@ -327,11 +260,11 @@ jobs:
- name: stage-milestone
serial: true
plan:
- get: spring-framework-ci-image
- get: ci-image
- get: git-repo
trigger: false
- task: stage
image: spring-framework-ci-image
image: ci-image
file: git-repo/ci/tasks/stage-version.yml
params:
RELEASE_TYPE: M
@@ -346,7 +279,7 @@ jobs:
- name: promote-milestone
serial: true
plan:
- get: spring-framework-ci-image
- get: ci-image
- get: git-repo
trigger: false
- get: artifactory-repo
@@ -356,7 +289,7 @@ jobs:
download_artifacts: false
save_build_info: true
- task: promote
image: spring-framework-ci-image
image: ci-image
file: git-repo/ci/tasks/promote-version.yml
params:
RELEASE_TYPE: M
@@ -372,11 +305,11 @@ jobs:
- name: stage-rc
serial: true
plan:
- get: spring-framework-ci-image
- get: ci-image
- get: git-repo
trigger: false
- task: stage
image: spring-framework-ci-image
image: ci-image
file: git-repo/ci/tasks/stage-version.yml
params:
RELEASE_TYPE: RC
@@ -391,7 +324,7 @@ jobs:
- name: promote-rc
serial: true
plan:
- get: spring-framework-ci-image
- get: ci-image
- get: git-repo
trigger: false
- get: artifactory-repo
@@ -401,7 +334,7 @@ jobs:
download_artifacts: false
save_build_info: true
- task: promote
image: spring-framework-ci-image
image: ci-image
file: git-repo/ci/tasks/promote-version.yml
params:
RELEASE_TYPE: RC
@@ -417,11 +350,11 @@ jobs:
- name: stage-release
serial: true
plan:
- get: spring-framework-ci-image
- get: ci-image
- get: git-repo
trigger: false
- task: stage
image: spring-framework-ci-image
image: ci-image
file: git-repo/ci/tasks/stage-version.yml
params:
RELEASE_TYPE: RELEASE
@@ -436,26 +369,26 @@ jobs:
- name: promote-release
serial: true
plan:
- get: spring-framework-ci-image
- get: ci-image
- get: git-repo
trigger: false
- get: artifactory-repo
trigger: false
passed: [stage-release]
params:
download_artifacts: false
download_artifacts: true
save_build_info: true
- task: promote
image: spring-framework-ci-image
image: ci-image
file: git-repo/ci/tasks/promote-version.yml
params:
RELEASE_TYPE: RELEASE
<<: *artifactory-task-params
<<: *bintray-task-params
- name: sync-to-maven-central
<<: *sonatype-task-params
- name: create-github-release
serial: true
plan:
- get: spring-framework-ci-image
- get: ci-image
- get: git-repo
- get: artifactory-repo
trigger: true
@@ -463,12 +396,6 @@ jobs:
params:
download_artifacts: false
save_build_info: true
- task: sync-to-maven-central
image: spring-framework-ci-image
file: git-repo/ci/tasks/sync-to-maven-central.yml
params:
<<: *bintray-task-params
<<: *sonatype-task-params
- task: generate-changelog
file: git-repo/ci/tasks/generate-changelog.yml
params:
@@ -480,8 +407,8 @@ jobs:
groups:
- name: "builds"
jobs: ["build", "jdk11-build", "jdk14-build", "jdk15-build"]
jobs: ["build", "jdk11-build", "jdk15-build"]
- name: "releases"
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone","promote-rc", "promote-release", "sync-to-maven-central"]
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release"]
- name: "ci-images"
jobs: ["build-spring-framework-ci-images"]
jobs: ["build-ci-images"]
+2 -1
View File
@@ -4,5 +4,6 @@ set -e
source $(dirname $0)/common.sh
pushd git-repo > /dev/null
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false --no-daemon --max-workers=4 check
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Dorg.gradle.java.installations.fromEnv=JDK11,JDK15 \
-PmainToolchain=$MAIN_TOOLCHAIN -PtestToolchain=$TEST_TOOLCHAIN --no-daemon --max-workers=4 check
popd > /dev/null
+1 -1
View File
@@ -2,7 +2,7 @@
set -e
CONFIG_DIR=git-repo/ci/config
version=$( cat version/version )
version=$( cat artifactory-repo/build-info.json | jq -r '.buildInfo.modules[0].id' | sed 's/.*:.*:\(.*\)/\1/' )
java -jar /github-changelog-generator.jar \
--spring.config.location=${CONFIG_DIR}/changelog-generator.yml \
+4 -2
View File
@@ -6,11 +6,13 @@ CONFIG_DIR=git-repo/ci/config
version=$( cat artifactory-repo/build-info.json | jq -r '.buildInfo.modules[0].id' | sed 's/.*:.*:\(.*\)/\1/' )
export BUILD_INFO_LOCATION=$(pwd)/artifactory-repo/build-info.json
java -jar /opt/concourse-release-scripts.jar promote $RELEASE_TYPE $BUILD_INFO_LOCATION > /dev/null || { exit 1; }
java -jar /opt/concourse-release-scripts.jar \
--spring.config.location=${CONFIG_DIR}/release-scripts.yml \
publishToCentral $RELEASE_TYPE $BUILD_INFO_LOCATION artifactory-repo || { exit 1; }
java -jar /opt/concourse-release-scripts.jar \
--spring.config.location=${CONFIG_DIR}/release-scripts.yml \
distribute $RELEASE_TYPE $BUILD_INFO_LOCATION > /dev/null || { exit 1; }
promote $RELEASE_TYPE $BUILD_INFO_LOCATION || { exit 1; }
echo "Promotion complete"
echo $version > version/version
+2
View File
@@ -10,6 +10,8 @@ caches:
params:
BRANCH:
CI: true
MAIN_TOOLCHAIN:
TEST_TOOLCHAIN:
GRADLE_ENTERPRISE_ACCESS_KEY:
GRADLE_ENTERPRISE_CACHE_USERNAME:
GRADLE_ENTERPRISE_CACHE_PASSWORD:
+3 -3
View File
@@ -1,13 +1,13 @@
---
platform: linux
image_resource:
type: docker-image
type: registry-image
source:
repository: springio/github-changelog-generator
tag: '0.0.4'
tag: '0.0.6'
inputs:
- name: git-repo
- name: version
- name: artifactory-repo
outputs:
- name: generated-changelog
params:
+4 -4
View File
@@ -10,9 +10,9 @@ params:
ARTIFACTORY_SERVER:
ARTIFACTORY_USERNAME:
ARTIFACTORY_PASSWORD:
BINTRAY_SUBJECT:
BINTRAY_REPO:
BINTRAY_USERNAME:
BINTRAY_API_KEY:
SONATYPE_USER:
SONATYPE_PASSWORD:
SONATYPE_URL:
SONATYPE_STAGING_PROFILE_ID:
run:
path: git-repo/ci/scripts/promote-version.sh
-16
View File
@@ -1,16 +0,0 @@
---
platform: linux
inputs:
- name: git-repo
- name: artifactory-repo
outputs:
- name: version
params:
BINTRAY_REPO:
BINTRAY_SUBJECT:
BINTRAY_USERNAME:
BINTRAY_API_KEY:
SONATYPE_USER_TOKEN:
SONATYPE_PASSWORD_TOKEN:
run:
path: git-repo/ci/scripts/sync-to-maven-central.sh
+1 -1
View File
@@ -1,4 +1,4 @@
version=5.3.1-SNAPSHOT
version=5.3.6
org.gradle.jvmargs=-Xmx1536M
org.gradle.caching=true
org.gradle.parallel=true
-80
View File
@@ -1,80 +0,0 @@
// -----------------------------------------------------------------------------
//
// This script adds support for the following two JVM system properties
// that control the build for alternative JDKs (i.e., a JDK other than
// the one used to launch the Gradle process).
//
// - customJavaHome: absolute path to the alternate JDK installation to
// use to compile Java code and execute tests. This system property
// is also used in spring-oxm.gradle to determine whether JiBX is
// supported.
//
// - customJavaSourceVersion: Java version supplied to the `--release`
// command line flag to control the Java source and target
// compatibility version. Supported versions include 9 or higher.
// Do not set this system property if Java 8 should be used.
//
// Examples:
//
// ./gradlew -DcustomJavaHome=/Library/Java/JavaVirtualMachines/jdk-14.jdk/Contents/Home test
//
// ./gradlew --no-build-cache -DcustomJavaHome=/Library/Java/JavaVirtualMachines/jdk-14.jdk/Contents/Home test
//
// ./gradlew -DcustomJavaHome=/Library/Java/JavaVirtualMachines/jdk-14.jdk/Contents/Home -DcustomJavaSourceVersion=14 test
//
//
// Credits: inspired by work from Marc Philipp and Stephane Nicoll
//
// -----------------------------------------------------------------------------
import org.gradle.internal.os.OperatingSystem
// import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile
def customJavaHome = System.getProperty("customJavaHome")
if (customJavaHome) {
def customJavaHomeDir = new File(customJavaHome)
def customJavaSourceVersion = System.getProperty("customJavaSourceVersion")
tasks.withType(JavaCompile) {
logger.info("Java home for " + it.name + " task in " + project.name + ": " + customJavaHomeDir)
options.forkOptions.javaHome = customJavaHomeDir
inputs.property("customJavaHome", customJavaHome)
if (customJavaSourceVersion) {
options.compilerArgs += [ "--release", customJavaSourceVersion]
inputs.property("customJavaSourceVersion", customJavaSourceVersion)
}
}
tasks.withType(GroovyCompile) {
logger.info("Java home for " + it.name + " task in " + project.name + ": " + customJavaHomeDir)
options.forkOptions.javaHome = customJavaHomeDir
inputs.property("customJavaHome", customJavaHome)
if (customJavaSourceVersion) {
options.compilerArgs += [ "--release", customJavaSourceVersion]
inputs.property("customJavaSourceVersion", customJavaSourceVersion)
}
}
/*
tasks.withType(KotlinJvmCompile) {
logger.info("Java home for " + it.name + " task in " + project.name + ": " + customJavaHome)
kotlinOptions.jdkHome = customJavaHomeDir
inputs.property("customJavaHome", customJavaHome)
}
*/
tasks.withType(Test) {
def javaExecutable = customJavaHome + "/bin/java"
if (OperatingSystem.current().isWindows()) {
javaExecutable += ".exe"
}
logger.info("Java executable for " + it.name + " task in " + project.name + ": " + javaExecutable)
executable = javaExecutable
inputs.property("customJavaHome", customJavaHome)
if (customJavaSourceVersion) {
inputs.property("customJavaSourceVersion", customJavaSourceVersion)
}
}
}
+2 -2
View File
@@ -3,7 +3,7 @@ configurations {
}
dependencies {
asciidoctorExt("io.spring.asciidoctor:spring-asciidoctor-extensions-block-switch:0.4.3.RELEASE")
asciidoctorExt("io.spring.asciidoctor:spring-asciidoctor-extensions-block-switch:0.5.0")
}
repositories {
@@ -113,7 +113,7 @@ dokka {
}
task downloadResources(type: Download) {
def version = "0.2.2.RELEASE"
def version = "0.2.5"
src "https://repo.spring.io/release/io/spring/docresources/" +
"spring-doc-resources/$version/spring-doc-resources-${version}.zip"
dest project.file("$buildDir/docs/spring-doc-resources.zip")
+10 -2
View File
@@ -29,12 +29,11 @@ eclipse.classpath.file.whenMerged { classpath ->
classpath.entries.removeAll { entry -> (entry.path =~ /(?!.*?repack.*\.jar).*?\/([^\/]+)\/build\/libs\/[^\/]+\.jar/) }
}
// Use separate main/test outputs (prevents WTP from packaging test classes)
eclipse.classpath.defaultOutputDir = file(project.name+"/bin/eclipse")
eclipse.classpath.file.beforeMerged { classpath ->
classpath.entries.findAll{ it instanceof SourceFolder }.each {
if(it.output.startsWith("bin/")) {
if (it.output.startsWith("bin/")) {
it.output = null
}
}
@@ -56,6 +55,15 @@ eclipse.classpath.file.whenMerged { classpath ->
}
}
// Ensure that JMH sources and resources are treated as test classpath entries
// so that they can see test fixtures.
// https://github.com/melix/jmh-gradle-plugin/issues/157
eclipse.classpath.file.whenMerged {
entries.findAll { it.path =~ /src\/jmh\/(java|resources)/ }.each {
it.entryAttributes['test'] = 'true'
}
}
// Allow projects to be used as WTP modules
eclipse.project.natures "org.eclipse.wst.common.project.facet.core.nature"
+23 -2
View File
@@ -1,17 +1,38 @@
apply plugin: 'java-library'
apply plugin: 'org.springframework.build.compile'
apply plugin: 'org.springframework.build.optional-dependencies'
// Uncomment the following for Shadow support in the jmhJar block.
// Currently commented out due to ZipException: archive is not a ZIP archive
// apply plugin: 'com.github.johnrengelman.shadow'
apply plugin: 'me.champeau.gradle.jmh'
apply from: "$rootDir/gradle/publications.gradle"
dependencies {
jmh 'org.openjdk.jmh:jmh-core:1.23'
jmh 'org.openjdk.jmh:jmh-generator-annprocess:1.23'
jmh 'org.openjdk.jmh:jmh-core:1.25'
jmh 'org.openjdk.jmh:jmh-generator-annprocess:1.25'
jmh 'net.sf.jopt-simple:jopt-simple:4.6'
}
jmh {
duplicateClassesStrategy = DuplicatesStrategy.EXCLUDE
}
jmhJar {
// Uncomment the following for Shadow's Transformer support.
// mergeServiceFiles()
// append('META-INF/spring.handlers')
// append('META-INF/spring.schemas')
// append('META-INF/spring.tooling')
exclude 'LICENSE'
exclude 'THIRD-PARTY'
exclude 'META-INF/license.txt'
exclude 'META-INF/notice.txt'
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/LICENSE*'
exclude 'META-INF/NOTICE'
exclude 'META-INF/THIRD-PARTY'
}
jar {
manifest.attributes["Implementation-Title"] = project.name
manifest.attributes["Implementation-Version"] = project.version
+124
View File
@@ -0,0 +1,124 @@
/**
* Apply the JVM Toolchain conventions
* See https://docs.gradle.org/current/userguide/toolchains.html
*
* One can choose the toolchain to use for compiling the MAIN sources and/or compiling
* and running the TEST sources. These options apply to Java, Kotlin and Groovy sources
* when available.
* {@code "./gradlew check -PmainToolchain=8 -PtestToolchain=11"} will use:
* <ul>
* <li>a JDK8 toolchain for compiling the main SourceSet
* <li>a JDK11 toolchain for compiling and running the test SourceSet
* </ul>
*
* Gradle will automatically detect JDK distributions in well-known locations.
* The following command will list the detected JDKs on the host.
* {@code
* $ ./gradlew -q javaToolchains
* }
*
* We can also configure ENV variables and let Gradle know about them:
* {@code
* $ echo JDK11
* /opt/openjdk/java11
* $ echo JDK15
* /opt/openjdk/java15
* $ ./gradlew -Dorg.gradle.java.installations.fromEnv=JDK11,JDK15 check
* }
*
* @author Brian Clozel
*/
plugins.withType(JavaPlugin) {
// Configure the Java Toolchain if the 'mainToolchain' property is defined
if (project.hasProperty('mainToolchain') && project.mainToolchain) {
def mainLanguageVersion = JavaLanguageVersion.of(project.mainToolchain.toString())
java {
toolchain {
languageVersion = mainLanguageVersion
}
}
}
else {
// Fallback to JDK8
java {
sourceCompatibility = JavaVersion.VERSION_1_8
}
}
// Configure a specific Java Toolchain for compiling and running tests if the 'testToolchain' property is defined
if (project.hasProperty('testToolchain') && project.testToolchain) {
def testLanguageVersion = JavaLanguageVersion.of(project.testToolchain.toString());
tasks.withType(JavaCompile).matching { it.name.contains("Test") }.configureEach {
javaCompiler = javaToolchains.compilerFor {
languageVersion = testLanguageVersion
}
}
tasks.withType(Test).configureEach{
javaLauncher = javaToolchains.launcherFor {
languageVersion = testLanguageVersion
}
}
}
}
plugins.withType(GroovyPlugin) {
// Fallback to JDK8
if (!project.hasProperty('mainToolchain')) {
compileGroovy {
sourceCompatibility = JavaVersion.VERSION_1_8
}
}
}
// Configure the Kotlin compiler if the 'mainToolchain' property is defined
pluginManager.withPlugin("kotlin") {
if (project.hasProperty('mainToolchain') && project.mainToolchain) {
def mainLanguageVersion = JavaLanguageVersion.of(project.mainToolchain.toString());
def compiler = javaToolchains.compilerFor {
languageVersion = mainLanguageVersion
}
// See https://kotlinlang.org/docs/gradle.html#attributes-specific-for-jvm
def javaVersion = mainLanguageVersion.toString() == '8' ? '1.8' : mainLanguageVersion.toString()
compileKotlin {
kotlinOptions {
jvmTarget = javaVersion
jdkHome = compiler.get().metadata.installationPath.asFile.absolutePath
}
}
// Compile the test classes with the same version, 'testToolchain' will override if defined
compileTestKotlin {
kotlinOptions {
jvmTarget = javaVersion
jdkHome = compiler.get().metadata.installationPath.asFile.absolutePath
}
}
}
else {
// Fallback to JDK8
compileKotlin {
kotlinOptions {
jvmTarget = '1.8'
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = '1.8'
}
}
}
if (project.hasProperty('testToolchain') && project.testToolchain) {
def testLanguageVersion = JavaLanguageVersion.of(project.testToolchain.toString());
def compiler = javaToolchains.compilerFor {
languageVersion = testLanguageVersion
}
// See https://kotlinlang.org/docs/gradle.html#attributes-specific-for-jvm
def javaVersion = testLanguageVersion.toString() == '8' ? '1.8' : testLanguageVersion.toString()
compileTestKotlin {
kotlinOptions {
jvmTarget = javaVersion
jdkHome = compiler.get().metadata.installationPath.asFile.absolutePath
}
}
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
+6 -6
View File
@@ -6,8 +6,8 @@ pluginManagement {
}
plugins {
id "com.gradle.enterprise" version "3.2"
id "io.spring.gradle-enterprise-conventions" version "0.0.2"
id "com.gradle.enterprise" version "3.6.1"
id "io.spring.ge.conventions" version "0.0.7"
}
include "spring-aop"
@@ -45,11 +45,11 @@ rootProject.children.each {project ->
settings.gradle.projectsLoaded {
gradleEnterprise {
buildScan {
if (settings.gradle.rootProject.hasProperty('customJavaHome')) {
value("Custom JAVA_HOME", settings.gradle.rootProject.getProperty('customJavaHome'))
if (settings.gradle.rootProject.hasProperty('mainToolchain')) {
value("Main toolchain", 'JDK' + settings.gradle.rootProject.getProperty('mainToolchain'))
}
if (settings.gradle.rootProject.hasProperty('customJavaSourceVersion')) {
value("Custom Java Source Version", settings.gradle.rootProject.getProperty('customJavaSourceVersion'))
if (settings.gradle.rootProject.hasProperty('testToolchain')) {
value("Test toolchain", 'JDK' + settings.gradle.rootProject.getProperty('testToolchain'))
}
File buildDir = settings.gradle.rootProject.getBuildDir()
buildDir.mkdirs()
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,7 +26,7 @@ import org.aopalliance.aop.Advice;
* <p>Introductions are often <b>mixins</b>, enabling the building of composite
* objects that can achieve many of the goals of multiple inheritance in Java.
*
* <p>Compared to {qlink IntroductionInfo}, this interface allows an advice to
* <p>Compared to {@link IntroductionInfo}, this interface allows an advice to
* implement a range of interfaces that is not necessarily known in advance.
* Thus an {@link IntroductionAdvisor} can be used to specify which interfaces
* will be exposed in an advised object.
@@ -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.
@@ -350,17 +350,8 @@ public abstract class AbstractAspectJAdvice implements Advice, AspectJPrecedence
return this.discoveredThrowingType;
}
private boolean isVariableName(String name) {
char[] chars = name.toCharArray();
if (!Character.isJavaIdentifierStart(chars[0])) {
return false;
}
for (int i = 1; i < chars.length; i++) {
if (!Character.isJavaIdentifierPart(chars[i])) {
return false;
}
}
return true;
private static boolean isVariableName(String name) {
return AspectJProxyUtils.isVariableName(name);
}
@@ -640,7 +631,6 @@ public abstract class AbstractAspectJAdvice implements Advice, AspectJPrecedence
}
try {
ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
// TODO AopUtils.invokeJoinpointUsingReflection
return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
}
catch (IllegalArgumentException ex) {
@@ -470,22 +470,10 @@ public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscov
*/
@Nullable
private String maybeExtractVariableName(@Nullable String candidateToken) {
if (!StringUtils.hasLength(candidateToken)) {
return null;
}
if (Character.isJavaIdentifierStart(candidateToken.charAt(0)) &&
Character.isLowerCase(candidateToken.charAt(0))) {
char[] tokenChars = candidateToken.toCharArray();
for (char tokenChar : tokenChars) {
if (!Character.isJavaIdentifierPart(tokenChar)) {
return null;
}
}
if (AspectJProxyUtils.isVariableName(candidateToken)) {
return candidateToken;
}
else {
return null;
}
return null;
}
/**
@@ -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.
@@ -21,6 +21,8 @@ import java.util.List;
import org.springframework.aop.Advisor;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
/**
* Utility methods for working with AspectJ proxies.
@@ -73,4 +75,19 @@ public abstract class AspectJProxyUtils {
((PointcutAdvisor) advisor).getPointcut() instanceof AspectJExpressionPointcut));
}
static boolean isVariableName(@Nullable String name) {
if (!StringUtils.hasLength(name)) {
return false;
}
if (!Character.isJavaIdentifierStart(name.charAt(0))) {
return false;
}
for (int i = 1; i < name.length(); i++) {
if (!Character.isJavaIdentifierPart(name.charAt(i))) {
return false;
}
}
return true;
}
}
@@ -51,6 +51,7 @@ import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConvertingComparator;
import org.springframework.lang.Nullable;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.MethodFilter;
import org.springframework.util.StringUtils;
import org.springframework.util.comparator.InstanceComparator;
@@ -70,7 +71,11 @@ import org.springframework.util.comparator.InstanceComparator;
@SuppressWarnings("serial")
public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFactory implements Serializable {
private static final Comparator<Method> METHOD_COMPARATOR;
// Exclude @Pointcut methods
private static final MethodFilter adviceMethodFilter = ReflectionUtils.USER_DECLARED_METHODS
.and(method -> (AnnotationUtils.getAnnotation(method, Pointcut.class) == null));
private static final Comparator<Method> adviceMethodComparator;
static {
// Note: although @After is ordered before @AfterReturning and @AfterThrowing,
@@ -86,7 +91,7 @@ public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFacto
return (ann != null ? ann.getAnnotation() : null);
});
Comparator<Method> methodNameComparator = new ConvertingComparator<>(Method::getName);
METHOD_COMPARATOR = adviceKindComparator.thenComparing(methodNameComparator);
adviceMethodComparator = adviceKindComparator.thenComparing(methodNameComparator);
}
@@ -160,15 +165,10 @@ public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFacto
}
private List<Method> getAdvisorMethods(Class<?> aspectClass) {
final List<Method> methods = new ArrayList<>();
ReflectionUtils.doWithMethods(aspectClass, method -> {
// Exclude pointcuts
if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {
methods.add(method);
}
}, ReflectionUtils.USER_DECLARED_METHODS);
List<Method> methods = new ArrayList<>();
ReflectionUtils.doWithMethods(aspectClass, methods::add, adviceMethodFilter);
if (methods.size() > 1) {
methods.sort(METHOD_COMPARATOR);
methods.sort(adviceMethodComparator);
}
return methods;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@ import java.util.concurrent.ConcurrentHashMap;
import org.springframework.aop.Advisor;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.SmartClassLoader;
import org.springframework.lang.Nullable;
/**
@@ -89,7 +90,13 @@ public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSu
}
proxyFactory.addAdvisor(this.advisor);
customizeProxyFactory(proxyFactory);
return proxyFactory.getProxy(getProxyClassLoader());
// Use original ClassLoader if bean class not locally loaded in overriding class loader
ClassLoader classLoader = getProxyClassLoader();
if (classLoader instanceof SmartClassLoader && classLoader != bean.getClass().getClassLoader()) {
classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
}
return proxyFactory.getProxy(classLoader);
}
// No proxy needed.
@@ -48,6 +48,7 @@ import org.springframework.cglib.proxy.Factory;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.cglib.proxy.NoOp;
import org.springframework.core.KotlinDetector;
import org.springframework.core.SmartClassLoader;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -752,10 +753,17 @@ class CglibAopProxy implements AopProxy, Serializable {
throw ex;
}
catch (Exception ex) {
if (ReflectionUtils.declaresException(getMethod(), ex.getClass())) {
if (ReflectionUtils.declaresException(getMethod(), ex.getClass()) ||
KotlinDetector.isKotlinType(getMethod().getDeclaringClass())) {
// Propagate original exception if declared on the target method
// (with callers expecting it). Always propagate it for Kotlin code
// since checked exceptions do not have to be explicitly declared there.
throw ex;
}
else {
// Checked exception thrown in the interceptor but not declared on the
// target method signature -> apply an UndeclaredThrowableException,
// aligned with standard JDK dynamic proxy behavior.
throw new UndeclaredThrowableException(ex);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ import java.io.Serializable;
import java.lang.reflect.Proxy;
import org.springframework.aop.SpringProxy;
import org.springframework.core.NativeDetector;
/**
* Default {@link AopProxyFactory} implementation, creating either a CGLIB proxy
@@ -47,17 +48,10 @@ import org.springframework.aop.SpringProxy;
@SuppressWarnings("serial")
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
/**
* Whether this environment lives within a native image.
* Exposed as a private static field rather than in a {@code NativeImageDetector.inNativeImage()} static method due to https://github.com/oracle/graal/issues/2594.
* @see <a href="https://github.com/oracle/graal/blob/master/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/ImageInfo.java">ImageInfo.java</a>
*/
private static final boolean IN_NATIVE_IMAGE = (System.getProperty("org.graalvm.nativeimage.imagecode") != null);
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (!IN_NATIVE_IMAGE &&
if (!NativeDetector.inNativeImage() &&
(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author 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,6 +46,7 @@ import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
import org.springframework.core.SmartClassLoader;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -458,7 +459,12 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
proxyFactory.setPreFiltered(true);
}
return proxyFactory.getProxy(getProxyClassLoader());
// Use original ClassLoader if bean class not locally loaded in overriding class loader
ClassLoader classLoader = getProxyClassLoader();
if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
}
return proxyFactory.getProxy(classLoader);
}
/**
@@ -503,7 +509,10 @@ public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
List<Object> allInterceptors = new ArrayList<>();
if (specificInterceptors != null) {
allInterceptors.addAll(Arrays.asList(specificInterceptors));
if (specificInterceptors.length > 0) {
// specificInterceptors may equals PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS
allInterceptors.addAll(Arrays.asList(specificInterceptors));
}
if (commonInterceptors.length > 0) {
if (this.applyCommonInterceptorsFirst) {
allInterceptors.addAll(0, Arrays.asList(commonInterceptors));
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -116,7 +116,7 @@ public abstract class AbstractPoolingTargetSource extends AbstractPrototypeBased
/**
* Return an IntroductionAdvisor that providing a mixin
* Return an IntroductionAdvisor that provides a mixin
* exposing statistics about the pool maintained by this object.
*/
public DefaultIntroductionAdvisor getPoolingConfigMixin() {
@@ -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.
@@ -59,7 +59,8 @@ public abstract aspect AbstractTransactionAspect extends TransactionAspectSuppor
@Override
public void destroy() {
clearTransactionManagerCache(); // An aspect is basically a singleton
// An aspect is basically a singleton -> cleanup on destruction
clearTransactionManagerCache();
}
@SuppressAjWarnings("adviceDidNotMatch")
-2
View File
@@ -23,8 +23,6 @@ sourceSets {
}
compileGroovy {
sourceCompatibility = 1.8
targetCompatibility = 1.8
options.compilerArgs += "-Werror"
}
@@ -1,14 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "https://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<beans>
<bean id="bean1" class="org.springframework.beans.factory.ConcurrentBeanFactoryTests$ConcurrentBean"
<bean id="bean1" class="org.springframework.beans.factory.ConcurrentBeanFactoryBenchmark$ConcurrentBean"
scope="prototype">
<property name="date" value="2004/08/08"/>
</bean>
<bean id="bean2" class="org.springframework.beans.factory.ConcurrentBeanFactoryTests$ConcurrentBean"
<bean id="bean2" class="org.springframework.beans.factory.ConcurrentBeanFactoryBenchmark$ConcurrentBean"
scope="prototype">
<property name="date" value="2000/02/02"/>
</bean>
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -305,8 +305,10 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA
Class<?> componentType = propValue.getClass().getComponentType();
Object newArray = Array.newInstance(componentType, arrayIndex + 1);
System.arraycopy(propValue, 0, newArray, 0, length);
setPropertyValue(tokens.actualName, newArray);
propValue = getPropertyValue(tokens.actualName);
int lastKeyIndex = tokens.canonicalName.lastIndexOf('[');
String propName = tokens.canonicalName.substring(0, lastKeyIndex);
setPropertyValue(propName, newArray);
propValue = getPropertyValue(propName);
}
Array.set(propValue, arrayIndex, convertedValue);
}
@@ -268,10 +268,7 @@ public abstract class BeanUtils {
public static <T> Constructor<T> findPrimaryConstructor(Class<T> clazz) {
Assert.notNull(clazz, "Class must not be null");
if (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(clazz)) {
Constructor<T> kotlinPrimaryConstructor = KotlinDelegate.findPrimaryConstructor(clazz);
if (kotlinPrimaryConstructor != null) {
return kotlinPrimaryConstructor;
}
return KotlinDelegate.findPrimaryConstructor(clazz);
}
return null;
}
@@ -543,6 +540,7 @@ public abstract class BeanUtils {
if (targetType == null || targetType.isArray() || unknownEditorTypes.contains(targetType)) {
return null;
}
ClassLoader cl = targetType.getClassLoader();
if (cl == null) {
try {
@@ -559,28 +557,34 @@ public abstract class BeanUtils {
return null;
}
}
String targetTypeName = targetType.getName();
String editorName = targetTypeName + "Editor";
try {
Class<?> editorClass = cl.loadClass(editorName);
if (!PropertyEditor.class.isAssignableFrom(editorClass)) {
if (logger.isInfoEnabled()) {
logger.info("Editor class [" + editorName +
"] does not implement [java.beans.PropertyEditor] interface");
if (editorClass != null) {
if (!PropertyEditor.class.isAssignableFrom(editorClass)) {
if (logger.isInfoEnabled()) {
logger.info("Editor class [" + editorName +
"] does not implement [java.beans.PropertyEditor] interface");
}
unknownEditorTypes.add(targetType);
return null;
}
unknownEditorTypes.add(targetType);
return null;
return (PropertyEditor) instantiateClass(editorClass);
}
return (PropertyEditor) instantiateClass(editorClass);
// Misbehaving ClassLoader returned null instead of ClassNotFoundException
// - fall back to unknown editor type registration below
}
catch (ClassNotFoundException ex) {
if (logger.isTraceEnabled()) {
logger.trace("No property editor [" + editorName + "] found for type " +
targetTypeName + " according to 'Editor' suffix convention");
}
unknownEditorTypes.add(targetType);
return null;
// Ignore - fall back to unknown editor type registration below
}
if (logger.isTraceEnabled()) {
logger.trace("No property editor [" + editorName + "] found for type " +
targetTypeName + " according to 'Editor' suffix convention");
}
unknownEditorTypes.add(targetType);
return null;
}
/**
@@ -772,7 +776,14 @@ public abstract class BeanUtils {
if (readMethod != null) {
ResolvableType sourceResolvableType = ResolvableType.forMethodReturnType(readMethod);
ResolvableType targetResolvableType = ResolvableType.forMethodParameter(writeMethod, 0);
if (targetResolvableType.isAssignableFrom(sourceResolvableType)) {
// Ignore generic types in assignable check if either ResolvableType has unresolvable generics.
boolean isAssignable =
(sourceResolvableType.hasUnresolvableGenerics() || targetResolvableType.hasUnresolvableGenerics() ?
ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType()) :
targetResolvableType.isAssignableFrom(sourceResolvableType));
if (isAssignable) {
try {
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
readMethod.setAccessible(true);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2021 the original author 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,10 +41,10 @@ public class TypeMismatchException extends PropertyAccessException {
private String propertyName;
@Nullable
private transient Object value;
private final transient Object value;
@Nullable
private Class<?> requiredType;
private final Class<?> requiredType;
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -628,45 +628,58 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
Field field = (Field) this.member;
Object value;
if (this.cached) {
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
try {
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
}
catch (NoSuchBeanDefinitionException ex) {
// Unexpected removal of target bean for cached argument -> re-resolve
value = resolveFieldValue(field, bean, beanName);
}
}
else {
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
desc.setContainingClass(bean.getClass());
Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
Assert.state(beanFactory != null, "No BeanFactory available");
TypeConverter typeConverter = beanFactory.getTypeConverter();
try {
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
}
synchronized (this) {
if (!this.cached) {
Object cachedFieldValue = null;
if (value != null || this.required) {
cachedFieldValue = desc;
registerDependentBeans(beanName, autowiredBeanNames);
if (autowiredBeanNames.size() == 1) {
String autowiredBeanName = autowiredBeanNames.iterator().next();
if (beanFactory.containsBean(autowiredBeanName) &&
beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
cachedFieldValue = new ShortcutDependencyDescriptor(
desc, autowiredBeanName, field.getType());
}
}
}
this.cachedFieldValue = cachedFieldValue;
this.cached = true;
}
}
value = resolveFieldValue(field, bean, beanName);
}
if (value != null) {
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
}
@Nullable
private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
desc.setContainingClass(bean.getClass());
Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
Assert.state(beanFactory != null, "No BeanFactory available");
TypeConverter typeConverter = beanFactory.getTypeConverter();
Object value;
try {
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
}
synchronized (this) {
if (!this.cached) {
Object cachedFieldValue = null;
if (value != null || this.required) {
cachedFieldValue = desc;
registerDependentBeans(beanName, autowiredBeanNames);
if (autowiredBeanNames.size() == 1) {
String autowiredBeanName = autowiredBeanNames.iterator().next();
if (beanFactory.containsBean(autowiredBeanName) &&
beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
cachedFieldValue = new ShortcutDependencyDescriptor(
desc, autowiredBeanName, field.getType());
}
}
}
this.cachedFieldValue = cachedFieldValue;
this.cached = true;
}
}
return value;
}
}
@@ -695,58 +708,16 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
Method method = (Method) this.member;
Object[] arguments;
if (this.cached) {
// Shortcut for avoiding synchronization...
arguments = resolveCachedArguments(beanName);
try {
arguments = resolveCachedArguments(beanName);
}
catch (NoSuchBeanDefinitionException ex) {
// Unexpected removal of target bean for cached argument -> re-resolve
arguments = resolveMethodArguments(method, bean, beanName);
}
}
else {
int argumentCount = method.getParameterCount();
arguments = new Object[argumentCount];
DependencyDescriptor[] descriptors = new DependencyDescriptor[argumentCount];
Set<String> autowiredBeans = new LinkedHashSet<>(argumentCount);
Assert.state(beanFactory != null, "No BeanFactory available");
TypeConverter typeConverter = beanFactory.getTypeConverter();
for (int i = 0; i < arguments.length; i++) {
MethodParameter methodParam = new MethodParameter(method, i);
DependencyDescriptor currDesc = new DependencyDescriptor(methodParam, this.required);
currDesc.setContainingClass(bean.getClass());
descriptors[i] = currDesc;
try {
Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter);
if (arg == null && !this.required) {
arguments = null;
break;
}
arguments[i] = arg;
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(methodParam), ex);
}
}
synchronized (this) {
if (!this.cached) {
if (arguments != null) {
DependencyDescriptor[] cachedMethodArguments = Arrays.copyOf(descriptors, arguments.length);
registerDependentBeans(beanName, autowiredBeans);
if (autowiredBeans.size() == argumentCount) {
Iterator<String> it = autowiredBeans.iterator();
Class<?>[] paramTypes = method.getParameterTypes();
for (int i = 0; i < paramTypes.length; i++) {
String autowiredBeanName = it.next();
if (beanFactory.containsBean(autowiredBeanName) &&
beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) {
cachedMethodArguments[i] = new ShortcutDependencyDescriptor(
descriptors[i], autowiredBeanName, paramTypes[i]);
}
}
}
this.cachedMethodArguments = cachedMethodArguments;
}
else {
this.cachedMethodArguments = null;
}
this.cached = true;
}
}
arguments = resolveMethodArguments(method, bean, beanName);
}
if (arguments != null) {
try {
@@ -771,6 +742,59 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA
}
return arguments;
}
@Nullable
private Object[] resolveMethodArguments(Method method, Object bean, @Nullable String beanName) {
int argumentCount = method.getParameterCount();
Object[] arguments = new Object[argumentCount];
DependencyDescriptor[] descriptors = new DependencyDescriptor[argumentCount];
Set<String> autowiredBeans = new LinkedHashSet<>(argumentCount);
Assert.state(beanFactory != null, "No BeanFactory available");
TypeConverter typeConverter = beanFactory.getTypeConverter();
for (int i = 0; i < arguments.length; i++) {
MethodParameter methodParam = new MethodParameter(method, i);
DependencyDescriptor currDesc = new DependencyDescriptor(methodParam, this.required);
currDesc.setContainingClass(bean.getClass());
descriptors[i] = currDesc;
try {
Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter);
if (arg == null && !this.required) {
arguments = null;
break;
}
arguments[i] = arg;
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(methodParam), ex);
}
}
synchronized (this) {
if (!this.cached) {
if (arguments != null) {
DependencyDescriptor[] cachedMethodArguments = Arrays.copyOf(descriptors, arguments.length);
registerDependentBeans(beanName, autowiredBeans);
if (autowiredBeans.size() == argumentCount) {
Iterator<String> it = autowiredBeans.iterator();
Class<?>[] paramTypes = method.getParameterTypes();
for (int i = 0; i < paramTypes.length; i++) {
String autowiredBeanName = it.next();
if (beanFactory.containsBean(autowiredBeanName) &&
beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) {
cachedMethodArguments[i] = new ShortcutDependencyDescriptor(
descriptors[i], autowiredBeanName, paramTypes[i]);
}
}
}
this.cachedMethodArguments = cachedMethodArguments;
}
else {
this.cachedMethodArguments = null;
}
this.cached = true;
}
}
return arguments;
}
}
@@ -141,8 +141,7 @@ public class InjectionMetadata {
* Return an {@code InjectionMetadata} instance, possibly for empty elements.
* @param elements the elements to inject (possibly empty)
* @param clazz the target class
* @return a new {@link #InjectionMetadata(Class, Collection)} instance,
* or {@link #EMPTY} in case of no elements
* @return a new {@link #InjectionMetadata(Class, Collection)} instance
* @since 5.2
*/
public static InjectionMetadata forElements(Collection<InjectedElement> elements, Class<?> clazz) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2021 the original author 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,15 +36,16 @@ import org.springframework.util.StringValueResolver;
* Example XML bean definition:
*
* <pre class="code">
* &lt;bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"/&gt;
* &lt;property name="driverClassName" value="${driver}"/&gt;
* &lt;property name="url" value="jdbc:${dbname}"/&gt;
* &lt;bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"&gt;
* &lt;property name="driverClassName" value="${driver}" /&gt;
* &lt;property name="url" value="jdbc:${dbname}" /&gt;
* &lt;/bean&gt;
* </pre>
*
* Example properties file:
*
* <pre class="code">driver=com.mysql.jdbc.Driver
* <pre class="code">
* driver=com.mysql.jdbc.Driver
* dbname=mysql:mydb</pre>
*
* Annotated bean definitions may take advantage of property replacement using
@@ -56,7 +57,8 @@ import org.springframework.util.StringValueResolver;
* in bean references. Furthermore, placeholder values can also cross-reference
* other placeholders, like:
*
* <pre class="code">rootPath=myrootdir
* <pre class="code">
* rootPath=myrootdir
* subPath=${rootPath}/subdir</pre>
*
* In contrast to {@link PropertyOverrideConfigurer}, subclasses of this type allow
@@ -71,13 +73,13 @@ import org.springframework.util.StringValueResolver;
*
* <p>Default property values can be defined globally for each configurer instance
* via the {@link #setProperties properties} property, or on a property-by-property basis
* using the default value separator which is {@code ":"} by default and
* customizable via {@link #setValueSeparator(String)}.
* using the value separator which is {@code ":"} by default and customizable via
* {@link #setValueSeparator(String)}.
*
* <p>Example XML property with default value:
*
* <pre class="code">
* <property name="url" value="jdbc:${dbname:defaultdb}"/>
* &lt;property name="url" value="jdbc:${dbname:defaultdb}" /&gt;
* </pre>
*
* @author Chris Beams
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -52,6 +52,7 @@ import org.springframework.util.StringUtils;
* @author Dave Syer
* @author Juergen Hoeller
* @author Sam Brannen
* @author Brian Clozel
* @since 4.1
*/
public abstract class YamlProcessor {
@@ -128,10 +129,11 @@ public abstract class YamlProcessor {
/**
* Set the supported types that can be loaded from YAML documents.
* <p>If no supported types are configured, all types encountered in YAML
* documents will be supported. If an unsupported type is encountered, an
* {@link IllegalStateException} will be thrown when the corresponding YAML
* node is processed.
* <p>If no supported types are configured, only Java standard classes
* (as defined in {@link org.yaml.snakeyaml.constructor.SafeConstructor})
* encountered in YAML documents will be supported.
* If an unsupported type is encountered, an {@link IllegalStateException}
* will be thrown when the corresponding YAML node is processed.
* @param supportedTypes the supported types, or an empty array to clear the
* supported types
* @since 5.1.16
@@ -182,12 +184,8 @@ public abstract class YamlProcessor {
protected Yaml createYaml() {
LoaderOptions loaderOptions = new LoaderOptions();
loaderOptions.setAllowDuplicateKeys(false);
if (!this.supportedTypes.isEmpty()) {
return new Yaml(new FilteringConstructor(loaderOptions), new Representer(),
new DumperOptions(), loaderOptions);
}
return new Yaml(loaderOptions);
return new Yaml(new FilteringConstructor(loaderOptions), new Representer(),
new DumperOptions(), loaderOptions);
}
private boolean process(MatchCallback callback, Yaml yaml, Resource resource) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -489,8 +489,8 @@ public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader imp
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;
for (Object key : namedArgs.keySet()) {
String propName = (String) key;
setProperty(propName, namedArgs.get(propName));
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -73,6 +73,7 @@ import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.MethodParameter;
import org.springframework.core.NamedThreadLocal;
import org.springframework.core.NativeDetector;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.ResolvableType;
@@ -123,14 +124,6 @@ import org.springframework.util.StringUtils;
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
implements AutowireCapableBeanFactory {
/**
* Whether this environment lives within a native image.
* Exposed as a private static field rather than in a {@code NativeImageDetector.inNativeImage()} static method due to https://github.com/oracle/graal/issues/2594.
* @see <a href="https://github.com/oracle/graal/blob/master/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/ImageInfo.java">ImageInfo.java</a>
*/
private static final boolean IN_NATIVE_IMAGE = (System.getProperty("org.graalvm.nativeimage.imagecode") != null);
/** Strategy for creating bean instances. */
private InstantiationStrategy instantiationStrategy;
@@ -184,7 +177,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
ignoreDependencyInterface(BeanNameAware.class);
ignoreDependencyInterface(BeanFactoryAware.class);
ignoreDependencyInterface(BeanClassLoaderAware.class);
if (IN_NATIVE_IMAGE) {
if (NativeDetector.inNativeImage()) {
this.instantiationStrategy = new SimpleInstantiationStrategy();
}
else {
@@ -1022,6 +1015,11 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
throw ex;
}
catch (BeanCreationException ex) {
// Don't swallow a linkage error since it contains a full stacktrace on
// first occurrence... and just a plain NoClassDefFoundError afterwards.
if (ex.contains(LinkageError.class)) {
throw ex;
}
// Instantiation failure, maybe too early...
if (logger.isDebugEnabled()) {
logger.debug("Bean creation exception on singleton FactoryBean type check: " + ex);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -894,7 +894,7 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
}
/**
* Return if there are property values values defined for this bean.
* Return if there are property values defined for this bean.
* @since 5.0.2
*/
@Override
@@ -1194,8 +1194,8 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
this.primary == that.primary &&
this.nonPublicAccessAllowed == that.nonPublicAccessAllowed &&
this.lenientConstructorResolution == that.lenientConstructorResolution &&
ObjectUtils.nullSafeEquals(this.constructorArgumentValues, that.constructorArgumentValues) &&
ObjectUtils.nullSafeEquals(this.propertyValues, that.propertyValues) &&
equalsConstructorArgumentValues(that) &&
equalsPropertyValues(that) &&
ObjectUtils.nullSafeEquals(this.methodOverrides, that.methodOverrides) &&
ObjectUtils.nullSafeEquals(this.factoryBeanName, that.factoryBeanName) &&
ObjectUtils.nullSafeEquals(this.factoryMethodName, that.factoryMethodName) &&
@@ -1208,12 +1208,30 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
super.equals(other));
}
private boolean equalsConstructorArgumentValues(AbstractBeanDefinition other) {
if (!hasConstructorArgumentValues()) {
return !other.hasConstructorArgumentValues();
}
return ObjectUtils.nullSafeEquals(this.constructorArgumentValues, other.constructorArgumentValues);
}
private boolean equalsPropertyValues(AbstractBeanDefinition other) {
if (!hasPropertyValues()) {
return !other.hasPropertyValues();
}
return ObjectUtils.nullSafeEquals(this.propertyValues, other.propertyValues);
}
@Override
public int hashCode() {
int hashCode = ObjectUtils.nullSafeHashCode(getBeanClassName());
hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.scope);
hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.constructorArgumentValues);
hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.propertyValues);
if (hasConstructorArgumentValues()) {
hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.constructorArgumentValues);
}
if (hasPropertyValues()) {
hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.propertyValues);
}
hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.factoryBeanName);
hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.factoryMethodName);
hashCode = 29 * hashCode + super.hashCode();
@@ -250,7 +250,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
throws BeansException {
String beanName = transformedBeanName(name);
Object bean;
Object beanInstance;
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
@@ -264,7 +264,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
@@ -342,7 +342,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
else if (mbd.isPrototype()) {
@@ -355,7 +355,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
@@ -377,7 +377,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
afterPrototypeCreation(beanName);
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new ScopeNotActiveException(beanName, scopeName, ex);
@@ -395,14 +395,19 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
}
}
return adaptBeanInstance(name, beanInstance, requiredType);
}
@SuppressWarnings("unchecked")
<T> T adaptBeanInstance(String name, Object bean, @Nullable Class<?> requiredType) {
// Check if required type matches the type of the actual bean instance.
if (requiredType != null && !requiredType.isInstance(bean)) {
try {
T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
Object convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
if (convertedBean == null) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return convertedBean;
return (T) convertedBean;
}
catch (TypeMismatchException ex) {
if (logger.isTraceEnabled()) {
@@ -67,6 +67,7 @@ import org.springframework.util.StringUtils;
/**
* Delegate for resolving constructors and factory methods.
*
* <p>Performs constructor resolution through argument matching.
*
* @author Juergen Hoeller
@@ -85,7 +86,7 @@ class ConstructorResolver {
private static final Object[] EMPTY_ARGS = new Object[0];
/**
* Marker for autowired arguments in a cached argument array, to be later replaced
* Marker for autowired arguments in a cached argument array, to be replaced
* by a {@linkplain #resolveAutowiredArgument resolved autowired argument}.
*/
private static final Object autowiredArgumentMarker = new Object();
@@ -149,7 +150,7 @@ class ConstructorResolver {
}
}
if (argsToResolve != null) {
argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve, true);
argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve);
}
}
@@ -410,6 +411,7 @@ class ConstructorResolver {
if (mbd.isSingleton() && this.beanFactory.containsSingleton(beanName)) {
throw new ImplicitlyAppearedSingletonException();
}
this.beanFactory.registerDependentBean(factoryBeanName, beanName);
factoryClass = factoryBean.getClass();
isStatic = false;
}
@@ -444,7 +446,7 @@ class ConstructorResolver {
}
}
if (argsToResolve != null) {
argsToUse = resolvePreparedArguments(beanName, mbd, bw, factoryMethodToUse, argsToResolve, true);
argsToUse = resolvePreparedArguments(beanName, mbd, bw, factoryMethodToUse, argsToResolve);
}
}
@@ -816,7 +818,7 @@ class ConstructorResolver {
* Resolve the prepared arguments stored in the given bean definition.
*/
private Object[] resolvePreparedArguments(String beanName, RootBeanDefinition mbd, BeanWrapper bw,
Executable executable, Object[] argsToResolve, boolean fallback) {
Executable executable, Object[] argsToResolve) {
TypeConverter customConverter = this.beanFactory.getCustomTypeConverter();
TypeConverter converter = (customConverter != null ? customConverter : bw);
@@ -829,7 +831,7 @@ class ConstructorResolver {
Object argValue = argsToResolve[argIndex];
MethodParameter methodParam = MethodParameter.forExecutable(executable, argIndex);
if (argValue == autowiredArgumentMarker) {
argValue = resolveAutowiredArgument(methodParam, beanName, null, converter, fallback);
argValue = resolveAutowiredArgument(methodParam, beanName, null, converter, true);
}
else if (argValue instanceof BeanMetadataElement) {
argValue = valueResolver.resolveValueIfNecessary("constructor argument", argValue);
@@ -1231,8 +1231,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
}
if (candidateNames.length == 1) {
String beanName = candidateNames[0];
return new NamedBeanHolder<>(beanName, (T) getBean(beanName, requiredType.toClass(), args));
return resolveNamedBean(candidateNames[0], requiredType, args);
}
else if (candidateNames.length > 1) {
Map<String, Object> candidates = CollectionUtils.newLinkedHashMap(candidateNames.length);
@@ -1251,8 +1250,11 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
}
if (candidateName != null) {
Object beanInstance = candidates.get(candidateName);
if (beanInstance == null || beanInstance instanceof Class) {
beanInstance = getBean(candidateName, requiredType.toClass(), args);
if (beanInstance == null) {
return null;
}
if (beanInstance instanceof Class) {
return resolveNamedBean(candidateName, requiredType, args);
}
return new NamedBeanHolder<>(candidateName, (T) beanInstance);
}
@@ -1264,6 +1266,17 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
return null;
}
@Nullable
private <T> NamedBeanHolder<T> resolveNamedBean(
String beanName, ResolvableType requiredType, @Nullable Object[] args) throws BeansException {
Object bean = getBean(beanName, null, args);
if (bean instanceof NullBean) {
return null;
}
return new NamedBeanHolder<T>(beanName, adaptBeanInstance(beanName, bean, requiredType.toClass()));
}
@Override
@Nullable
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -119,14 +119,16 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
}
}
else {
Class<?>[] paramTypes = destroyMethod.getParameterTypes();
if (paramTypes.length > 1) {
throw new BeanDefinitionValidationException("Method '" + destroyMethodName + "' of bean '" +
beanName + "' has more than one parameter - not supported as destroy method");
}
else if (paramTypes.length == 1 && boolean.class != paramTypes[0]) {
throw new BeanDefinitionValidationException("Method '" + destroyMethodName + "' of bean '" +
beanName + "' has a non-boolean parameter - not supported as destroy method");
if (destroyMethod.getParameterCount() > 0) {
Class<?>[] paramTypes = destroyMethod.getParameterTypes();
if (paramTypes.length > 1) {
throw new BeanDefinitionValidationException("Method '" + destroyMethodName + "' of bean '" +
beanName + "' has more than one parameter - not supported as destroy method");
}
else if (paramTypes.length == 1 && boolean.class != paramTypes[0]) {
throw new BeanDefinitionValidationException("Method '" + destroyMethodName + "' of bean '" +
beanName + "' has a non-boolean parameter - not supported as destroy method");
}
}
destroyMethod = ClassUtils.getInterfaceMethodIfPossible(destroyMethod);
}
@@ -170,66 +172,6 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
}
/**
* If the current value of the given beanDefinition's "destroyMethodName" property is
* {@link AbstractBeanDefinition#INFER_METHOD}, then attempt to infer a destroy method.
* Candidate methods are currently limited to public, no-arg methods named "close" or
* "shutdown" (whether declared locally or inherited). The given BeanDefinition's
* "destroyMethodName" is updated to be null if no such method is found, otherwise set
* to the name of the inferred method. This constant serves as the default for the
* {@code @Bean#destroyMethod} attribute and the value of the constant may also be
* used in XML within the {@code <bean destroy-method="">} or {@code
* <beans default-destroy-method="">} attributes.
* <p>Also processes the {@link java.io.Closeable} and {@link java.lang.AutoCloseable}
* interfaces, reflectively calling the "close" method on implementing beans as well.
*/
@Nullable
private String inferDestroyMethodIfNecessary(Object bean, RootBeanDefinition beanDefinition) {
String destroyMethodName = beanDefinition.getDestroyMethodName();
if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName) ||
(destroyMethodName == null && bean instanceof AutoCloseable)) {
// Only perform destroy method inference or Closeable detection
// in case of the bean not explicitly implementing DisposableBean
if (!(bean instanceof DisposableBean)) {
try {
return bean.getClass().getMethod(CLOSE_METHOD_NAME).getName();
}
catch (NoSuchMethodException ex) {
try {
return bean.getClass().getMethod(SHUTDOWN_METHOD_NAME).getName();
}
catch (NoSuchMethodException ex2) {
// no candidate destroy method found
}
}
}
return null;
}
return (StringUtils.hasLength(destroyMethodName) ? destroyMethodName : null);
}
/**
* Search for all DestructionAwareBeanPostProcessors in the List.
* @param processors the List to search
* @return the filtered List of DestructionAwareBeanPostProcessors
*/
@Nullable
private List<DestructionAwareBeanPostProcessor> filterPostProcessors(
List<DestructionAwareBeanPostProcessor> processors, Object bean) {
List<DestructionAwareBeanPostProcessor> filteredPostProcessors = null;
if (!CollectionUtils.isEmpty(processors)) {
filteredPostProcessors = new ArrayList<>(processors.size());
for (DestructionAwareBeanPostProcessor processor : processors) {
if (processor.requiresDestruction(bean)) {
filteredPostProcessors.add(processor);
}
}
}
return filteredPostProcessors;
}
@Override
public void run() {
destroy();
@@ -384,12 +326,50 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
if (bean instanceof DisposableBean || bean instanceof AutoCloseable) {
return true;
}
String destroyMethodName = beanDefinition.getDestroyMethodName();
if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName)) {
return (ClassUtils.hasMethod(bean.getClass(), CLOSE_METHOD_NAME) ||
ClassUtils.hasMethod(bean.getClass(), SHUTDOWN_METHOD_NAME));
return inferDestroyMethodIfNecessary(bean, beanDefinition) != null;
}
/**
* If the current value of the given beanDefinition's "destroyMethodName" property is
* {@link AbstractBeanDefinition#INFER_METHOD}, then attempt to infer a destroy method.
* Candidate methods are currently limited to public, no-arg methods named "close" or
* "shutdown" (whether declared locally or inherited). The given BeanDefinition's
* "destroyMethodName" is updated to be null if no such method is found, otherwise set
* to the name of the inferred method. This constant serves as the default for the
* {@code @Bean#destroyMethod} attribute and the value of the constant may also be
* used in XML within the {@code <bean destroy-method="">} or {@code
* <beans default-destroy-method="">} attributes.
* <p>Also processes the {@link java.io.Closeable} and {@link java.lang.AutoCloseable}
* interfaces, reflectively calling the "close" method on implementing beans as well.
*/
@Nullable
private static String inferDestroyMethodIfNecessary(Object bean, RootBeanDefinition beanDefinition) {
String destroyMethodName = beanDefinition.resolvedDestroyMethodName;
if (destroyMethodName == null) {
destroyMethodName = beanDefinition.getDestroyMethodName();
if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName) ||
(destroyMethodName == null && bean instanceof AutoCloseable)) {
// Only perform destroy method inference or Closeable detection
// in case of the bean not explicitly implementing DisposableBean
destroyMethodName = null;
if (!(bean instanceof DisposableBean)) {
try {
destroyMethodName = bean.getClass().getMethod(CLOSE_METHOD_NAME).getName();
}
catch (NoSuchMethodException ex) {
try {
destroyMethodName = bean.getClass().getMethod(SHUTDOWN_METHOD_NAME).getName();
}
catch (NoSuchMethodException ex2) {
// no candidate destroy method found
}
}
}
}
beanDefinition.resolvedDestroyMethodName = (destroyMethodName != null ? destroyMethodName : "");
}
return StringUtils.hasLength(destroyMethodName);
return (StringUtils.hasLength(destroyMethodName) ? destroyMethodName : null);
}
/**
@@ -408,4 +388,25 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
return false;
}
/**
* Search for all DestructionAwareBeanPostProcessors in the List.
* @param processors the List to search
* @return the filtered List of DestructionAwareBeanPostProcessors
*/
@Nullable
private static List<DestructionAwareBeanPostProcessor> filterPostProcessors(
List<DestructionAwareBeanPostProcessor> processors, Object bean) {
List<DestructionAwareBeanPostProcessor> filteredPostProcessors = null;
if (!CollectionUtils.isEmpty(processors)) {
filteredPostProcessors = new ArrayList<>(processors.size());
for (DestructionAwareBeanPostProcessor processor : processors) {
if (processor.requiresDestruction(bean)) {
filteredPostProcessors.add(processor);
}
}
}
return filteredPostProcessors;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -86,6 +86,10 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
@Nullable
volatile Method factoryMethodToIntrospect;
/** Package-visible field for caching a resolved destroy method name (also for inferred). */
@Nullable
volatile String resolvedDestroyMethodName;
/** Common lock for the four constructor fields below. */
final Object constructorArgumentLock = new Object();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,8 +26,8 @@ import java.nio.file.Paths;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceEditor;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.Assert;
import org.springframework.util.ResourceUtils;
/**
* Editor for {@code java.nio.file.Path}, to directly populate a Path
@@ -74,7 +74,7 @@ public class PathEditor extends PropertyEditorSupport {
@Override
public void setAsText(String text) throws IllegalArgumentException {
boolean nioPathCandidate = !text.startsWith(ResourceLoader.CLASSPATH_URL_PREFIX);
boolean nioPathCandidate = !text.startsWith(ResourceUtils.CLASSPATH_URL_PREFIX);
if (nioPathCandidate && !text.startsWith("/")) {
try {
URI uri = new URI(text);
@@ -85,9 +85,13 @@ public class PathEditor extends PropertyEditorSupport {
return;
}
}
catch (URISyntaxException | FileSystemNotFoundException ex) {
// Not a valid URI (let's try as Spring resource location),
// or a URI scheme not registered for NIO (let's try URL
catch (URISyntaxException ex) {
// Not a valid URI; potentially a Windows-style path after
// a file prefix (let's try as Spring resource location)
nioPathCandidate = !text.startsWith(ResourceUtils.FILE_URL_PREFIX);
}
catch (FileSystemNotFoundException ex) {
// URI scheme not registered for NIO (let's try URL
// protocol handlers via Spring's resource mechanism).
}
}
@@ -97,7 +101,7 @@ public class PathEditor extends PropertyEditorSupport {
if (resource == null) {
setValue(null);
}
else if (!resource.exists() && nioPathCandidate) {
else if (nioPathCandidate && !resource.exists()) {
setValue(Paths.get(text).normalize());
}
else {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,12 +19,16 @@ package org.springframework.beans;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.URI;
import java.net.URL;
import java.time.DayOfWeek;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Locale;
@@ -196,6 +200,29 @@ class BeanUtilsTests {
assertThat(longListHolder.getList()).isEmpty();
}
@Test // gh-26531
void copyPropertiesIgnoresGenericsIfSourceOrTargetHasUnresolvableGenerics() throws Exception {
Order original = new Order("test", Arrays.asList("foo", "bar"));
// Create a Proxy that loses the generic type information for the getLineItems() method.
OrderSummary proxy = proxyOrder(original);
assertThat(OrderSummary.class.getDeclaredMethod("getLineItems").toGenericString())
.contains("java.util.List<java.lang.String>");
assertThat(proxy.getClass().getDeclaredMethod("getLineItems").toGenericString())
.contains("java.util.List")
.doesNotContain("<java.lang.String>");
// Ensure that our custom Proxy works as expected.
assertThat(proxy.getId()).isEqualTo("test");
assertThat(proxy.getLineItems()).containsExactly("foo", "bar");
// Copy from proxy to target.
Order target = new Order();
BeanUtils.copyProperties(proxy, target);
assertThat(target.getId()).isEqualTo("test");
assertThat(target.getLineItems()).containsExactly("foo", "bar");
}
@Test
void copyPropertiesWithEditable() throws Exception {
TestBean tb = new TestBean();
@@ -633,4 +660,77 @@ class BeanUtilsTests {
}
}
@SuppressWarnings("unused")
private static class Order {
private String id;
private List<String> lineItems;
Order() {
}
Order(String id, List<String> lineItems) {
this.id = id;
this.lineItems = lineItems;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public List<String> getLineItems() {
return this.lineItems;
}
public void setLineItems(List<String> lineItems) {
this.lineItems = lineItems;
}
@Override
public String toString() {
return "Order [id=" + this.id + ", lineItems=" + this.lineItems + "]";
}
}
private interface OrderSummary {
String getId();
List<String> getLineItems();
}
private OrderSummary proxyOrder(Order order) {
return (OrderSummary) Proxy.newProxyInstance(getClass().getClassLoader(),
new Class<?>[] { OrderSummary.class }, new OrderInvocationHandler(order));
}
private static class OrderInvocationHandler implements InvocationHandler {
private final Order order;
OrderInvocationHandler(Order order) {
this.order = order;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// Ignore args since OrderSummary doesn't declare any methods with arguments,
// and we're not supporting equals(Object), etc.
return Order.class.getDeclaredMethod(method.getName()).invoke(this.order);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,6 +28,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* @author Keith Donald
* @author Juergen Hoeller
* @author Sam Brannen
*/
public class BeanWrapperAutoGrowingTests {
@@ -66,11 +67,6 @@ public class BeanWrapperAutoGrowingTests {
assertThat(bean.getArray()[0]).isInstanceOf(Bean.class);
}
private void assertNotNull(Object propertyValue) {
assertThat(propertyValue).isNotNull();
}
@Test
public void setPropertyValueAutoGrowArray() {
wrapper.setPropertyValue("array[0].prop", "test");
@@ -93,12 +89,39 @@ public class BeanWrapperAutoGrowingTests {
}
@Test
public void getPropertyValueAutoGrowMultiDimensionalArray() {
public void getPropertyValueAutoGrow2dArray() {
assertNotNull(wrapper.getPropertyValue("multiArray[0][0]"));
assertThat(bean.getMultiArray()[0].length).isEqualTo(1);
assertThat(bean.getMultiArray()[0][0]).isInstanceOf(Bean.class);
}
@Test
public void getPropertyValueAutoGrow3dArray() {
assertNotNull(wrapper.getPropertyValue("threeDimensionalArray[1][2][3]"));
assertThat(bean.getThreeDimensionalArray()[1].length).isEqualTo(3);
assertThat(bean.getThreeDimensionalArray()[1][2][3]).isInstanceOf(Bean.class);
}
@Test
public void setPropertyValueAutoGrow2dArray() {
Bean newBean = new Bean();
newBean.setProp("enigma");
wrapper.setPropertyValue("multiArray[2][3]", newBean);
assertThat(bean.getMultiArray()[2][3])
.isInstanceOf(Bean.class)
.extracting(Bean::getProp).isEqualTo("enigma");
}
@Test
public void setPropertyValueAutoGrow3dArray() {
Bean newBean = new Bean();
newBean.setProp("enigma");
wrapper.setPropertyValue("threeDimensionalArray[2][3][4]", newBean);
assertThat(bean.getThreeDimensionalArray()[2][3][4])
.isInstanceOf(Bean.class)
.extracting(Bean::getProp).isEqualTo("enigma");
}
@Test
public void getPropertyValueAutoGrowList() {
assertNotNull(wrapper.getPropertyValue("list[0]"));
@@ -131,7 +154,7 @@ public class BeanWrapperAutoGrowingTests {
public void getPropertyValueAutoGrowListFailsAgainstLimit() {
wrapper.setAutoGrowCollectionLimit(2);
assertThatExceptionOfType(InvalidPropertyException.class).isThrownBy(() ->
assertNotNull(wrapper.getPropertyValue("list[4]")))
wrapper.getPropertyValue("list[4]"))
.withRootCauseInstanceOf(IndexOutOfBoundsException.class);
}
@@ -161,6 +184,11 @@ public class BeanWrapperAutoGrowingTests {
}
private static void assertNotNull(Object propertyValue) {
assertThat(propertyValue).isNotNull();
}
@SuppressWarnings("rawtypes")
public static class Bean {
@@ -174,6 +202,8 @@ public class BeanWrapperAutoGrowingTests {
private Bean[][] multiArray;
private Bean[][][] threeDimensionalArray;
private List<Bean> list;
private List<List<Bean>> multiList;
@@ -214,6 +244,14 @@ public class BeanWrapperAutoGrowingTests {
this.multiArray = multiArray;
}
public Bean[][][] getThreeDimensionalArray() {
return threeDimensionalArray;
}
public void setThreeDimensionalArray(Bean[][][] threeDimensionalArray) {
this.threeDimensionalArray = threeDimensionalArray;
}
public List<Bean> getList() {
return list;
}
@@ -63,9 +63,8 @@ public class BeanFactoryUtilsTests {
@BeforeEach
public void setUp() {
public void setup() {
// Interesting hierarchical factory to test counts.
// Slow to read so we cache it.
DefaultListableBeanFactory grandParent = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(grandParent).loadBeanDefinitions(ROOT_CONTEXT);
@@ -93,7 +92,7 @@ public class BeanFactoryUtilsTests {
* Check that override doesn't count as two separate beans.
*/
@Test
public void testHierarchicalCountBeansWithOverride() throws Exception {
public void testHierarchicalCountBeansWithOverride() {
// Leaf count
assertThat(this.listableBeanFactory.getBeanDefinitionCount() == 1).isTrue();
// Count minus duplicate
@@ -101,14 +100,14 @@ public class BeanFactoryUtilsTests {
}
@Test
public void testHierarchicalNamesWithNoMatch() throws Exception {
public void testHierarchicalNamesWithNoMatch() {
List<String> names = Arrays.asList(
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.listableBeanFactory, NoOp.class));
assertThat(names.size()).isEqualTo(0);
}
@Test
public void testHierarchicalNamesWithMatchOnlyInRoot() throws Exception {
public void testHierarchicalNamesWithMatchOnlyInRoot() {
List<String> names = Arrays.asList(
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.listableBeanFactory, IndexedTestBean.class));
assertThat(names.size()).isEqualTo(1);
@@ -118,7 +117,7 @@ public class BeanFactoryUtilsTests {
}
@Test
public void testGetBeanNamesForTypeWithOverride() throws Exception {
public void testGetBeanNamesForTypeWithOverride() {
List<String> names = Arrays.asList(
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.listableBeanFactory, ITestBean.class));
// includes 2 TestBeans from FactoryBeans (DummyFactory definitions)
@@ -236,7 +235,7 @@ public class BeanFactoryUtilsTests {
}
@Test
public void testHierarchicalResolutionWithOverride() throws Exception {
public void testHierarchicalResolutionWithOverride() {
Object test3 = this.listableBeanFactory.getBean("test3");
Object test = this.listableBeanFactory.getBean("test");
@@ -276,14 +275,14 @@ public class BeanFactoryUtilsTests {
}
@Test
public void testHierarchicalNamesForAnnotationWithNoMatch() throws Exception {
public void testHierarchicalNamesForAnnotationWithNoMatch() {
List<String> names = Arrays.asList(
BeanFactoryUtils.beanNamesForAnnotationIncludingAncestors(this.listableBeanFactory, Override.class));
assertThat(names.size()).isEqualTo(0);
}
@Test
public void testHierarchicalNamesForAnnotationWithMatchOnlyInRoot() throws Exception {
public void testHierarchicalNamesForAnnotationWithMatchOnlyInRoot() {
List<String> names = Arrays.asList(
BeanFactoryUtils.beanNamesForAnnotationIncludingAncestors(this.listableBeanFactory, TestAnnotation.class));
assertThat(names.size()).isEqualTo(1);
@@ -293,7 +292,7 @@ public class BeanFactoryUtilsTests {
}
@Test
public void testGetBeanNamesForAnnotationWithOverride() throws Exception {
public void testGetBeanNamesForAnnotationWithOverride() {
AnnotatedBean annotatedBean = new AnnotatedBean();
this.listableBeanFactory.registerSingleton("anotherAnnotatedBean", annotatedBean);
List<String> names = Arrays.asList(
@@ -433,6 +432,7 @@ public class BeanFactoryUtilsTests {
String basePackage() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@ControllerAdvice
@interface RestControllerAdvice {
@@ -444,18 +444,23 @@ public class BeanFactoryUtilsTests {
String basePackage() default "";
}
@ControllerAdvice("com.example")
static class ControllerAdviceClass {
}
@RestControllerAdvice("com.example")
static class RestControllerAdviceClass {
}
static class TestBeanSmartFactoryBean implements SmartFactoryBean<TestBean> {
private final TestBean testBean = new TestBean("enigma", 42);
private final boolean singleton;
private final boolean prototype;
TestBeanSmartFactoryBean(boolean singleton, boolean prototype) {
@@ -478,7 +483,7 @@ public class BeanFactoryUtilsTests {
return TestBean.class;
}
public TestBean getObject() throws Exception {
public TestBean getObject() {
// We don't really care if the actual instance is a singleton or prototype
// for the tests that use this factory.
return this.testBean;
@@ -785,12 +785,13 @@ class DefaultListableBeanFactoryTests {
factory.registerBeanDefinition("child", childDefinition);
factory.registerAlias("parent", "alias");
TestBean child = (TestBean) factory.getBean("child");
TestBean child = factory.getBean("child", TestBean.class);
assertThat(child.getName()).isEqualTo(EXPECTED_NAME);
assertThat(child.getAge()).isEqualTo(EXPECTED_AGE);
Object mergedBeanDefinition2 = factory.getMergedBeanDefinition("child");
BeanDefinition mergedBeanDefinition1 = factory.getMergedBeanDefinition("child");
BeanDefinition mergedBeanDefinition2 = factory.getMergedBeanDefinition("child");
assertThat(mergedBeanDefinition2).as("Use cached merged bean definition").isEqualTo(mergedBeanDefinition2);
assertThat(mergedBeanDefinition1).as("Use cached merged bean definition").isSameAs(mergedBeanDefinition2);
}
@Test
@@ -1838,8 +1839,7 @@ class DefaultListableBeanFactoryTests {
assertThat(factoryBean).as("The FactoryBean should have been registered.").isNotNull();
FactoryBeanDependentBean bean = (FactoryBeanDependentBean) lbf.autowire(FactoryBeanDependentBean.class,
AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true);
Object mergedBeanDefinition2 = bean.getFactoryBean();
assertThat(mergedBeanDefinition2).as("The FactoryBeanDependentBean should have been autowired 'by type' with the LazyInitFactory.").isEqualTo(mergedBeanDefinition2);
assertThat(bean.getFactoryBean()).as("The FactoryBeanDependentBean should have been autowired 'by type' with the LazyInitFactory.").isEqualTo(factoryBean);
}
@Test
@@ -2388,8 +2388,7 @@ class DefaultListableBeanFactoryTests {
BeanWithDestroyMethod.closeCount = 0;
lbf.preInstantiateSingletons();
lbf.destroySingletons();
Object mergedBeanDefinition2 = BeanWithDestroyMethod.closeCount;
assertThat(mergedBeanDefinition2).as("Destroy methods invoked").isEqualTo(mergedBeanDefinition2);
assertThat(BeanWithDestroyMethod.closeCount).as("Destroy methods invoked").isEqualTo(1);
}
@Test
@@ -2403,8 +2402,7 @@ class DefaultListableBeanFactoryTests {
BeanWithDestroyMethod.closeCount = 0;
lbf.preInstantiateSingletons();
lbf.destroySingletons();
Object mergedBeanDefinition2 = BeanWithDestroyMethod.closeCount;
assertThat(mergedBeanDefinition2).as("Destroy methods invoked").isEqualTo(mergedBeanDefinition2);
assertThat(BeanWithDestroyMethod.closeCount).as("Destroy methods invoked").isEqualTo(2);
}
@Test
@@ -2419,8 +2417,7 @@ class DefaultListableBeanFactoryTests {
BeanWithDestroyMethod.closeCount = 0;
lbf.preInstantiateSingletons();
lbf.destroySingletons();
Object mergedBeanDefinition2 = BeanWithDestroyMethod.closeCount;
assertThat(mergedBeanDefinition2).as("Destroy methods invoked").isEqualTo(mergedBeanDefinition2);
assertThat(BeanWithDestroyMethod.closeCount).as("Destroy methods invoked").isEqualTo(1);
}
@Test
@@ -2542,14 +2539,15 @@ class DefaultListableBeanFactoryTests {
factory.registerBeanDefinition("child", child);
AbstractBeanDefinition def = (AbstractBeanDefinition) factory.getBeanDefinition("child");
Object mergedBeanDefinition2 = def.getScope();
assertThat(mergedBeanDefinition2).as("Child 'scope' not overriding parent scope (it must).").isEqualTo(mergedBeanDefinition2);
assertThat(def.getScope()).as("Child 'scope' not overriding parent scope (it must).").isEqualTo(theChildScope);
}
@Test
void scopeInheritanceForChildBeanDefinitions() {
String theParentScope = "bonanza!";
RootBeanDefinition parent = new RootBeanDefinition();
parent.setScope("bonanza!");
parent.setScope(theParentScope);
AbstractBeanDefinition child = new ChildBeanDefinition("parent");
child.setBeanClass(TestBean.class);
@@ -2559,8 +2557,7 @@ class DefaultListableBeanFactoryTests {
factory.registerBeanDefinition("child", child);
BeanDefinition def = factory.getMergedBeanDefinition("child");
Object mergedBeanDefinition2 = def.getScope();
assertThat(mergedBeanDefinition2).as("Child 'scope' not inherited").isEqualTo(mergedBeanDefinition2);
assertThat(def.getScope()).as("Child 'scope' not inherited").isEqualTo(theParentScope);
}
@Test
@@ -2596,15 +2593,12 @@ class DefaultListableBeanFactoryTests {
});
lbf.preInstantiateSingletons();
TestBean tb = (TestBean) lbf.getBean("test");
Object mergedBeanDefinition2 = tb.getName();
assertThat(mergedBeanDefinition2).as("Name was set on field by IAPP").isEqualTo(mergedBeanDefinition2);
assertThat(tb.getName()).as("Name was set on field by IAPP").isEqualTo(nameSetOnField);
if (!skipPropertyPopulation) {
Object mergedBeanDefinition21 = tb.getAge();
assertThat(mergedBeanDefinition21).as("Property value still set").isEqualTo(mergedBeanDefinition21);
assertThat(tb.getAge()).as("Property value still set").isEqualTo(ageSetByPropertyValue);
}
else {
Object mergedBeanDefinition21 = tb.getAge();
assertThat(mergedBeanDefinition21).as("Property value was NOT set and still has default value").isEqualTo(mergedBeanDefinition21);
assertThat(tb.getAge()).as("Property value was NOT set and still has default value").isEqualTo(0);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -297,6 +297,121 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.nestedTestBeansField[1]).isSameAs(ntb2);
}
@Test
public void testOptionalResourceInjectionWithSingletonRemoval() {
RootBeanDefinition rbd = new RootBeanDefinition(OptionalResourceInjectionBean.class);
rbd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
bf.registerBeanDefinition("annotatedBean", rbd);
TestBean tb = new TestBean();
bf.registerSingleton("testBean", tb);
IndexedTestBean itb = new IndexedTestBean();
bf.registerSingleton("indexedTestBean", itb);
NestedTestBean ntb1 = new NestedTestBean();
bf.registerSingleton("nestedTestBean1", ntb1);
NestedTestBean ntb2 = new NestedTestBean();
bf.registerSingleton("nestedTestBean2", ntb2);
OptionalResourceInjectionBean bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
assertThat(bean.getTestBean3()).isSameAs(tb);
assertThat(bean.getTestBean4()).isSameAs(tb);
assertThat(bean.getIndexedTestBean()).isSameAs(itb);
assertThat(bean.getNestedTestBeans().length).isEqualTo(2);
assertThat(bean.getNestedTestBeans()[0]).isSameAs(ntb1);
assertThat(bean.getNestedTestBeans()[1]).isSameAs(ntb2);
assertThat(bean.nestedTestBeansField.length).isEqualTo(2);
assertThat(bean.nestedTestBeansField[0]).isSameAs(ntb1);
assertThat(bean.nestedTestBeansField[1]).isSameAs(ntb2);
bf.destroySingleton("testBean");
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isNull();
assertThat(bean.getTestBean2()).isNull();
assertThat(bean.getTestBean3()).isNull();
assertThat(bean.getTestBean4()).isNull();
assertThat(bean.getIndexedTestBean()).isSameAs(itb);
assertThat(bean.getNestedTestBeans().length).isEqualTo(2);
assertThat(bean.getNestedTestBeans()[0]).isSameAs(ntb1);
assertThat(bean.getNestedTestBeans()[1]).isSameAs(ntb2);
assertThat(bean.nestedTestBeansField.length).isEqualTo(2);
assertThat(bean.nestedTestBeansField[0]).isSameAs(ntb1);
assertThat(bean.nestedTestBeansField[1]).isSameAs(ntb2);
bf.registerSingleton("testBean", tb);
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
assertThat(bean.getTestBean3()).isSameAs(tb);
assertThat(bean.getTestBean4()).isSameAs(tb);
assertThat(bean.getIndexedTestBean()).isSameAs(itb);
assertThat(bean.getNestedTestBeans().length).isEqualTo(2);
assertThat(bean.getNestedTestBeans()[0]).isSameAs(ntb1);
assertThat(bean.getNestedTestBeans()[1]).isSameAs(ntb2);
assertThat(bean.nestedTestBeansField.length).isEqualTo(2);
assertThat(bean.nestedTestBeansField[0]).isSameAs(ntb1);
assertThat(bean.nestedTestBeansField[1]).isSameAs(ntb2);
}
@Test
public void testOptionalResourceInjectionWithBeanDefinitionRemoval() {
RootBeanDefinition rbd = new RootBeanDefinition(OptionalResourceInjectionBean.class);
rbd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
bf.registerBeanDefinition("annotatedBean", rbd);
bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
IndexedTestBean itb = new IndexedTestBean();
bf.registerSingleton("indexedTestBean", itb);
NestedTestBean ntb1 = new NestedTestBean();
bf.registerSingleton("nestedTestBean1", ntb1);
NestedTestBean ntb2 = new NestedTestBean();
bf.registerSingleton("nestedTestBean2", ntb2);
OptionalResourceInjectionBean bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
assertThat(bean.getTestBean2()).isSameAs(bf.getBean("testBean"));
assertThat(bean.getTestBean3()).isSameAs(bf.getBean("testBean"));
assertThat(bean.getTestBean4()).isSameAs(bf.getBean("testBean"));
assertThat(bean.getIndexedTestBean()).isSameAs(itb);
assertThat(bean.getNestedTestBeans().length).isEqualTo(2);
assertThat(bean.getNestedTestBeans()[0]).isSameAs(ntb1);
assertThat(bean.getNestedTestBeans()[1]).isSameAs(ntb2);
assertThat(bean.nestedTestBeansField.length).isEqualTo(2);
assertThat(bean.nestedTestBeansField[0]).isSameAs(ntb1);
assertThat(bean.nestedTestBeansField[1]).isSameAs(ntb2);
bf.removeBeanDefinition("testBean");
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isNull();
assertThat(bean.getTestBean2()).isNull();
assertThat(bean.getTestBean3()).isNull();
assertThat(bean.getTestBean4()).isNull();
assertThat(bean.getIndexedTestBean()).isSameAs(itb);
assertThat(bean.getNestedTestBeans().length).isEqualTo(2);
assertThat(bean.getNestedTestBeans()[0]).isSameAs(ntb1);
assertThat(bean.getNestedTestBeans()[1]).isSameAs(ntb2);
assertThat(bean.nestedTestBeansField.length).isEqualTo(2);
assertThat(bean.nestedTestBeansField[0]).isSameAs(ntb1);
assertThat(bean.nestedTestBeansField[1]).isSameAs(ntb2);
bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isSameAs(bf.getBean("testBean"));
assertThat(bean.getTestBean2()).isSameAs(bf.getBean("testBean"));
assertThat(bean.getTestBean3()).isSameAs(bf.getBean("testBean"));
assertThat(bean.getTestBean4()).isSameAs(bf.getBean("testBean"));
assertThat(bean.getIndexedTestBean()).isSameAs(itb);
assertThat(bean.getNestedTestBeans().length).isEqualTo(2);
assertThat(bean.getNestedTestBeans()[0]).isSameAs(ntb1);
assertThat(bean.getNestedTestBeans()[1]).isSameAs(ntb2);
assertThat(bean.nestedTestBeansField.length).isEqualTo(2);
assertThat(bean.nestedTestBeansField[0]).isSameAs(ntb1);
assertThat(bean.nestedTestBeansField[1]).isSameAs(ntb2);
}
@Test
public void testOptionalCollectionResourceInjection() {
RootBeanDefinition rbd = new RootBeanDefinition(OptionalCollectionResourceInjectionBean.class);
@@ -533,6 +648,83 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertThat(bean.getBeanFactory()).isSameAs(bf);
}
@Test
public void testConstructorResourceInjectionWithSingletonRemoval() {
RootBeanDefinition bd = new RootBeanDefinition(ConstructorResourceInjectionBean.class);
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
bf.registerBeanDefinition("annotatedBean", bd);
TestBean tb = new TestBean();
bf.registerSingleton("testBean", tb);
NestedTestBean ntb = new NestedTestBean();
bf.registerSingleton("nestedTestBean", ntb);
ConstructorResourceInjectionBean bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
assertThat(bean.getTestBean3()).isSameAs(tb);
assertThat(bean.getTestBean4()).isSameAs(tb);
assertThat(bean.getNestedTestBean()).isSameAs(ntb);
assertThat(bean.getBeanFactory()).isSameAs(bf);
bf.destroySingleton("nestedTestBean");
bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
assertThat(bean.getTestBean3()).isSameAs(tb);
assertThat(bean.getTestBean4()).isSameAs(tb);
assertThat(bean.getNestedTestBean()).isNull();
assertThat(bean.getBeanFactory()).isSameAs(bf);
bf.registerSingleton("nestedTestBean", ntb);
bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
assertThat(bean.getTestBean3()).isSameAs(tb);
assertThat(bean.getTestBean4()).isSameAs(tb);
assertThat(bean.getNestedTestBean()).isSameAs(ntb);
assertThat(bean.getBeanFactory()).isSameAs(bf);
}
@Test
public void testConstructorResourceInjectionWithBeanDefinitionRemoval() {
RootBeanDefinition bd = new RootBeanDefinition(ConstructorResourceInjectionBean.class);
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
bf.registerBeanDefinition("annotatedBean", bd);
TestBean tb = new TestBean();
bf.registerSingleton("testBean", tb);
bf.registerBeanDefinition("nestedTestBean", new RootBeanDefinition(NestedTestBean.class));
ConstructorResourceInjectionBean bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
assertThat(bean.getTestBean3()).isSameAs(tb);
assertThat(bean.getTestBean4()).isSameAs(tb);
assertThat(bean.getNestedTestBean()).isSameAs(bf.getBean("nestedTestBean"));
assertThat(bean.getBeanFactory()).isSameAs(bf);
bf.removeBeanDefinition("nestedTestBean");
bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
assertThat(bean.getTestBean3()).isSameAs(tb);
assertThat(bean.getTestBean4()).isSameAs(tb);
assertThat(bean.getNestedTestBean()).isNull();
assertThat(bean.getBeanFactory()).isSameAs(bf);
bf.registerBeanDefinition("nestedTestBean", new RootBeanDefinition(NestedTestBean.class));
bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean");
assertThat(bean.getTestBean()).isSameAs(tb);
assertThat(bean.getTestBean2()).isSameAs(tb);
assertThat(bean.getTestBean3()).isSameAs(tb);
assertThat(bean.getTestBean4()).isSameAs(tb);
assertThat(bean.getNestedTestBean()).isSameAs(bf.getBean("nestedTestBean"));
assertThat(bean.getBeanFactory()).isSameAs(bf);
}
@Test
public void testConstructorResourceInjectionWithNullFromFactoryBean() {
RootBeanDefinition bd = new RootBeanDefinition(ConstructorResourceInjectionBean.class);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author 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,9 +17,11 @@
package org.springframework.beans.factory.config;
import java.net.URL;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.junit.jupiter.api.Test;
import org.yaml.snakeyaml.constructor.ConstructorException;
@@ -28,7 +30,6 @@ import org.yaml.snakeyaml.scanner.ScannerException;
import org.springframework.core.io.ByteArrayResource;
import static java.util.stream.Collectors.toList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.entry;
@@ -39,10 +40,12 @@ import static org.assertj.core.api.Assertions.entry;
* @author Dave Syer
* @author Juergen Hoeller
* @author Sam Brannen
* @author Brian Clozel
*/
class YamlProcessorTests {
private final YamlProcessor processor = new YamlProcessor() {};
private final YamlProcessor processor = new YamlProcessor() {
};
@Test
@@ -79,8 +82,8 @@ class YamlProcessorTests {
void badResource() {
setYaml("foo: bar\ncd\nspam:\n foo: baz");
assertThatExceptionOfType(ScannerException.class)
.isThrownBy(() -> this.processor.process((properties, map) -> {}))
.withMessageContaining("line 3, column 1");
.isThrownBy(() -> this.processor.process((properties, map) -> {}))
.withMessageContaining("line 3, column 1");
}
@Test
@@ -127,8 +130,8 @@ class YamlProcessorTests {
Map<String, Object> bar = (Map<String, Object>) map.get("bar");
assertThat(bar.get("spam")).isEqualTo("bucket");
List<Object> keysFromProperties = properties.keySet().stream().collect(toList());
List<String> keysFromFlattenedMap = flattenedMap.keySet().stream().collect(toList());
List<Object> keysFromProperties = new ArrayList<>(properties.keySet());
List<String> keysFromFlattenedMap = new ArrayList<>(flattenedMap.keySet());
assertThat(keysFromProperties).containsExactlyInAnyOrderElementsOf(keysFromFlattenedMap);
// Keys in the Properties object are sorted.
assertThat(keysFromProperties).containsExactly("bar.spam", "cat", "foo");
@@ -138,14 +141,23 @@ class YamlProcessorTests {
}
@Test
void customTypeSupportedByDefault() throws Exception {
void standardTypesSupportedByDefault() throws Exception {
setYaml("value: !!set\n ? first\n ? second");
this.processor.process((properties, map) -> {
assertThat(properties).containsExactly(entry("value[0]", "first"), entry("value[1]", "second"));
assertThat(map.get("value")).isInstanceOf(Set.class);
Set<String> set = (Set<String>) map.get("value");
assertThat(set).containsExactly("first", "second");
});
}
@Test
void customTypeNotSupportedByDefault() throws Exception {
URL url = new URL("https://localhost:9000/");
setYaml("value: !!java.net.URL [\"" + url + "\"]");
this.processor.process((properties, map) -> {
assertThat(properties).containsExactly(entry("value", url));
assertThat(map).containsExactly(entry("value", url));
});
assertThatExceptionOfType(ConstructorException.class)
.isThrownBy(() -> this.processor.process((properties, map) -> {}))
.withMessageContaining("Unsupported type encountered in YAML document: java.net.URL");
}
@Test
@@ -168,8 +180,8 @@ class YamlProcessorTests {
setYaml("value: !!java.net.URL [\"https://localhost:9000/\"]");
assertThatExceptionOfType(ConstructorException.class)
.isThrownBy(() -> this.processor.process((properties, map) -> {}))
.withMessageContaining("Unsupported type encountered in YAML document: java.net.URL");
.isThrownBy(() -> this.processor.process((properties, map) -> {}))
.withMessageContaining("Unsupported type encountered in YAML document: java.net.URL");
}
private void setYaml(String yaml) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -133,6 +133,16 @@ public class BeanDefinitionTests {
assertThat(bd.equals(otherBd)).isTrue();
assertThat(otherBd.equals(bd)).isTrue();
assertThat(bd.hashCode() == otherBd.hashCode()).isTrue();
bd.getPropertyValues();
assertThat(bd.equals(otherBd)).isTrue();
assertThat(otherBd.equals(bd)).isTrue();
assertThat(bd.hashCode() == otherBd.hashCode()).isTrue();
bd.getConstructorArgumentValues();
assertThat(bd.equals(otherBd)).isTrue();
assertThat(otherBd.equals(bd)).isTrue();
assertThat(bd.hashCode() == otherBd.hashCode()).isTrue();
}
@Test
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,57 +34,78 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
public class PathEditorTests {
@Test
public void testClasspathPathName() throws Exception {
public void testClasspathPathName() {
PropertyEditor pathEditor = new PathEditor();
pathEditor.setAsText("classpath:" + ClassUtils.classPackageAsResourcePath(getClass()) + "/" +
ClassUtils.getShortName(getClass()) + ".class");
Object value = pathEditor.getValue();
boolean condition = value instanceof Path;
assertThat(condition).isTrue();
assertThat(value instanceof Path).isTrue();
Path path = (Path) value;
assertThat(path.toFile().exists()).isTrue();
}
@Test
public void testWithNonExistentResource() throws Exception {
public void testWithNonExistentResource() {
PropertyEditor propertyEditor = new PathEditor();
assertThatIllegalArgumentException().isThrownBy(() ->
propertyEditor.setAsText("classpath:/no_way_this_file_is_found.doc"));
}
@Test
public void testWithNonExistentPath() throws Exception {
public void testWithNonExistentPath() {
PropertyEditor pathEditor = new PathEditor();
pathEditor.setAsText("file:/no_way_this_file_is_found.doc");
Object value = pathEditor.getValue();
boolean condition1 = value instanceof Path;
assertThat(condition1).isTrue();
assertThat(value instanceof Path).isTrue();
Path path = (Path) value;
boolean condition = !path.toFile().exists();
assertThat(condition).isTrue();
assertThat(!path.toFile().exists()).isTrue();
}
@Test
public void testAbsolutePath() throws Exception {
public void testAbsolutePath() {
PropertyEditor pathEditor = new PathEditor();
pathEditor.setAsText("/no_way_this_file_is_found.doc");
Object value = pathEditor.getValue();
boolean condition1 = value instanceof Path;
assertThat(condition1).isTrue();
assertThat(value instanceof Path).isTrue();
Path path = (Path) value;
boolean condition = !path.toFile().exists();
assertThat(condition).isTrue();
assertThat(!path.toFile().exists()).isTrue();
}
@Test
public void testUnqualifiedPathNameFound() throws Exception {
public void testWindowsAbsolutePath() {
PropertyEditor pathEditor = new PathEditor();
pathEditor.setAsText("C:\\no_way_this_file_is_found.doc");
Object value = pathEditor.getValue();
assertThat(value instanceof Path).isTrue();
Path path = (Path) value;
assertThat(!path.toFile().exists()).isTrue();
}
@Test
public void testWindowsAbsoluteFilePath() {
PropertyEditor pathEditor = new PathEditor();
try {
pathEditor.setAsText("file://C:\\no_way_this_file_is_found.doc");
Object value = pathEditor.getValue();
assertThat(value instanceof Path).isTrue();
Path path = (Path) value;
assertThat(!path.toFile().exists()).isTrue();
}
catch (IllegalArgumentException ex) {
if (File.separatorChar == '\\') { // on Windows, otherwise silently ignore
throw ex;
}
}
}
@Test
public void testUnqualifiedPathNameFound() {
PropertyEditor pathEditor = new PathEditor();
String fileName = ClassUtils.classPackageAsResourcePath(getClass()) + "/" +
ClassUtils.getShortName(getClass()) + ".class";
pathEditor.setAsText(fileName);
Object value = pathEditor.getValue();
boolean condition = value instanceof Path;
assertThat(condition).isTrue();
assertThat(value instanceof Path).isTrue();
Path path = (Path) value;
File file = path.toFile();
assertThat(file.exists()).isTrue();
@@ -96,14 +117,13 @@ public class PathEditorTests {
}
@Test
public void testUnqualifiedPathNameNotFound() throws Exception {
public void testUnqualifiedPathNameNotFound() {
PropertyEditor pathEditor = new PathEditor();
String fileName = ClassUtils.classPackageAsResourcePath(getClass()) + "/" +
ClassUtils.getShortName(getClass()) + ".clazz";
pathEditor.setAsText(fileName);
Object value = pathEditor.getValue();
boolean condition = value instanceof Path;
assertThat(condition).isTrue();
assertThat(value instanceof Path).isTrue();
Path path = (Path) value;
File file = path.toFile();
assertThat(file.exists()).isFalse();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,7 +31,8 @@ import org.springframework.core.annotation.AliasFor;
*
* <p>In contrast to the {@link Cacheable @Cacheable} annotation, this annotation
* does not cause the advised method to be skipped. Rather, it always causes the
* method to be invoked and its result to be stored in the associated cache. Note
* method to be invoked and its result to be stored in the associated cache if the
* {@link #condition()} and {@link #unless()} expressions match accordingly. Note
* that Java8's {@code Optional} return types are automatically handled and its
* content is stored in the cache if present.
*
@@ -118,10 +119,15 @@ public @interface CachePut {
/**
* Spring Expression Language (SpEL) expression used for making the cache
* put operation conditional.
* <p>This expression is evaluated after the method has been called due to the
* nature of the put operation and can therefore refer to the {@code result}.
* <p>Default is {@code ""}, meaning the method result is always cached.
* <p>The SpEL expression evaluates against a dedicated context that provides the
* following meta-data:
* <ul>
* <li>{@code #result} for a reference to the result of the method invocation. For
* supported wrappers such as {@code Optional}, {@code #result} refers to the actual
* object, not the wrapper</li>
* <li>{@code #root.method}, {@code #root.target}, and {@code #root.caches} for
* references to the {@link java.lang.reflect.Method method}, target object, and
* affected cache(s) respectively.</li>
@@ -136,8 +142,6 @@ public @interface CachePut {
/**
* Spring Expression Language (SpEL) expression used to veto the cache put operation.
* <p>Unlike {@link #condition}, this expression is evaluated after the method
* has been called and can therefore refer to the {@code result}.
* <p>Default is {@code ""}, meaning that caching is never vetoed.
* <p>The SpEL expression evaluates against a dedicated context that provides the
* following meta-data:
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,6 +35,8 @@ import java.util.function.Consumer;
* @param <E> the specific {@code ApplicationEvent} subclass to listen to
* @see org.springframework.context.ApplicationEvent
* @see org.springframework.context.event.ApplicationEventMulticaster
* @see org.springframework.context.event.SmartApplicationListener
* @see org.springframework.context.event.GenericApplicationListener
* @see org.springframework.context.event.EventListener
*/
@FunctionalInterface
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2021 the original author 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.context;
import java.util.function.Consumer;
import org.springframework.core.ResolvableType;
import org.springframework.core.ResolvableTypeProvider;
import org.springframework.util.Assert;
@@ -23,11 +25,12 @@ import org.springframework.util.Assert;
/**
* An {@link ApplicationEvent} that carries an arbitrary payload.
*
* <p>Mainly intended for internal use within the framework.
*
* @author Stephane Nicoll
* @author Juergen Hoeller
* @since 4.2
* @param <T> the payload type of the event
* @see ApplicationEventPublisher#publishEvent(Object)
* @see ApplicationListener#forPayload(Consumer)
*/
@SuppressWarnings("serial")
public class PayloadApplicationEvent<T> extends ApplicationEvent implements ResolvableTypeProvider {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,10 +26,10 @@ import org.springframework.core.io.ResourceLoader;
* the {@link org.springframework.context.ApplicationContextAware} interface.
*
* <p>Note that {@link org.springframework.core.io.Resource} dependencies can also
* be exposed as bean properties of type {@code Resource}, populated via Strings
* with automatic type conversion by the bean factory. This removes the need for
* implementing any callback interface just for the purpose of accessing a
* specific file resource.
* be exposed as bean properties of type {@code Resource} or {@code Resource[]},
* populated via Strings with automatic type conversion by the bean factory. This
* removes the need for implementing any callback interface just for the purpose
* of accessing specific file resources.
*
* <p>You typically need a {@link ResourceLoader} when your application object has to
* access a variety of file resources whose names are calculated. A good strategy is
@@ -48,7 +48,7 @@ import org.springframework.core.io.ResourceLoader;
* default; see also the {@code ResourcePatternUtils.getResourcePatternResolver} method.
*
* <p>As an alternative to a {@code ResourcePatternResolver} dependency, consider
* exposing bean properties of type {@code Resource} array, populated via pattern
* exposing bean properties of type {@code Resource[]} array, populated via pattern
* Strings with automatic type conversion by the bean factory at binding time.
*
* @author Juergen Hoeller
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -52,6 +52,7 @@ import org.springframework.context.ApplicationStartupAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ConfigurationClassEnhancer.EnhancedConfiguration;
import org.springframework.core.NativeDetector;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.env.Environment;
@@ -74,16 +75,17 @@ import org.springframework.util.ClassUtils;
*
* <p>Registered by default when using {@code <context:annotation-config/>} or
* {@code <context:component-scan/>}. Otherwise, may be declared manually as
* with any other BeanFactoryPostProcessor.
* with any other {@link BeanFactoryPostProcessor}.
*
* <p>This post processor is priority-ordered as it is important that any
* {@link Bean} methods declared in {@code @Configuration} classes have
* {@link Bean @Bean} methods declared in {@code @Configuration} classes have
* their corresponding bean definitions registered before any other
* {@link BeanFactoryPostProcessor} executes.
* {@code BeanFactoryPostProcessor} executes.
*
* @author Chris Beams
* @author Juergen Hoeller
* @author Phillip Webb
* @author Sam Brannen
* @since 3.0
*/
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
@@ -104,13 +106,6 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
private static final String IMPORT_REGISTRY_BEAN_NAME =
ConfigurationClassPostProcessor.class.getName() + ".importRegistry";
/**
* Whether this environment lives within a native image.
* Exposed as a private static field rather than in a {@code NativeImageDetector.inNativeImage()} static method due to https://github.com/oracle/graal/issues/2594.
* @see <a href="https://github.com/oracle/graal/blob/master/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/ImageInfo.java">ImageInfo.java</a>
*/
private static final boolean IN_NATIVE_IMAGE = (System.getProperty("org.graalvm.nativeimage.imagecode") != null);
private final Log logger = LogFactory.getLog(getClass());
@@ -395,21 +390,30 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
AnnotationMetadata annotationMetadata = null;
MethodMetadata methodMetadata = null;
if (beanDef instanceof AnnotatedBeanDefinition) {
methodMetadata = ((AnnotatedBeanDefinition) beanDef).getFactoryMethodMetadata();
AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDef;
annotationMetadata = annotatedBeanDefinition.getMetadata();
methodMetadata = annotatedBeanDefinition.getFactoryMethodMetadata();
}
if ((configClassAttr != null || methodMetadata != null) && beanDef instanceof AbstractBeanDefinition) {
// Configuration class (full or lite) or a configuration-derived @Bean method
// -> resolve bean class at this point...
// -> eagerly resolve bean class at this point, unless it's a 'lite' configuration
// or component class without @Bean methods.
AbstractBeanDefinition abd = (AbstractBeanDefinition) beanDef;
if (!abd.hasBeanClass()) {
try {
abd.resolveBeanClass(this.beanClassLoader);
}
catch (Throwable ex) {
throw new IllegalStateException(
"Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
boolean liteConfigurationCandidateWithoutBeanMethods =
(ConfigurationClassUtils.CONFIGURATION_CLASS_LITE.equals(configClassAttr) &&
annotationMetadata != null && !ConfigurationClassUtils.hasBeanMethods(annotationMetadata));
if (!liteConfigurationCandidateWithoutBeanMethods) {
try {
abd.resolveBeanClass(this.beanClassLoader);
}
catch (Throwable ex) {
throw new IllegalStateException(
"Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
}
}
}
}
@@ -427,15 +431,11 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
}
}
if (configBeanDefs.isEmpty()) {
if (configBeanDefs.isEmpty() || NativeDetector.inNativeImage()) {
// nothing to enhance -> return immediately
enhanceConfigClasses.end();
return;
}
if (IN_NATIVE_IMAGE) {
throw new BeanDefinitionStoreException("@Configuration classes need to be marked as " +
"proxyBeanMethods=false. Found: " + configBeanDefs.keySet());
}
ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 the original author 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,6 +46,7 @@ import org.springframework.stereotype.Component;
*
* @author Chris Beams
* @author Juergen Hoeller
* @author Sam Brannen
* @since 3.1
*/
abstract class ConfigurationClassUtils {
@@ -162,6 +163,10 @@ abstract class ConfigurationClassUtils {
}
// Finally, let's look for @Bean methods...
return hasBeanMethods(metadata);
}
static boolean hasBeanMethods(AnnotationMetadata metadata) {
try {
return metadata.hasAnnotatedMethods(Bean.class.getName());
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.beans.factory.BeanClassLoaderAware;
@@ -137,6 +138,22 @@ public abstract class AbstractApplicationEventMulticaster
}
}
@Override
public void removeApplicationListeners(Predicate<ApplicationListener<?>> predicate) {
synchronized (this.defaultRetriever) {
this.defaultRetriever.applicationListeners.removeIf(predicate);
this.retrieverCache.clear();
}
}
@Override
public void removeApplicationListenerBeans(Predicate<String> predicate) {
synchronized (this.defaultRetriever) {
this.defaultRetriever.applicationListenerBeans.removeIf(predicate);
this.retrieverCache.clear();
}
}
@Override
public void removeAllListeners() {
synchronized (this.defaultRetriever) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 the original author 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.context.event;
import java.util.function.Predicate;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.ResolvableType;
@@ -39,31 +41,68 @@ public interface ApplicationEventMulticaster {
/**
* Add a listener to be notified of all events.
* @param listener the listener to add
* @see #removeApplicationListener(ApplicationListener)
* @see #removeApplicationListeners(Predicate)
*/
void addApplicationListener(ApplicationListener<?> listener);
/**
* Add a listener bean to be notified of all events.
* @param listenerBeanName the name of the listener bean to add
* @see #removeApplicationListenerBean(String)
* @see #removeApplicationListenerBeans(Predicate)
*/
void addApplicationListenerBean(String listenerBeanName);
/**
* Remove a listener from the notification list.
* @param listener the listener to remove
* @see #addApplicationListener(ApplicationListener)
* @see #removeApplicationListeners(Predicate)
*/
void removeApplicationListener(ApplicationListener<?> listener);
/**
* Remove a listener bean from the notification list.
* @param listenerBeanName the name of the listener bean to remove
* @see #addApplicationListenerBean(String)
* @see #removeApplicationListenerBeans(Predicate)
*/
void removeApplicationListenerBean(String listenerBeanName);
/**
* Remove all matching listeners from the set of registered
* {@code ApplicationListener} instances (which includes adapter classes
* such as {@link ApplicationListenerMethodAdapter}, e.g. for annotated
* {@link EventListener} methods).
* <p>Note: This just applies to instance registrations, not to listeners
* registered by bean name.
* @param predicate the predicate to identify listener instances to remove,
* e.g. checking {@link SmartApplicationListener#getListenerId()}
* @since 5.3.5
* @see #addApplicationListener(ApplicationListener)
* @see #removeApplicationListener(ApplicationListener)
*/
void removeApplicationListeners(Predicate<ApplicationListener<?>> predicate);
/**
* Remove all matching listener beans from the set of registered
* listener bean names (referring to bean classes which in turn
* implement the {@link ApplicationListener} interface directly).
* <p>Note: This just applies to bean name registrations, not to
* programmatically registered {@code ApplicationListener} instances.
* @param predicate the predicate to identify listener bean names to remove
* @since 5.3.5
* @see #addApplicationListenerBean(String)
* @see #removeApplicationListenerBean(String)
*/
void removeApplicationListenerBeans(Predicate<String> predicate);
/**
* Remove all listeners registered with this multicaster.
* <p>After a remove call, the multicaster will perform no action
* on event notification until new listeners are registered.
* @see #removeApplicationListeners(Predicate)
*/
void removeAllListeners();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.StringJoiner;
import java.util.concurrent.CompletionStage;
import org.apache.commons.logging.Log;
@@ -89,6 +90,9 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
private final int order;
@Nullable
private volatile String listenerId;
@Nullable
private ApplicationContext applicationContext;
@@ -113,6 +117,8 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
this.declaredEventTypes = resolveDeclaredEventTypes(method, ann);
this.condition = (ann != null ? ann.condition() : null);
this.order = resolveOrder(this.targetMethod);
String id = (ann != null ? ann.id() : "");
this.listenerId = (!id.isEmpty() ? id : null);
}
private static List<ResolvableType> resolveDeclaredEventTypes(Method method, @Nullable EventListener ann) {
@@ -186,6 +192,32 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
return this.order;
}
@Override
public String getListenerId() {
String id = this.listenerId;
if (id == null) {
id = getDefaultListenerId();
this.listenerId = id;
}
return id;
}
/**
* Determine the default id for the target listener, to be applied in case of
* no {@link EventListener#id() annotation-specified id value}.
* <p>The default implementation builds a method name with parameter types.
* @since 5.3.5
* @see #getListenerId()
*/
protected String getDefaultListenerId() {
Method method = getTargetMethod();
StringJoiner sj = new StringJoiner(",", "(", ")");
for (Class<?> paramType : method.getParameterTypes()) {
sj.add(paramType.getName());
}
return ClassUtils.getQualifiedMethodName(method) + sj.toString();
}
/**
* Process the specified {@link ApplicationEvent}, checking if the condition
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.function.Predicate;
import org.springframework.context.ApplicationEvent;
import org.springframework.core.annotation.AliasFor;
@@ -128,4 +129,13 @@ public @interface EventListener {
*/
String condition() default "";
/**
* An optional identifier for the listener, defaulting to the fully-qualified
* signature of the declaring method (e.g. "mypackage.MyClass.myMethod()").
* @since 5.3.5
* @see org.springframework.context.ApplicationListener#getListenerId()
* @see ApplicationEventMulticaster#removeApplicationListeners(Predicate)
*/
String id() default "";
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2021 the original author 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,7 @@ package org.springframework.context.event;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.Ordered;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;
/**
* Extended variant of the standard {@link ApplicationListener} interface,
@@ -28,13 +26,26 @@ import org.springframework.lang.Nullable;
*
* <p>As of Spring Framework 4.2, this interface supersedes the Class-based
* {@link SmartApplicationListener} with full handling of generic event types.
* As of 5.3.5, it formally extends {@link SmartApplicationListener}, adapting
* {@link #supportsEventType(Class)} to {@link #supportsEventType(ResolvableType)}
* with a default method.
*
* @author Stephane Nicoll
* @author Juergen Hoeller
* @since 4.2
* @see SmartApplicationListener
* @see GenericApplicationListenerAdapter
*/
public interface GenericApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
public interface GenericApplicationListener extends SmartApplicationListener {
/**
* Overrides {@link SmartApplicationListener#supportsEventType(Class)} with
* delegation to {@link #supportsEventType(ResolvableType)}.
*/
@Override
default boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
return supportsEventType(ResolvableType.forClass(eventType));
}
/**
* Determine whether this listener actually supports the given event type.
@@ -42,22 +53,4 @@ public interface GenericApplicationListener extends ApplicationListener<Applicat
*/
boolean supportsEventType(ResolvableType eventType);
/**
* Determine whether this listener actually supports the given source type.
* <p>The default implementation always returns {@code true}.
* @param sourceType the source type, or {@code null} if no source
*/
default boolean supportsSourceType(@Nullable Class<?> sourceType) {
return true;
}
/**
* Determine this listener's order in a set of listeners for the same event.
* <p>The default implementation returns {@link #LOWEST_PRECEDENCE}.
*/
@Override
default int getOrder() {
return LOWEST_PRECEDENCE;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,7 +36,7 @@ import org.springframework.util.ConcurrentReferenceHashMap;
* @since 3.0
* @see org.springframework.context.ApplicationListener#onApplicationEvent
*/
public class GenericApplicationListenerAdapter implements GenericApplicationListener, SmartApplicationListener {
public class GenericApplicationListenerAdapter implements GenericApplicationListener {
private static final Map<Class<?>, ResolvableType> eventTypeCache = new ConcurrentReferenceHashMap<>();
@@ -67,7 +67,10 @@ public class GenericApplicationListenerAdapter implements GenericApplicationList
@Override
@SuppressWarnings("unchecked")
public boolean supportsEventType(ResolvableType eventType) {
if (this.delegate instanceof SmartApplicationListener) {
if (this.delegate instanceof GenericApplicationListener) {
return ((GenericApplicationListener) this.delegate).supportsEventType(eventType);
}
else if (this.delegate instanceof SmartApplicationListener) {
Class<? extends ApplicationEvent> eventClass = (Class<? extends ApplicationEvent>) eventType.resolve();
return (eventClass != null && ((SmartApplicationListener) this.delegate).supportsEventType(eventClass));
}
@@ -76,11 +79,6 @@ public class GenericApplicationListenerAdapter implements GenericApplicationList
}
}
@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
return supportsEventType(ResolvableType.forClass(eventType));
}
@Override
public boolean supportsSourceType(@Nullable Class<?> sourceType) {
return !(this.delegate instanceof SmartApplicationListener) ||
@@ -92,6 +90,12 @@ public class GenericApplicationListenerAdapter implements GenericApplicationList
return (this.delegate instanceof Ordered ? ((Ordered) this.delegate).getOrder() : Ordered.LOWEST_PRECEDENCE);
}
@Override
public String getListenerId() {
return (this.delegate instanceof SmartApplicationListener ?
((SmartApplicationListener) this.delegate).getListenerId() : "");
}
@Nullable
private static ResolvableType resolveDeclaredEventType(ApplicationListener<ApplicationEvent> listener) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author 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,8 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.PayloadApplicationEvent;
import org.springframework.core.ResolvableType;
import org.springframework.core.metrics.ApplicationStartup;
import org.springframework.core.metrics.StartupStep;
import org.springframework.lang.Nullable;
import org.springframework.util.ErrorHandler;
@@ -58,7 +57,7 @@ public class SimpleApplicationEventMulticaster extends AbstractApplicationEventM
private ErrorHandler errorHandler;
@Nullable
private ApplicationStartup applicationStartup;
private volatile Log lazyLogger;
/**
@@ -127,22 +126,6 @@ public class SimpleApplicationEventMulticaster extends AbstractApplicationEventM
return this.errorHandler;
}
/**
* Set the {@link ApplicationStartup} to track event listener invocations during startup.
* @since 5.3
*/
public void setApplicationStartup(@Nullable ApplicationStartup applicationStartup) {
this.applicationStartup = applicationStartup;
}
/**
* Return the current application startup for this multicaster.
*/
@Nullable
public ApplicationStartup getApplicationStartup() {
return this.applicationStartup;
}
@Override
public void multicastEvent(ApplicationEvent event) {
multicastEvent(event, resolveDefaultEventType(event));
@@ -156,16 +139,6 @@ public class SimpleApplicationEventMulticaster extends AbstractApplicationEventM
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else if (this.applicationStartup != null) {
StartupStep invocationStep = this.applicationStartup.start("spring.event.invoke-listener");
invokeListener(listener, event);
invocationStep.tag("event", event::toString);
if (eventType != null) {
invocationStep.tag("eventType", eventType::toString);
}
invocationStep.tag("listener", listener::toString);
invocationStep.end();
}
else {
invokeListener(listener, event);
}
@@ -204,12 +177,18 @@ public class SimpleApplicationEventMulticaster extends AbstractApplicationEventM
}
catch (ClassCastException ex) {
String msg = ex.getMessage();
if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
if (msg == null || matchesClassCastMessage(msg, event.getClass()) ||
(event instanceof PayloadApplicationEvent &&
matchesClassCastMessage(msg, ((PayloadApplicationEvent) event).getPayload().getClass()))) {
// Possibly a lambda-defined listener which we could not resolve the generic event type for
// -> let's suppress the exception and just log a debug message.
Log logger = LogFactory.getLog(getClass());
if (logger.isTraceEnabled()) {
logger.trace("Non-matching event type for listener: " + listener, ex);
// -> let's suppress the exception.
Log loggerToUse = this.lazyLogger;
if (loggerToUse == null) {
loggerToUse = LogFactory.getLog(getClass());
this.lazyLogger = loggerToUse;
}
if (loggerToUse.isTraceEnabled()) {
loggerToUse.trace("Non-matching event type for listener: " + listener, ex);
}
}
else {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -59,4 +59,15 @@ public interface SmartApplicationListener extends ApplicationListener<Applicatio
return LOWEST_PRECEDENCE;
}
/**
* Return an optional identifier for the listener.
* <p>The default value is an empty String.
* @since 5.3.5
* @see EventListener#id
* @see ApplicationEventMulticaster#removeApplicationListeners
*/
default String getListenerId() {
return "";
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,7 +34,7 @@ import org.springframework.lang.Nullable;
* @author Stephane Nicoll
* @since 2.0.5
*/
public class SourceFilteringListener implements GenericApplicationListener, SmartApplicationListener {
public class SourceFilteringListener implements GenericApplicationListener {
private final Object source;
@@ -79,11 +79,6 @@ public class SourceFilteringListener implements GenericApplicationListener, Smar
return (this.delegate == null || this.delegate.supportsEventType(eventType));
}
@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
return supportsEventType(ResolvableType.forType(eventType));
}
@Override
public boolean supportsSourceType(@Nullable Class<?> sourceType) {
return (sourceType != null && sourceType.isInstance(this.source));
@@ -94,6 +89,11 @@ public class SourceFilteringListener implements GenericApplicationListener, Smar
return (this.delegate != null ? this.delegate.getOrder() : Ordered.LOWEST_PRECEDENCE);
}
@Override
public String getListenerId() {
return (this.delegate != null ? this.delegate.getListenerId() : "");
}
/**
* Actually process the event, after having filtered according to the
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -48,13 +48,13 @@ public final class CandidateComponentsIndexLoader {
public static final String COMPONENTS_RESOURCE_LOCATION = "META-INF/spring.components";
/**
* System property that instructs Spring to ignore the index, i.e.
* System property that instructs Spring to ignore the components index, i.e.
* to always return {@code null} from {@link #loadIndex(ClassLoader)}.
* <p>The default is "false", allowing for regular use of the index. Switching this
* flag to {@code true} fulfills a corner case scenario when an index is partially
* available for some libraries (or use cases) but couldn't be built for the whole
* application. In this case, the application context fallbacks to a regular
* classpath arrangement (i.e. as no index was present at all).
* classpath arrangement (i.e. as though no index were present at all).
*/
public static final String IGNORE_INDEX = "spring.index.ignore";
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author 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,6 +46,7 @@ import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ApplicationStartupAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.context.EnvironmentAware;
@@ -66,6 +67,7 @@ import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.context.expression.StandardBeanExpressionResolver;
import org.springframework.context.weaving.LoadTimeWeaverAware;
import org.springframework.context.weaving.LoadTimeWeaverAwareProcessor;
import org.springframework.core.NativeDetector;
import org.springframework.core.ResolvableType;
import org.springframework.core.SpringProperties;
import org.springframework.core.annotation.AnnotationUtils;
@@ -164,13 +166,6 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
*/
private static final boolean shouldIgnoreSpel = SpringProperties.getFlag("spring.spel.ignore");
/**
* Whether this environment lives within a native image.
* Exposed as a private static field rather than in a {@code NativeImageDetector.inNativeImage()} static method due to https://github.com/oracle/graal/issues/2594.
* @see <a href="https://github.com/oracle/graal/blob/master/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/ImageInfo.java">ImageInfo.java</a>
*/
private static final boolean IN_NATIVE_IMAGE = (System.getProperty("org.graalvm.nativeimage.imagecode") != null);
static {
// Eagerly load the ContextClosedEvent class to avoid weird classloader issues
@@ -698,7 +693,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
beanFactory.ignoreDependencyInterface(ApplicationStartup.class);
beanFactory.ignoreDependencyInterface(ApplicationStartupAware.class);
// BeanFactory interface not registered as resolvable type in a plain factory.
// MessageSource registered (and found for autowiring) as a bean.
@@ -711,7 +706,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
// Detect a LoadTimeWeaver and prepare for weaving, if found.
if (!IN_NATIVE_IMAGE && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
if (!NativeDetector.inNativeImage() && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
// Set a temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
@@ -752,7 +747,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
if (!IN_NATIVE_IMAGE && beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
if (!NativeDetector.inNativeImage() && beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
@@ -815,9 +810,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
}
}
else {
SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
simpleApplicationEventMulticaster.setApplicationStartup(getApplicationStartup());
this.applicationEventMulticaster = simpleApplicationEventMulticaster;
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isTraceEnabled()) {
logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
@@ -902,8 +895,8 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
// Register a default embedded value resolver if no bean post-processor
// (such as a PropertyPlaceholderConfigurer bean) registered any before:
// Register a default embedded value resolver if no BeanFactoryPostProcessor
// (such as a PropertySourcesPlaceholderConfigurer bean) registered any before:
// at this point, primarily for resolution in annotation attribute values.
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
@@ -945,7 +938,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
publishEvent(new ContextRefreshedEvent(this));
// Participate in LiveBeansView MBean, if active.
if (!IN_NATIVE_IMAGE) {
if (!NativeDetector.inNativeImage()) {
LiveBeansView.registerApplicationContext(this);
}
}
@@ -1056,7 +1049,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
logger.debug("Closing " + this);
}
if (!IN_NATIVE_IMAGE) {
if (!NativeDetector.inNativeImage()) {
LiveBeansView.unregisterApplicationContext(this);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2021 the original author 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.context.support;
import java.lang.reflect.Method;
import java.security.ProtectionDomain;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -74,6 +75,11 @@ class ContextTypeMatchClassLoader extends DecoratingClassLoader implements Smart
return (clazz.getClassLoader() instanceof ContextOverridingClassLoader);
}
@Override
public Class<?> publicDefineClass(String name, byte[] b, @Nullable ProtectionDomain protectionDomain) {
return defineClass(name, b, 0, b.length, protectionDomain);
}
/**
* ClassLoader to be created for each loaded class.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -59,6 +59,19 @@ final class PostProcessorRegistrationDelegate {
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
// WARNING: Although it may appear that the body of this method can be easily
// refactored to avoid the use of multiple loops and multiple lists, the use
// of multiple lists and multiple passes over the names of processors is
// intentional. We must ensure that we honor the contracts for PriorityOrdered
// and Ordered processors. Specifically, we must NOT cause processors to be
// instantiated (via getBean() invocations) or registered in the ApplicationContext
// in the wrong order.
//
// Before submitting a pull request (PR) to change this method, please review the
// list of all declined PRs involving changes to PostProcessorRegistrationDelegate
// to ensure that your proposal does not result in a breaking change:
// https://github.com/spring-projects/spring-framework/issues?q=PostProcessorRegistrationDelegate+is%3Aclosed+label%3A%22status%3A+declined%22
// Invoke BeanDefinitionRegistryPostProcessors first, if any.
Set<String> processedBeans = new HashSet<>();
@@ -192,6 +205,19 @@ final class PostProcessorRegistrationDelegate {
public static void registerBeanPostProcessors(
ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
// WARNING: Although it may appear that the body of this method can be easily
// refactored to avoid the use of multiple loops and multiple lists, the use
// of multiple lists and multiple passes over the names of processors is
// intentional. We must ensure that we honor the contracts for PriorityOrdered
// and Ordered processors. Specifically, we must NOT cause processors to be
// instantiated (via getBean() invocations) or registered in the ApplicationContext
// in the wrong order.
//
// Before submitting a pull request (PR) to change this method, please review the
// list of all declined PRs involving changes to PostProcessorRegistrationDelegate
// to ensure that your proposal does not result in a breaking change:
// https://github.com/spring-projects/spring-framework/issues?q=PostProcessorRegistrationDelegate+is%3Aclosed+label%3A%22status%3A+declined%22
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
// Register BeanPostProcessorChecker that logs an info message when
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,27 +26,44 @@ import java.lang.annotation.Target;
* Declares that a field or method parameter should be formatted as a date or time.
*
* <p>Supports formatting by style pattern, ISO date time pattern, or custom format pattern string.
* Can be applied to {@code java.util.Date}, {@code java.util.Calendar}, {@code Long} (for
* millisecond timestamps) as well as JSR-310 <code>java.time</code> and Joda-Time value types.
* Can be applied to {@link java.util.Date}, {@link java.util.Calendar}, {@link Long} (for
* millisecond timestamps) as well as JSR-310 {@code java.time} and Joda-Time value types.
*
* <p>For style-based formatting, set the {@link #style} attribute to be the style pattern code.
* <p>For style-based formatting, set the {@link #style} attribute to the desired style pattern code.
* The first character of the code is the date style, and the second character is the time style.
* Specify a character of 'S' for short style, 'M' for medium, 'L' for long, and 'F' for full.
* A date or time may be omitted by specifying the style character '-'.
* The date or time may be omitted by specifying the style character '-' &mdash; for example,
* 'M-' specifies a medium format for the date with no time.
*
* <p>For ISO-based formatting, set the {@link #iso} attribute to be the desired {@link ISO} format,
* such as {@link ISO#DATE}. For custom formatting, set the {@link #pattern} attribute to be the
* DateTime pattern, such as {@code yyyy/MM/dd hh:mm:ss a}.
* <p>For ISO-based formatting, set the {@link #iso} attribute to the desired {@link ISO} format,
* such as {@link ISO#DATE}.
*
* <p>For custom formatting, set the {@link #pattern} attribute to a date time pattern, such as
* {@code "yyyy/MM/dd hh:mm:ss a"}.
*
* <p>Each attribute is mutually exclusive, so only set one attribute per annotation instance
* (the one most convenient one for your formatting needs).
* When the pattern attribute is specified, it takes precedence over both the style and ISO attribute.
* When the {@link #iso} attribute is specified, it takes precedence over the style attribute.
* When no annotation attributes are specified, the default format applied is style-based
* with a style code of 'SS' (short date, short time).
* (the one most convenient for your formatting needs).
*
* <ul>
* <li>When the pattern attribute is specified, it takes precedence over both the style and ISO attribute.</li>
* <li>When the {@link #iso} attribute is specified, it takes precedence over the style attribute.</li>
* <li>When no annotation attributes are specified, the default format applied is style-based
* with a style code of 'SS' (short date, short time).</li>
* </ul>
*
* <h3>Time Zones</h3>
* <p>Whenever the {@link #style} or {@link #pattern} attribute is used, the
* {@linkplain java.util.TimeZone#getDefault() default time zone} of the JVM will
* be used when formatting {@link java.util.Date} values. Whenever the {@link #iso}
* attribute is used when formatting {@link java.util.Date} values, {@code UTC}
* will be used as the time zone. The same time zone will be applied to any
* {@linkplain #fallbackPatterns fallback patterns} as well. In order to enforce
* consistent use of {@code UTC} as the time zone, you can bootstrap the JVM with
* {@code -Duser.timezone=UTC}.
*
* @author Keith Donald
* @author Juergen Hoeller
* @author Sam Brannen
* @since 3.0
* @see java.time.format.DateTimeFormatter
* @see org.joda.time.format.DateTimeFormat
@@ -57,34 +74,59 @@ import java.lang.annotation.Target;
public @interface DateTimeFormat {
/**
* The style pattern to use to format the field.
* <p>Defaults to 'SS' for short date time. Set this attribute when you wish to format
* your field in accordance with a common style other than the default style.
* The style pattern to use to format the field or method parameter.
* <p>Defaults to 'SS' for short date, short time. Set this attribute when you
* wish to format your field or method parameter in accordance with a common
* style other than the default style.
* @see #fallbackPatterns
*/
String style() default "SS";
/**
* The ISO pattern to use to format the field.
* <p>The possible ISO patterns are defined in the {@link ISO} enum.
* The ISO pattern to use to format the field or method parameter.
* <p>Supported ISO patterns are defined in the {@link ISO} enum.
* <p>Defaults to {@link ISO#NONE}, indicating this attribute should be ignored.
* Set this attribute when you wish to format your field in accordance with an ISO format.
* Set this attribute when you wish to format your field or method parameter
* in accordance with an ISO format.
* @see #fallbackPatterns
*/
ISO iso() default ISO.NONE;
/**
* The custom pattern to use to format the field.
* <p>Defaults to empty String, indicating no custom pattern String has been specified.
* Set this attribute when you wish to format your field in accordance with a custom
* date time pattern not represented by a style or ISO format.
* The custom pattern to use to format the field or method parameter.
* <p>Defaults to empty String, indicating no custom pattern String has been
* specified. Set this attribute when you wish to format your field or method
* parameter in accordance with a custom date time pattern not represented by
* a style or ISO format.
* <p>Note: This pattern follows the original {@link java.text.SimpleDateFormat} style,
* as also supported by Joda-Time, with strict parsing semantics towards overflows
* (e.g. rejecting a Feb 29 value for a non-leap-year). As a consequence, 'yy'
* characters indicate a year in the traditional style, not a "year-of-era" as in the
* {@link java.time.format.DateTimeFormatter} specification (i.e. 'yy' turns into 'uu'
* when going through that {@code DateTimeFormatter} with strict resolution mode).
* when going through a {@code DateTimeFormatter} with strict resolution mode).
* @see #fallbackPatterns
*/
String pattern() default "";
/**
* The set of custom patterns to use as a fallback in case parsing fails for
* the primary {@link #pattern}, {@link #iso}, or {@link #style} attribute.
* <p>For example, if you wish to use the ISO date format for parsing and
* printing but allow for lenient parsing of user input for various date
* formats, you could configure something similar to the following.
* <pre style="code">
* {@literal @}DateTimeFormat(iso = ISO.DATE, fallbackPatterns = { "M/d/yy", "dd.MM.yyyy" })
* </pre>
* <p>Fallback patterns are only used for parsing. They are not used for
* printing the value as a String. The primary {@link #pattern}, {@link #iso},
* or {@link #style} attribute is always used for printing. For details on
* which time zone is used for fallback patterns, see the
* {@linkplain DateTimeFormat class-level documentation}.
* <p>Fallback patterns are not supported for Joda-Time value types.
* @since 5.3.5
*/
String[] fallbackPatterns() default {};
/**
* Common ISO date time format patterns.
@@ -92,21 +134,20 @@ public @interface DateTimeFormat {
enum ISO {
/**
* The most common ISO Date Format {@code yyyy-MM-dd},
* e.g. "2000-10-31".
* The most common ISO Date Format {@code yyyy-MM-dd} &mdash; for example,
* "2000-10-31".
*/
DATE,
/**
* The most common ISO Time Format {@code HH:mm:ss.SSSXXX},
* e.g. "01:30:00.000-05:00".
* The most common ISO Time Format {@code HH:mm:ss.SSSXXX} &mdash; for example,
* "01:30:00.000-05:00".
*/
TIME,
/**
* The most common ISO DateTime Format {@code yyyy-MM-dd'T'HH:mm:ss.SSSXXX},
* e.g. "2000-10-31T01:30:00.000-05:00".
* <p>This is the default if no annotation value is specified.
* The most common ISO Date Time Format {@code yyyy-MM-dd'T'HH:mm:ss.SSSXXX}
* &mdash; for example, "2000-10-31T01:30:00.000-05:00".
*/
DATE_TIME,
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,17 +27,21 @@ import java.util.Map;
import java.util.TimeZone;
import org.springframework.format.Formatter;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.DateTimeFormat.ISO;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* A formatter for {@link java.util.Date} types.
* Allows the configuration of an explicit date pattern and locale.
* <p>Supports the configuration of an explicit date time pattern, timezone,
* locale, and fallback date time patterns for lenient parsing.
*
* @author Keith Donald
* @author Juergen Hoeller
* @author Phillip Webb
* @author Sam Brannen
* @since 3.0
* @see SimpleDateFormat
*/
@@ -56,9 +60,15 @@ public class DateFormatter implements Formatter<Date> {
}
@Nullable
private Object source;
@Nullable
private String pattern;
@Nullable
private String[] fallbackPatterns;
private int style = DateFormat.DEFAULT;
@Nullable
@@ -74,19 +84,33 @@ public class DateFormatter implements Formatter<Date> {
/**
* Create a new default DateFormatter.
* Create a new default {@code DateFormatter}.
*/
public DateFormatter() {
}
/**
* Create a new DateFormatter for the given date pattern.
* Create a new {@code DateFormatter} for the given date time pattern.
*/
public DateFormatter(String pattern) {
this.pattern = pattern;
}
/**
* Set the source of the configuration for this {@code DateFormatter} &mdash;
* for example, an instance of the {@link DateTimeFormat @DateTimeFormat}
* annotation if such an annotation was used to configure this {@code DateFormatter}.
* <p>The supplied source object will only be used for descriptive purposes
* by invoking its {@code toString()} method &mdash; for example, when
* generating an exception message to provide further context.
* @param source the source of the configuration
* @since 5.3.5
*/
public void setSource(Object source) {
this.source = source;
}
/**
* Set the pattern to use to format date values.
* <p>If not specified, DateFormat's default style will be used.
@@ -96,7 +120,19 @@ public class DateFormatter implements Formatter<Date> {
}
/**
* Set the ISO format used for this date.
* Set additional patterns to use as a fallback in case parsing fails for the
* configured {@linkplain #setPattern pattern}, {@linkplain #setIso ISO format},
* {@linkplain #setStyle style}, or {@linkplain #setStylePattern style pattern}.
* @param fallbackPatterns the fallback parsing patterns
* @since 5.3.5
* @see DateTimeFormat#fallbackPatterns()
*/
public void setFallbackPatterns(String... fallbackPatterns) {
this.fallbackPatterns = fallbackPatterns;
}
/**
* Set the ISO format to use to format date values.
* @param iso the {@link ISO} format
* @since 3.2
*/
@@ -105,7 +141,7 @@ public class DateFormatter implements Formatter<Date> {
}
/**
* Set the style to use to format date values.
* Set the {@link DateFormat} style to use to format date values.
* <p>If not specified, DateFormat's default style will be used.
* @see DateFormat#DEFAULT
* @see DateFormat#SHORT
@@ -118,8 +154,10 @@ public class DateFormatter implements Formatter<Date> {
}
/**
* Set the two character to use to format date values. The first character used for
* the date style, the second is for the time style. Supported characters are
* Set the two characters to use to format date values.
* <p>The first character is used for the date style; the second is used for
* the time style.
* <p>Supported characters:
* <ul>
* <li>'S' = Small</li>
* <li>'M' = Medium</li>
@@ -136,7 +174,7 @@ public class DateFormatter implements Formatter<Date> {
}
/**
* Set the TimeZone to normalize the date values into, if any.
* Set the {@link TimeZone} to normalize the date values into, if any.
*/
public void setTimeZone(TimeZone timeZone) {
this.timeZone = timeZone;
@@ -159,12 +197,43 @@ public class DateFormatter implements Formatter<Date> {
@Override
public Date parse(String text, Locale locale) throws ParseException {
return getDateFormat(locale).parse(text);
try {
return getDateFormat(locale).parse(text);
}
catch (ParseException ex) {
if (!ObjectUtils.isEmpty(this.fallbackPatterns)) {
for (String pattern : this.fallbackPatterns) {
try {
DateFormat dateFormat = configureDateFormat(new SimpleDateFormat(pattern, locale));
// Align timezone for parsing format with printing format if ISO is set.
if (this.iso != null && this.iso != ISO.NONE) {
dateFormat.setTimeZone(UTC);
}
return dateFormat.parse(text);
}
catch (ParseException ignoredException) {
// Ignore fallback parsing exceptions since the exception thrown below
// will include information from the "source" if available -- for example,
// the toString() of a @DateTimeFormat annotation.
}
}
}
if (this.source != null) {
throw new ParseException(
String.format("Unable to parse date time value \"%s\" using configuration from %s", text, this.source),
ex.getErrorOffset());
}
// else rethrow original exception
throw ex;
}
}
protected DateFormat getDateFormat(Locale locale) {
DateFormat dateFormat = createDateFormat(locale);
return configureDateFormat(createDateFormat(locale));
}
private DateFormat configureDateFormat(DateFormat dateFormat) {
if (this.timeZone != null) {
dateFormat.setTimeZone(this.timeZone);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2021 the original author 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,10 +16,12 @@
package org.springframework.format.datetime;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.context.support.EmbeddedValueResolutionSupport;
@@ -34,6 +36,7 @@ import org.springframework.util.StringUtils;
* Formats fields annotated with the {@link DateTimeFormat} annotation using a {@link DateFormatter}.
*
* @author Phillip Webb
* @author Sam Brannen
* @since 3.2
* @see org.springframework.format.datetime.joda.JodaDateTimeFormatAnnotationFormatterFactory
*/
@@ -68,15 +71,30 @@ public class DateTimeFormatAnnotationFormatterFactory extends EmbeddedValueReso
protected Formatter<Date> getFormatter(DateTimeFormat annotation, Class<?> fieldType) {
DateFormatter formatter = new DateFormatter();
formatter.setSource(annotation);
formatter.setIso(annotation.iso());
String style = resolveEmbeddedValue(annotation.style());
if (StringUtils.hasLength(style)) {
formatter.setStylePattern(style);
}
formatter.setIso(annotation.iso());
String pattern = resolveEmbeddedValue(annotation.pattern());
if (StringUtils.hasLength(pattern)) {
formatter.setPattern(pattern);
}
List<String> resolvedFallbackPatterns = new ArrayList<>();
for (String fallbackPattern : annotation.fallbackPatterns()) {
String resolvedFallbackPattern = resolveEmbeddedValue(fallbackPattern);
if (StringUtils.hasLength(resolvedFallbackPattern)) {
resolvedFallbackPatterns.add(resolvedFallbackPattern);
}
}
if (!resolvedFallbackPatterns.isEmpty()) {
formatter.setFallbackPatterns(resolvedFallbackPatterns.toArray(new String[0]));
}
return formatter;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2021 the original author 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,7 @@ import org.springframework.lang.Nullable;
/**
* A context that holds user-specific <code>java.time</code> (JSR-310) settings
* such as the user's Chronology (calendar system) and time zone.
* A {@code null} property value indicate the user has not specified a setting.
* <p>A {@code null} property value indicates the user has not specified a setting.
*
* @author Juergen Hoeller
* @since 4.0
@@ -81,8 +81,8 @@ public class DateTimeContext {
/**
* Get the DateTimeFormatter with the this context's settings
* applied to the base {@code formatter}.
* Get the DateTimeFormatter with this context's settings applied to the
* base {@code formatter}.
* @param formatter the base formatter that establishes default
* formatting rules, generally context-independent
* @return the contextual DateTimeFormatter
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -69,9 +69,8 @@ public final class DateTimeContextHolder {
return dateTimeContextHolder.get();
}
/**
* Obtain a DateTimeFormatter with user-specific settings applied to the given base Formatter.
* Obtain a DateTimeFormatter with user-specific settings applied to the given base formatter.
* @param formatter the base formatter that establishes default formatting rules
* (generally user independent)
* @param locale the current user locale (may be {@code null} if not known)
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,7 +18,6 @@ package org.springframework.format.datetime.standard;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.time.format.ResolverStyle;
import java.util.TimeZone;
import org.springframework.format.annotation.DateTimeFormat.ISO;
@@ -34,6 +33,7 @@ import org.springframework.util.StringUtils;
*
* @author Juergen Hoeller
* @author Phillip Webb
* @author Sam Brannen
* @since 4.0
* @see #createDateTimeFormatter()
* @see #createDateTimeFormatter(DateTimeFormatter)
@@ -180,11 +180,7 @@ public class DateTimeFormatterFactory {
public DateTimeFormatter createDateTimeFormatter(DateTimeFormatter fallbackFormatter) {
DateTimeFormatter dateTimeFormatter = null;
if (StringUtils.hasLength(this.pattern)) {
// Using strict parsing to align with Joda-Time and standard DateFormat behavior:
// otherwise, an overflow like e.g. Feb 29 for a non-leap-year wouldn't get rejected.
// However, with strict parsing, a year digit needs to be specified as 'u'...
String patternToUse = StringUtils.replace(this.pattern, "yy", "uu");
dateTimeFormatter = DateTimeFormatter.ofPattern(patternToUse).withResolverStyle(ResolverStyle.STRICT);
dateTimeFormatter = DateTimeFormatterUtils.createStrictDateTimeFormatter(this.pattern);
}
else if (this.iso != null && this.iso != ISO.NONE) {
switch (this.iso) {
@@ -0,0 +1,40 @@
/*
* Copyright 2002-2021 the original author 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.time.format.DateTimeFormatter;
import java.time.format.ResolverStyle;
import org.springframework.util.StringUtils;
/**
* Internal {@link DateTimeFormatter} utilities.
*
* @author Juergen Hoeller
* @since 5.3.5
*/
abstract class DateTimeFormatterUtils {
static DateTimeFormatter createStrictDateTimeFormatter(String pattern) {
// Using strict parsing to align with Joda-Time and standard DateFormat behavior:
// otherwise, an overflow like e.g. Feb 29 for a non-leap-year wouldn't get rejected.
// However, with strict parsing, a year digit needs to be specified as 'u'...
String patternToUse = StringUtils.replace(pattern, "yy", "uu");
return DateTimeFormatter.ofPattern(patternToUse).withResolverStyle(ResolverStyle.STRICT);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2021 the original author 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,8 +24,10 @@ import java.time.OffsetTime;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.context.support.EmbeddedValueResolutionSupport;
@@ -40,6 +42,7 @@ import org.springframework.util.StringUtils;
* JSR-310 <code>java.time</code> package in JDK 8.
*
* @author Juergen Hoeller
* @author Sam Brannen
* @since 4.0
* @see org.springframework.format.annotation.DateTimeFormat
*/
@@ -93,8 +96,17 @@ public class Jsr310DateTimeFormatAnnotationFormatterFactory extends EmbeddedValu
@Override
@SuppressWarnings("unchecked")
public Parser<?> getParser(DateTimeFormat annotation, Class<?> fieldType) {
List<String> resolvedFallbackPatterns = new ArrayList<>();
for (String fallbackPattern : annotation.fallbackPatterns()) {
String resolvedFallbackPattern = resolveEmbeddedValue(fallbackPattern);
if (StringUtils.hasLength(resolvedFallbackPattern)) {
resolvedFallbackPatterns.add(resolvedFallbackPattern);
}
}
DateTimeFormatter formatter = getFormatter(annotation, fieldType);
return new TemporalAccessorParser((Class<? extends TemporalAccessor>) fieldType, formatter);
return new TemporalAccessorParser((Class<? extends TemporalAccessor>) fieldType,
formatter, resolvedFallbackPatterns.toArray(new String[0]), annotation);
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2021 the original author 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,16 +24,20 @@ import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.TemporalAccessor;
import java.util.Locale;
import org.springframework.format.Parser;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
/**
* {@link Parser} implementation for a JSR-310 {@link java.time.temporal.TemporalAccessor},
* using a {@link java.time.format.DateTimeFormatter}) (the contextual one, if available).
* using a {@link java.time.format.DateTimeFormatter} (the contextual one, if available).
*
* @author Juergen Hoeller
* @author Sam Brannen
* @since 4.0
* @see DateTimeContextHolder#getFormatter
* @see java.time.LocalDate#parse(CharSequence, java.time.format.DateTimeFormatter)
@@ -49,6 +53,12 @@ public final class TemporalAccessorParser implements Parser<TemporalAccessor> {
private final DateTimeFormatter formatter;
@Nullable
private final String[] fallbackPatterns;
@Nullable
private final Object source;
/**
* Create a new TemporalAccessorParser for the given TemporalAccessor type.
@@ -57,14 +67,49 @@ public final class TemporalAccessorParser implements Parser<TemporalAccessor> {
* @param formatter the base DateTimeFormatter instance
*/
public TemporalAccessorParser(Class<? extends TemporalAccessor> temporalAccessorType, DateTimeFormatter formatter) {
this(temporalAccessorType, formatter, null, null);
}
TemporalAccessorParser(Class<? extends TemporalAccessor> temporalAccessorType, DateTimeFormatter formatter,
@Nullable String[] fallbackPatterns, @Nullable Object source) {
this.temporalAccessorType = temporalAccessorType;
this.formatter = formatter;
this.fallbackPatterns = fallbackPatterns;
this.source = source;
}
@Override
public TemporalAccessor parse(String text, Locale locale) throws ParseException {
DateTimeFormatter formatterToUse = DateTimeContextHolder.getFormatter(this.formatter, locale);
try {
return doParse(text, locale, this.formatter);
}
catch (DateTimeParseException ex) {
if (!ObjectUtils.isEmpty(this.fallbackPatterns)) {
for (String pattern : this.fallbackPatterns) {
try {
DateTimeFormatter fallbackFormatter = DateTimeFormatterUtils.createStrictDateTimeFormatter(pattern);
return doParse(text, locale, fallbackFormatter);
}
catch (DateTimeParseException ignoredException) {
// Ignore fallback parsing exceptions since the exception thrown below
// will include information from the "source" if available -- for example,
// the toString() of a @DateTimeFormat annotation.
}
}
}
if (this.source != null) {
throw new DateTimeParseException(
String.format("Unable to parse date time value \"%s\" using configuration from %s", text, this.source),
text, ex.getErrorIndex(), ex);
}
// else rethrow original exception
throw ex;
}
}
private TemporalAccessor doParse(String text, Locale locale, DateTimeFormatter formatter) throws DateTimeParseException {
DateTimeFormatter formatterToUse = DateTimeContextHolder.getFormatter(formatter, locale);
if (LocalDate.class == this.temporalAccessorType) {
return LocalDate.parse(text, formatterToUse);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -85,7 +85,7 @@ public @interface Scheduled {
* trigger, primarily meant for externally specified values resolved by a
* <code>${...}</code> placeholder.
* @return an expression that can be parsed to a cron schedule
* @see org.springframework.scheduling.support.CronSequenceGenerator
* @see org.springframework.scheduling.support.CronExpression#parse(String)
*/
String cron() default "";
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -358,9 +358,9 @@ public class ScheduledAnnotationBeanPostProcessor
AnnotationUtils.isCandidateClass(targetClass, Arrays.asList(Scheduled.class, Schedules.class))) {
Map<Method, Set<Scheduled>> annotatedMethods = MethodIntrospector.selectMethods(targetClass,
(MethodIntrospector.MetadataLookup<Set<Scheduled>>) method -> {
Set<Scheduled> scheduledMethods = AnnotatedElementUtils.getMergedRepeatableAnnotations(
Set<Scheduled> scheduledAnnotations = AnnotatedElementUtils.getMergedRepeatableAnnotations(
method, Scheduled.class, Schedules.class);
return (!scheduledMethods.isEmpty() ? scheduledMethods : null);
return (!scheduledAnnotations.isEmpty() ? scheduledAnnotations : null);
});
if (annotatedMethods.isEmpty()) {
this.nonAnnotatedClasses.add(targetClass);
@@ -370,8 +370,8 @@ public class ScheduledAnnotationBeanPostProcessor
}
else {
// Non-empty set of methods
annotatedMethods.forEach((method, scheduledMethods) ->
scheduledMethods.forEach(scheduled -> processScheduled(scheduled, method, bean)));
annotatedMethods.forEach((method, scheduledAnnotations) ->
scheduledAnnotations.forEach(scheduled -> processScheduled(scheduled, method, bean)));
if (logger.isTraceEnabled()) {
logger.trace(annotatedMethods.size() + " @Scheduled methods processed on bean '" + beanName +
"': " + annotatedMethods);

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