Compare commits

...

71 Commits

Author SHA1 Message Date
Spring Builds 14fa75c98a Release v6.0.18 2024-03-14 09:18:22 +00:00
rstoyanchev f2fd2f1226 Extract reusable checkSchemeAndPort method
Closes gh-32440
2024-03-14 08:48:42 +00:00
Juergen Hoeller 072ebb6ffc Additional unit tests for operations on empty UriTemplate
See gh-32432

(cherry picked from commit 54a6d89da7)
2024-03-13 18:18:52 +01:00
Kasper Bisgaard 08e7f7efa4 Allow UriTemplate to be built with an empty template
Closes gh-32437
2024-03-13 17:32:48 +01:00
Juergen Hoeller b1fafbf7e1 Upgrade to Reactor 2022.0.17
Includes Groovy 4.0.19, OpenPDF 1.3.42, Mockito 5.11

Closes gh-32421
2024-03-12 20:26:44 +01:00
Juergen Hoeller a3647a8c5e Polishing
(cherry picked from commit 723c94e5ac)
2024-03-12 20:26:24 +01:00
ZeroCyan 5c8d9cd0b2 Fix order of sections in Validation chapter of reference manual
Closes gh-32408
2024-03-10 17:31:30 +01:00
Juergen Hoeller 40d5196243 Polishing 2024-03-08 19:40:45 +01:00
Brian Clozel ec2c9b5d0e Set error on observation in WebClient instrumentation
Prior to this commit, error signals flowing from the client response
publisher in `WebClient` would be set on the `Observation.Context`. This
is enough for the observation convention to collect data about the error
but observation handlers are not notified of this error.

This commit sets the error instead on the observation directly to fix
this issue.

Fixes gh-32399
2024-03-08 11:36:20 +01:00
rstoyanchev 36539bdaa9 Use wrapped response in HandlerFunctionAdapter
webmvc.fn now also uses the StandardServletAsyncWebRequest wrapped response
to enforce lifecycle rules from Servlet spec (section 2.3.3.4).

See gh-32341
2024-03-07 14:51:01 +00:00
rstoyanchev 67ba7dd1da DisconnectedClientHelper recognizes AsyncRequestNotUsableException
See gh-32341
2024-03-06 18:20:31 +00:00
rstoyanchev 7b3fcf2647 Fix Javadoc error 2024-03-05 13:06:12 +00:00
rstoyanchev fd76c33589 Fix Javadoc error 2024-03-05 12:40:32 +00:00
rstoyanchev 1a7a6f421f Backport tests for wrapping of response for async requests
This is a backport of commits 4b96cd and ef0717.

Closes gh-32341
2024-03-05 12:03:48 +00:00
Juergen Hoeller 9ac1feceb5 Restore ability to return original method for proxy-derived method
Closes gh-32365
2024-03-04 23:32:27 +01:00
Juergen Hoeller 7029042e44 Polishing
(cherry picked from commit e9110c0729)
2024-03-04 23:31:51 +01:00
rstoyanchev 1a5661d426 Improve concurrent handling of result in WebAsyncManager
1. Use state transitions
2. Increase synchronized scope in setConcurrentResultAndDispatch

See gh-32341
2024-03-03 20:32:00 +00:00
rstoyanchev b208c63414 Add state and response wrapping to StandardServletAsyncWebRequest
The wrapped response prevents use after AsyncListener onError or completion
to ensure compliance with Servlet Spec 2.3.3.4.

The wrapped response is applied in RequestMappingHandlerAdapter.

The wrapped response raises AsyncRequestNotUsableException that is now
handled in DefaultHandlerExceptionResolver.

See gh-32341
2024-03-03 20:31:45 +00:00
rstoyanchev 814c003b43 Align 5.3.x with 6.1.x
In preparation for a larger update, start by aligning with
6.1.x, which includes changes for gh-32042 and gh-30232.

See gh-32341
2024-02-29 17:46:18 +00:00
Juergen Hoeller 5c34e1d11a Upgrade to Undertow 2.3.12, OpenPDF 1.3.41, JRuby 9.4.6 2024-02-28 21:38:57 +01:00
Juergen Hoeller d57775bbb2 Polishing 2024-02-28 21:38:38 +01:00
Juergen Hoeller b598ad3f33 Polishing 2024-02-28 19:26:11 +01:00
Juergen Hoeller b44ef70bc3 Direct reference to PushBuilder API on Servlet 5.0 baseline
See gh-29435
2024-02-28 19:17:13 +01:00
Sam Brannen d1b3107398 Do not cache Content-Type in ContentCachingResponseWrapper
Based on feedback from several members of the community, we have
decided to revert the caching of the Content-Type header that was
introduced in ContentCachingResponseWrapper in 375e0e6827.

This commit therefore completely removes Content-Type caching in
ContentCachingResponseWrapper and updates the existing tests
accordingly.

To provide guards against future regressions in this area, this commit
also introduces explicit tests for the 6 ways to set the content length
in ContentCachingResponseWrapper and modifies a test in
ShallowEtagHeaderFilterTests to ensure that a Content-Type header set
directly on ContentCachingResponseWrapper is propagated to the
underlying response even if content caching is disabled for the
ShallowEtagHeaderFilter.

See gh-32039
See gh-32317
Closes gh-32321
2024-02-28 10:51:48 +01:00
Sam Brannen 629c560316 Polish ShallowEtagHeaderFilterTests 2024-02-28 10:49:13 +01:00
Sébastien Deleuze 7bf07ef393 Refine *HttpMessageConverter#getContentLength null safety
Closes gh-32333
2024-02-27 15:48:36 +01:00
Sam Brannen ca602ef874 Honor Content-[Type|Length] headers from wrapped response again
Commit 375e0e6827 introduced a regression in
ContentCachingResponseWrapper (CCRW). Specifically, CCRW no longer
honors Content-Type and Content-Length headers that have been set in
the wrapped response and now incorrectly returns null for those header
values if they have not been set directly in the CCRW.

This commit fixes this regression as follows.

- The Content-Type and Content-Length headers set in the wrapped
  response are honored in getContentType(), containsHeader(),
  getHeader(), and getHeaders() unless those headers have been set
  directly in the CCRW.

- In copyBodyToResponse(), the Content-Type in the wrapped response is
  only overridden if the Content-Type has been set directly in the CCRW.

Furthermore, prior to this commit, getHeaderNames() returned duplicates
for the Content-Type and Content-Length headers if they were set in the
wrapped response as well as in CCRW.

This commit fixes that by returning a unique set from getHeaderNames().

This commit also updates ContentCachingResponseWrapperTests to verify
the expected behavior for Content-Type and Content-Length headers that
are set in the wrapped response as well as in CCRW.

See gh-32039
See gh-32317
Closes gh-32321
2024-02-25 17:35:54 +01:00
Sam Brannen e9bf5f5569 Polish ContentCachingResponseWrapper[Tests] 2024-02-25 17:35:54 +01:00
Juergen Hoeller 9a6f636e17 Consistent nullability for internal field access 2024-02-24 08:31:56 +01:00
Sébastien Deleuze 7686f5467e Adapt Hibernate native support for HHH-17643
This commit adapts Hibernate native support to handle
the changes performed as part of HHH-17643 which impacts
Hibernate versions 6.4.3+ and 6.2.21+.

It ignores the BytecodeProvider services loaded by the
service loader feature in order to default to the
"no-op" provider with native.

gh-32314 is expected to remove the need for such
substitutions which are not great for maintainability
by design.

Closes gh-32312
2024-02-22 17:06:53 +01:00
Juergen Hoeller 429c477f6a Polishing 2024-02-22 16:59:53 +01:00
Juergen Hoeller 5187281b50 Polishing 2024-02-21 22:56:21 +01:00
Juergen Hoeller 24a4487050 Add test for cleanup after configuration class creation failure
See gh-23343
2024-02-21 22:47:23 +01:00
Juergen Hoeller 8fba4a448a Polishing 2024-02-16 22:41:50 +01:00
Spring Builds 8f286de773 Next development version (v6.0.18-SNAPSHOT) 2024-02-15 12:34:07 +00:00
Juergen Hoeller ee100cfba7 Upgrade to Mockito 5.10, Mutiny 1.10, Undertow 2.3.11 2024-02-15 10:49:54 +01:00
Juergen Hoeller aaf90f5a96 Upgrade to Reactor 2022.0.16
Includes SLF4J 2.0.12, Groovy 4.0.18, Jetty 11.0.20, Netty 4.1.107, OpenPDF 1.3.40
2024-02-15 10:28:18 +01:00
Simon Baslé 70be04ba81 Infer reflection hints for Jackson annotations builder attributes
This notably enables Jackson to reflectively call a user-provided
builder class and invoke its declared methods (setters and build) in
a native app.

See gh-32238
Closes gh-32257
2024-02-13 15:16:04 +01:00
Juergen Hoeller b976ee3f67 Consistent Lock field declaration (instead of ReentrantLock field type)
(cherry picked from commit b4153618a4)
2024-02-13 11:14:10 +01:00
rstoyanchev b11ff96652 Update user info pattern
Closes gh-32211
2024-02-13 07:14:12 +00:00
Arjen Poutsma 667e4e7531 Implement MatchableHandlerMapping in RouterFunctionMapping
See gh-32221
Closes gh-32222
2024-02-08 12:25:00 +01:00
Juergen Hoeller 5fd9fab0ce Polishing (backported from main) 2024-02-07 23:40:11 +01:00
Juergen Hoeller 95a8646309 Polishing 2024-02-06 17:06:05 +01:00
Juergen Hoeller 5434edd726 Avoid sendError call when response committed already (Tomcat 10.1.16)
Closes gh-32206

(cherry picked from commit 4ed337247c)
2024-02-06 16:48:15 +01:00
Brian Clozel 5f13ea95fb Polish
See gh-32197
2024-02-05 11:15:49 +01:00
Patrick Strawderman 74bb42b78f Optimize Map methods in ServletAttributesMap
ServletAttributesMap inherited default implementations of the size
and isEmpty methods from AbstractMap which delegates to the Set returned
by entrySet. ServletAttributesMap's entrySet method made this fairly
expensive, since it would copy the attributes to a List, then use a
Stream to build the Set. To avoid the cost, add implementations of
isEmpty / size that don't need to call entrySet at all.

Additionally, change entrySet to return a Set view that simply lazily
delegates to the underlying servlet request for iteration.

Closes gh-32197
2024-02-05 10:42:46 +01:00
Stéphane Nicoll 55717adf88 Upgrade to Gradle 8.6
Closes gh-32193
2024-02-03 11:36:40 +01:00
Juergen Hoeller 72835f10b9 Polishing 2024-02-01 14:58:13 +01:00
Stéphane Nicoll 125ebd029c Prevent AOT from failing with spring-orm without JPA
This commit improves PersistenceManagedTypesBeanRegistrationAotProcessor
so that it does not attempt to load JPA classes when checking for the
presence of a PersistenceManagedTypes bean. To make it more clear a
check on the presence for JPA has been added to prevent the nested
classes to be loaded regardless of the presence of the bean.

Closes gh-32160
2024-01-30 15:42:19 +01:00
Juergen Hoeller d8d4fa0e24 Polishing 2024-01-29 16:42:32 +01:00
Juergen Hoeller bfd3b3ad88 Eagerly initialize ZERO_NANOS constant 2024-01-29 16:21:35 +01:00
Juergen Hoeller 0909161ba1 Polishing 2024-01-29 14:19:48 +01:00
Juergen Hoeller 4910c217dc Explicit documentation note on cron-vs-quartz parsing convention
Closes gh-32128

(cherry picked from commit a738e4d5fd)
2024-01-29 13:58:39 +01:00
Sébastien Deleuze f262046ef9 Update basics.adoc
Closes gh-32151
2024-01-29 09:15:17 +01:00
Juergen Hoeller d50d4a9092 Polishing 2024-01-24 22:44:29 +01:00
Juergen Hoeller b06305e64f Compare qualifier value arrays with equality semantics
Closes gh-32106

(cherry picked from commit c5a75219ce)
2024-01-24 22:39:24 +01:00
Arjen Poutsma c749a14326 Guard against multiple body subscriptions
Before this commit, the JDK and Jetty connectors do not have any
safeguards against multiple body subscriptions. Such as check has now
been added.

See gh-32100
Closes gh-32102
2024-01-24 13:42:25 +01:00
Juergen Hoeller 3817936ca5 Declare current observation context as available since 6.0.15
See gh-31609
See gh-31646
2024-01-24 12:35:30 +01:00
Juergen Hoeller 9bd2be80b9 Declare allowPrivateNetwork as available since 5.3.32
See gh-28546
See gh-31974
2024-01-24 12:34:53 +01:00
Juergen Hoeller c6e9cd0c62 Polishing 2024-01-24 11:59:15 +01:00
Juergen Hoeller e2a5cfb459 Consistent nullability for concurrent result
(cherry picked from commit b92877990d)
2024-01-24 11:59:10 +01:00
Brian Clozel b484ab116f Record errors thrown by custom handler in RestTemplate observations
Prior to this commit, the `RestTemplate` observation instrumentation
would only record `RestClientException` and `IOException` as errors in
the observation. Other types of errors can be thrown by custom
components, such as `ResponseErrorHandler` and in this case they aren't
recorded with the observation.
Also, the current instrumentation does not create any observation scope
around the execution. While this would have a limited benefit as no
application code is executed there, developers could set up custom
components (such as, again, `ResponseErrorHandler`) that could use
contextual logging with trace ids.

This commit ensures that all `Throwable` are recorded as errors with the
observations and that an observation `Scope` is created around the
execution of the client exchange.

Fixes gh-32063
2024-01-22 11:28:07 +01:00
Juergen Hoeller c668473186 Polishing 2024-01-19 17:18:28 +01:00
Arjen Poutsma 38595c6a99 Handle Content-Length in ShallowEtagHeaderFilter more robustly
This commit ensures that setting the Content-Length through
setHeader("Content-Length", x") has the same effect as calling
setContentLength in the ShallowEtagHeaderFilter. It also filters out
Content-Type headers similarly to Content-Length.

See gh-32039
Closes gh-32050
2024-01-18 15:46:14 +01:00
Brian Clozel d756c2b128 Remove JDK 20 variant from CI build. 2024-01-17 21:26:37 +01:00
Brian Clozel 3be4322f71 Upgrade CI image to JDK 17.0.10+13 2024-01-17 21:21:55 +01:00
Brian Clozel 328e444db3 Upgrade CI image to Ubuntu Jammy 20240111 2024-01-17 21:19:27 +01:00
Stéphane Nicoll 94bdd4380f Fix incorrect assertions using json path
Closes gh-32040
2024-01-16 11:02:36 +01:00
rstoyanchev 3f9d479583 Double-checked lock in ChannelSendOperator#request
Closes gh-31865
2024-01-12 17:16:23 +00:00
Sébastien Deleuze 338922f03d Find destroy methods in superclass interfaces
Related tests will be added in
https://github.com/spring-projects/spring-aot-smoke-tests.

Closes gh-32017
2024-01-12 12:28:07 +01:00
Spring Builds fad857e06a Next development version (v6.0.17-SNAPSHOT) 2024-01-11 10:33:22 +00:00
155 changed files with 3184 additions and 1243 deletions
+1 -2
View File
@@ -1,4 +1,4 @@
FROM ubuntu:jammy-20231211.1
FROM ubuntu:jammy-20240111
ADD setup.sh /setup.sh
ADD get-jdk-url.sh /get-jdk-url.sh
@@ -6,6 +6,5 @@ RUN ./setup.sh
ENV JAVA_HOME /opt/openjdk/java17
ENV JDK17 /opt/openjdk/java17
ENV JDK20 /opt/openjdk/java20
ENV PATH $JAVA_HOME/bin:$PATH
+1 -4
View File
@@ -3,10 +3,7 @@ set -e
case "$1" in
java17)
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.9+11/bellsoft-jdk17.0.9+11-linux-amd64.tar.gz"
;;
java20)
echo "https://github.com/bell-sw/Liberica/releases/download/20.0.1+10/bellsoft-jdk20.0.1+10-linux-amd64.tar.gz"
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.10%2B13/bellsoft-jdk17.0.10+13-linux-amd64.tar.gz"
;;
*)
echo $"Unknown java version"
+1 -1
View File
@@ -20,7 +20,7 @@ curl https://raw.githubusercontent.com/spring-io/concourse-java-scripts/v0.0.4/c
mkdir -p /opt/openjdk
pushd /opt/openjdk > /dev/null
for jdk in java17 java20
for jdk in java17
do
JDK_URL=$( /get-jdk-url.sh $jdk )
mkdir $jdk
+1 -44
View File
@@ -90,13 +90,6 @@ resources:
<<: *docker-resource-source
repository: ((docker-hub-organization))/spring-framework-ci
tag: ((milestone))
- name: every-morning
type: time
icon: alarm
source:
start: 8:00 AM
stop: 9:00 AM
location: Europe/Vienna
- name: artifactory-repo
type: artifactory-resource
icon: package-variant
@@ -113,14 +106,6 @@ resources:
access_token: ((github-ci-status-token))
branch: ((branch))
context: build
- name: repo-status-jdk20-build
type: github-status-resource
icon: eye-check-outline
source:
repository: ((github-repo-name))
access_token: ((github-ci-status-token))
branch: ((branch))
context: jdk20-build
- name: slack-alert
type: slack-notification
icon: slack
@@ -217,34 +202,6 @@ jobs:
"zip.type": "schema"
get_params:
threads: 8
- name: jdk20-build
serial: true
public: true
plan:
- get: ci-image
- get: git-repo
- get: every-morning
trigger: true
- put: repo-status-jdk20-build
params: { state: "pending", commit: "git-repo" }
- do:
- task: check-project
image: ci-image
file: git-repo/ci/tasks/check-project.yml
privileged: true
timeout: ((task-timeout))
params:
TEST_TOOLCHAIN: 20
<<: *build-project-task-params
on_failure:
do:
- put: repo-status-jdk20-build
params: { state: "failure", commit: "git-repo" }
- put: slack-alert
params:
<<: *slack-fail-params
- put: repo-status-jdk20-build
params: { state: "success", commit: "git-repo" }
- name: stage-milestone
serial: true
plan:
@@ -396,7 +353,7 @@ jobs:
groups:
- name: "builds"
jobs: ["build", "jdk20-build"]
jobs: ["build"]
- name: "releases"
jobs: ["stage-milestone", "stage-rc", "stage-release", "promote-milestone", "promote-rc", "promote-release", "create-github-release"]
- name: "ci-images"
+1 -1
View File
@@ -4,6 +4,6 @@ set -e
source $(dirname $0)/common.sh
pushd git-repo > /dev/null
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK17,JDK20 \
./gradlew -Dorg.gradle.internal.launcher.welcomeMessageEnabled=false -Porg.gradle.java.installations.fromEnv=JDK17 \
-PmainToolchain=${MAIN_TOOLCHAIN} -PtestToolchain=${TEST_TOOLCHAIN} --no-daemon --max-workers=4 check antora
popd > /dev/null
+2 -2
View File
@@ -39,8 +39,8 @@
** xref:core/resources.adoc[]
** xref:core/validation.adoc[]
*** xref:core/validation/validator.adoc[]
*** xref:core/validation/conversion.adoc[]
*** xref:core/validation/beans-beans.adoc[]
*** xref:core/validation/conversion.adoc[]
*** xref:core/validation/convert.adoc[]
*** xref:core/validation/format.adoc[]
*** xref:core/validation/format-configuring-formatting-globaldatetimeformat.adoc[]
@@ -431,4 +431,4 @@
** xref:languages/groovy.adoc[]
** xref:languages/dynamic.adoc[]
* xref:appendix.adoc[]
* https://github.com/spring-projects/spring-framework/wiki[Wiki]
* https://github.com/spring-projects/spring-framework/wiki[Wiki]
@@ -141,7 +141,7 @@ Kotlin::
====
After you learn about Spring's IoC container, you may want to know more about Spring's
`Resource` abstraction (as described in
xref:web/webflux-webclient/client-builder.adoc#webflux-client-builder-reactor-resources[Resources])
xref:core/resources.adoc[Resources])
which provides a convenient mechanism for reading an InputStream from locations defined
in a URI syntax. In particular, `Resource` paths are used to construct applications contexts,
as described in xref:core/resources.adoc#resources-app-ctx[Application Contexts and Resource Paths].
@@ -4,7 +4,7 @@
Projection lets a collection drive the evaluation of a sub-expression, and the result is
a new collection. The syntax for projection is `.![projectionExpression]`. For example,
suppose we have a list of inventors but want the list of cities where they were born.
Effectively, we want to evaluate 'placeOfBirth.city' for every entry in the inventor
Effectively, we want to evaluate `placeOfBirth.city` for every entry in the inventor
list. The following example uses projection to do so:
[tabs]
@@ -13,16 +13,18 @@ Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
// returns ['Smiljan', 'Idvor' ]
List placesOfBirth = (List)parser.parseExpression("members.![placeOfBirth.city]");
// evaluates to ["SmilJan", "Idvor"]
List placesOfBirth = parser.parseExpression("members.![placeOfBirth.city]")
.getValue(societyContext, List.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
// returns ['Smiljan', 'Idvor' ]
val placesOfBirth = parser.parseExpression("members.![placeOfBirth.city]") as List<*>
// evaluates to ["SmilJan", "Idvor"]
val placesOfBirth = parser.parseExpression("members.![placeOfBirth.city]")
.getValue(societyContext) as List<*>
----
======
@@ -28,13 +28,14 @@ Kotlin::
======
Selection is supported for arrays and anything that implements `java.lang.Iterable` or
`java.util.Map`. For a list or array, the selection criteria is evaluated against each
individual element. Against a map, the selection criteria is evaluated against each map
entry (objects of the Java type `Map.Entry`). Each map entry has its `key` and `value`
accessible as properties for use in the selection.
`java.util.Map`. For an array or `Iterable`, the selection expression is evaluated
against each individual element. Against a map, the selection expression is evaluated
against each map entry (objects of the Java type `Map.Entry`). Each map entry has its
`key` and `value` accessible as properties for use in the selection.
The following expression returns a new map that consists of those elements of the
original map where the entry's value is less than 27:
Given a `Map` stored in a variable named `#map`, the following expression returns a new
map that consists of those elements of the original map where the entry's value is less
than 27:
[tabs]
======
@@ -42,21 +43,21 @@ Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
Map newMap = parser.parseExpression("map.?[value<27]").getValue();
Map newMap = parser.parseExpression("#map.?[value < 27]").getValue(Map.class);
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
val newMap = parser.parseExpression("map.?[value<27]").getValue()
val newMap = parser.parseExpression("#map.?[value < 27]").getValue() as Map
----
======
In addition to returning all the selected elements, you can retrieve only the first or
the last element. To obtain the first element matching the selection, the syntax is
`.^[selectionExpression]`. To obtain the last matching selection, the syntax is
`.$[selectionExpression]`.
the last element. To obtain the first element matching the selection expression, the
syntax is `.^[selectionExpression]`. To obtain the last element matching the selection
expression, the syntax is `.$[selectionExpression]`.
@@ -268,8 +268,8 @@ The actual JPA provider bootstrapping is handed off to the specified executor an
running in parallel, to the application bootstrap thread. The exposed `EntityManagerFactory`
proxy can be injected into other application components and is even able to respond to
`EntityManagerFactoryInfo` configuration inspection. However, once the actual JPA provider
is being accessed by other components (for example, calling `createEntityManager`), those calls
block until the background bootstrapping has completed. In particular, when you use
is being accessed by other components (for example, calling `createEntityManager`), those
calls block until the background bootstrapping has completed. In particular, when you use
Spring Data JPA, make sure to set up deferred bootstrapping for its repositories as well.
@@ -284,9 +284,9 @@ to a newly created `EntityManager` per operation, in effect making its usage thr
It is possible to write code against the plain JPA without any Spring dependencies, by
using an injected `EntityManagerFactory` or `EntityManager`. Spring can understand the
`@PersistenceUnit` and `@PersistenceContext` annotations both at the field and the method level
if a `PersistenceAnnotationBeanPostProcessor` is enabled. The following example shows a plain
JPA DAO implementation that uses the `@PersistenceUnit` annotation:
`@PersistenceUnit` and `@PersistenceContext` annotations both at the field and the method
level if a `PersistenceAnnotationBeanPostProcessor` is enabled. The following example
shows a plain JPA DAO implementation that uses the `@PersistenceUnit` annotation:
[tabs]
======
@@ -152,7 +152,7 @@ Kotlin::
@Autowired
lateinit var accountService: AccountService
lateinit mockMvc: MockMvc
lateinit var mockMvc: MockMvc
@BeforeEach
fun setup(wac: WebApplicationContext) {
+13 -13
View File
@@ -9,23 +9,23 @@ javaPlatform {
dependencies {
api(platform("com.fasterxml.jackson:jackson-bom:2.14.3"))
api(platform("io.micrometer:micrometer-bom:1.10.13"))
api(platform("io.netty:netty-bom:4.1.104.Final"))
api(platform("io.netty:netty-bom:4.1.107.Final"))
api(platform("io.netty:netty5-bom:5.0.0.Alpha5"))
api(platform("io.projectreactor:reactor-bom:2022.0.15"))
api(platform("io.projectreactor:reactor-bom:2022.0.17"))
api(platform("io.rsocket:rsocket-bom:1.1.3"))
api(platform("org.apache.groovy:groovy-bom:4.0.17"))
api(platform("org.apache.groovy:groovy-bom:4.0.19"))
api(platform("org.apache.logging.log4j:log4j-bom:2.21.1"))
api(platform("org.eclipse.jetty:jetty-bom:11.0.19"))
api(platform("org.eclipse.jetty:jetty-bom:11.0.20"))
api(platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.6.4"))
api(platform("org.jetbrains.kotlinx:kotlinx-serialization-bom:1.4.0"))
api(platform("org.junit:junit-bom:5.9.3"))
api(platform("org.mockito:mockito-bom:5.8.0"))
api(platform("org.mockito:mockito-bom:5.11.0"))
constraints {
api("com.fasterxml:aalto-xml:1.3.2")
api("com.fasterxml.woodstox:woodstox-core:6.5.1")
api("com.github.ben-manes.caffeine:caffeine:3.1.8")
api("com.github.librepdf:openpdf:1.3.36")
api("com.github.librepdf:openpdf:1.3.42")
api("com.google.code.findbugs:findbugs:3.0.1")
api("com.google.code.findbugs:jsr305:3.0.2")
api("com.google.code.gson:gson:2.10.1")
@@ -53,10 +53,10 @@ dependencies {
api("io.r2dbc:r2dbc-spi-test:1.0.0.RELEASE")
api("io.r2dbc:r2dbc-spi:1.0.0.RELEASE")
api("io.reactivex.rxjava3:rxjava:3.1.8")
api("io.smallrye.reactive:mutiny:1.9.0")
api("io.undertow:undertow-core:2.3.10.Final")
api("io.undertow:undertow-servlet:2.3.10.Final")
api("io.undertow:undertow-websockets-jsr:2.3.10.Final")
api("io.smallrye.reactive:mutiny:1.10.0")
api("io.undertow:undertow-core:2.3.12.Final")
api("io.undertow:undertow-servlet:2.3.12.Final")
api("io.undertow:undertow-websockets-jsr:2.3.12.Final")
api("io.vavr:vavr:0.10.4")
api("jakarta.activation:jakarta.activation-api:2.0.1")
api("jakarta.annotation:jakarta.annotation-api:2.0.0")
@@ -111,7 +111,7 @@ dependencies {
api("org.bouncycastle:bcpkix-jdk18on:1.72")
api("org.codehaus.jettison:jettison:1.5.4")
api("org.dom4j:dom4j:2.1.4")
api("org.eclipse.jetty:jetty-reactive-httpclient:3.0.10")
api("org.eclipse.jetty:jetty-reactive-httpclient:3.0.12")
api("org.eclipse.persistence:org.eclipse.persistence.jpa:3.0.4")
api("org.eclipse:yasson:2.0.4")
api("org.ehcache:ehcache:3.10.8")
@@ -126,7 +126,7 @@ dependencies {
api("org.hibernate:hibernate-validator:7.0.5.Final")
api("org.hsqldb:hsqldb:2.7.2")
api("org.javamoney:moneta:1.4.2")
api("org.jruby:jruby:9.4.5.0")
api("org.jruby:jruby:9.4.6.0")
api("org.junit.support:testng-engine:1.0.4")
api("org.mozilla:rhino:1.7.14")
api("org.ogce:xpp3:1.1.6")
@@ -135,7 +135,7 @@ dependencies {
api("org.seleniumhq.selenium:htmlunit-driver:2.70.0")
api("org.seleniumhq.selenium:selenium-java:3.141.59")
api("org.skyscreamer:jsonassert:1.5.1")
api("org.slf4j:slf4j-api:2.0.11")
api("org.slf4j:slf4j-api:2.0.12")
api("org.testng:testng:7.8.0")
api("org.webjars:underscorejs:1.8.3")
api("org.webjars:webjars-locator-core:0.55")
+1 -1
View File
@@ -1,4 +1,4 @@
version=6.0.16-SNAPSHOT
version=6.0.18
org.gradle.caching=true
org.gradle.jvmargs=-Xmx2048m
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Vendored
+10 -10
View File
@@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
@@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -186,10 +186,10 @@ public abstract class AopUtils {
* this method resolves bridge methods in order to retrieve attributes from
* the <i>original</i> method definition.
* @param method the method to be invoked, which may come from an interface
* @param targetClass the target class for the current invocation.
* May be {@code null} or may not even implement the method.
* @param targetClass the target class for the current invocation
* (can be {@code null} or may not even implement the method)
* @return the specific target method, or the original method if the
* {@code targetClass} doesn't implement it or is {@code null}
* {@code targetClass} does not implement it
* @see org.springframework.util.ClassUtils#getMostSpecificMethod
*/
public static Method getMostSpecificMethod(Method method, @Nullable Class<?> targetClass) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2024 the original author 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,29 +17,35 @@
package org.springframework.aop.support;
import java.lang.reflect.Method;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
import org.springframework.aop.target.EmptyTargetSource;
import org.springframework.aop.testfixture.interceptor.NopInterceptor;
import org.springframework.beans.testfixture.beans.TestBean;
import org.springframework.core.ResolvableType;
import org.springframework.core.testfixture.io.SerializationTestUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Rod Johnson
* @author Chris Beams
* @author Sebastien Deleuze
* @author Juergen Hoeller
*/
public class AopUtilsTests {
class AopUtilsTests {
@Test
public void testPointcutCanNeverApply() {
void testPointcutCanNeverApply() {
class TestPointcut extends StaticMethodMatcherPointcut {
@Override
public boolean matches(Method method, @Nullable Class<?> clazzy) {
@@ -52,13 +58,13 @@ public class AopUtilsTests {
}
@Test
public void testPointcutAlwaysApplies() {
void testPointcutAlwaysApplies() {
assertThat(AopUtils.canApply(new DefaultPointcutAdvisor(new NopInterceptor()), Object.class)).isTrue();
assertThat(AopUtils.canApply(new DefaultPointcutAdvisor(new NopInterceptor()), TestBean.class)).isTrue();
}
@Test
public void testPointcutAppliesToOneMethodOnObject() {
void testPointcutAppliesToOneMethodOnObject() {
class TestPointcut extends StaticMethodMatcherPointcut {
@Override
public boolean matches(Method method, @Nullable Class<?> clazz) {
@@ -78,7 +84,7 @@ public class AopUtilsTests {
* that's subverted the singleton construction limitation.
*/
@Test
public void testCanonicalFrameworkClassesStillCanonicalOnDeserialization() throws Exception {
void testCanonicalFrameworkClassesStillCanonicalOnDeserialization() throws Exception {
assertThat(SerializationTestUtils.serializeAndDeserialize(MethodMatcher.TRUE)).isSameAs(MethodMatcher.TRUE);
assertThat(SerializationTestUtils.serializeAndDeserialize(ClassFilter.TRUE)).isSameAs(ClassFilter.TRUE);
assertThat(SerializationTestUtils.serializeAndDeserialize(Pointcut.TRUE)).isSameAs(Pointcut.TRUE);
@@ -88,4 +94,45 @@ public class AopUtilsTests {
assertThat(SerializationTestUtils.serializeAndDeserialize(ExposeInvocationInterceptor.INSTANCE)).isSameAs(ExposeInvocationInterceptor.INSTANCE);
}
@Test
void testInvokeJoinpointUsingReflection() throws Throwable {
String name = "foo";
TestBean testBean = new TestBean(name);
Method method = ReflectionUtils.findMethod(TestBean.class, "getName");
Object result = AopUtils.invokeJoinpointUsingReflection(testBean, method, new Object[0]);
assertThat(result).isEqualTo(name);
}
@Test // gh-32365
void mostSpecificMethodBetweenJdkProxyAndTarget() throws Exception {
Class<?> proxyClass = new ProxyFactory(new WithInterface()).getProxyClass(getClass().getClassLoader());
Method specificMethod = AopUtils.getMostSpecificMethod(proxyClass.getMethod("handle", List.class), WithInterface.class);
assertThat(ResolvableType.forMethodParameter(specificMethod, 0).getGeneric().toClass()).isEqualTo(String.class);
}
@Test // gh-32365
void mostSpecificMethodBetweenCglibProxyAndTarget() throws Exception {
Class<?> proxyClass = new ProxyFactory(new WithoutInterface()).getProxyClass(getClass().getClassLoader());
Method specificMethod = AopUtils.getMostSpecificMethod(proxyClass.getMethod("handle", List.class), WithoutInterface.class);
assertThat(ResolvableType.forMethodParameter(specificMethod, 0).getGeneric().toClass()).isEqualTo(String.class);
}
interface ProxyInterface {
void handle(List<String> list);
}
static class WithInterface implements ProxyInterface {
public void handle(List<String> list) {
}
}
static class WithoutInterface {
public void handle(List<String> list) {
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -292,7 +292,7 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa
if (actualValue != null) {
actualValue = typeConverter.convertIfNecessary(actualValue, expectedValue.getClass());
}
if (!expectedValue.equals(actualValue)) {
if (!ObjectUtils.nullSafeEquals(expectedValue, actualValue)) {
return false;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -161,6 +161,9 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
/** Map from scope identifier String to corresponding Scope. */
private final Map<String, Scope> scopes = new LinkedHashMap<>(8);
/** Application startup metrics. **/
private ApplicationStartup applicationStartup = ApplicationStartup.DEFAULT;
/** Map from bean name to merged RootBeanDefinition. */
private final Map<String, RootBeanDefinition> mergedBeanDefinitions = new ConcurrentHashMap<>(256);
@@ -171,8 +174,6 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
private final ThreadLocal<Object> prototypesCurrentlyInCreation =
new NamedThreadLocal<>("Prototype beans currently in creation");
/** Application startup metrics. **/
private ApplicationStartup applicationStartup = ApplicationStartup.DEFAULT;
/**
* Create a new AbstractBeanFactory.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -609,13 +609,10 @@ class ConstructorResolver {
String argDesc = StringUtils.collectionToCommaDelimitedString(argTypes);
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"No matching factory method found on class [" + factoryClass.getName() + "]: " +
(mbd.getFactoryBeanName() != null ?
"factory bean '" + mbd.getFactoryBeanName() + "'; " : "") +
(mbd.getFactoryBeanName() != null ? "factory bean '" + mbd.getFactoryBeanName() + "'; " : "") +
"factory method '" + mbd.getFactoryMethodName() + "(" + argDesc + ")'. " +
"Check that a method with the specified name " +
(minNrOfArgs > 0 ? "and arguments " : "") +
"exists and that it is " +
(isStatic ? "static" : "non-static") + ".");
"Check that a method with the specified name " + (minNrOfArgs > 0 ? "and arguments " : "") +
"exists and that it is " + (isStatic ? "static" : "non-static") + ".");
}
else if (void.class == factoryMethodToUse.getReturnType()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -567,16 +567,16 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
*/
protected void destroyBean(String beanName, @Nullable DisposableBean bean) {
// Trigger destruction of dependent beans first...
Set<String> dependencies;
Set<String> dependentBeanNames;
synchronized (this.dependentBeanMap) {
// Within full synchronization in order to guarantee a disconnected Set
dependencies = this.dependentBeanMap.remove(beanName);
dependentBeanNames = this.dependentBeanMap.remove(beanName);
}
if (dependencies != null) {
if (dependentBeanNames != null) {
if (logger.isTraceEnabled()) {
logger.trace("Retrieved dependent beans for bean '" + beanName + "': " + dependencies);
logger.trace("Retrieved dependent beans for bean '" + beanName + "': " + dependentBeanNames);
}
for (String dependentBeanName : dependencies) {
for (String dependentBeanName : dependentBeanNames) {
destroySingleton(dependentBeanName);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -261,7 +261,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
if (destroyMethod != null) {
return destroyMethod;
}
for (Class<?> beanInterface : beanClass.getInterfaces()) {
for (Class<?> beanInterface : ClassUtils.getAllInterfacesForClass(beanClass)) {
destroyMethod = findDestroyMethod(beanInterface, methodName);
if (destroyMethod != null) {
return destroyMethod;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,12 +29,10 @@ import org.springframework.core.MethodClassKey;
import org.springframework.lang.Nullable;
/**
* Abstract implementation of {@link JCacheOperationSource} that caches attributes
* Abstract implementation of {@link JCacheOperationSource} that caches operations
* for methods and implements a fallback policy: 1. specific target method;
* 2. declaring method.
*
* <p>This implementation caches attributes by method after they are first used.
*
* @author Stephane Nicoll
* @author Juergen Hoeller
* @since 4.1
@@ -43,24 +41,25 @@ import org.springframework.lang.Nullable;
public abstract class AbstractFallbackJCacheOperationSource implements JCacheOperationSource {
/**
* Canonical value held in cache to indicate no caching attribute was
* found for this method and we don't need to look again.
* Canonical value held in cache to indicate no cache operation was
* found for this method, and we don't need to look again.
*/
private static final Object NULL_CACHING_ATTRIBUTE = new Object();
private static final Object NULL_CACHING_MARKER = new Object();
protected final Log logger = LogFactory.getLog(getClass());
private final Map<MethodClassKey, Object> cache = new ConcurrentHashMap<>(1024);
private final Map<MethodClassKey, Object> operationCache = new ConcurrentHashMap<>(1024);
@Override
@Nullable
public JCacheOperation<?> getCacheOperation(Method method, @Nullable Class<?> targetClass) {
MethodClassKey cacheKey = new MethodClassKey(method, targetClass);
Object cached = this.cache.get(cacheKey);
Object cached = this.operationCache.get(cacheKey);
if (cached != null) {
return (cached != NULL_CACHING_ATTRIBUTE ? (JCacheOperation<?>) cached : null);
return (cached != NULL_CACHING_MARKER ? (JCacheOperation<?>) cached : null);
}
else {
JCacheOperation<?> operation = computeCacheOperation(method, targetClass);
@@ -68,10 +67,10 @@ public abstract class AbstractFallbackJCacheOperationSource implements JCacheOpe
if (logger.isDebugEnabled()) {
logger.debug("Adding cacheable method '" + method.getName() + "' with operation: " + operation);
}
this.cache.put(cacheKey, operation);
this.operationCache.put(cacheKey, operation);
}
else {
this.cache.put(cacheKey, NULL_CACHING_ATTRIBUTE);
this.operationCache.put(cacheKey, NULL_CACHING_MARKER);
}
return operation;
}
@@ -84,7 +83,7 @@ public abstract class AbstractFallbackJCacheOperationSource implements JCacheOpe
return null;
}
// The method may be on an interface, but we need attributes from the target class.
// The method may be on an interface, but we need metadata from the target class.
// If the target class is null, the method will be unchanged.
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -212,10 +212,8 @@ public abstract class AnnotationJCacheOperationSource extends AbstractFallbackJC
for (Class<?> parameterType : parameterTypes) {
parameters.add(parameterType.getName());
}
return method.getDeclaringClass().getName()
+ '.' + method.getName()
+ '(' + StringUtils.collectionToCommaDelimitedString(parameters) + ')';
return method.getDeclaringClass().getName() + '.' + method.getName() +
'(' + StringUtils.collectionToCommaDelimitedString(parameters) + ')';
}
private int countNonNull(Object... instances) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author 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 @@ public class BeanFactoryJCacheOperationSourceAdvisor extends AbstractBeanFactory
* Set the cache operation attribute source which is used to find cache
* attributes. This should usually be identical to the source reference
* set on the cache interceptor itself.
* @see JCacheInterceptor#setCacheOperationSource
*/
public void setCacheOperationSource(JCacheOperationSource cacheOperationSource) {
this.pointcut.setCacheOperationSource(cacheOperationSource);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,7 +34,7 @@ public interface JCacheOperationSource {
* Return the cache operations for this method, or {@code null}
* if the method contains no <em>JSR-107</em> related metadata.
* @param method the method to introspect
* @param targetClass the target class (may be {@code null}, in which case
* @param targetClass the target class (can be {@code null}, in which case
* the declaring class of the method must be used)
* @return the cache operation for this method, or {@code null} if none found
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,7 +24,7 @@ import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
/**
* A Pointcut that matches if the underlying {@link JCacheOperationSource}
* A {@code Pointcut} that matches if the underlying {@link JCacheOperationSource}
* has an operation for a given method.
*
* @author Stephane Nicoll
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,20 +32,16 @@ import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
/**
* Abstract implementation of {@link CacheOperation} that caches attributes
* Abstract implementation of {@link CacheOperationSource} that caches operations
* for methods and implements a fallback policy: 1. specific target method;
* 2. target class; 3. declaring method; 4. declaring class/interface.
*
* <p>Defaults to using the target class's caching attribute if none is
* associated with the target method. Any caching attribute associated with
* the target method completely overrides a class caching attribute.
* <p>Defaults to using the target class's declared cache operations if none are
* associated with the target method. Any cache operations associated with
* the target method completely override any class-level declarations.
* If none found on the target class, the interface that the invoked method
* has been called through (in case of a JDK proxy) will be checked.
*
* <p>This implementation caches attributes by method after they are first
* used. If it is ever desirable to allow dynamic changing of cacheable
* attributes (which is very unlikely), caching could be made configurable.
*
* @author Costin Leau
* @author Juergen Hoeller
* @since 3.1
@@ -53,10 +49,10 @@ import org.springframework.util.ClassUtils;
public abstract class AbstractFallbackCacheOperationSource implements CacheOperationSource {
/**
* Canonical value held in cache to indicate no caching attribute was
* found for this method and we don't need to look again.
* Canonical value held in cache to indicate no cache operation was
* found for this method, and we don't need to look again.
*/
private static final Collection<CacheOperation> NULL_CACHING_ATTRIBUTE = Collections.emptyList();
private static final Collection<CacheOperation> NULL_CACHING_MARKER = Collections.emptyList();
/**
@@ -71,14 +67,14 @@ public abstract class AbstractFallbackCacheOperationSource implements CacheOpera
* <p>As this base class is not marked Serializable, the cache will be recreated
* after serialization - provided that the concrete subclass is Serializable.
*/
private final Map<Object, Collection<CacheOperation>> attributeCache = new ConcurrentHashMap<>(1024);
private final Map<Object, Collection<CacheOperation>> operationCache = new ConcurrentHashMap<>(1024);
/**
* Determine the caching attribute for this method invocation.
* <p>Defaults to the class's caching attribute if no method attribute is found.
* Determine the cache operations for this method invocation.
* <p>Defaults to class-declared metadata if no method-level metadata is found.
* @param method the method for the current invocation (never {@code null})
* @param targetClass the target class for this invocation (may be {@code null})
* @param targetClass the target class for this invocation (can be {@code null})
* @return {@link CacheOperation} for this method, or {@code null} if the method
* is not cacheable
*/
@@ -90,21 +86,21 @@ public abstract class AbstractFallbackCacheOperationSource implements CacheOpera
}
Object cacheKey = getCacheKey(method, targetClass);
Collection<CacheOperation> cached = this.attributeCache.get(cacheKey);
Collection<CacheOperation> cached = this.operationCache.get(cacheKey);
if (cached != null) {
return (cached != NULL_CACHING_ATTRIBUTE ? cached : null);
return (cached != NULL_CACHING_MARKER ? cached : null);
}
else {
Collection<CacheOperation> cacheOps = computeCacheOperations(method, targetClass);
if (cacheOps != null) {
if (logger.isTraceEnabled()) {
logger.trace("Adding cacheable method '" + method.getName() + "' with attribute: " + cacheOps);
logger.trace("Adding cacheable method '" + method.getName() + "' with operations: " + cacheOps);
}
this.attributeCache.put(cacheKey, cacheOps);
this.operationCache.put(cacheKey, cacheOps);
}
else {
this.attributeCache.put(cacheKey, NULL_CACHING_ATTRIBUTE);
this.operationCache.put(cacheKey, NULL_CACHING_MARKER);
}
return cacheOps;
}
@@ -129,7 +125,7 @@ public abstract class AbstractFallbackCacheOperationSource implements CacheOpera
return null;
}
// The method may be on an interface, but we need attributes from the target class.
// The method may be on an interface, but we need metadata from the target class.
// If the target class is null, the method will be unchanged.
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
@@ -163,19 +159,19 @@ public abstract class AbstractFallbackCacheOperationSource implements CacheOpera
/**
* Subclasses need to implement this to return the caching attribute for the
* Subclasses need to implement this to return the cache operations for the
* given class, if any.
* @param clazz the class to retrieve the attribute for
* @return all caching attribute associated with this class, or {@code null} if none
* @param clazz the class to retrieve the cache operations for
* @return all cache operations associated with this class, or {@code null} if none
*/
@Nullable
protected abstract Collection<CacheOperation> findCacheOperations(Class<?> clazz);
/**
* Subclasses need to implement this to return the caching attribute for the
* Subclasses need to implement this to return the cache operations for the
* given method, if any.
* @param method the method to retrieve the attribute for
* @return all caching attribute associated with this method, or {@code null} if none
* @param method the method to retrieve the cache operations for
* @return all cache operations associated with this method, or {@code null} if none
*/
@Nullable
protected abstract Collection<CacheOperation> findCacheOperations(Method method);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -54,7 +54,7 @@ public interface CacheOperationSource {
* Return the collection of cache operations for this method,
* or {@code null} if the method contains no <em>cacheable</em> annotations.
* @param method the method to introspect
* @param targetClass the target class (may be {@code null}, in which case
* @param targetClass the target class (can be {@code null}, in which case
* the declaring class of the method must be used)
* @return all cache operations for this method, or {@code null} if none found
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,7 +28,7 @@ import org.springframework.util.ObjectUtils;
/**
* A {@code Pointcut} that matches if the underlying {@link CacheOperationSource}
* has an attribute for a given method.
* has an operation for a given method.
*
* @author Costin Leau
* @author Juergen Hoeller
@@ -36,7 +36,7 @@ import org.springframework.util.ObjectUtils;
* @since 3.1
*/
@SuppressWarnings("serial")
class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
final class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
@Nullable
private CacheOperationSource cacheOperationSource;
@@ -78,7 +78,7 @@ class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implement
* {@link ClassFilter} that delegates to {@link CacheOperationSource#isCandidateClass}
* for filtering classes whose methods are not worth searching to begin with.
*/
private class CacheOperationSourceClassFilter implements ClassFilter {
private final class CacheOperationSourceClassFilter implements ClassFilter {
@Override
public boolean matches(Class<?> clazz) {
@@ -88,6 +88,7 @@ class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implement
return (cacheOperationSource == null || cacheOperationSource.isCandidateClass(clazz));
}
@Nullable
private CacheOperationSource getCacheOperationSource() {
return cacheOperationSource;
}
@@ -95,7 +96,7 @@ class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implement
@Override
public boolean equals(@Nullable Object other) {
return (this == other || (other instanceof CacheOperationSourceClassFilter that &&
ObjectUtils.nullSafeEquals(cacheOperationSource, that.getCacheOperationSource())));
ObjectUtils.nullSafeEquals(getCacheOperationSource(), that.getCacheOperationSource())));
}
@Override
@@ -105,9 +106,8 @@ class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implement
@Override
public String toString() {
return CacheOperationSourceClassFilter.class.getName() + ": " + cacheOperationSource;
return CacheOperationSourceClassFilter.class.getName() + ": " + getCacheOperationSource();
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -121,7 +121,7 @@ public interface SmartLifecycle extends Lifecycle, Phased {
/**
* Return the phase that this lifecycle object is supposed to run in.
* <p>The default implementation returns {@link #DEFAULT_PHASE} in order to
* let {@code stop()} callbacks execute after regular {@code Lifecycle}
* let {@code stop()} callbacks execute before regular {@code Lifecycle}
* implementations.
* @see #isAutoStartup()
* @see #start()
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -73,7 +73,6 @@ final class ConfigurationClass {
* Create a new {@link ConfigurationClass} with the given name.
* @param metadataReader reader used to parse the underlying {@link Class}
* @param beanName must not be {@code null}
* @see ConfigurationClass#ConfigurationClass(Class, ConfigurationClass)
*/
ConfigurationClass(MetadataReader metadataReader, String beanName) {
Assert.notNull(beanName, "Bean name must not be null");
@@ -87,10 +86,10 @@ final class ConfigurationClass {
* using the {@link Import} annotation or automatically processed as a nested
* configuration class (if importedBy is not {@code null}).
* @param metadataReader reader used to parse the underlying {@link Class}
* @param importedBy the configuration class importing this one or {@code null}
* @param importedBy the configuration class importing this one
* @since 3.1.1
*/
ConfigurationClass(MetadataReader metadataReader, @Nullable ConfigurationClass importedBy) {
ConfigurationClass(MetadataReader metadataReader, ConfigurationClass importedBy) {
this.metadata = metadataReader.getAnnotationMetadata();
this.resource = metadataReader.getResource();
this.importedBy.add(importedBy);
@@ -100,7 +99,6 @@ final class ConfigurationClass {
* Create a new {@link ConfigurationClass} with the given name.
* @param clazz the underlying {@link Class} to represent
* @param beanName name of the {@code @Configuration} class bean
* @see ConfigurationClass#ConfigurationClass(Class, ConfigurationClass)
*/
ConfigurationClass(Class<?> clazz, String beanName) {
Assert.notNull(beanName, "Bean name must not be null");
@@ -114,10 +112,10 @@ final class ConfigurationClass {
* using the {@link Import} annotation or automatically processed as a nested
* configuration class (if imported is {@code true}).
* @param clazz the underlying {@link Class} to represent
* @param importedBy the configuration class importing this one (or {@code null})
* @param importedBy the configuration class importing this one
* @since 3.1.1
*/
ConfigurationClass(Class<?> clazz, @Nullable ConfigurationClass importedBy) {
ConfigurationClass(Class<?> clazz, ConfigurationClass importedBy) {
this.metadata = AnnotationMetadata.introspect(clazz);
this.resource = new DescriptiveResource(clazz.getName());
this.importedBy.add(importedBy);
@@ -127,7 +125,6 @@ final class ConfigurationClass {
* Create a new {@link ConfigurationClass} with the given name.
* @param metadata the metadata for the underlying class to represent
* @param beanName name of the {@code @Configuration} class bean
* @see ConfigurationClass#ConfigurationClass(Class, ConfigurationClass)
*/
ConfigurationClass(AnnotationMetadata metadata, String beanName) {
Assert.notNull(beanName, "Bean name must not be null");
@@ -149,12 +146,12 @@ final class ConfigurationClass {
return ClassUtils.getShortName(getMetadata().getClassName());
}
void setBeanName(String beanName) {
void setBeanName(@Nullable String beanName) {
this.beanName = beanName;
}
@Nullable
public String getBeanName() {
String getBeanName() {
return this.beanName;
}
@@ -164,7 +161,7 @@ final class ConfigurationClass {
* @since 3.1.1
* @see #getImportedBy()
*/
public boolean isImported() {
boolean isImported() {
return !this.importedBy.isEmpty();
}
@@ -198,6 +195,10 @@ final class ConfigurationClass {
this.importedResources.put(importedResource, readerClass);
}
Map<String, Class<? extends BeanDefinitionReader>> getImportedResources() {
return this.importedResources;
}
void addImportBeanDefinitionRegistrar(ImportBeanDefinitionRegistrar registrar, AnnotationMetadata importingClassMetadata) {
this.importBeanDefinitionRegistrars.put(registrar, importingClassMetadata);
}
@@ -206,10 +207,6 @@ final class ConfigurationClass {
return this.importBeanDefinitionRegistrars;
}
Map<String, Class<? extends BeanDefinitionReader>> getImportedResources() {
return this.importedResources;
}
void validate(ProblemReporter problemReporter) {
Map<String, Object> attributes = this.metadata.getAnnotationAttributes(Configuration.class.getName());
@@ -135,17 +135,6 @@ import org.springframework.util.ReflectionUtils;
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
/**
* The name of the {@link LifecycleProcessor} bean in the context.
* If none is supplied, a {@link DefaultLifecycleProcessor} is used.
* @since 3.0
* @see org.springframework.context.LifecycleProcessor
* @see org.springframework.context.support.DefaultLifecycleProcessor
* @see #start()
* @see #stop()
*/
public static final String LIFECYCLE_PROCESSOR_BEAN_NAME = "lifecycleProcessor";
/**
* The name of the {@link MessageSource} bean in the context.
* If none is supplied, message resolution is delegated to the parent.
@@ -166,6 +155,17 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
*/
public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
/**
* The name of the {@link LifecycleProcessor} bean in the context.
* If none is supplied, a {@link DefaultLifecycleProcessor} is used.
* @since 3.0
* @see org.springframework.context.LifecycleProcessor
* @see org.springframework.context.support.DefaultLifecycleProcessor
* @see #start()
* @see #stop()
*/
public static final String LIFECYCLE_PROCESSOR_BEAN_NAME = "lifecycleProcessor";
static {
// Eagerly load the ContextClosedEvent class to avoid weird classloader issues
@@ -796,8 +796,9 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
}
/**
* Initialize the MessageSource.
* Use parent's if none defined in this context.
* Initialize the {@link MessageSource}.
* <p>Uses parent's {@code MessageSource} if none defined in this context.
* @see #MESSAGE_SOURCE_BEAN_NAME
*/
protected void initMessageSource() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
@@ -827,8 +828,9 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
}
/**
* Initialize the ApplicationEventMulticaster.
* Uses SimpleApplicationEventMulticaster if none defined in the context.
* Initialize the {@link ApplicationEventMulticaster}.
* <p>Uses {@link SimpleApplicationEventMulticaster} if none defined in the context.
* @see #APPLICATION_EVENT_MULTICASTER_BEAN_NAME
* @see org.springframework.context.event.SimpleApplicationEventMulticaster
*/
protected void initApplicationEventMulticaster() {
@@ -851,15 +853,16 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
}
/**
* Initialize the LifecycleProcessor.
* Uses DefaultLifecycleProcessor if none defined in the context.
* Initialize the {@link LifecycleProcessor}.
* <p>Uses {@link DefaultLifecycleProcessor} if none defined in the context.
* @since 3.0
* @see #LIFECYCLE_PROCESSOR_BEAN_NAME
* @see org.springframework.context.support.DefaultLifecycleProcessor
*/
protected void initLifecycleProcessor() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(LIFECYCLE_PROCESSOR_BEAN_NAME)) {
this.lifecycleProcessor =
beanFactory.getBean(LIFECYCLE_PROCESSOR_BEAN_NAME, LifecycleProcessor.class);
this.lifecycleProcessor = beanFactory.getBean(LIFECYCLE_PROCESSOR_BEAN_NAME, LifecycleProcessor.class);
if (logger.isTraceEnabled()) {
logger.trace("Using LifecycleProcessor [" + this.lifecycleProcessor + "]");
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -45,7 +45,11 @@ import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Default implementation of the {@link LifecycleProcessor} strategy.
* Spring's default implementation of the {@link LifecycleProcessor} strategy.
*
* <p>Provides interaction with {@link Lifecycle} and {@link SmartLifecycle} beans in
* groups for specific phases, on startup/shutdown as well as for explicit start/stop
* interactions on a {@link org.springframework.context.ConfigurableApplicationContext}.
*
* @author Mark Fisher
* @author Juergen Hoeller
@@ -314,6 +318,8 @@ public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactor
/**
* Helper class for maintaining a group of Lifecycle beans that should be started
* and stopped together based on their 'phase' value (or the default value of 0).
* The group is expected to be created in an ad-hoc fashion and group members are
* expected to always have the same 'phase' value.
*/
private class LifecycleGroup {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@ import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.context.ResourceLoaderAware;
@@ -400,7 +401,7 @@ public class ReloadableResourceBundleMessageSource extends AbstractResourceBased
/**
* Refresh the PropertiesHolder for the given bundle filename.
* The holder can be {@code null} if not cached before, or a timed-out cache entry
* <p>The holder can be {@code null} if not cached before, or a timed-out cache entry
* (potentially getting re-validated against the current last-modified timestamp).
* @param filename the bundle filename (basename + Locale)
* @param propHolder the current PropertiesHolder for the bundle
@@ -561,7 +562,7 @@ public class ReloadableResourceBundleMessageSource extends AbstractResourceBased
private volatile long refreshTimestamp = -2;
private final ReentrantLock refreshLock = new ReentrantLock();
private final Lock refreshLock = new ReentrantLock();
/** Cache to hold already generated MessageFormats per message code. */
private final ConcurrentMap<String, Map<Locale, MessageFormat>> cachedMessageFormats =
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -98,8 +98,8 @@ public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAd
* applying the corresponding default if a supplier is not resolvable.
* @since 5.1
*/
public void configure(
@Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {
public void configure(@Nullable Supplier<Executor> executor,
@Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {
this.executor = executor;
this.exceptionHandler = exceptionHandler;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -73,7 +73,7 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
/**
* Set the ThreadFactory to use for the ExecutorService's thread pool.
* Default is the underlying ExecutorService's default thread factory.
* The default is the underlying ExecutorService's default thread factory.
* <p>In a Jakarta EE or other managed environment with JSR-236 support,
* consider specifying a JNDI-located ManagedThreadFactory: by default,
* to be found at "java:comp/DefaultManagedThreadFactory".
@@ -108,7 +108,7 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
/**
* Set whether to wait for scheduled tasks to complete on shutdown,
* not interrupting running tasks and executing all tasks in the queue.
* <p>Default is {@code false}, shutting down immediately through interrupting
* <p>The default is {@code false}, shutting down immediately through interrupting
* ongoing tasks and clearing the queue. Switch this flag to {@code true} if
* you prefer fully completed tasks at the expense of a longer shutdown phase.
* <p>Note that Spring's container shutdown continues while ongoing tasks
@@ -119,6 +119,8 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
* property instead of or in addition to this property.
* @see java.util.concurrent.ExecutorService#shutdown()
* @see java.util.concurrent.ExecutorService#shutdownNow()
* @see #shutdown()
* @see #setAwaitTerminationSeconds
*/
public void setWaitForTasksToCompleteOnShutdown(boolean waitForJobsToCompleteOnShutdown) {
this.waitForTasksToCompleteOnShutdown = waitForJobsToCompleteOnShutdown;
@@ -237,7 +239,7 @@ public abstract class ExecutorConfigurationSupport extends CustomizableThreadFac
}
/**
* Cancel the given remaining task which never commended execution,
* Cancel the given remaining task which never commenced execution,
* as returned from {@link ExecutorService#shutdownNow()}.
* @param task the task to cancel (typically a {@link RunnableFuture})
* @since 5.0.5
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,17 +29,15 @@ import org.springframework.util.StringUtils;
* Created using the {@code parse*} methods.
*
* @author Arjen Poutsma
* @author Juergen Hoeller
* @since 5.3
*/
final class BitsCronField extends CronField {
public static final BitsCronField ZERO_NANOS = forZeroNanos();
private static final long MASK = 0xFFFFFFFFFFFFFFFFL;
@Nullable
private static BitsCronField zeroNanos = null;
// we store at most 60 bits, for seconds and minutes, so a 64-bit long suffices
private long bits;
@@ -48,16 +46,14 @@ final class BitsCronField extends CronField {
super(type);
}
/**
* Return a {@code BitsCronField} enabled for 0 nanoseconds.
*/
public static BitsCronField zeroNanos() {
if (zeroNanos == null) {
BitsCronField field = new BitsCronField(Type.NANO);
field.setBit(0);
zeroNanos = field;
}
return zeroNanos;
private static BitsCronField forZeroNanos() {
BitsCronField field = new BitsCronField(Type.NANO);
field.setBit(0);
return field;
}
/**
@@ -108,7 +104,6 @@ final class BitsCronField extends CronField {
return result;
}
private static BitsCronField parseDate(String value, BitsCronField.Type type) {
if (value.equals("?")) {
value = "*";
@@ -174,6 +169,7 @@ final class BitsCronField extends CronField {
}
}
@Nullable
@Override
public <T extends Temporal & Comparable<? super T>> T nextOrSame(T temporal) {
@@ -217,7 +213,6 @@ final class BitsCronField extends CronField {
else {
return -1;
}
}
private void setBits(ValueRange range) {
@@ -247,7 +242,14 @@ final class BitsCronField extends CronField {
}
private void clearBit(int index) {
this.bits &= ~(1L << index);
this.bits &= ~(1L << index);
}
@Override
public boolean equals(Object other) {
return (this == other || (other instanceof BitsCronField that &&
type() == that.type() && this.bits == that.bits));
}
@Override
@@ -255,17 +257,6 @@ final class BitsCronField extends CronField {
return Long.hashCode(this.bits);
}
@Override
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
if (!(o instanceof BitsCronField other)) {
return false;
}
return type() == other.type() && this.bits == other.bits;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder(type().toString());
@@ -29,9 +29,14 @@ import org.springframework.util.StringUtils;
* <a href="https://www.manpagez.com/man/5/crontab/">crontab expression</a>
* that can calculate the next time it matches.
*
* <p>{@code CronExpression} instances are created through
* {@link #parse(String)}; the next match is determined with
* {@link #next(Temporal)}.
* <p>{@code CronExpression} instances are created through {@link #parse(String)};
* the next match is determined with {@link #next(Temporal)}.
*
* <p>Supports a Quartz day-of-month/week field with an L/# expression. Follows
* common cron conventions in every other respect, including 0-6 for SUN-SAT
* (plus 7 for SUN as well). Note that Quartz deviates from the day-of-week
* convention in cron through 1-7 for SUN-SAT whereas Spring strictly follows
* cron even in combination with the optional Quartz-specific L/# expressions.
*
* @author Arjen Poutsma
* @since 5.3
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,17 +29,24 @@ import org.springframework.util.StringUtils;
/**
* Single field in a cron pattern. Created using the {@code parse*} methods,
* main and only entry point is {@link #nextOrSame(Temporal)}.
* the main and only entry point is {@link #nextOrSame(Temporal)}.
*
* <p>Supports a Quartz day-of-month/week field with an L/# expression. Follows
* common cron conventions in every other respect, including 0-6 for SUN-SAT
* (plus 7 for SUN as well). Note that Quartz deviates from the day-of-week
* convention in cron through 1-7 for SUN-SAT whereas Spring strictly follows
* cron even in combination with the optional Quartz-specific L/# expressions.
*
* @author Arjen Poutsma
* @since 5.3
*/
abstract class CronField {
private static final String[] MONTHS = new String[]{"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP",
"OCT", "NOV", "DEC"};
private static final String[] MONTHS = new String[]
{"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};
private static final String[] DAYS = new String[]{"MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"};
private static final String[] DAYS = new String[]
{"MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"};
private final Type type;
@@ -48,11 +55,12 @@ abstract class CronField {
this.type = type;
}
/**
* Return a {@code CronField} enabled for 0 nanoseconds.
*/
public static CronField zeroNanos() {
return BitsCronField.zeroNanos();
return BitsCronField.ZERO_NANOS;
}
/**
@@ -169,6 +177,7 @@ abstract class CronField {
* day-of-month, month, day-of-week.
*/
protected enum Type {
NANO(ChronoField.NANO_OF_SECOND, ChronoUnit.SECONDS),
SECOND(ChronoField.SECOND_OF_MINUTE, ChronoUnit.MINUTES, ChronoField.NANO_OF_SECOND),
MINUTE(ChronoField.MINUTE_OF_HOUR, ChronoUnit.HOURS, ChronoField.SECOND_OF_MINUTE, ChronoField.NANO_OF_SECOND),
@@ -177,21 +186,18 @@ abstract class CronField {
MONTH(ChronoField.MONTH_OF_YEAR, ChronoUnit.YEARS, ChronoField.DAY_OF_MONTH, ChronoField.HOUR_OF_DAY, ChronoField.MINUTE_OF_HOUR, ChronoField.SECOND_OF_MINUTE, ChronoField.NANO_OF_SECOND),
DAY_OF_WEEK(ChronoField.DAY_OF_WEEK, ChronoUnit.WEEKS, ChronoField.HOUR_OF_DAY, ChronoField.MINUTE_OF_HOUR, ChronoField.SECOND_OF_MINUTE, ChronoField.NANO_OF_SECOND);
private final ChronoField field;
private final ChronoUnit higherOrder;
private final ChronoField[] lowerOrders;
Type(ChronoField field, ChronoUnit higherOrder, ChronoField... lowerOrders) {
this.field = field;
this.higherOrder = higherOrder;
this.lowerOrders = lowerOrders;
}
/**
* Return the value of this type for the given temporal.
* @return the value of this type
@@ -27,8 +27,14 @@ import org.springframework.scheduling.TriggerContext;
import org.springframework.util.Assert;
/**
* {@link Trigger} implementation for cron expressions.
* Wraps a {@link CronExpression}.
* {@link Trigger} implementation for cron expressions. Wraps a
* {@link CronExpression} which parses according to common crontab conventions.
*
* <p>Supports a Quartz day-of-month/week field with an L/# expression. Follows
* common cron conventions in every other respect, including 0-6 for SUN-SAT
* (plus 7 for SUN as well). Note that Quartz deviates from the day-of-week
* convention in cron through 1-7 for SUN-SAT whereas Spring strictly follows
* cron even in combination with the optional Quartz-specific L/# expressions.
*
* @author Juergen Hoeller
* @author Arjen Poutsma
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,10 +30,15 @@ import org.springframework.util.Assert;
/**
* Extension of {@link CronField} for
* <a href="https://www.quartz-scheduler.org">Quartz</a>-specific fields.
*
* <p>Created using the {@code parse*} methods, uses a {@link TemporalAdjuster}
* Created using the {@code parse*} methods, uses a {@link TemporalAdjuster}
* internally.
*
* <p>Supports a Quartz day-of-month/week field with an L/# expression. Follows
* common cron conventions in every other respect, including 0-6 for SUN-SAT
* (plus 7 for SUN as well). Note that Quartz deviates from the day-of-week
* convention in cron through 1-7 for SUN-SAT whereas Spring strictly follows
* cron even in combination with the optional Quartz-specific L/# expressions.
*
* @author Arjen Poutsma
* @since 5.3
*/
@@ -61,8 +66,9 @@ final class QuartzCronField extends CronField {
this.rollForwardType = rollForwardType;
}
/**
* Returns whether the given value is a Quartz day-of-month field.
* Determine whether the given value is a Quartz day-of-month field.
*/
public static boolean isQuartzDaysOfMonthField(String value) {
return value.contains("L") || value.contains("W");
@@ -80,14 +86,14 @@ final class QuartzCronField extends CronField {
if (idx != 0) {
throw new IllegalArgumentException("Unrecognized characters before 'L' in '" + value + "'");
}
else if (value.length() == 2 && value.charAt(1) == 'W') { // "LW"
else if (value.length() == 2 && value.charAt(1) == 'W') { // "LW"
adjuster = lastWeekdayOfMonth();
}
else {
if (value.length() == 1) { // "L"
if (value.length() == 1) { // "L"
adjuster = lastDayOfMonth();
}
else { // "L-[0-9]+"
else { // "L-[0-9]+"
int offset = Integer.parseInt(value, idx + 1, value.length(), 10);
if (offset >= 0) {
throw new IllegalArgumentException("Offset '" + offset + " should be < 0 '" + value + "'");
@@ -105,7 +111,7 @@ final class QuartzCronField extends CronField {
else if (idx != value.length() - 1) {
throw new IllegalArgumentException("Unrecognized characters after 'W' in '" + value + "'");
}
else { // "[0-9]+W"
else { // "[0-9]+W"
int dayOfMonth = Integer.parseInt(value, 0, idx, 10);
dayOfMonth = Type.DAY_OF_MONTH.checkValidValue(dayOfMonth);
TemporalAdjuster adjuster = weekdayNearestTo(dayOfMonth);
@@ -116,7 +122,7 @@ final class QuartzCronField extends CronField {
}
/**
* Returns whether the given value is a Quartz day-of-week field.
* Determine whether the given value is a Quartz day-of-week field.
*/
public static boolean isQuartzDaysOfWeekField(String value) {
return value.contains("L") || value.contains("#");
@@ -138,7 +144,7 @@ final class QuartzCronField extends CronField {
if (idx == 0) {
throw new IllegalArgumentException("No day-of-week before 'L' in '" + value + "'");
}
else { // "[0-7]L"
else { // "[0-7]L"
DayOfWeek dayOfWeek = parseDayOfWeek(value.substring(0, idx));
adjuster = lastInMonth(dayOfWeek);
}
@@ -160,7 +166,6 @@ final class QuartzCronField extends CronField {
throw new IllegalArgumentException("Ordinal '" + ordinal + "' in '" + value +
"' must be positive number ");
}
TemporalAdjuster adjuster = dayOfWeekInMonth(ordinal, dayOfWeek);
return new QuartzCronField(Type.DAY_OF_WEEK, Type.DAY_OF_MONTH, adjuster, value);
}
@@ -170,14 +175,13 @@ final class QuartzCronField extends CronField {
private static DayOfWeek parseDayOfWeek(String value) {
int dayOfWeek = Integer.parseInt(value);
if (dayOfWeek == 0) {
dayOfWeek = 7; // cron is 0 based; java.time 1 based
dayOfWeek = 7; // cron is 0 based; java.time 1 based
}
try {
return DayOfWeek.of(dayOfWeek);
}
catch (DateTimeException ex) {
String msg = ex.getMessage() + " '" + value + "'";
throw new IllegalArgumentException(msg, ex);
throw new IllegalArgumentException(ex.getMessage() + " '" + value + "'", ex);
}
}
@@ -216,10 +220,10 @@ final class QuartzCronField extends CronField {
Temporal lastDom = adjuster.adjustInto(temporal);
Temporal result;
int dow = lastDom.get(ChronoField.DAY_OF_WEEK);
if (dow == 6) { // Saturday
if (dow == 6) { // Saturday
result = lastDom.minus(1, ChronoUnit.DAYS);
}
else if (dow == 7) { // Sunday
else if (dow == 7) { // Sunday
result = lastDom.minus(2, ChronoUnit.DAYS);
}
else {
@@ -256,10 +260,10 @@ final class QuartzCronField extends CronField {
int current = Type.DAY_OF_MONTH.get(temporal);
DayOfWeek dayOfWeek = DayOfWeek.from(temporal);
if ((current == dayOfMonth && isWeekday(dayOfWeek)) || // dayOfMonth is a weekday
(dayOfWeek == DayOfWeek.FRIDAY && current == dayOfMonth - 1) || // dayOfMonth is a Saturday, so Friday before
(dayOfWeek == DayOfWeek.MONDAY && current == dayOfMonth + 1) || // dayOfMonth is a Sunday, so Monday after
(dayOfWeek == DayOfWeek.MONDAY && dayOfMonth == 1 && current == 3)) { // dayOfMonth is Saturday 1st, so Monday 3rd
if ((current == dayOfMonth && isWeekday(dayOfWeek)) || // dayOfMonth is a weekday
(dayOfWeek == DayOfWeek.FRIDAY && current == dayOfMonth - 1) || // dayOfMonth is a Saturday, so Friday before
(dayOfWeek == DayOfWeek.MONDAY && current == dayOfMonth + 1) || // dayOfMonth is a Sunday, so Monday after
(dayOfWeek == DayOfWeek.MONDAY && dayOfMonth == 1 && current == 3)) { // dayOfMonth is Saturday 1st, so Monday 3rd
return temporal;
}
int count = 0;
@@ -332,6 +336,7 @@ final class QuartzCronField extends CronField {
}
}
@Override
public <T extends Temporal & Comparable<? super T>> T nextOrSame(T temporal) {
T result = adjust(temporal);
@@ -348,7 +353,6 @@ final class QuartzCronField extends CronField {
return result;
}
@Nullable
@SuppressWarnings("unchecked")
private <T extends Temporal & Comparable<? super T>> T adjust(T temporal) {
@@ -356,27 +360,20 @@ final class QuartzCronField extends CronField {
}
@Override
public boolean equals(@Nullable Object other) {
return (this == other || (other instanceof QuartzCronField that &&
type() == that.type() && this.value.equals(that.value)));
}
@Override
public int hashCode() {
return this.value.hashCode();
}
@Override
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
if (!(o instanceof QuartzCronField other)) {
return false;
}
return type() == other.type() &&
this.value.equals(other.value);
}
@Override
public String toString() {
return type() + " '" + this.value + "'";
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,7 +39,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Juergen Hoeller
* @author Chris Beams
*/
public class ScopedProxyTests {
class ScopedProxyTests {
private static final Class<?> CLASS = ScopedProxyTests.class;
private static final String CLASSNAME = CLASS.getSimpleName();
@@ -51,27 +51,24 @@ public class ScopedProxyTests {
@Test // SPR-2108
public void testProxyAssignable() throws Exception {
void testProxyAssignable() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(MAP_CONTEXT);
Object baseMap = bf.getBean("singletonMap");
boolean condition = baseMap instanceof Map;
assertThat(condition).isTrue();
assertThat(baseMap instanceof Map).isTrue();
}
@Test
public void testSimpleProxy() throws Exception {
void testSimpleProxy() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(MAP_CONTEXT);
Object simpleMap = bf.getBean("simpleMap");
boolean condition1 = simpleMap instanceof Map;
assertThat(condition1).isTrue();
boolean condition = simpleMap instanceof HashMap;
assertThat(condition).isTrue();
assertThat(simpleMap instanceof Map).isTrue();
assertThat(simpleMap instanceof HashMap).isTrue();
}
@Test
public void testScopedOverride() throws Exception {
void testScopedOverride() {
GenericApplicationContext ctx = new GenericApplicationContext();
new XmlBeanDefinitionReader(ctx).loadBeanDefinitions(OVERRIDE_CONTEXT);
SimpleMapScope scope = new SimpleMapScope();
@@ -87,7 +84,7 @@ public class ScopedProxyTests {
}
@Test
public void testJdkScopedProxy() throws Exception {
void testJdkScopedProxy() throws Exception {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(TESTBEAN_CONTEXT);
bf.setSerializationId("X");
@@ -97,8 +94,7 @@ public class ScopedProxyTests {
ITestBean bean = (ITestBean) bf.getBean("testBean");
assertThat(bean).isNotNull();
assertThat(AopUtils.isJdkDynamicProxy(bean)).isTrue();
boolean condition1 = bean instanceof ScopedObject;
assertThat(condition1).isTrue();
assertThat(bean instanceof ScopedObject).isTrue();
ScopedObject scoped = (ScopedObject) bean;
assertThat(scoped.getTargetObject().getClass()).isEqualTo(TestBean.class);
bean.setAge(101);
@@ -110,8 +106,7 @@ public class ScopedProxyTests {
assertThat(deserialized).isNotNull();
assertThat(AopUtils.isJdkDynamicProxy(deserialized)).isTrue();
assertThat(bean.getAge()).isEqualTo(101);
boolean condition = deserialized instanceof ScopedObject;
assertThat(condition).isTrue();
assertThat(deserialized instanceof ScopedObject).isTrue();
ScopedObject scopedDeserialized = (ScopedObject) deserialized;
assertThat(scopedDeserialized.getTargetObject().getClass()).isEqualTo(TestBean.class);
@@ -119,7 +114,7 @@ public class ScopedProxyTests {
}
@Test
public void testCglibScopedProxy() throws Exception {
void testCglibScopedProxy() throws Exception {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(LIST_CONTEXT);
bf.setSerializationId("Y");
@@ -128,8 +123,7 @@ public class ScopedProxyTests {
TestBean tb = (TestBean) bf.getBean("testBean");
assertThat(AopUtils.isCglibProxy(tb.getFriends())).isTrue();
boolean condition1 = tb.getFriends() instanceof ScopedObject;
assertThat(condition1).isTrue();
assertThat(tb.getFriends() instanceof ScopedObject).isTrue();
ScopedObject scoped = (ScopedObject) tb.getFriends();
assertThat(scoped.getTargetObject().getClass()).isEqualTo(ArrayList.class);
tb.getFriends().add("myFriend");
@@ -137,12 +131,11 @@ public class ScopedProxyTests {
assertThat(scope.getMap().containsKey("scopedTarget.scopedList")).isTrue();
assertThat(scope.getMap().get("scopedTarget.scopedList").getClass()).isEqualTo(ArrayList.class);
ArrayList<?> deserialized = (ArrayList<?>) SerializationTestUtils.serializeAndDeserialize(tb.getFriends());
ArrayList<Object> deserialized = (ArrayList<Object>) SerializationTestUtils.serializeAndDeserialize(tb.getFriends());
assertThat(deserialized).isNotNull();
assertThat(AopUtils.isCglibProxy(deserialized)).isTrue();
assertThat(deserialized.contains("myFriend")).isTrue();
boolean condition = deserialized instanceof ScopedObject;
assertThat(condition).isTrue();
assertThat(deserialized).contains("myFriend");
assertThat(deserialized instanceof ScopedObject).isTrue();
ScopedObject scopedDeserialized = (ScopedObject) deserialized;
assertThat(scopedDeserialized.getTargetObject().getClass()).isEqualTo(ArrayList.class);
@@ -63,12 +63,12 @@ public class QualifierAnnotationAutowireContextTests {
new RootBeanDefinition(QualifiedFieldTestBean.class));
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
context::refresh)
.satisfies(ex -> {
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
assertThat(ex.getBeanName()).isEqualTo("autowired");
});
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(context::refresh)
.satisfies(ex -> {
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
assertThat(ex.getBeanName()).isEqualTo("autowired");
});
}
@Test
@@ -81,12 +81,13 @@ public class QualifierAnnotationAutowireContextTests {
context.registerBeanDefinition("autowired",
new RootBeanDefinition(QualifiedMethodParameterTestBean.class));
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
context::refresh)
.satisfies(ex -> {
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
assertThat(ex.getBeanName()).isEqualTo("autowired");
});
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(context::refresh)
.satisfies(ex -> {
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
assertThat(ex.getBeanName()).isEqualTo("autowired");
});
}
@@ -100,9 +101,10 @@ public class QualifierAnnotationAutowireContextTests {
context.registerBeanDefinition("autowired",
new RootBeanDefinition(QualifiedConstructorArgumentTestBean.class));
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(
context::refresh)
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("autowired"));
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
.isThrownBy(context::refresh)
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("autowired"));
}
@Test
@@ -205,12 +207,13 @@ public class QualifierAnnotationAutowireContextTests {
context.registerBeanDefinition("autowired",
new RootBeanDefinition(QualifiedFieldTestBean.class));
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
context::refresh)
.satisfies(ex -> {
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
assertThat(ex.getBeanName()).isEqualTo("autowired");
});
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(context::refresh)
.satisfies(ex -> {
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
assertThat(ex.getBeanName()).isEqualTo("autowired");
});
}
@Test
@@ -227,12 +230,13 @@ public class QualifierAnnotationAutowireContextTests {
context.registerBeanDefinition("autowired",
new RootBeanDefinition(QualifiedMethodParameterTestBean.class));
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
context::refresh)
.satisfies(ex -> {
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
assertThat(ex.getBeanName()).isEqualTo("autowired");
});
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(context::refresh)
.satisfies(ex -> {
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
assertThat(ex.getBeanName()).isEqualTo("autowired");
});
}
@Test
@@ -249,9 +253,10 @@ public class QualifierAnnotationAutowireContextTests {
context.registerBeanDefinition("autowired",
new RootBeanDefinition(QualifiedConstructorArgumentTestBean.class));
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(
context::refresh)
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("autowired"));
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
.isThrownBy(context::refresh)
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("autowired"));
}
@Test
@@ -374,12 +379,13 @@ public class QualifierAnnotationAutowireContextTests {
context.registerBeanDefinition("autowired",
new RootBeanDefinition(QualifiedFieldWithDefaultValueTestBean.class));
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
context::refresh)
.satisfies(ex -> {
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
assertThat(ex.getBeanName()).isEqualTo("autowired");
});
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(context::refresh)
.satisfies(ex -> {
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
assertThat(ex.getBeanName()).isEqualTo("autowired");
});
}
@Test
@@ -451,12 +457,13 @@ public class QualifierAnnotationAutowireContextTests {
context.registerBeanDefinition("autowired",
new RootBeanDefinition(QualifiedFieldWithMultipleAttributesTestBean.class));
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
context::refresh)
.satisfies(ex -> {
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
assertThat(ex.getBeanName()).isEqualTo("autowired");
});
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(context::refresh)
.satisfies(ex -> {
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
assertThat(ex.getBeanName()).isEqualTo("autowired");
});
}
@Test
@@ -507,12 +514,13 @@ public class QualifierAnnotationAutowireContextTests {
context.registerBeanDefinition("autowired",
new RootBeanDefinition(QualifiedFieldWithMultipleAttributesTestBean.class));
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
context::refresh)
.satisfies(ex -> {
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
assertThat(ex.getBeanName()).isEqualTo("autowired");
});
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(context::refresh)
.satisfies(ex -> {
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
assertThat(ex.getBeanName()).isEqualTo("autowired");
});
}
@Test
@@ -574,9 +582,10 @@ public class QualifierAnnotationAutowireContextTests {
context.registerBeanDefinition("autowired",
new RootBeanDefinition(QualifiedConstructorArgumentWithBaseQualifierNonDefaultValueTestBean.class));
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(
context::refresh)
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("autowired"));
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
.isThrownBy(context::refresh)
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("autowired"));
}
@@ -752,7 +761,7 @@ public class QualifierAnnotationAutowireContextTests {
@Qualifier
@interface TestQualifierWithMultipleAttributes {
String value() default "default";
String[] value() default "default";
int number();
}
@@ -58,9 +58,10 @@ public class QualifierAnnotationTests {
BeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
reader.loadBeanDefinitions(CONFIG_LOCATION);
context.registerSingleton("testBean", NonQualifiedTestBean.class);
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
context::refresh)
.withMessageContaining("found 6");
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(context::refresh)
.withMessageContaining("found 6");
}
@Test
@@ -191,9 +192,10 @@ public class QualifierAnnotationTests {
BeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
reader.loadBeanDefinitions(CONFIG_LOCATION);
context.registerSingleton("testBean", QualifiedByAttributesTestBean.class);
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
context::refresh)
.withMessageContaining("found 6");
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(context::refresh)
.withMessageContaining("found 6");
}
@Test
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -268,6 +268,7 @@ public class ClassPathBeanDefinitionScannerTests {
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
scanner.setIncludeAnnotationConfig(false);
scanner.scan("org.springframework.context.annotation2");
assertThatIllegalStateException().isThrownBy(() -> scanner.scan(BASE_PACKAGE))
.withMessageContaining("myNamedDao")
.withMessageContaining(NamedStubDao.class.getName())
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 the original author 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 org.springframework.aop.support.AopUtils;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.ObjectProvider;
@@ -321,8 +323,8 @@ class ConfigurationClassPostProcessorTests {
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
pp.setEnvironment(new StandardEnvironment());
pp.postProcessBeanFactory(beanFactory);
assertThatExceptionOfType(NoSuchBeanDefinitionException.class).isThrownBy(() ->
beanFactory.getBean(SimpleComponent.class));
assertThatExceptionOfType(NoSuchBeanDefinitionException.class)
.isThrownBy(() -> beanFactory.getBean(SimpleComponent.class));
}
@Test
@@ -372,11 +374,11 @@ class ConfigurationClassPostProcessorTests {
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(SingletonBeanConfig.class));
beanFactory.setAllowBeanDefinitionOverriding(false);
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
assertThatExceptionOfType(BeanDefinitionStoreException.class).isThrownBy(() ->
pp.postProcessBeanFactory(beanFactory))
.withMessageContaining("bar")
.withMessageContaining("SingletonBeanConfig")
.withMessageContaining(TestBean.class.getName());
assertThatExceptionOfType(BeanDefinitionStoreException.class)
.isThrownBy(() -> pp.postProcessBeanFactory(beanFactory))
.withMessageContaining("bar")
.withMessageContaining("SingletonBeanConfig")
.withMessageContaining(TestBean.class.getName());
}
@Test // gh-25430
@@ -429,12 +431,12 @@ class ConfigurationClassPostProcessorTests {
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
pp.postProcessBeanFactory(beanFactory);
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() ->
beanFactory.getBean(Bar.class))
.withMessageContaining("OverridingSingletonBeanConfig.foo")
.withMessageContaining(ExtendedFoo.class.getName())
.withMessageContaining(Foo.class.getName())
.withMessageContaining("InvalidOverridingSingletonBeanConfig");
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(() -> beanFactory.getBean(Bar.class))
.withMessageContaining("OverridingSingletonBeanConfig.foo")
.withMessageContaining(ExtendedFoo.class.getName())
.withMessageContaining(Foo.class.getName())
.withMessageContaining("InvalidOverridingSingletonBeanConfig");
}
@Test // SPR-15384
@@ -944,8 +946,8 @@ class ConfigurationClassPostProcessorTests {
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(ConcreteConfig.class));
beanFactory.registerBeanDefinition("serviceBeanProvider", new RootBeanDefinition(ServiceBeanProvider.class));
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
beanFactory.preInstantiateSingletons();
beanFactory.preInstantiateSingletons();
beanFactory.getBean(ServiceBean.class);
}
@@ -958,8 +960,8 @@ class ConfigurationClassPostProcessorTests {
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(ConcreteConfigWithDefaultMethods.class));
beanFactory.registerBeanDefinition("serviceBeanProvider", new RootBeanDefinition(ServiceBeanProvider.class));
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
beanFactory.preInstantiateSingletons();
beanFactory.preInstantiateSingletons();
beanFactory.getBean(ServiceBean.class);
}
@@ -972,11 +974,25 @@ class ConfigurationClassPostProcessorTests {
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(ConcreteConfigWithDefaultMethods.class.getName()));
beanFactory.registerBeanDefinition("serviceBeanProvider", new RootBeanDefinition(ServiceBeanProvider.class.getName()));
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
beanFactory.preInstantiateSingletons();
beanFactory.preInstantiateSingletons();
beanFactory.getBean(ServiceBean.class);
}
@Test
void testConfigWithFailingInit() { // gh-23343
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(beanFactory);
beanFactory.addBeanPostProcessor(bpp);
beanFactory.addBeanPostProcessor(new CommonAnnotationBeanPostProcessor());
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(ConcreteConfigWithFailingInit.class));
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(beanFactory::preInstantiateSingletons);
assertThat(beanFactory.containsSingleton("configClass")).isFalse();
assertThat(beanFactory.containsSingleton("provider")).isFalse();
}
@Test
void testCircularDependency() {
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
@@ -985,16 +1001,17 @@ class ConfigurationClassPostProcessorTests {
beanFactory.registerBeanDefinition("configClass1", new RootBeanDefinition(A.class));
beanFactory.registerBeanDefinition("configClass2", new RootBeanDefinition(AStrich.class));
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
beanFactory::preInstantiateSingletons)
.withMessageContaining("Circular reference");
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(beanFactory::preInstantiateSingletons)
.withMessageContaining("Circular reference");
}
@Test
void testCircularDependencyWithApplicationContext() {
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() ->
new AnnotationConfigApplicationContext(A.class, AStrich.class))
.withMessageContaining("Circular reference");
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(() -> new AnnotationConfigApplicationContext(A.class, AStrich.class))
.withMessageContaining("Circular reference");
}
@Test
@@ -1048,9 +1065,7 @@ class ConfigurationClassPostProcessorTests {
void testCollectionArgumentOnBeanMethod() {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(CollectionArgumentConfiguration.class, TestBean.class);
CollectionArgumentConfiguration bean = ctx.getBean(CollectionArgumentConfiguration.class);
assertThat(bean.testBeans).isNotNull();
assertThat(bean.testBeans).hasSize(1);
assertThat(bean.testBeans.get(0)).isSameAs(ctx.getBean(TestBean.class));
assertThat(bean.testBeans).containsExactly(ctx.getBean(TestBean.class));
ctx.close();
}
@@ -1058,8 +1073,7 @@ class ConfigurationClassPostProcessorTests {
void testEmptyCollectionArgumentOnBeanMethod() {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(CollectionArgumentConfiguration.class);
CollectionArgumentConfiguration bean = ctx.getBean(CollectionArgumentConfiguration.class);
assertThat(bean.testBeans).isNotNull();
assertThat(bean.testBeans.isEmpty()).isTrue();
assertThat(bean.testBeans).isEmpty();
ctx.close();
}
@@ -1067,9 +1081,7 @@ class ConfigurationClassPostProcessorTests {
void testMapArgumentOnBeanMethod() {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(MapArgumentConfiguration.class, DummyRunnable.class);
MapArgumentConfiguration bean = ctx.getBean(MapArgumentConfiguration.class);
assertThat(bean.testBeans).isNotNull();
assertThat(bean.testBeans).hasSize(1);
assertThat(bean.testBeans.values().iterator().next()).isSameAs(ctx.getBean(Runnable.class));
assertThat(bean.testBeans).hasSize(1).containsValue(ctx.getBean(Runnable.class));
ctx.close();
}
@@ -1077,8 +1089,7 @@ class ConfigurationClassPostProcessorTests {
void testEmptyMapArgumentOnBeanMethod() {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(MapArgumentConfiguration.class);
MapArgumentConfiguration bean = ctx.getBean(MapArgumentConfiguration.class);
assertThat(bean.testBeans).isNotNull();
assertThat(bean.testBeans.isEmpty()).isTrue();
assertThat(bean.testBeans).isEmpty();
ctx.close();
}
@@ -1086,9 +1097,7 @@ class ConfigurationClassPostProcessorTests {
void testCollectionInjectionFromSameConfigurationClass() {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(CollectionInjectionConfiguration.class);
CollectionInjectionConfiguration bean = ctx.getBean(CollectionInjectionConfiguration.class);
assertThat(bean.testBeans).isNotNull();
assertThat(bean.testBeans).hasSize(1);
assertThat(bean.testBeans.get(0)).isSameAs(ctx.getBean(TestBean.class));
assertThat(bean.testBeans).containsExactly(ctx.getBean(TestBean.class));
ctx.close();
}
@@ -1096,25 +1105,21 @@ class ConfigurationClassPostProcessorTests {
void testMapInjectionFromSameConfigurationClass() {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(MapInjectionConfiguration.class);
MapInjectionConfiguration bean = ctx.getBean(MapInjectionConfiguration.class);
assertThat(bean.testBeans).isNotNull();
assertThat(bean.testBeans).hasSize(1);
assertThat(bean.testBeans.get("testBean")).isSameAs(ctx.getBean(Runnable.class));
assertThat(bean.testBeans).containsOnly(Map.entry("testBean", ctx.getBean(Runnable.class)));
ctx.close();
}
@Test
void testBeanLookupFromSameConfigurationClass() {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(BeanLookupConfiguration.class);
BeanLookupConfiguration bean = ctx.getBean(BeanLookupConfiguration.class);
assertThat(bean.getTestBean()).isNotNull();
assertThat(bean.getTestBean()).isSameAs(ctx.getBean(TestBean.class));
assertThat(ctx.getBean(BeanLookupConfiguration.class).getTestBean()).isSameAs(ctx.getBean(TestBean.class));
ctx.close();
}
@Test
void testNameClashBetweenConfigurationClassAndBean() {
assertThatExceptionOfType(BeanDefinitionStoreException.class)
.isThrownBy(() -> new AnnotationConfigApplicationContext(MyTestBean.class).getBean("myTestBean", TestBean.class));
.isThrownBy(() -> new AnnotationConfigApplicationContext(MyTestBean.class).getBean("myTestBean", TestBean.class));
}
@Test
@@ -1131,11 +1136,11 @@ class ConfigurationClassPostProcessorTests {
@Order(1)
static class SingletonBeanConfig {
public @Bean Foo foo() {
@Bean public Foo foo() {
return new Foo();
}
public @Bean Bar bar() {
@Bean public Bar bar() {
return new Bar(foo());
}
}
@@ -1143,11 +1148,11 @@ class ConfigurationClassPostProcessorTests {
@Configuration(proxyBeanMethods = false)
static class NonEnhancedSingletonBeanConfig {
public @Bean Foo foo() {
@Bean public Foo foo() {
return new Foo();
}
public @Bean Bar bar() {
@Bean public Bar bar() {
return new Bar(foo());
}
}
@@ -1155,11 +1160,13 @@ class ConfigurationClassPostProcessorTests {
@Configuration
static class StaticSingletonBeanConfig {
public static @Bean Foo foo() {
@Bean
public static Foo foo() {
return new Foo();
}
public static @Bean Bar bar() {
@Bean
public static Bar bar() {
return new Bar(foo());
}
}
@@ -1168,11 +1175,11 @@ class ConfigurationClassPostProcessorTests {
@Order(2)
static class OverridingSingletonBeanConfig {
public @Bean ExtendedFoo foo() {
@Bean public ExtendedFoo foo() {
return new ExtendedFoo();
}
public @Bean Bar bar() {
@Bean public Bar bar() {
return new Bar(foo());
}
}
@@ -1180,7 +1187,7 @@ class ConfigurationClassPostProcessorTests {
@Configuration
static class OverridingAgainSingletonBeanConfig {
public @Bean ExtendedAgainFoo foo() {
@Bean public ExtendedAgainFoo foo() {
return new ExtendedAgainFoo();
}
}
@@ -1188,7 +1195,7 @@ class ConfigurationClassPostProcessorTests {
@Configuration
static class InvalidOverridingSingletonBeanConfig {
public @Bean Foo foo() {
@Bean public Foo foo() {
return new Foo();
}
}
@@ -1200,11 +1207,11 @@ class ConfigurationClassPostProcessorTests {
@Order(1)
static class SingletonBeanConfig {
public @Bean Foo foo() {
@Bean public Foo foo() {
return new Foo();
}
public @Bean Bar bar() {
@Bean public Bar bar() {
return new Bar(foo());
}
}
@@ -1213,11 +1220,11 @@ class ConfigurationClassPostProcessorTests {
@Order(2)
static class OverridingSingletonBeanConfig {
public @Bean ExtendedFoo foo() {
@Bean public ExtendedFoo foo() {
return new ExtendedFoo();
}
public @Bean Bar bar() {
@Bean public Bar bar() {
return new Bar(foo());
}
}
@@ -1233,11 +1240,11 @@ class ConfigurationClassPostProcessorTests {
public SingletonBeanConfig(ConfigWithOrderedInnerClasses other) {
}
public @Bean Foo foo() {
@Bean public Foo foo() {
return new Foo();
}
public @Bean Bar bar() {
@Bean public Bar bar() {
return new Bar(foo());
}
}
@@ -1250,11 +1257,11 @@ class ConfigurationClassPostProcessorTests {
other.getObject();
}
public @Bean ExtendedFoo foo() {
@Bean public ExtendedFoo foo() {
return new ExtendedFoo();
}
public @Bean Bar bar() {
@Bean public Bar bar() {
return new Bar(foo());
}
}
@@ -1281,7 +1288,7 @@ class ConfigurationClassPostProcessorTests {
@Configuration
static class UnloadedConfig {
public @Bean Foo foo() {
@Bean public Foo foo() {
return new Foo();
}
}
@@ -1289,7 +1296,7 @@ class ConfigurationClassPostProcessorTests {
@Configuration
static class LoadedConfig {
public @Bean Bar bar() {
@Bean public Bar bar() {
return new Bar(new Foo());
}
}
@@ -1598,7 +1605,7 @@ class ConfigurationClassPostProcessorTests {
public static class WildcardWithGenericExtendsConfiguration {
@Bean
public Repository<? extends Object> genericRepo() {
public Repository<?> genericRepo() {
return new Repository<String>();
}
@@ -1707,7 +1714,7 @@ class ConfigurationClassPostProcessorTests {
}
@Configuration
public static abstract class AbstractConfig {
public abstract static class AbstractConfig {
@Bean
public ServiceBean serviceBean() {
@@ -1758,7 +1765,6 @@ class ConfigurationClassPostProcessorTests {
}
public interface DefaultMethodsConfig extends BaseDefaultMethods {
}
@Configuration
@@ -1779,6 +1785,29 @@ class ConfigurationClassPostProcessorTests {
}
}
@Configuration
public static class ConcreteConfigWithFailingInit implements DefaultMethodsConfig, BeanFactoryAware {
private BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Bean
@Override
public ServiceBeanProvider provider() {
return new ServiceBeanProvider();
}
@PostConstruct
public void validate() {
beanFactory.getBean("provider");
throw new IllegalStateException();
}
}
@Primary
public static class ServiceBeanProvider {
@@ -1891,7 +1920,7 @@ class ConfigurationClassPostProcessorTests {
}
}
static abstract class FooFactory {
abstract static class FooFactory {
abstract DependingFoo createFoo(BarArgument bar);
}
@@ -2010,7 +2039,7 @@ class ConfigurationClassPostProcessorTests {
}
@Configuration
static abstract class BeanLookupConfiguration {
abstract static class BeanLookupConfiguration {
@Bean
public TestBean thing() {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -204,7 +204,7 @@ class ConfigurationClassProcessingTests {
BeanFactory factory = initBeanFactory(ConfigWithNullReference.class);
TestBean foo = factory.getBean("foo", TestBean.class);
assertThat(factory.getBean("bar").equals(null)).isTrue();
assertThat(factory.getBean("bar")).isEqualTo(null);
assertThat(foo.getSpouse()).isNull();
}
@@ -426,7 +426,7 @@ class ConfigurationClassProcessingTests {
@Configuration
static class ConfigWithFinalBean {
public final @Bean TestBean testBean() {
@Bean public final TestBean testBean() {
return new TestBean();
}
}
@@ -435,7 +435,7 @@ class ConfigurationClassProcessingTests {
@Configuration
static class SimplestPossibleConfig {
public @Bean String stringBean() {
@Bean public String stringBean() {
return "foo";
}
}
@@ -444,11 +444,11 @@ class ConfigurationClassProcessingTests {
@Configuration
static class ConfigWithNonSpecificReturnTypes {
public @Bean Object stringBean() {
@Bean public Object stringBean() {
return "foo";
}
public @Bean FactoryBean<?> factoryBean() {
@Bean public FactoryBean<?> factoryBean() {
ListFactoryBean fb = new ListFactoryBean();
fb.setSourceList(Arrays.asList("element1", "element2"));
return fb;
@@ -459,13 +459,13 @@ class ConfigurationClassProcessingTests {
@Configuration
static class ConfigWithPrototypeBean {
public @Bean TestBean foo() {
@Bean public TestBean foo() {
TestBean foo = new SpousyTestBean("foo");
foo.setSpouse(bar());
return foo;
}
public @Bean TestBean bar() {
@Bean public TestBean bar() {
TestBean bar = new SpousyTestBean("bar");
bar.setSpouse(baz());
return bar;
@@ -605,15 +605,15 @@ class ConfigurationClassProcessingTests {
void register(GenericApplicationContext ctx) {
ctx.registerBean("spouse", TestBean.class,
() -> new TestBean("functional"));
Supplier<TestBean> testBeanSupplier = () -> new TestBean(ctx.getBean("spouse", TestBean.class));
ctx.registerBean(TestBean.class,
testBeanSupplier,
Supplier<TestBean> testBeanSupplier =
() -> new TestBean(ctx.getBean("spouse", TestBean.class));
ctx.registerBean(TestBean.class, testBeanSupplier,
bd -> bd.setPrimary(true));
}
@Bean
public NestedTestBean nestedTestBean(TestBean testBean) {
return new NestedTestBean(testBean.getSpouse().getName());
public NestedTestBean nestedTestBean(TestBean spouse) {
return new NestedTestBean(spouse.getSpouse().getName());
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2024 the original author 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,27 +35,32 @@ class BitsCronFieldTests {
@Test
void parse() {
assertThat(BitsCronField.parseSeconds("42")).has(clearRange(0, 41)).has(set(42)).has(clearRange(43, 59));
assertThat(BitsCronField.parseSeconds("0-4,8-12")).has(setRange(0, 4)).has(clearRange(5,7)).has(setRange(8, 12)).has(clearRange(13,59));
assertThat(BitsCronField.parseSeconds("57/2")).has(clearRange(0, 56)).has(set(57)).has(clear(58)).has(set(59));
assertThat(BitsCronField.parseSeconds("0-4,8-12")).has(setRange(0, 4)).has(clearRange(5,7))
.has(setRange(8, 12)).has(clearRange(13,59));
assertThat(BitsCronField.parseSeconds("57/2")).has(clearRange(0, 56)).has(set(57))
.has(clear(58)).has(set(59));
assertThat(BitsCronField.parseMinutes("30")).has(set(30)).has(clearRange(1, 29)).has(clearRange(31, 59));
assertThat(BitsCronField.parseHours("23")).has(set(23)).has(clearRange(0, 23));
assertThat(BitsCronField.parseHours("0-23/2")).has(set(0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22)).has(clear(1,3,5,7,9,11,13,15,17,19,21,23));
assertThat(BitsCronField.parseHours("0-23/2")).has(set(0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22))
.has(clear(1,3,5,7,9,11,13,15,17,19,21,23));
assertThat(BitsCronField.parseDaysOfMonth("1")).has(set(1)).has(clearRange(2, 31));
assertThat(BitsCronField.parseMonth("1")).has(set(1)).has(clearRange(2, 12));
assertThat(BitsCronField.parseDaysOfWeek("0")).has(set(7, 7)).has(clearRange(0, 6));
assertThat(BitsCronField.parseDaysOfWeek("7-5")).has(clear(0)).has(setRange(1, 5)).has(clear(6)).has(set(7));
assertThat(BitsCronField.parseDaysOfWeek("7-5")).has(clear(0)).has(setRange(1, 5))
.has(clear(6)).has(set(7));
}
@Test
void parseLists() {
assertThat(BitsCronField.parseSeconds("15,30")).has(set(15, 30)).has(clearRange(1, 15)).has(clearRange(31, 59));
assertThat(BitsCronField.parseMinutes("1,2,5,9")).has(set(1, 2, 5, 9)).has(clear(0)).has(clearRange(3, 4)).has(clearRange(6, 8)).has(clearRange(10, 59));
assertThat(BitsCronField.parseSeconds("15,30")).has(set(15, 30)).has(clearRange(1, 15))
.has(clearRange(31, 59));
assertThat(BitsCronField.parseMinutes("1,2,5,9")).has(set(1, 2, 5, 9)).has(clear(0))
.has(clearRange(3, 4)).has(clearRange(6, 8)).has(clearRange(10, 59));
assertThat(BitsCronField.parseHours("1,2,3")).has(set(1, 2, 3)).has(clearRange(4, 23));
assertThat(BitsCronField.parseDaysOfMonth("1,2,3")).has(set(1, 2, 3)).has(clearRange(4, 31));
assertThat(BitsCronField.parseMonth("1,2,3")).has(set(1, 2, 3)).has(clearRange(4, 12));
@@ -107,6 +112,7 @@ class BitsCronFieldTests {
.has(clear(0)).has(setRange(1, 7));
}
private static Condition<BitsCronField> set(int... indices) {
return new Condition<>(String.format("set bits %s", Arrays.toString(indices))) {
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2024 the original author 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.assertThatIllegalArgumentException
* Unit tests for {@link QuartzCronField}.
*
* @author Arjen Poutsma
* @author Juergen Hoeller
*/
class QuartzCronFieldTests {
@@ -71,6 +72,46 @@ class QuartzCronFieldTests {
assertThat(field.nextOrSame(last)).isEqualTo(expected);
}
@Test
void dayOfWeek_0(){
// third Sunday (0) of the month
QuartzCronField field = QuartzCronField.parseDaysOfWeek("0#3");
LocalDate last = LocalDate.of(2024, 1, 1);
LocalDate expected = LocalDate.of(2024, 1, 21);
assertThat(field.nextOrSame(last)).isEqualTo(expected);
}
@Test
void dayOfWeek_1(){
// third Monday (1) of the month
QuartzCronField field = QuartzCronField.parseDaysOfWeek("1#3");
LocalDate last = LocalDate.of(2024, 1, 1);
LocalDate expected = LocalDate.of(2024, 1, 15);
assertThat(field.nextOrSame(last)).isEqualTo(expected);
}
@Test
void dayOfWeek_2(){
// third Tuesday (2) of the month
QuartzCronField field = QuartzCronField.parseDaysOfWeek("2#3");
LocalDate last = LocalDate.of(2024, 1, 1);
LocalDate expected = LocalDate.of(2024, 1, 16);
assertThat(field.nextOrSame(last)).isEqualTo(expected);
}
@Test
void dayOfWeek_7() {
// third Sunday (7 as alternative to 0) of the month
QuartzCronField field = QuartzCronField.parseDaysOfWeek("7#3");
LocalDate last = LocalDate.of(2024, 1, 1);
LocalDate expected = LocalDate.of(2024, 1, 21);
assertThat(field.nextOrSame(last)).isEqualTo(expected);
}
@Test
void invalidValues() {
assertThatIllegalArgumentException().isThrownBy(() -> QuartzCronField.parseDaysOfMonth(""));
@@ -1984,7 +1984,7 @@ class DataBinderTests {
.withMessageContaining("DataBinder is already initialized - call setAutoGrowCollectionLimit before other configuration methods");
}
@Test // SPR-15009
@Test // SPR-15009
void setCustomMessageCodesResolverBeforeInitializeBindingResultForBeanPropertyAccess() {
TestBean testBean = new TestBean();
DataBinder binder = new DataBinder(testBean, "testBean");
@@ -2001,7 +2001,7 @@ class DataBinderTests {
assertThat(((BeanWrapper) binder.getInternalBindingResult().getPropertyAccessor()).getAutoGrowCollectionLimit()).isEqualTo(512);
}
@Test // SPR-15009
@Test // SPR-15009
void setCustomMessageCodesResolverBeforeInitializeBindingResultForDirectFieldAccess() {
TestBean testBean = new TestBean();
DataBinder binder = new DataBinder(testBean, "testBean");
@@ -2055,7 +2055,7 @@ class DataBinderTests {
.withMessageContaining("DataBinder is already initialized with MessageCodesResolver");
}
@Test // gh-24347
@Test // gh-24347
void overrideBindingResultType() {
TestBean testBean = new TestBean();
DataBinder binder = new DataBinder(testBean, "testBean");
@@ -78,8 +78,7 @@ public class MethodValidationTests {
ac.close();
}
@Test // gh-29782
@SuppressWarnings("unchecked")
@Test // gh-29782
public void testMethodValidationPostProcessorForInterfaceOnlyProxy() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(MethodValidationPostProcessor.class);
@@ -195,9 +195,15 @@ public class BindingReflectionHintsRegistrar {
}
private void registerHintsForClassAttributes(ReflectionHints hints, MergedAnnotation<Annotation> annotation) {
annotation.getRoot().asMap().values().forEach(value -> {
annotation.getRoot().asMap().forEach((key,value) -> {
if (value instanceof Class<?> classValue && value != Void.class) {
hints.registerType(classValue, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
if (key.equals("builder")) {
hints.registerType(classValue, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
MemberCategory.INVOKE_DECLARED_METHODS);
}
else {
hints.registerType(classValue, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
}
}
});
}
@@ -647,7 +647,7 @@ public class ResolvableType implements Serializable {
* @param nestingLevel the required nesting level, indexed from 1 for the
* current type, 2 for the first nested generic, 3 for the second and so on
* @param typeIndexesPerLevel a map containing the generic index for a given
* nesting level (may be {@code null})
* nesting level (can be {@code null})
* @return a {@code ResolvableType} for the nested level, or {@link #NONE}
*/
public ResolvableType getNested(int nestingLevel, @Nullable Map<Integer, Integer> typeIndexesPerLevel) {
@@ -679,7 +679,7 @@ public class ResolvableType implements Serializable {
* generic is returned.
* <p>If no generic is available at the specified indexes {@link #NONE} is returned.
* @param indexes the indexes that refer to the generic parameter
* (may be omitted to return the first generic)
* (can be omitted to return the first generic)
* @return a {@code ResolvableType} for the specified generic, or {@link #NONE}
* @see #hasGenerics()
* @see #getGenerics()
@@ -782,7 +782,7 @@ public class ResolvableType implements Serializable {
* Convenience method that will {@link #getGeneric(int...) get} and
* {@link #resolve() resolve} a specific generic parameter.
* @param indexes the indexes that refer to the generic parameter
* (may be omitted to return the first generic)
* (can be omitted to return the first generic)
* @return a resolved {@link Class} or {@code null}
* @see #getGeneric(int...)
* @see #resolve()
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,7 +38,7 @@ public abstract class OrderUtils {
/** Cache marker for a non-annotated Class. */
private static final Object NOT_ANNOTATED = new Object();
private static final String JAVAX_PRIORITY_ANNOTATION = "jakarta.annotation.Priority";
private static final String JAKARTA_PRIORITY_ANNOTATION = "jakarta.annotation.Priority";
/** Cache for @Order value (or NOT_ANNOTATED marker) per Class. */
static final Map<AnnotatedElement, Object> orderCache = new ConcurrentReferenceHashMap<>(64);
@@ -124,7 +124,7 @@ public abstract class OrderUtils {
if (orderAnnotation.isPresent()) {
return orderAnnotation.getInt(MergedAnnotation.VALUE);
}
MergedAnnotation<?> priorityAnnotation = annotations.get(JAVAX_PRIORITY_ANNOTATION);
MergedAnnotation<?> priorityAnnotation = annotations.get(JAKARTA_PRIORITY_ANNOTATION);
if (priorityAnnotation.isPresent()) {
return priorityAnnotation.getInt(MergedAnnotation.VALUE);
}
@@ -139,7 +139,7 @@ public abstract class OrderUtils {
*/
@Nullable
public static Integer getPriority(Class<?> type) {
return MergedAnnotations.from(type, SearchStrategy.TYPE_HIERARCHY).get(JAVAX_PRIORITY_ANNOTATION)
return MergedAnnotations.from(type, SearchStrategy.TYPE_HIERARCHY).get(JAKARTA_PRIORITY_ANNOTATION)
.getValue(MergedAnnotation.VALUE, Integer.class).orElse(null);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -152,8 +152,7 @@ public interface Resource extends InputStreamSource {
}
/**
* Returns the contents of this resource as a string, using the specified
* charset.
* Return the contents of this resource as a string, using the specified charset.
* @param charset the charset to use for decoding
* @return the contents of this resource as a {@code String}
* @throws java.io.FileNotFoundException if the resource cannot be resolved as
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -557,7 +557,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
String rootDirPath = determineRootDir(locationPattern);
String subPattern = locationPattern.substring(rootDirPath.length());
Resource[] rootDirResources = getResources(rootDirPath);
Set<Resource> result = new LinkedHashSet<>(16);
Set<Resource> result = new LinkedHashSet<>(64);
for (Resource rootDirResource : rootDirResources) {
rootDirResource = resolveRootDirResource(rootDirResource);
URL rootDirUrl = rootDirResource.getURL();
@@ -706,7 +706,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
// The Sun JRE does not return a slash here, but BEA JRockit does.
rootEntryPath = rootEntryPath + "/";
}
Set<Resource> result = new LinkedHashSet<>(8);
Set<Resource> result = new LinkedHashSet<>(64);
for (Enumeration<JarEntry> entries = jarFile.entries(); entries.hasMoreElements();) {
JarEntry entry = entries.nextElement();
String entryPath = entry.getName();
@@ -756,7 +756,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
protected Set<Resource> doFindPathMatchingFileResources(Resource rootDirResource, String subPattern)
throws IOException {
Set<Resource> result = new LinkedHashSet<>();
Set<Resource> result = new LinkedHashSet<>(64);
URI rootDirUri;
try {
rootDirUri = rootDirResource.getURI();
@@ -865,7 +865,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
* @see PathMatcher#match(String, String)
*/
protected Set<Resource> findAllModulePathResources(String locationPattern) throws IOException {
Set<Resource> result = new LinkedHashSet<>(16);
Set<Resource> result = new LinkedHashSet<>(64);
// Skip scanning the module path when running in a native image.
if (NativeDetector.inNativeImage()) {
@@ -966,7 +966,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
private final String rootPath;
private final Set<Resource> resources = new LinkedHashSet<>();
private final Set<Resource> resources = new LinkedHashSet<>(64);
public PatternVirtualFileVisitor(String rootPath, String subPattern, PathMatcher pathMatcher) {
this.subPattern = subPattern;
@@ -997,7 +997,6 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
else if ("toString".equals(methodName)) {
return toString();
}
throw new IllegalStateException("Unexpected method invocation: " + method);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -243,7 +243,7 @@ public abstract class ClassUtils {
* style (e.g. "java.lang.Thread.State" instead of "java.lang.Thread$State").
* @param name the name of the Class
* @param classLoader the class loader to use
* (may be {@code null}, which indicates the default class loader)
* (can be {@code null}, which indicates the default class loader)
* @return a class instance for the supplied name
* @throws ClassNotFoundException if the class was not found
* @throws LinkageError if the class file could not be loaded
@@ -314,7 +314,7 @@ public abstract class ClassUtils {
* the exceptions thrown in case of class loading failure.
* @param className the name of the Class
* @param classLoader the class loader to use
* (may be {@code null}, which indicates the default class loader)
* (can be {@code null}, which indicates the default class loader)
* @return a class instance for the supplied name
* @throws IllegalArgumentException if the class name was not resolvable
* (that is, the class could not be found or the class file could not be loaded)
@@ -348,7 +348,7 @@ public abstract class ClassUtils {
* one of its dependencies is not present or cannot be loaded.
* @param className the name of the class to check
* @param classLoader the class loader to use
* (may be {@code null} which indicates the default class loader)
* (can be {@code null} which indicates the default class loader)
* @return whether the specified class is present (including all of its
* superclasses and interfaces)
* @throws IllegalStateException if the corresponding class is resolvable but
@@ -375,7 +375,7 @@ public abstract class ClassUtils {
* Check whether the given class is visible in the given ClassLoader.
* @param clazz the class to check (typically an interface)
* @param classLoader the ClassLoader to check against
* (may be {@code null} in which case this method will always return {@code true})
* (can be {@code null} in which case this method will always return {@code true})
*/
public static boolean isVisible(Class<?> clazz, @Nullable ClassLoader classLoader) {
if (classLoader == null) {
@@ -399,7 +399,7 @@ public abstract class ClassUtils {
* i.e. whether it is loaded by the given ClassLoader or a parent of it.
* @param clazz the class to analyze
* @param classLoader the ClassLoader to potentially cache metadata in
* (may be {@code null} which indicates the system class loader)
* (can be {@code null} which indicates the system class loader)
*/
public static boolean isCacheSafe(Class<?> clazz, @Nullable ClassLoader classLoader) {
Assert.notNull(clazz, "Class must not be null");
@@ -663,7 +663,7 @@ public abstract class ClassUtils {
* in the given collection.
* <p>Basically like {@code AbstractCollection.toString()}, but stripping
* the "class "/"interface " prefix before every class name.
* @param classes a Collection of Class objects (may be {@code null})
* @param classes a Collection of Class objects (can be {@code null})
* @return a String of form "[com.foo.Bar, com.foo.Baz]"
* @see java.util.AbstractCollection#toString()
*/
@@ -718,7 +718,7 @@ public abstract class ClassUtils {
* <p>If the class itself is an interface, it gets returned as sole interface.
* @param clazz the class to analyze for interfaces
* @param classLoader the ClassLoader that the interfaces need to be visible in
* (may be {@code null} when accepting all declared interfaces)
* (can be {@code null} when accepting all declared interfaces)
* @return all interfaces that the given object implements as an array
*/
public static Class<?>[] getAllInterfacesForClass(Class<?> clazz, @Nullable ClassLoader classLoader) {
@@ -753,7 +753,7 @@ public abstract class ClassUtils {
* <p>If the class itself is an interface, it gets returned as sole interface.
* @param clazz the class to analyze for interfaces
* @param classLoader the ClassLoader that the interfaces need to be visible in
* (may be {@code null} when accepting all declared interfaces)
* (can be {@code null} when accepting all declared interfaces)
* @return all interfaces that the given object implements as a Set
*/
public static Set<Class<?>> getAllInterfacesForClassAsSet(Class<?> clazz, @Nullable ClassLoader classLoader) {
@@ -1082,7 +1082,7 @@ public abstract class ClassUtils {
* fully qualified interface/class name + "." + method name.
* @param method the method
* @param clazz the clazz that the method is being invoked on
* (may be {@code null} to indicate the method's declaring class)
* (can be {@code null} to indicate the method's declaring class)
* @return the qualified name of the method
* @since 4.3.4
*/
@@ -1163,7 +1163,7 @@ public abstract class ClassUtils {
* @param clazz the clazz to analyze
* @param methodName the name of the method
* @param paramTypes the parameter types of the method
* (may be {@code null} to indicate any signature)
* (can be {@code null} to indicate any signature)
* @return the method (never {@code null})
* @throws IllegalStateException if the method has not been found
* @see Class#getMethod
@@ -1202,7 +1202,7 @@ public abstract class ClassUtils {
* @param clazz the clazz to analyze
* @param methodName the name of the method
* @param paramTypes the parameter types of the method
* (may be {@code null} to indicate any signature)
* (can be {@code null} to indicate any signature)
* @return the method, or {@code null} if not found
* @see Class#getMethod
*/
@@ -1291,13 +1291,14 @@ public abstract class ClassUtils {
* implementation will fall back to returning the originally provided method.
* @param method the method to be invoked, which may come from an interface
* @param targetClass the target class for the current invocation
* (may be {@code null} or may not even implement the method)
* (can be {@code null} or may not even implement the method)
* @return the specific target method, or the original method if the
* {@code targetClass} does not implement it
* @see #getInterfaceMethodIfPossible(Method, Class)
*/
public static Method getMostSpecificMethod(Method method, @Nullable Class<?> targetClass) {
if (targetClass != null && targetClass != method.getDeclaringClass() && isOverridable(method, targetClass)) {
if (targetClass != null && targetClass != method.getDeclaringClass() &&
(isOverridable(method, targetClass) || !method.getDeclaringClass().isAssignableFrom(targetClass))) {
try {
if (Modifier.isPublic(method.getModifiers())) {
try {
@@ -289,7 +289,8 @@ class BindingReflectionHintsRegistrarTests {
bindingRegistrar.registerReflectionHints(this.hints.reflection(), SampleRecordWithJacksonCustomStrategy.class);
assertThat(RuntimeHintsPredicates.reflection().onType(PropertyNamingStrategies.UpperSnakeCaseStrategy.class).withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS))
.accepts(this.hints);
assertThat(RuntimeHintsPredicates.reflection().onType(SampleRecordWithJacksonCustomStrategy.Builder.class).withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS))
assertThat(RuntimeHintsPredicates.reflection().onType(SampleRecordWithJacksonCustomStrategy.Builder.class)
.withMemberCategories(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS))
.accepts(this.hints);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -51,14 +51,14 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
*/
class PathMatchingResourcePatternResolverTests {
private static final String[] CLASSES_IN_CORE_IO_SUPPORT = { "EncodedResource.class",
private static final String[] CLASSES_IN_CORE_IO_SUPPORT = {"EncodedResource.class",
"LocalizedResourceHelper.class", "PathMatchingResourcePatternResolver.class", "PropertiesLoaderSupport.class",
"PropertiesLoaderUtils.class", "ResourceArrayPropertyEditor.class", "ResourcePatternResolver.class",
"ResourcePatternUtils.class", "SpringFactoriesLoader.class" };
"ResourcePatternUtils.class", "SpringFactoriesLoader.class"};
private static final String[] TEST_CLASSES_IN_CORE_IO_SUPPORT = { "PathMatchingResourcePatternResolverTests.class" };
private static final String[] TEST_CLASSES_IN_CORE_IO_SUPPORT = {"PathMatchingResourcePatternResolverTests.class"};
private static final String[] CLASSES_IN_REACTOR_UTIL_ANNOTATION = { "NonNull.class", "NonNullApi.class", "Nullable.class" };
private static final String[] CLASSES_IN_REACTOR_UTIL_ANNOTATION = {"NonNull.class", "NonNullApi.class", "Nullable.class"};
private PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
@@ -200,7 +200,7 @@ class PathMatchingResourcePatternResolverTests {
}
@Test
void usingFileProtocolWithoutWildcardInPatternAndEndingInSlashStarStar() {
void usingFileProtocolWithoutWildcardInPatternAndEndingInSlashStarStar() {
Path testResourcesDir = Paths.get("src/test/resources").toAbsolutePath();
String pattern = String.format("file:%s/scanned-resources/**", testResourcesDir);
String pathPrefix = ".+?resources/";
@@ -329,8 +329,8 @@ class PathMatchingResourcePatternResolverTests {
}
private String getPath(Resource resource) {
// Tests fail if we use resouce.getURL().getPath(). They would also fail on Mac OS when
// using resouce.getURI().getPath() if the resource paths are not Unicode normalized.
// Tests fail if we use resource.getURL().getPath(). They would also fail on macOS when
// using resource.getURI().getPath() if the resource paths are not Unicode normalized.
//
// On the JVM, all tests should pass when using resouce.getFile().getPath(); however,
// we use FileSystemResource#getPath since this test class is sometimes run within a
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,7 +22,8 @@ import org.springframework.util.ObjectUtils;
/**
* Encapsulates an object and a {@link TypeDescriptor} that describes it.
* The type descriptor can contain generic declarations that would not
*
* <p>The type descriptor can contain generic declarations that would not
* be accessible through a simple {@code getClass()} call on the object.
*
* @author Andy Clement
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 the original author 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,9 @@ import org.springframework.util.ObjectUtils;
/**
* Represents selection over a map or collection.
* For example: {1,2,3,4,5,6,7,8,9,10}.?{#isEven(#this) == 'y'} returns [2, 4, 6, 8, 10]
*
* <p>For example, <code>{1,2,3,4,5,6,7,8,9,10}.?{#isEven(#this)}</code> evaluates
* to {@code [2, 4, 6, 8, 10]}.
*
* <p>Basically a subset of the input data is returned based on the
* evaluation of the expression supplied as selection criteria.
@@ -100,11 +102,10 @@ public class Selection extends SpelNodeImpl {
Object val = selectionCriteria.getValueInternal(state).getValue();
if (val instanceof Boolean b) {
if (b) {
result.put(entry.getKey(), entry.getValue());
if (this.variant == FIRST) {
result.put(entry.getKey(), entry.getValue());
return new ValueRef.TypedValueHolderValueRef(new TypedValue(result), this);
}
result.put(entry.getKey(), entry.getValue());
lastKey = entry.getKey();
}
}
@@ -120,22 +121,22 @@ public class Selection extends SpelNodeImpl {
}
if ((this.variant == FIRST || this.variant == LAST) && result.isEmpty()) {
return new ValueRef.TypedValueHolderValueRef(new TypedValue(null), this);
return new ValueRef.TypedValueHolderValueRef(TypedValue.NULL, this);
}
if (this.variant == LAST) {
Map<Object, Object> resultMap = new HashMap<>();
Object lastValue = result.get(lastKey);
resultMap.put(lastKey,lastValue);
return new ValueRef.TypedValueHolderValueRef(new TypedValue(resultMap),this);
resultMap.put(lastKey, lastValue);
return new ValueRef.TypedValueHolderValueRef(new TypedValue(resultMap), this);
}
return new ValueRef.TypedValueHolderValueRef(new TypedValue(result),this);
return new ValueRef.TypedValueHolderValueRef(new TypedValue(result), this);
}
if (operand instanceof Iterable || ObjectUtils.isArray(operand)) {
Iterable<?> data = (operand instanceof Iterable<?> iterable ?
iterable : Arrays.asList(ObjectUtils.toObjectArray(operand)));
Iterable<?> data = (operand instanceof Iterable<?> iterable ? iterable :
Arrays.asList(ObjectUtils.toObjectArray(operand)));
List<Object> result = new ArrayList<>();
int index = 0;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@ import org.springframework.dao.DataRetrievalFailureException;
/**
* Data access exception thrown when a result set did not have the correct column count,
* for example when expecting a single column but getting 0 or more than 1 columns.
* for example when expecting a single column but getting 0 or more than 1 column.
*
* @author Juergen Hoeller
* @since 2.0
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@ import org.springframework.dao.IncorrectUpdateSemanticsDataAccessException;
/**
* Exception thrown when a JDBC update affects an unexpected number of rows.
* Typically we expect an update to affect a single row, meaning it's an
* Typically, we expect an update to affect a single row, meaning it is an
* error if it affects multiple rows.
*
* @author Rod Johnson
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,7 +22,7 @@ package org.springframework.jdbc.core;
*
* <p>This interface allows you to signal the end of a batch rather than
* having to determine the exact batch size upfront. Batch size is still
* being honored but it is now the maximum size of the batch.
* being honored, but it is now the maximum size of the batch.
*
* <p>The {@link #isBatchExhausted} method is called after each call to
* {@link #setValues} to determine whether there were some values added,
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -158,7 +158,7 @@ public class GenericTableMetaDataProvider implements TableMetaDataProvider {
}
catch (SQLException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Error retrieving 'DatabaseMetaData.getGeneratedKeys': " + ex.getMessage());
logger.warn("Error retrieving 'DatabaseMetaData.supportsGetGeneratedKeys': " + ex.getMessage());
}
}
try {
@@ -290,7 +290,7 @@ public class GenericTableMetaDataProvider implements TableMetaDataProvider {
}
/**
* Provide access to default schema for subclasses.
* Provide access to the default schema for subclasses.
*/
@Nullable
protected String getDefaultSchema() {
@@ -298,7 +298,7 @@ public class GenericTableMetaDataProvider implements TableMetaDataProvider {
}
/**
* Provide access to version info for subclasses.
* Provide access to the version info for subclasses.
*/
@Nullable
protected String getDatabaseVersion() {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -63,9 +63,6 @@ public class TableMetaDataContext {
@Nullable
private String schemaName;
// List of columns objects to be used in this context
private List<String> tableColumns = new ArrayList<>();
// Should we access insert parameter meta-data info or not
private boolean accessTableColumnMetaData = true;
@@ -76,6 +73,9 @@ public class TableMetaDataContext {
@Nullable
private TableMetaDataProvider metaDataProvider;
// List of columns objects to be used in this context
private List<String> tableColumns = new ArrayList<>();
// Are we using generated key columns
private boolean generatedKeyColumnsUsed = false;
@@ -139,7 +139,6 @@ public class TableMetaDataContext {
return this.accessTableColumnMetaData;
}
/**
* Specify whether we should override default for accessing synonyms.
*/
@@ -266,7 +265,6 @@ public class TableMetaDataContext {
return values;
}
/**
* Build the insert string based on configuration and meta-data information.
* @return the insert string to be used
@@ -303,8 +301,8 @@ public class TableMetaDataContext {
}
}
else {
String message = "Unable to locate columns for table '" + getTableName()
+ "' so an insert statement can't be generated.";
String message = "Unable to locate columns for table '" + getTableName() +
"' so an insert statement can't be generated.";
if (isAccessTableColumnMetaData()) {
message += " Consider specifying explicit column names -- for example, via SimpleJdbcInsert#usingColumns().";
}
@@ -349,26 +347,27 @@ public class TableMetaDataContext {
/**
* Does this database support the JDBC 3.0 feature of retrieving generated keys:
* {@link java.sql.DatabaseMetaData#supportsGetGeneratedKeys()}?
* Does this database support the JDBC feature for retrieving generated keys?
* @see java.sql.DatabaseMetaData#supportsGetGeneratedKeys()
*/
public boolean isGetGeneratedKeysSupported() {
return obtainMetaDataProvider().isGetGeneratedKeysSupported();
}
/**
* Does this database support simple query to retrieve generated keys
* when the JDBC 3.0 feature is not supported:
* {@link java.sql.DatabaseMetaData#supportsGetGeneratedKeys()}?
* Does this database support a simple query to retrieve generated keys when
* the JDBC feature for retrieving generated keys is not supported?
* @see #isGetGeneratedKeysSupported()
* @see #getSimpleQueryForGetGeneratedKey(String, String)
*/
public boolean isGetGeneratedKeysSimulated() {
return obtainMetaDataProvider().isGetGeneratedKeysSimulated();
}
/**
* Does this database support a simple query to retrieve generated keys
* when the JDBC 3.0 feature is not supported:
* {@link java.sql.DatabaseMetaData#supportsGetGeneratedKeys()}?
* Get the simple query to retrieve generated keys when the JDBC feature for
* retrieving generated keys is not supported.
* @see #isGetGeneratedKeysSimulated()
*/
@Nullable
public String getSimpleQueryForGetGeneratedKey(String tableName, String keyColumnName) {
@@ -376,8 +375,9 @@ public class TableMetaDataContext {
}
/**
* Is a column name String array for retrieving generated keys supported:
* {@link java.sql.Connection#createStruct(String, Object[])}?
* Does this database support a column name String array for retrieving generated
* keys?
* @see java.sql.Connection#createStruct(String, Object[])
*/
public boolean isGeneratedKeysColumnNameArraySupported() {
return obtainMetaDataProvider().isGeneratedKeysColumnNameArraySupported();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -45,8 +45,8 @@ import org.springframework.util.Assert;
* lobCreator.setBlobAsBinaryStream(ps, 2, contentStream, contentLength);
* lobCreator.setClobAsString(ps, 3, description);
* }
* }
* );</pre>
* });
* </pre>
*
* @author Juergen Hoeller
* @since 1.0.2
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2024 the original author 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,13 +41,13 @@ import org.springframework.lang.Nullable;
* final LobHandler lobHandler = new DefaultLobHandler(); // reusable object
*
* jdbcTemplate.query(
* "SELECT content FROM imagedb WHERE image_name=?", new Object[] {name},
* new AbstractLobStreamingResultSetExtractor() {
* public void streamData(ResultSet rs) throws SQLException, IOException {
* FileCopyUtils.copy(lobHandler.getBlobAsBinaryStream(rs, 1), contentStream);
* }
* }
* );</pre>
* "SELECT content FROM imagedb WHERE image_name=?", new Object[] {name},
* new AbstractLobStreamingResultSetExtractor() {
* public void streamData(ResultSet rs) throws SQLException, IOException {
* FileCopyUtils.copy(lobHandler.getBlobAsBinaryStream(rs, 1), contentStream);
* }
* });
* </pre>
*
* @author Juergen Hoeller
* @since 1.0.2
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -209,7 +209,7 @@ public class SqlLobValue implements DisposableSqlTypeValue {
}
/**
* Close the LobCreator, if any.
* Close the LobCreator.
*/
@Override
public void cleanup() {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -128,8 +128,8 @@ public abstract class RdbmsOperation implements InitializingBean {
/**
* Set the maximum number of rows for this RDBMS operation. This is important
* for processing subsets of large result sets, avoiding to read and hold
* the entire result set in the database or in the JDBC driver.
* for processing subsets of large result sets, in order to avoid reading and
* holding the entire result set in the database or in the JDBC driver.
* <p>Default is -1, indicating to use the driver's default.
* @see org.springframework.jdbc.core.JdbcTemplate#setMaxRows
*/
@@ -175,7 +175,7 @@ public abstract class RdbmsOperation implements InitializingBean {
public void setUpdatableResults(boolean updatableResults) {
if (isCompiled()) {
throw new InvalidDataAccessApiUsageException(
"The updateableResults flag must be set before the operation is compiled");
"The updatableResults flag must be set before the operation is compiled");
}
this.updatableResults = updatableResults;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2024 the original author 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.jdbc.core.SqlParameter;
/**
* Superclass for object abstractions of RDBMS stored procedures.
* This class is abstract and it is intended that subclasses will provide a typed
* This class is abstract, and it is intended that subclasses will provide a typed
* method for invocation that delegates to the supplied {@link #execute} method.
*
* <p>The inherited {@link #setSql sql} property is the name of the stored procedure
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,9 +25,9 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.lang.Nullable;
/**
* Registry for custom {@link org.springframework.jdbc.support.SQLExceptionTranslator} instances associated with
* specific databases allowing for overriding translation based on values contained in the configuration file
* named "sql-error-codes.xml".
* Registry for custom {@link SQLExceptionTranslator} instances associated with
* specific databases allowing for overriding translation based on values
* contained in the configuration file named "sql-error-codes.xml".
*
* @author Thomas Risberg
* @since 3.1.1
@@ -38,7 +38,7 @@ public final class CustomSQLExceptionTranslatorRegistry {
private static final Log logger = LogFactory.getLog(CustomSQLExceptionTranslatorRegistry.class);
/**
* Keep track of a single instance so we can return it to classes that request it.
* Keep track of a single instance, so we can return it to classes that request it.
*/
private static final CustomSQLExceptionTranslatorRegistry instance = new CustomSQLExceptionTranslatorRegistry();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -219,7 +219,7 @@ public abstract class JdbcUtils {
return NumberUtils.convertNumberToTargetClass(number, Integer.class);
}
else {
// e.g. on Postgres: getObject returns a PGObject but we need a String
// e.g. on Postgres: getObject returns a PGObject, but we need a String
return rs.getString(index);
}
}
@@ -415,14 +415,14 @@ public abstract class JdbcUtils {
}
/**
* Return whether the given JDBC driver supports JDBC 2.0 batch updates.
* Return whether the given JDBC driver supports JDBC batch updates.
* <p>Typically invoked right before execution of a given set of statements:
* to decide whether the set of SQL statements should be executed through
* the JDBC 2.0 batch mechanism or simply in a traditional one-by-one fashion.
* the JDBC batch mechanism or simply in a traditional one-by-one fashion.
* <p>Logs a warning if the "supportsBatchUpdates" methods throws an exception
* and simply returns {@code false} in that case.
* @param con the Connection to check
* @return whether JDBC 2.0 batch updates are supported
* @return whether JDBC batch updates are supported
* @see java.sql.DatabaseMetaData#supportsBatchUpdates()
*/
public static boolean supportsBatchUpdates(Connection con) {
@@ -492,8 +492,8 @@ public abstract class JdbcUtils {
/**
* Determine the column name to use. The column name is determined based on a
* lookup using ResultSetMetaData.
* <p>This method implementation takes into account recent clarifications
* expressed in the JDBC 4.0 specification:
* <p>This method's implementation takes into account clarifications expressed
* in the JDBC 4.0 specification:
* <p><i>columnLabel - the label for the column specified with the SQL AS clause.
* If the SQL AS clause was not specified, then the label is the name of the column</i>.
* @param resultSetMetaData the current meta-data to use
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -67,7 +67,7 @@ public class SQLErrorCodesFactory {
private static final Log logger = LogFactory.getLog(SQLErrorCodesFactory.class);
/**
* Keep track of a single instance so we can return it to classes that request it.
* Keep track of a single instance, so we can return it to classes that request it.
* Lazily initialized in order to avoid making {@code SQLErrorCodesFactory} constructor
* reachable on native images when not needed.
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* Copyright 2002-2024 the original author 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,9 +20,7 @@ import org.springframework.jdbc.support.SqlValue;
/**
* Subinterface of {@link org.springframework.jdbc.support.SqlValue}
* that supports passing in XML data to specified column and adds a
* cleanup callback, to be invoked after the value has been set and
* the corresponding statement has been executed.
* that specifically indicates passing in XML data to a specified column.
*
* @author Thomas Risberg
* @since 2.5.5
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -284,6 +284,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
* @see #CACHE_CONNECTION
* @see #CACHE_SESSION
* @see #CACHE_CONSUMER
* @see #CACHE_AUTO
* @see #setCacheLevelName
* @see #setTransactionManager
*/
@@ -570,8 +571,7 @@ public class DefaultMessageListenerContainer extends AbstractPollingMessageListe
if (this.taskExecutor == null) {
this.taskExecutor = createDefaultTaskExecutor();
}
else if (this.taskExecutor instanceof SchedulingTaskExecutor ste &&
ste.prefersShortLivedTasks() &&
else if (this.taskExecutor instanceof SchedulingTaskExecutor ste && ste.prefersShortLivedTasks() &&
this.maxMessagesPerTask == Integer.MIN_VALUE) {
// TaskExecutor indicated a preference for short-lived tasks. According to
// setMaxMessagesPerTask javadoc, we'll use 10 message per task in this case
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,7 +19,6 @@ package org.springframework.jms.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import jakarta.jms.JMSException;
import jakarta.jms.MessageListener;
import org.junit.jupiter.api.Test;
@@ -103,24 +102,20 @@ class EnableJmsTests extends AbstractJmsAnnotationDrivenTests {
}
@Test
@SuppressWarnings("resource")
void containerAreStartedByDefault() {
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(
EnableJmsDefaultContainerFactoryConfig.class, DefaultBean.class);
JmsListenerContainerTestFactory factory =
context.getBean(JmsListenerContainerTestFactory.class);
JmsListenerContainerTestFactory factory = context.getBean(JmsListenerContainerTestFactory.class);
MessageListenerTestContainer container = factory.getListenerContainers().get(0);
assertThat(container.isAutoStartup()).isTrue();
assertThat(container.isStarted()).isTrue();
}
@Test
@SuppressWarnings("resource")
void containerCanBeStarterViaTheRegistry() {
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(
EnableJmsAutoStartupFalseConfig.class, DefaultBean.class);
JmsListenerContainerTestFactory factory =
context.getBean(JmsListenerContainerTestFactory.class);
JmsListenerContainerTestFactory factory = context.getBean(JmsListenerContainerTestFactory.class);
MessageListenerTestContainer container = factory.getListenerContainers().get(0);
assertThat(container.isAutoStartup()).isFalse();
assertThat(container.isStarted()).isFalse();
@@ -131,13 +126,13 @@ class EnableJmsTests extends AbstractJmsAnnotationDrivenTests {
@Override
@Test
void jmsHandlerMethodFactoryConfiguration() throws JMSException {
void jmsHandlerMethodFactoryConfiguration() {
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(
EnableJmsHandlerMethodFactoryConfig.class, ValidationBean.class);
assertThatExceptionOfType(ListenerExecutionFailedException.class).isThrownBy(() ->
testJmsHandlerMethodFactoryConfiguration(context))
.withCauseInstanceOf(MethodArgumentNotValidException.class);
assertThatExceptionOfType(ListenerExecutionFailedException.class)
.isThrownBy(() -> testJmsHandlerMethodFactoryConfiguration(context))
.withCauseInstanceOf(MethodArgumentNotValidException.class);
}
@Override
@@ -159,19 +154,20 @@ class EnableJmsTests extends AbstractJmsAnnotationDrivenTests {
@Test
void composedJmsListeners() {
try (ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(
EnableJmsDefaultContainerFactoryConfig.class, ComposedJmsListenersBean.class)) {
JmsListenerContainerTestFactory simpleFactory = context.getBean("jmsListenerContainerFactory",
JmsListenerContainerTestFactory.class);
EnableJmsDefaultContainerFactoryConfig.class, ComposedJmsListenersBean.class)) {
JmsListenerContainerTestFactory simpleFactory =
context.getBean("jmsListenerContainerFactory", JmsListenerContainerTestFactory.class);
assertThat(simpleFactory.getListenerContainers()).hasSize(2);
MethodJmsListenerEndpoint first = (MethodJmsListenerEndpoint) simpleFactory.getListenerContainer(
"first").getEndpoint();
MethodJmsListenerEndpoint first = (MethodJmsListenerEndpoint)
simpleFactory.getListenerContainer("first").getEndpoint();
assertThat(first.getId()).isEqualTo("first");
assertThat(first.getDestination()).isEqualTo("orderQueue");
assertThat(first.getConcurrency()).isNull();
MethodJmsListenerEndpoint second = (MethodJmsListenerEndpoint) simpleFactory.getListenerContainer(
"second").getEndpoint();
MethodJmsListenerEndpoint second = (MethodJmsListenerEndpoint)
simpleFactory.getListenerContainer("second").getEndpoint();
assertThat(second.getId()).isEqualTo("second");
assertThat(second.getDestination()).isEqualTo("billingQueue");
assertThat(second.getConcurrency()).isEqualTo("2-10");
@@ -179,12 +175,11 @@ class EnableJmsTests extends AbstractJmsAnnotationDrivenTests {
}
@Test
@SuppressWarnings("resource")
void unknownFactory() {
// not found
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() ->
new AnnotationConfigApplicationContext(EnableJmsSampleConfig.class, CustomBean.class))
.withMessageContaining("customFactory");
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(() -> new AnnotationConfigApplicationContext(EnableJmsSampleConfig.class, CustomBean.class))
.withMessageContaining("customFactory");
}
@Test
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,15 +28,13 @@ public class JmsListenerContainerTestFactory implements JmsListenerContainerFact
private boolean autoStartup = true;
private final Map<String, MessageListenerTestContainer> listenerContainers =
new LinkedHashMap<>();
private final Map<String, MessageListenerTestContainer> listenerContainers = new LinkedHashMap<>();
public void setAutoStartup(boolean autoStartup) {
this.autoStartup = autoStartup;
}
public List<MessageListenerTestContainer> getListenerContainers() {
return new ArrayList<>(this.listenerContainers.values());
}
@@ -281,6 +281,10 @@ class ChannelSendOperator<T> extends Mono<Void> implements Scannable {
return;
}
synchronized (this) {
if (this.state == State.READY_TO_WRITE) {
s.request(n);
return;
}
if (this.writeSubscriber != null) {
if (this.state == State.EMITTING_CACHED_SIGNALS) {
this.demandBeforeReadyToWrite = n;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -264,8 +264,9 @@ public class LocalContainerEntityManagerFactoryBean extends AbstractEntityManage
* @see jakarta.persistence.spi.PersistenceUnitInfo#getNonJtaDataSource()
* @see #setPersistenceUnitManager
*/
public void setDataSource(DataSource dataSource) {
this.internalPersistenceUnitManager.setDataSourceLookup(new SingleDataSourceLookup(dataSource));
public void setDataSource(@Nullable DataSource dataSource) {
this.internalPersistenceUnitManager.setDataSourceLookup(
dataSource != null ? new SingleDataSourceLookup(dataSource) : null);
this.internalPersistenceUnitManager.setDefaultDataSource(dataSource);
}
@@ -281,8 +282,9 @@ public class LocalContainerEntityManagerFactoryBean extends AbstractEntityManage
* @see jakarta.persistence.spi.PersistenceUnitInfo#getJtaDataSource()
* @see #setPersistenceUnitManager
*/
public void setJtaDataSource(DataSource jtaDataSource) {
this.internalPersistenceUnitManager.setDataSourceLookup(new SingleDataSourceLookup(jtaDataSource));
public void setJtaDataSource(@Nullable DataSource jtaDataSource) {
this.internalPersistenceUnitManager.setDataSourceLookup(
jtaDataSource != null ? new SingleDataSourceLookup(jtaDataSource) : null);
this.internalPersistenceUnitManager.setDefaultJtaDataSource(jtaDataSource);
}
@@ -427,6 +429,7 @@ public class LocalContainerEntityManagerFactoryBean extends AbstractEntityManage
}
@Override
@Nullable
public DataSource getDataSource() {
if (this.persistenceUnitInfo != null) {
return (this.persistenceUnitInfo.getJtaDataSource() != null ?
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2024 the original author 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,28 +28,18 @@ import jakarta.persistence.spi.PersistenceProvider;
* shared JPA EntityManagerFactory in a Spring application context; the
* EntityManagerFactory can then be passed to JPA-based DAOs via
* dependency injection. Note that switching to a JNDI lookup or to a
* {@link LocalContainerEntityManagerFactoryBean}
* definition is just a matter of configuration!
* {@link LocalContainerEntityManagerFactoryBean} definition based on the
* JPA container contract is just a matter of configuration!
*
* <p>Configuration settings are usually read from a {@code META-INF/persistence.xml}
* config file, residing in the class path, according to the JPA standalone bootstrap
* contract. Additionally, most JPA providers will require a special VM agent
* (specified on JVM startup) that allows them to instrument application classes.
* See the Java Persistence API specification and your provider documentation
* for setup details.
*
* <p>This EntityManagerFactory bootstrap is appropriate for standalone applications
* which solely use JPA for data access. If you want to set up your persistence
* provider for an external DataSource and/or for global transactions which span
* multiple resources, you will need to either deploy it into a full Jakarta EE
* application server and access the deployed EntityManagerFactory via JNDI,
* or use Spring's {@link LocalContainerEntityManagerFactoryBean} with appropriate
* configuration for local setup according to JPA's container contract.
* contract. See the Java Persistence API specification and your persistence provider
* documentation for setup details. Additionally, JPA properties can also be added
* on this FactoryBean via {@link #setJpaProperties}/{@link #setJpaPropertyMap}.
*
* <p><b>Note:</b> This FactoryBean has limited configuration power in terms of
* what configuration it is able to pass to the JPA provider. If you need more
* flexible configuration, for example passing a Spring-managed JDBC DataSource
* to the JPA provider, consider using Spring's more powerful
* the configuration that it is able to pass to the JPA provider. If you need
* more flexible configuration options, consider using Spring's more powerful
* {@link LocalContainerEntityManagerFactoryBean} instead.
*
* @author Juergen Hoeller
@@ -67,20 +67,26 @@ import org.springframework.util.ReflectionUtils;
*/
class PersistenceManagedTypesBeanRegistrationAotProcessor implements BeanRegistrationAotProcessor {
private static final List<Class<? extends Annotation>> CALLBACK_TYPES = List.of(PreUpdate.class,
PostUpdate.class, PrePersist.class, PostPersist.class, PreRemove.class, PostRemove.class, PostLoad.class);
private static final boolean jpaPresent = ClassUtils.isPresent("jakarta.persistence.Entity",
PersistenceManagedTypesBeanRegistrationAotProcessor.class.getClassLoader());
@Nullable
@Override
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
if (PersistenceManagedTypes.class.isAssignableFrom(registeredBean.getBeanClass())) {
return BeanRegistrationAotContribution.withCustomCodeFragments(codeFragments ->
new JpaManagedTypesBeanRegistrationCodeFragments(codeFragments, registeredBean));
if (jpaPresent) {
if (PersistenceManagedTypes.class.isAssignableFrom(registeredBean.getBeanClass())) {
return BeanRegistrationAotContribution.withCustomCodeFragments(codeFragments ->
new JpaManagedTypesBeanRegistrationCodeFragments(codeFragments, registeredBean));
}
}
return null;
}
private static class JpaManagedTypesBeanRegistrationCodeFragments extends BeanRegistrationCodeFragmentsDecorator {
private static final class JpaManagedTypesBeanRegistrationCodeFragments extends BeanRegistrationCodeFragmentsDecorator {
private static final List<Class<? extends Annotation>> CALLBACK_TYPES = List.of(PreUpdate.class,
PostUpdate.class, PrePersist.class, PostPersist.class, PreRemove.class, PostRemove.class, PostLoad.class);
private static final ParameterizedTypeName LIST_OF_STRINGS_TYPE = ParameterizedTypeName.get(List.class, String.class);
@@ -88,7 +94,7 @@ class PersistenceManagedTypesBeanRegistrationAotProcessor implements BeanRegistr
private final BindingReflectionHintsRegistrar bindingRegistrar = new BindingReflectionHintsRegistrar();
public JpaManagedTypesBeanRegistrationCodeFragments(BeanRegistrationCodeFragments codeFragments,
private JpaManagedTypesBeanRegistrationCodeFragments(BeanRegistrationCodeFragments codeFragments,
RegisteredBean registeredBean) {
super(codeFragments);
this.registeredBean = registeredBean;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 the original author 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,11 +73,16 @@ public final class PersistenceManagedTypesScanner {
private final CandidateComponentsIndex componentsIndex;
/**
* Create a new {@code PersistenceManagedTypesScanner} for the given resource loader.
* @param resourceLoader the {@code ResourceLoader} to use
*/
public PersistenceManagedTypesScanner(ResourceLoader resourceLoader) {
this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
this.componentsIndex = CandidateComponentsIndexLoader.loadIndex(resourceLoader.getClassLoader());
}
/**
* Scan the specified packages and return a {@link PersistenceManagedTypes} that
* represents the result of the scanning.
@@ -0,0 +1 @@
Args = -H:ServiceLoaderFeatureExcludeServices=org.hibernate.bytecode.spi.BytecodeProvider
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -70,13 +70,15 @@ public class SingleConnectionFactory extends DelegatingConnectionFactory
private boolean suppressClose;
/** Override auto-commit state?. */
private @Nullable Boolean autoCommit;
@Nullable
private Boolean autoCommit;
/** Wrapped Connection. */
private final AtomicReference<Connection> target = new AtomicReference<>();
/** Proxy Connection. */
private @Nullable Connection connection;
@Nullable
private Connection connection;
private final Mono<? extends Connection> connectionEmitter;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -292,6 +292,11 @@ public class MockHttpServletResponse implements HttpServletResponse {
doAddHeaderValue(HttpHeaders.CONTENT_LENGTH, contentLength, true);
}
/**
* Get the length of the content body from the HTTP Content-Length header.
* @return the value of the Content-Length header
* @see #setContentLength(int)
*/
public int getContentLength() {
return (int) this.contentLength;
}
@@ -742,7 +747,7 @@ public class MockHttpServletResponse implements HttpServletResponse {
@Override
public void setStatus(int status) {
if (!this.isCommitted()) {
if (!isCommitted()) {
this.status = status;
}
}
@@ -752,6 +757,9 @@ public class MockHttpServletResponse implements HttpServletResponse {
return this.status;
}
/**
* Return the error message used when calling {@link HttpServletResponse#sendError(int, String)}.
*/
@Nullable
public String getErrorMessage() {
return this.errorMessage;
@@ -35,7 +35,6 @@ import org.springframework.util.Assert;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.request;
/**
* A {@link ClientHttpRequestFactory} for requests executed via {@link MockMvc}.
*
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2024 the original author 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,13 +34,13 @@ public interface MvcResult {
/**
* Return the performed request.
* @return the request, never {@code null}
* @return the request (never {@code null})
*/
MockHttpServletRequest getRequest();
/**
* Return the resulting response.
* @return the response, never {@code null}
* @return the response (never {@code null})
*/
MockHttpServletResponse getResponse();
@@ -31,7 +31,6 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
/**
* Integration tests for {@link MockMvcWebConnection}.
*
@@ -64,14 +63,15 @@ public class MockMvcWebConnectionTests {
public void contextPathEmpty() throws IOException {
this.webClient.setWebConnection(new MockMvcWebConnection(this.mockMvc, this.webClient, ""));
// Empty context path (root context) should not match to a URL with a context path
assertThatExceptionOfType(FailingHttpStatusCodeException.class).isThrownBy(() ->
this.webClient.getPage("http://localhost/context/a"))
.satisfies(ex -> assertThat(ex.getStatusCode()).isEqualTo(404));
assertThatExceptionOfType(FailingHttpStatusCodeException.class)
.isThrownBy(() -> this.webClient.getPage("http://localhost/context/a"))
.satisfies(ex -> assertThat(ex.getStatusCode()).isEqualTo(404));
this.webClient.setWebConnection(new MockMvcWebConnection(this.mockMvc, this.webClient));
// No context is the same providing an empty context path
assertThatExceptionOfType(FailingHttpStatusCodeException.class).isThrownBy(() ->
this.webClient.getPage("http://localhost/context/a"))
.satisfies(ex -> assertThat(ex.getStatusCode()).isEqualTo(404));
assertThatExceptionOfType(FailingHttpStatusCodeException.class)
.isThrownBy(() -> this.webClient.getPage("http://localhost/context/a"))
.satisfies(ex -> assertThat(ex.getStatusCode()).isEqualTo(404));
}
@Test
@@ -84,8 +84,9 @@ public class MockMvcWebConnectionTests {
@Test
public void infiniteForward() {
this.webClient.setWebConnection(new MockMvcWebConnection(this.mockMvc, this.webClient, ""));
assertThatIllegalStateException().isThrownBy(() -> this.webClient.getPage("http://localhost/infiniteForward"))
.withMessage("Forwarded 100 times in a row, potential infinite forward loop");
assertThatIllegalStateException()
.isThrownBy(() -> this.webClient.getPage("http://localhost/infiniteForward"))
.withMessage("Forwarded 100 times in a row, potential infinite forward loop");
}
@Test
@@ -32,7 +32,6 @@ import org.springframework.mock.web.MockHttpServletResponse;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Tests for {@link MockWebResponseBuilder}.
*
@@ -55,8 +54,6 @@ public class MockWebResponseBuilderTests {
}
// --- constructor
@Test
public void constructorWithNullWebRequest() {
assertThatIllegalArgumentException().isThrownBy(() ->
@@ -66,12 +63,10 @@ public class MockWebResponseBuilderTests {
@Test
public void constructorWithNullResponse() throws Exception {
assertThatIllegalArgumentException().isThrownBy(() ->
new MockWebResponseBuilder(0L, new WebRequest(new URL("http://company.example:80/test/this/here")), null));
new MockWebResponseBuilder(0L,
new WebRequest(new URL("http://company.example:80/test/this/here")), null));
}
// --- build
@Test
public void buildContent() throws Exception {
this.response.getWriter().write("expected content");
@@ -124,8 +119,7 @@ public class MockWebResponseBuilderTests {
.endsWith("; Secure; HttpOnly");
}
// SPR-14169
@Test
@Test // SPR-14169
public void buildResponseHeadersNullDomainDefaulted() throws Exception {
Cookie cookie = new Cookie("cookieA", "valueA");
this.response.addCookie(cookie);
@@ -52,6 +52,7 @@ class MockMvcHtmlUnitDriverBuilderTests {
private HtmlUnitDriver driver;
MockMvcHtmlUnitDriverBuilderTests(WebApplicationContext wac) {
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
@@ -48,6 +48,7 @@ class WebConnectionHtmlUnitDriverTests {
@Mock
private WebConnection connection;
@BeforeEach
void setup() throws Exception {
given(this.connection.getResponse(any(WebRequest.class))).willThrow(new IOException(""));
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -110,7 +110,7 @@ class ExceptionHandlerTests {
.accept(MediaType.APPLICATION_JSON)
.exchange()
.expectStatus().isOk()
.expectBody().jsonPath("$.name", "Yoda");
.expectBody().jsonPath("$.name").isEqualTo("Yoda");
}
@Test
@@ -123,7 +123,7 @@ class ExceptionHandlerTests {
.accept(MediaType.APPLICATION_JSON)
.exchange()
.expectStatus().isOk()
.expectBody().jsonPath("$.error", "local - IllegalArgumentException");
.expectBody().jsonPath("$.error").isEqualTo("local - IllegalArgumentException");
}
@Test
@@ -136,7 +136,7 @@ class ExceptionHandlerTests {
.accept(MediaType.APPLICATION_JSON)
.exchange()
.expectStatus().isOk()
.expectBody().jsonPath("$.error", "global - IllegalArgumentException");
.expectBody().jsonPath("$.error").isEqualTo("global - IllegalStateException");
}
@Test
@@ -149,7 +149,7 @@ class ExceptionHandlerTests {
.accept(MediaType.APPLICATION_JSON)
.exchange()
.expectStatus().isOk()
.expectBody().jsonPath("$.error", "globalPersonController - IllegalStateException");
.expectBody().jsonPath("$.error").isEqualTo("globalPersonController - IllegalStateException");
}
@Test
@@ -163,7 +163,7 @@ class ExceptionHandlerTests {
.accept(MediaType.APPLICATION_JSON)
.exchange()
.expectStatus().isOk()
.expectBody().jsonPath("$.error", "global - NoHandlerFoundException");
.expectBody().jsonPath("$.error").isEqualTo("global - NoHandlerFoundException");
}
}
@@ -45,7 +45,7 @@ public class RequestParameterTests {
.exchange()
.expectStatus().isOk()
.expectHeader().contentType(MediaType.APPLICATION_JSON)
.expectBody().jsonPath("$.name", "George");
.expectBody().jsonPath("$.name").isEqualTo("George");
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2024 the original author 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 @@ class ViewResolutionTests {
.exchange()
.expectStatus().isOk()
.expectHeader().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)
.expectBody().jsonPath("$.person.name", "Corea");
.expectBody().jsonPath("$.person.name").isEqualTo("Corea");
}
@Test
@@ -143,7 +143,7 @@ class ViewResolutionTests {
.exchange()
.expectStatus().isOk()
.expectHeader().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)
.expectBody().jsonPath("$.person.name", "Corea");
.expectBody().jsonPath("$.person.name").isEqualTo("Corea");
testClient.get().uri("/person/Corea")
.accept(MediaType.APPLICATION_XML)
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -44,10 +44,11 @@ public class IncorrectUpdateSemanticsDataAccessException extends InvalidDataAcce
super(msg, cause);
}
/**
* Return whether data was updated.
* If this method returns false, there's nothing to roll back.
* <p>The default implementation always returns true.
* If this method returns {@code false}, there is nothing to roll back.
* <p>The default implementation always returns {@code true}.
* This can be overridden in subclasses.
*/
public boolean wasDataUpdated() {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -42,11 +42,6 @@ import org.springframework.util.StringValueResolver;
* If none found on the target class, the interface that the invoked method
* has been called through (in case of a JDK proxy) will be checked.
*
* <p>This implementation caches attributes by method after they are first used.
* If it is ever desirable to allow dynamic changing of transaction attributes
* (which is very unlikely), caching could be made configurable. Caching is
* desirable because of the cost of evaluating rollback rules.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 1.1
@@ -95,7 +90,7 @@ public abstract class AbstractFallbackTransactionAttributeSource
* Determine the transaction attribute for this method invocation.
* <p>Defaults to the class's transaction attribute if no method attribute is found.
* @param method the method for the current invocation (never {@code null})
* @param targetClass the target class for this invocation (may be {@code null})
* @param targetClass the target class for this invocation (can be {@code null})
* @return a TransactionAttribute for this method, or {@code null} if the method
* is not transactional
*/
@@ -106,27 +101,15 @@ public abstract class AbstractFallbackTransactionAttributeSource
return null;
}
// First, see if we have a cached value.
Object cacheKey = getCacheKey(method, targetClass);
TransactionAttribute cached = this.attributeCache.get(cacheKey);
if (cached != null) {
// Value will either be canonical value indicating there is no transaction attribute,
// or an actual transaction attribute.
if (cached == NULL_TRANSACTION_ATTRIBUTE) {
return null;
}
else {
return cached;
}
return (cached != NULL_TRANSACTION_ATTRIBUTE ? cached : null);
}
else {
// We need to work it out.
TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);
// Put it in the cache.
if (txAttr == null) {
this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
}
else {
if (txAttr != null) {
String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);
if (txAttr instanceof DefaultTransactionAttribute dta) {
dta.setDescriptor(methodIdentification);
@@ -137,6 +120,9 @@ public abstract class AbstractFallbackTransactionAttributeSource
}
this.attributeCache.put(cacheKey, txAttr);
}
else {
this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
}
return txAttr;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -57,7 +57,7 @@ public interface TransactionAttributeSource {
* Return the transaction attribute for the given method,
* or {@code null} if the method is non-transactional.
* @param method the method to introspect
* @param targetClass the target class (may be {@code null},
* @param targetClass the target class (can be {@code null},
* in which case the declaring class of the method must be used)
* @return the matching transaction attribute, or {@code null} if none found
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -77,7 +77,7 @@ final class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointc
* {@link ClassFilter} that delegates to {@link TransactionAttributeSource#isCandidateClass}
* for filtering classes whose methods are not worth searching to begin with.
*/
private class TransactionAttributeSourceClassFilter implements ClassFilter {
private final class TransactionAttributeSourceClassFilter implements ClassFilter {
@Override
public boolean matches(Class<?> clazz) {
@@ -89,6 +89,7 @@ final class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointc
return (transactionAttributeSource == null || transactionAttributeSource.isCandidateClass(clazz));
}
@Nullable
private TransactionAttributeSource getTransactionAttributeSource() {
return transactionAttributeSource;
}
@@ -96,7 +97,7 @@ final class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointc
@Override
public boolean equals(@Nullable Object other) {
return (this == other || (other instanceof TransactionAttributeSourceClassFilter that &&
ObjectUtils.nullSafeEquals(transactionAttributeSource, that.getTransactionAttributeSource())));
ObjectUtils.nullSafeEquals(getTransactionAttributeSource(), that.getTransactionAttributeSource())));
}
@Override
@@ -106,9 +107,8 @@ final class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointc
@Override
public String toString() {
return TransactionAttributeSourceClassFilter.class.getName() + ": " + transactionAttributeSource;
return TransactionAttributeSourceClassFilter.class.getName() + ": " + getTransactionAttributeSource();
}
}
}

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