Compare commits

..

1 Commits

Author SHA1 Message Date
Spring Builds 91cf5ebd2d Release v6.0.19 2024-04-11 07:44:39 +00:00
76 changed files with 629 additions and 821 deletions
@@ -35,7 +35,9 @@ jobs:
env:
CI: 'true'
GRADLE_ENTERPRISE_URL: 'https://ge.spring.io'
DEVELOCITY_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GRADLE_ENTERPRISE_CACHE_USER }}
GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }}
run: ./gradlew -PdeploymentRepository=$(pwd)/deployment-repository build publishAllPublicationsToDeploymentRepository
- name: Deploy
uses: spring-io/artifactory-deploy-action@v0.0.1
@@ -49,10 +51,9 @@ jobs:
signing-key: ${{ secrets.GPG_PRIVATE_KEY }}
signing-passphrase: ${{ secrets.GPG_PASSPHRASE }}
artifact-properties: |
/**/framework-docs-*.zip::zip.name=spring-framework,zip.deployed=false
/**/framework-docs-*-docs.zip::zip.type=docs
/**/framework-docs-*-dist.zip::zip.type=dist
/**/framework-docs-*-schema.zip::zip.type=schema
/**/framework-api-*.zip::zip.name=spring-framework,zip.deployed=false
/**/framework-api-*-docs.zip::zip.type=docs
/**/framework-api-*-schema.zip::zip.type=schema
- name: Send notification
uses: ./.github/actions/send-notification
if: always()
+3 -1
View File
@@ -66,7 +66,9 @@ jobs:
env:
CI: 'true'
GRADLE_ENTERPRISE_URL: 'https://ge.spring.io'
DEVELOCITY_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GRADLE_ENTERPRISE_CACHE_USER }}
GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }}
run: ./gradlew check antora
- name: Send notification
uses: ./.github/actions/send-notification
+1 -1
View File
@@ -1,3 +1,3 @@
# Enable auto-env through the sdkman_auto_env config
# Add key=value pairs of SDKs to use below
java=17.0.11-librca
java=17.0.8.1-librca
+3 -3
View File
@@ -3,12 +3,12 @@ plugins {
id 'io.freefair.aspectj' version '8.0.1' apply false
// kotlinVersion is managed in gradle.properties
id 'org.jetbrains.kotlin.plugin.serialization' version "${kotlinVersion}" apply false
id 'org.jetbrains.dokka' version '1.8.20'
id 'org.jetbrains.dokka' version '1.8.10'
id 'org.unbroken-dome.xjc' version '2.0.0' apply false
id 'com.github.ben-manes.versions' version '0.51.0'
id 'com.github.ben-manes.versions' version '0.49.0'
id 'com.github.johnrengelman.shadow' version '8.1.1' apply false
id 'de.undercouch.download' version '5.4.0'
id 'me.champeau.jmh' version '0.7.2' apply false
id 'me.champeau.jmh' version '0.7.1' apply false
}
ext {
+3 -1
View File
@@ -5,7 +5,9 @@ anchors:
password: ((github-ci-release-token))
branch: ((branch))
gradle-enterprise-task-params: &gradle-enterprise-task-params
DEVELOCITY_ACCESS_KEY: ((gradle_enterprise_secret_access_key))
GRADLE_ENTERPRISE_ACCESS_KEY: ((gradle_enterprise_secret_access_key))
GRADLE_ENTERPRISE_CACHE_USERNAME: ((gradle_enterprise_cache_user.username))
GRADLE_ENTERPRISE_CACHE_PASSWORD: ((gradle_enterprise_cache_user.password))
sonatype-task-params: &sonatype-task-params
SONATYPE_USERNAME: ((sonatype-username))
SONATYPE_PASSWORD: ((sonatype-password))
@@ -1,7 +1,7 @@
[[spring-mvc-test-vs-end-to-end-integration-tests]]
= MockMvc vs End-to-End Tests
MockMvc is built on Servlet API mock implementations from the
MockMVc is built on Servlet API mock implementations from the
`spring-test` module and does not rely on a running container. Therefore, there are
some differences when compared to full end-to-end integration tests with an actual
client and a live server running.
@@ -1,15 +1,38 @@
[[spring-mvc-test-vs-streaming-response]]
= Streaming Responses
You can use `WebTestClient` to test xref:testing/webtestclient.adoc#webtestclient-stream[streaming responses]
such as Server-Sent Events. However, `MockMvcWebTestClient` doesn't support infinite
streams because there is no way to cancel the server stream from the client side.
To test infinite streams, you'll need to
xref:testing/webtestclient.adoc#webtestclient-server-config[bind to] a running server,
or when using Spring Boot,
{docs-spring-boot}/spring-boot-features.html#boot-features-testing-spring-boot-applications-testing-with-running-server[test with a running server].
The best way to test streaming responses such as Server-Sent Events is through the
<<WebTestClient>> which can be used as a test client to connect to a `MockMvc` instance
to perform tests on Spring MVC controllers without a running server. For example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
WebTestClient client = MockMvcWebTestClient.bindToController(new SseController()).build();
FluxExchangeResult<Person> exchangeResult = client.get()
.uri("/persons")
.exchange()
.expectStatus().isOk()
.expectHeader().contentType("text/event-stream")
.returnResult(Person.class);
// Use StepVerifier from Project Reactor to test the streaming response
StepVerifier.create(exchangeResult.getResponseBody())
.expectNext(new Person("N0"), new Person("N1"), new Person("N2"))
.expectNextCount(4)
.consumeNextWith(person -> assertThat(person.getName()).endsWith("7"))
.thenCancel()
.verify();
----
======
`WebTestClient` can also connect to a live server and perform full end-to-end integration
tests. This is also supported in Spring Boot where you can
{docs-spring-boot}/html/spring-boot-features.html#boot-features-testing-spring-boot-applications-testing-with-running-server[test a running server].
`MockMvcWebTestClient` does support asynchronous responses, and even streaming responses.
The limitation is that it can't influence the server to stop, and therefore the server
must finish writing the response on its own.
@@ -52,10 +52,14 @@ The following example shows how to achieve the same configuration in XML:
</mvc:interceptors>
----
WARNING: Interceptors are not ideally suited as a security layer due to the potential for
a mismatch with annotated controller path matching. Generally, we recommend using Spring
Security, or alternatively a similar approach integrated with the Servlet filter chain,
and applied as early as possible.
NOTE: Interceptors are not ideally suited as a security layer due to the potential
for a mismatch with annotated controller path matching, which can also match trailing
slashes and path extensions transparently, along with other path matching options. Many
of these options have been deprecated but the potential for a mismatch remains.
Generally, we recommend using Spring Security which includes a dedicated
https://docs.spring.io/spring-security/reference/servlet/integrations/mvc.html#mvc-requestmatcher[MvcRequestMatcher]
to align with Spring MVC path matching and also has a security firewall that blocks many
unwanted characters in URL paths.
NOTE: The XML config declares interceptors as `MappedInterceptor` beans, and those are in
turn detected by any `HandlerMapping` bean, including those from other frameworks.
@@ -1,29 +1,34 @@
[[mvc-handlermapping-interceptor]]
= Interception
All `HandlerMapping` implementations support handler interception which is useful when
you want to apply functionality across requests. A `HandlerInterceptor` can implement the
following:
All `HandlerMapping` implementations support handler interceptors that are useful when
you want to apply specific functionality to certain requests -- for example, checking for
a principal. Interceptors must implement `HandlerInterceptor` from the
`org.springframework.web.servlet` package with three methods that should provide enough
flexibility to do all kinds of pre-processing and post-processing:
* `preHandle(..)` -- callback before the actual handler is run that returns a boolean.
If the method returns `true`, execution continues; if it returns `false`, the rest of the
execution chain is bypassed and the handler is not called.
* `postHandle(..)` -- callback after the handler is run.
* `afterCompletion(..)` -- callback after the complete request has finished.
* `preHandle(..)`: Before the actual handler is run
* `postHandle(..)`: After the handler is run
* `afterCompletion(..)`: After the complete request has finished
NOTE: For `@ResponseBody` and `ResponseEntity` controller methods, the response is written
and committed within the `HandlerAdapter`, before `postHandle` is called. That means it is
too late to change the response, such as to add an extra header. You can implement
`ResponseBodyAdvice` and declare it as an
xref:web/webmvc/mvc-controller/ann-advice.adoc[Controller Advice] bean or configure it
directly on `RequestMappingHandlerAdapter`.
The `preHandle(..)` method returns a boolean value. You can use this method to break or
continue the processing of the execution chain. When this method returns `true`, the
handler execution chain continues. When it returns false, the `DispatcherServlet`
assumes the interceptor itself has taken care of requests (and, for example, rendered an
appropriate view) and does not continue executing the other interceptors and the actual
handler in the execution chain.
See xref:web/webmvc/mvc-config/interceptors.adoc[Interceptors] in the section on MVC configuration for examples of how to
configure interceptors. You can also register them directly by using setters on individual
`HandlerMapping` implementations.
WARNING: Interceptors are not ideally suited as a security layer due to the potential for
a mismatch with annotated controller path matching. Generally, we recommend using Spring
Security, or alternatively a similar approach integrated with the Servlet filter chain,
and applied as early as possible.
`postHandle` method is less useful with `@ResponseBody` and `ResponseEntity` methods for
which the response is written and committed within the `HandlerAdapter` and before
`postHandle`. That means it is too late to make any changes to the response, such as adding
an extra header. For such scenarios, you can implement `ResponseBodyAdvice` and either
declare it as an xref:web/webmvc/mvc-controller/ann-advice.adoc[Controller Advice] bean or configure it directly on
`RequestMappingHandlerAdapter`.
+10 -10
View File
@@ -9,17 +9,17 @@ 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.109.Final"))
api(platform("io.netty:netty-bom:4.1.108.Final"))
api(platform("io.netty:netty5-bom:5.0.0.Alpha5"))
api(platform("io.projectreactor:reactor-bom:2022.0.19"))
api(platform("io.projectreactor:reactor-bom:2022.0.18"))
api(platform("io.rsocket:rsocket-bom:1.1.3"))
api(platform("org.apache.groovy:groovy-bom:4.0.21"))
api(platform("org.apache.groovy:groovy-bom:4.0.20"))
api(platform("org.apache.logging.log4j:log4j-bom:2.21.1"))
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.12.0"))
api(platform("org.mockito:mockito-bom:5.11.0"))
constraints {
api("com.fasterxml:aalto-xml:1.3.2")
@@ -54,9 +54,9 @@ dependencies {
api("io.r2dbc:r2dbc-spi:1.0.0.RELEASE")
api("io.reactivex.rxjava3:rxjava:3.1.8")
api("io.smallrye.reactive:mutiny:1.10.0")
api("io.undertow:undertow-core:2.3.13.Final")
api("io.undertow:undertow-servlet:2.3.13.Final")
api("io.undertow:undertow-websockets-jsr:2.3.13.Final")
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")
@@ -103,9 +103,9 @@ dependencies {
api("org.apache.tomcat.embed:tomcat-embed-websocket:10.1.15")
api("org.apache.tomcat:tomcat-util:10.1.15")
api("org.apache.tomcat:tomcat-websocket:10.1.15")
api("org.aspectj:aspectjrt:1.9.22.1")
api("org.aspectj:aspectjtools:1.9.22.1")
api("org.aspectj:aspectjweaver:1.9.22.1")
api("org.aspectj:aspectjrt:1.9.20.1")
api("org.aspectj:aspectjtools:1.9.20.1")
api("org.aspectj:aspectjweaver:1.9.20.1")
api("org.assertj:assertj-core:3.24.2")
api("org.awaitility:awaitility:4.2.0")
api("org.bouncycastle:bcpkix-jdk18on:1.72")
+1 -1
View File
@@ -1,4 +1,4 @@
version=6.0.20
version=6.0.19
org.gradle.caching=true
org.gradle.jvmargs=-Xmx2048m
-2
View File
@@ -6,7 +6,6 @@ tasks.findByName("dokkaHtmlPartial")?.configure {
classpath.from(sourceSets["main"].runtimeClasspath)
externalDocumentationLink {
url.set(new URL("https://docs.spring.io/spring-framework/docs/current/javadoc-api/"))
packageListUrl.set(new URL("https://docs.spring.io/spring-framework/docs/current/javadoc-api/element-list"))
}
externalDocumentationLink {
url.set(new URL("https://projectreactor.io/docs/core/release/api/"))
@@ -22,7 +21,6 @@ tasks.findByName("dokkaHtmlPartial")?.configure {
}
externalDocumentationLink {
url.set(new URL("https://javadoc.io/doc/jakarta.servlet/jakarta.servlet-api/latest/"))
packageListUrl.set(new URL("https://javadoc.io/doc/jakarta.servlet/jakarta.servlet-api/latest/element-list"))
}
externalDocumentationLink {
url.set(new URL("https://javadoc.io/static/io.rsocket/rsocket-core/1.1.1/"))
+3 -3
View File
@@ -7,8 +7,8 @@ pluginManagement {
}
plugins {
id "com.gradle.develocity" version "3.17.2"
id "io.spring.ge.conventions" version "0.0.17"
id "com.gradle.enterprise" version "3.12.6"
id "io.spring.ge.conventions" version "0.0.13"
}
include "spring-aop"
@@ -45,7 +45,7 @@ rootProject.children.each {project ->
}
settings.gradle.projectsLoaded {
develocity {
gradleEnterprise {
buildScan {
File buildDir = settings.gradle.rootProject
.getLayout().getBuildDirectory().getAsFile().get()
@@ -169,30 +169,25 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
@Override
public ClassFilter getClassFilter() {
checkExpression();
obtainPointcutExpression();
return this;
}
@Override
public MethodMatcher getMethodMatcher() {
checkExpression();
obtainPointcutExpression();
return this;
}
/**
* Check whether this pointcut is ready to match.
* Check whether this pointcut is ready to match,
* lazily building the underlying AspectJ pointcut expression.
*/
private void checkExpression() {
private PointcutExpression obtainPointcutExpression() {
if (getExpression() == null) {
throw new IllegalStateException("Must set property 'expression' before attempting to match");
}
}
/**
* Lazily build the underlying AspectJ pointcut expression.
*/
private PointcutExpression obtainPointcutExpression() {
if (this.pointcutExpression == null) {
this.pointcutClassLoader = determinePointcutClassLoader();
this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader);
@@ -269,9 +264,10 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
@Override
public boolean matches(Class<?> targetClass) {
PointcutExpression pointcutExpression = obtainPointcutExpression();
try {
try {
return obtainPointcutExpression().couldMatchJoinPointsInType(targetClass);
return pointcutExpression.couldMatchJoinPointsInType(targetClass);
}
catch (ReflectionWorldException ex) {
logger.debug("PointcutExpression matching rejected target class - trying fallback expression", ex);
@@ -282,9 +278,6 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
}
}
}
catch (IllegalArgumentException | IllegalStateException ex) {
throw ex;
}
catch (Throwable ex) {
logger.debug("PointcutExpression matching rejected target class", ex);
}
@@ -293,6 +286,7 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
@Override
public boolean matches(Method method, Class<?> targetClass, boolean hasIntroductions) {
obtainPointcutExpression();
ShadowMatch shadowMatch = getTargetShadowMatch(method, targetClass);
// Special handling for this, target, @this, @target, @annotation
@@ -330,6 +324,7 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
obtainPointcutExpression();
ShadowMatch shadowMatch = getTargetShadowMatch(method, targetClass);
// Bind Spring AOP proxy to AspectJ "this" and Spring AOP target to AspectJ target,
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@ package org.springframework.aop.aspectj.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.StringTokenizer;
@@ -55,6 +56,8 @@ import org.springframework.lang.Nullable;
*/
public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFactory {
private static final String AJC_MAGIC = "ajc$";
private static final Class<?>[] ASPECTJ_ANNOTATION_CLASSES = new Class<?>[] {
Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class};
@@ -65,11 +68,37 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac
protected final ParameterNameDiscoverer parameterNameDiscoverer = new AspectJAnnotationParameterNameDiscoverer();
/**
* We consider something to be an AspectJ aspect suitable for use by the Spring AOP system
* if it has the @Aspect annotation, and was not compiled by ajc. The reason for this latter test
* is that aspects written in the code-style (AspectJ language) also have the annotation present
* when compiled by ajc with the -1.5 flag, yet they cannot be consumed by Spring AOP.
*/
@Override
public boolean isAspect(Class<?> clazz) {
return (hasAspectAnnotation(clazz) && !compiledByAjc(clazz));
}
private boolean hasAspectAnnotation(Class<?> clazz) {
return (AnnotationUtils.findAnnotation(clazz, Aspect.class) != null);
}
/**
* We need to detect this as "code-style" AspectJ aspects should not be
* interpreted by Spring AOP.
*/
private boolean compiledByAjc(Class<?> clazz) {
// The AJTypeSystem goes to great lengths to provide a uniform appearance between code-style and
// annotation-style aspects. Therefore there is no 'clean' way to tell them apart. Here we rely on
// an implementation detail of the AspectJ compiler.
for (Field field : clazz.getDeclaredFields()) {
if (field.getName().startsWith(AJC_MAGIC)) {
return true;
}
}
return false;
}
@Override
public void validate(Class<?> aspectClass) throws AopConfigException {
AjType<?> ajType = AjTypeSystem.getAjType(aspectClass);
@@ -86,7 +115,6 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac
}
}
/**
* Find and return the first AspectJ annotation on the given method
* (there <i>should</i> only be one anyway...).
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -124,16 +124,10 @@ public class AspectMetadata implements Serializable {
* Extract contents from String of form {@code pertarget(contents)}.
*/
private String findPerClause(Class<?> aspectClass) {
Aspect ann = aspectClass.getAnnotation(Aspect.class);
if (ann == null) {
return "";
}
String value = ann.value();
int beginIndex = value.indexOf('(');
if (beginIndex < 0) {
return "";
}
return value.substring(beginIndex + 1, value.length() - 1);
String str = aspectClass.getAnnotation(Aspect.class).value();
int beginIndex = str.indexOf('(') + 1;
int endIndex = str.length() - 1;
return str.substring(beginIndex, endIndex);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -368,7 +368,8 @@ public class AdvisedSupport extends ProxyConfig implements Advised {
private void validateIntroductionAdvisor(IntroductionAdvisor advisor) {
advisor.validateInterfaces();
// If the advisor passed validation, we can make the change.
for (Class<?> ifc : advisor.getInterfaces()) {
Class<?>[] ifcs = advisor.getInterfaces();
for (Class<?> ifc : ifcs) {
addInterface(ifc);
}
}
@@ -23,6 +23,8 @@ import java.util.Map;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.aspectj.weaver.tools.PointcutPrimitive;
import org.aspectj.weaver.tools.UnsupportedPointcutPrimitiveException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import test.annotation.EmptySpringAnnotation;
@@ -39,6 +41,7 @@ import org.springframework.beans.testfixture.beans.TestBean;
import org.springframework.beans.testfixture.beans.subpkg.DeepBean;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
@@ -171,25 +174,25 @@ public class AspectJExpressionPointcutTests {
@Test
public void testFriendlyErrorOnNoLocationClassMatching() {
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
assertThatIllegalStateException()
.isThrownBy(() -> pc.getClassFilter().matches(ITestBean.class))
.withMessageContaining("expression");
assertThatIllegalStateException().isThrownBy(() ->
pc.matches(ITestBean.class))
.withMessageContaining("expression");
}
@Test
public void testFriendlyErrorOnNoLocation2ArgMatching() {
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
assertThatIllegalStateException()
.isThrownBy(() -> pc.getMethodMatcher().matches(getAge, ITestBean.class))
.withMessageContaining("expression");
assertThatIllegalStateException().isThrownBy(() ->
pc.matches(getAge, ITestBean.class))
.withMessageContaining("expression");
}
@Test
public void testFriendlyErrorOnNoLocation3ArgMatching() {
AspectJExpressionPointcut pc = new AspectJExpressionPointcut();
assertThatIllegalStateException()
.isThrownBy(() -> pc.getMethodMatcher().matches(getAge, ITestBean.class, (Object[]) null))
.withMessageContaining("expression");
assertThatIllegalStateException().isThrownBy(() ->
pc.matches(getAge, ITestBean.class, (Object[]) null))
.withMessageContaining("expression");
}
@@ -206,10 +209,8 @@ public class AspectJExpressionPointcutTests {
// not currently testable in a reliable fashion
//assertDoesNotMatchStringClass(classFilter);
assertThat(methodMatcher.matches(setSomeNumber, TestBean.class, 12D))
.as("Should match with setSomeNumber with Double input").isTrue();
assertThat(methodMatcher.matches(setSomeNumber, TestBean.class, 11))
.as("Should not match setSomeNumber with Integer input").isFalse();
assertThat(methodMatcher.matches(setSomeNumber, TestBean.class, 12D)).as("Should match with setSomeNumber with Double input").isTrue();
assertThat(methodMatcher.matches(setSomeNumber, TestBean.class, 11)).as("Should not match setSomeNumber with Integer input").isFalse();
assertThat(methodMatcher.matches(getAge, TestBean.class)).as("Should not match getAge").isFalse();
assertThat(methodMatcher.isRuntime()).as("Should be a runtime match").isTrue();
}
@@ -244,7 +245,7 @@ public class AspectJExpressionPointcutTests {
@Test
public void testInvalidExpression() {
String expression = "execution(void org.springframework.beans.testfixture.beans.TestBean.setSomeNumber(Number) && args(Double)";
assertThatIllegalArgumentException().isThrownBy(() -> getPointcut(expression).getClassFilter().matches(Object.class));
assertThatIllegalArgumentException().isThrownBy(getPointcut(expression)::getClassFilter); // call to getClassFilter forces resolution
}
private TestBean getAdvisedProxy(String pointcutExpression, CallCountingInterceptor interceptor) {
@@ -274,7 +275,9 @@ public class AspectJExpressionPointcutTests {
@Test
public void testWithUnsupportedPointcutPrimitive() {
String expression = "call(int org.springframework.beans.testfixture.beans.TestBean.getAge())";
assertThat(getPointcut(expression).getClassFilter().matches(Object.class)).isFalse();
assertThatExceptionOfType(UnsupportedPointcutPrimitiveException.class)
.isThrownBy(() -> getPointcut(expression).getClassFilter()) // call to getClassFilter forces resolution...
.satisfies(ex -> assertThat(ex.getUnsupportedPrimitive()).isEqualTo(PointcutPrimitive.CALL));
}
@Test
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -818,11 +818,11 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
/**
* This implementation attempts to query the FactoryBean's generic parameter metadata
* if present to determine the object type. If not present, i.e. the FactoryBean is
* declared as a raw type, it checks the FactoryBean's {@code getObjectType} method
* declared as a raw type, checks the FactoryBean's {@code getObjectType} method
* on a plain instance of the FactoryBean, without bean properties applied yet.
* If this doesn't return a type yet and {@code allowInit} is {@code true}, full
* creation of the FactoryBean is attempted as fallback (through delegation to the
* superclass implementation).
* If this doesn't return a type yet, and {@code allowInit} is {@code true} a
* full creation of the FactoryBean is used as fallback (through delegation to the
* superclass's implementation).
* <p>The shortcut check for a FactoryBean is only applied in case of a singleton
* FactoryBean. If the FactoryBean instance itself is not kept as singleton,
* it will be fully created to check the type of its exposed object.
@@ -1053,7 +1053,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
@Override
public void setApplicationStartup(ApplicationStartup applicationStartup) {
Assert.notNull(applicationStartup, "ApplicationStartup must not be null");
Assert.notNull(applicationStartup, "applicationStartup must not be null");
this.applicationStartup = applicationStartup;
}
@@ -1649,7 +1649,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
* already. The implementation is allowed to instantiate the target factory bean if
* {@code allowInit} is {@code true} and the type cannot be determined another way;
* otherwise it is restricted to introspecting signatures and related metadata.
* <p>If no {@link FactoryBean#OBJECT_TYPE_ATTRIBUTE} is set on the bean definition
* <p>If no {@link FactoryBean#OBJECT_TYPE_ATTRIBUTE} if set on the bean definition
* and {@code allowInit} is {@code true}, the default implementation will create
* the FactoryBean via {@code getBean} to call its {@code getObjectType} method.
* Subclasses are encouraged to optimize this, typically by inspecting the generic
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,12 +22,11 @@ import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.aot.hint.TypeHint.Builder;
import org.springframework.aot.hint.TypeReference;
import org.springframework.aot.hint.annotation.ReflectiveRuntimeHintsRegistrar;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
/**
* {@link RuntimeHintsRegistrar} implementation that makes sure {@link SchedulerFactoryBean}
* reflection hints are registered.
* reflection entries are registered.
*
* @author Sebastien Deleuze
* @author Stephane Nicoll
@@ -37,11 +36,11 @@ class SchedulerFactoryBeanRuntimeHints implements RuntimeHintsRegistrar {
private static final String SCHEDULER_FACTORY_CLASS_NAME = "org.quartz.impl.StdSchedulerFactory";
private static final ReflectiveRuntimeHintsRegistrar registrar = new ReflectiveRuntimeHintsRegistrar();
private final ReflectiveRuntimeHintsRegistrar reflectiveRegistrar = new ReflectiveRuntimeHintsRegistrar();
@Override
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
if (!ClassUtils.isPresent(SCHEDULER_FACTORY_CLASS_NAME, classLoader)) {
return;
}
@@ -49,7 +48,7 @@ class SchedulerFactoryBeanRuntimeHints implements RuntimeHintsRegistrar {
.registerType(TypeReference.of(SCHEDULER_FACTORY_CLASS_NAME), this::typeHint)
.registerTypes(TypeReference.listOf(ResourceLoaderClassLoadHelper.class,
LocalTaskExecutorThreadPool.class, LocalDataSourceJobStore.class), this::typeHint);
registrar.registerRuntimeHints(hints, LocalTaskExecutorThreadPool.class);
this.reflectiveRegistrar.registerRuntimeHints(hints, LocalTaskExecutorThreadPool.class);
}
private void typeHint(Builder typeHint) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -225,6 +225,7 @@ class ConfigurationClassEnhancer {
};
return new TransformingClassGenerator(cg, transformer);
}
}
@@ -333,7 +334,6 @@ class ConfigurationClassEnhancer {
return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}
@Nullable
private Object resolveBeanReference(Method beanMethod, Object[] beanMethodArgs,
ConfigurableBeanFactory beanFactory, String beanName) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -386,11 +386,11 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
});
// Detect any custom bean name generation strategy supplied through the enclosing application context
SingletonBeanRegistry singletonRegistry = null;
if (registry instanceof SingletonBeanRegistry sbr) {
singletonRegistry = sbr;
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry _sbr) {
sbr = _sbr;
if (!this.localBeanNameGeneratorSet) {
BeanNameGenerator generator = (BeanNameGenerator) singletonRegistry.getSingleton(
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
if (generator != null) {
this.componentScanBeanNameGenerator = generator;
@@ -451,8 +451,8 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
while (!candidates.isEmpty());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
if (singletonRegistry != null && !singletonRegistry.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
singletonRegistry.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
// Store the PropertySourceDescriptors to contribute them Ahead-of-time if necessary
@@ -550,7 +550,6 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
}
@Override
@Nullable
public PropertyValues postProcessProperties(@Nullable PropertyValues pvs, Object bean, String beanName) {
// Inject the BeanFactory before AutowiredAnnotationBeanPostProcessor's
// postProcessProperties method attempts to autowire other configuration beans.
@@ -646,8 +645,8 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
}
return mappings;
}
}
}
private static class PropertySourcesAotContribution implements BeanFactoryInitializationAotContribution {
@@ -744,14 +743,15 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
return nonNull.get();
}
}
}
}
private static class ConfigurationClassProxyBeanRegistrationCodeFragments extends BeanRegistrationCodeFragmentsDecorator {
private final Class<?> proxyClass;
public ConfigurationClassProxyBeanRegistrationCodeFragments(BeanRegistrationCodeFragments codeFragments, Class<?> proxyClass) {
public ConfigurationClassProxyBeanRegistrationCodeFragments(BeanRegistrationCodeFragments codeFragments,
Class<?> proxyClass) {
super(codeFragments);
this.proxyClass = proxyClass;
}
@@ -759,7 +759,6 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
@Override
public CodeBlock generateSetBeanDefinitionPropertiesCode(GenerationContext generationContext,
BeanRegistrationCode beanRegistrationCode, RootBeanDefinition beanDefinition, Predicate<String> attributeFilter) {
CodeBlock.Builder code = CodeBlock.builder();
code.add(super.generateSetBeanDefinitionPropertiesCode(generationContext,
beanRegistrationCode, beanDefinition, attributeFilter));
@@ -772,7 +771,6 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
public CodeBlock generateInstanceSupplierCode(GenerationContext generationContext,
BeanRegistrationCode beanRegistrationCode, Executable constructorOrFactoryMethod,
boolean allowDirectSupplierShortcut) {
Executable executableToUse = proxyExecutable(generationContext.getRuntimeHints(), constructorOrFactoryMethod);
return super.generateInstanceSupplierCode(generationContext, beanRegistrationCode,
executableToUse, allowDirectSupplierShortcut);
@@ -790,6 +788,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
}
return userExecutable;
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,8 +39,7 @@ import org.springframework.beans.factory.support.RegisteredBean;
*/
class ReflectiveProcessorBeanFactoryInitializationAotProcessor implements BeanFactoryInitializationAotProcessor {
private static final ReflectiveRuntimeHintsRegistrar registrar = new ReflectiveRuntimeHintsRegistrar();
private static final ReflectiveRuntimeHintsRegistrar REGISTRAR = new ReflectiveRuntimeHintsRegistrar();
@Override
public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) {
@@ -50,9 +49,7 @@ class ReflectiveProcessorBeanFactoryInitializationAotProcessor implements BeanFa
return new ReflectiveProcessorBeanFactoryInitializationAotContribution(beanTypes);
}
private static class ReflectiveProcessorBeanFactoryInitializationAotContribution
implements BeanFactoryInitializationAotContribution {
private static class ReflectiveProcessorBeanFactoryInitializationAotContribution implements BeanFactoryInitializationAotContribution {
private final Class<?>[] types;
@@ -63,8 +60,9 @@ class ReflectiveProcessorBeanFactoryInitializationAotProcessor implements BeanFa
@Override
public void applyTo(GenerationContext generationContext, BeanFactoryInitializationCode beanFactoryInitializationCode) {
RuntimeHints runtimeHints = generationContext.getRuntimeHints();
registrar.registerRuntimeHints(runtimeHints, this.types);
REGISTRAR.registerRuntimeHints(runtimeHints, this.types);
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -126,7 +126,6 @@ public abstract class AbstractRefreshableApplicationContext extends AbstractAppl
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
beanFactory.setApplicationStartup(getApplicationStartup());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,7 +18,6 @@ package org.springframework.context.support;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
@@ -424,37 +423,16 @@ public class GenericApplicationContext extends AbstractApplicationContext implem
PostProcessorRegistrationDelegate.loadBeanPostProcessors(
this.beanFactory, SmartInstantiationAwareBeanPostProcessor.class);
List<String> lazyBeans = new ArrayList<>();
// First round: non-lazy singleton beans in definition order,
// matching preInstantiateSingletons.
for (String beanName : this.beanFactory.getBeanDefinitionNames()) {
BeanDefinition bd = getBeanDefinition(beanName);
if (bd.isSingleton() && !bd.isLazyInit()) {
preDetermineBeanType(beanName, bpps, runtimeHints);
}
else {
lazyBeans.add(beanName);
}
}
// Second round: lazy singleton beans and scoped beans.
for (String beanName : lazyBeans) {
preDetermineBeanType(beanName, bpps, runtimeHints);
}
}
private void preDetermineBeanType(String beanName, List<SmartInstantiationAwareBeanPostProcessor> bpps,
RuntimeHints runtimeHints) {
Class<?> beanType = this.beanFactory.getType(beanName);
if (beanType != null) {
ClassHintUtils.registerProxyIfNecessary(beanType, runtimeHints);
for (SmartInstantiationAwareBeanPostProcessor bpp : bpps) {
Class<?> newBeanType = bpp.determineBeanType(beanType, beanName);
if (newBeanType != beanType) {
ClassHintUtils.registerProxyIfNecessary(newBeanType, runtimeHints);
beanType = newBeanType;
Class<?> beanType = this.beanFactory.getType(beanName);
if (beanType != null) {
ClassHintUtils.registerProxyIfNecessary(beanType, runtimeHints);
for (SmartInstantiationAwareBeanPostProcessor bpp : bpps) {
Class<?> newBeanType = bpp.determineBeanType(beanType, beanName);
if (newBeanType != beanType) {
ClassHintUtils.registerProxyIfNecessary(newBeanType, runtimeHints);
beanType = newBeanType;
}
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -99,8 +99,8 @@ class ConfigurationClassPostProcessorAotContributionTests {
initializer.accept(freshBeanFactory);
freshContext.refresh();
assertThat(freshBeanFactory.getBeanPostProcessors()).filteredOn(ImportAwareAotBeanPostProcessor.class::isInstance)
.singleElement().satisfies(postProcessor ->
assertPostProcessorEntry(postProcessor, ImportAwareConfiguration.class, ImportConfiguration.class));
.singleElement().satisfies(postProcessor -> assertPostProcessorEntry(postProcessor, ImportAwareConfiguration.class,
ImportConfiguration.class));
freshContext.close();
});
}
@@ -117,8 +117,8 @@ class ConfigurationClassPostProcessorAotContributionTests {
freshContext.refresh();
TestAwareCallbackBean bean = freshContext.getBean(TestAwareCallbackBean.class);
assertThat(bean.instances).hasSize(2);
assertThat(bean.instances).element(0).isEqualTo(freshContext);
assertThat(bean.instances).element(1).isInstanceOfSatisfying(AnnotationMetadata.class, metadata ->
assertThat(bean.instances.get(0)).isEqualTo(freshContext);
assertThat(bean.instances.get(1)).isInstanceOfSatisfying(AnnotationMetadata.class, metadata ->
assertThat(metadata.getClassName()).isEqualTo(TestAwareCallbackConfiguration.class.getName()));
freshContext.close();
});
@@ -236,14 +236,13 @@ class ConfigurationClassPostProcessorAotContributionTests {
}
@Override
public void afterPropertiesSet() {
public void afterPropertiesSet() throws Exception {
Assert.notNull(this.metadata, "Metadata was not injected");
}
}
}
@Nested
class PropertySourceTests {
@@ -363,8 +362,8 @@ class ConfigurationClassPostProcessorAotContributionTests {
static class PropertySourceWithCustomFactoryConfiguration {
}
}
}
@Nested
class ConfigurationClassProxyTests {
@@ -385,13 +384,14 @@ class ConfigurationClassPostProcessorAotContributionTests {
getRegisteredBean(CglibConfiguration.class))).isNotNull();
}
private RegisteredBean getRegisteredBean(Class<?> bean) {
this.beanFactory.registerBeanDefinition("test", new RootBeanDefinition(bean));
this.processor.postProcessBeanFactory(this.beanFactory);
return RegisteredBean.of(this.beanFactory, "test");
}
}
}
@Nullable
private BeanFactoryInitializationAotContribution getContribution(Class<?>... types) {
@@ -410,8 +410,8 @@ class ConfigurationClassPostProcessorAotContributionTests {
.containsExactly(entry(key.getName(), value.getName()));
}
static class CustomPropertySourcesFactory extends DefaultPropertySourceFactory {
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,9 +29,7 @@ import org.springframework.beans.factory.support.AbstractBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.ResolvableType;
import org.springframework.core.type.AnnotationMetadata;
import static org.assertj.core.api.Assertions.assertThat;
@@ -41,62 +39,51 @@ import static org.assertj.core.api.Assertions.assertThat;
* {@link FactoryBean FactoryBeans} defined in the configuration.
*
* @author Phillip Webb
* @author Juergen Hoeller
*/
class ConfigurationWithFactoryBeanEarlyDeductionTests {
public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
@Test
void preFreezeDirect() {
public void preFreezeDirect() {
assertPreFreeze(DirectConfiguration.class);
}
@Test
void postFreezeDirect() {
public void postFreezeDirect() {
assertPostFreeze(DirectConfiguration.class);
}
@Test
void preFreezeGenericMethod() {
public void preFreezeGenericMethod() {
assertPreFreeze(GenericMethodConfiguration.class);
}
@Test
void postFreezeGenericMethod() {
public void postFreezeGenericMethod() {
assertPostFreeze(GenericMethodConfiguration.class);
}
@Test
void preFreezeGenericClass() {
public void preFreezeGenericClass() {
assertPreFreeze(GenericClassConfiguration.class);
}
@Test
void postFreezeGenericClass() {
public void postFreezeGenericClass() {
assertPostFreeze(GenericClassConfiguration.class);
}
@Test
void preFreezeAttribute() {
public void preFreezeAttribute() {
assertPreFreeze(AttributeClassConfiguration.class);
}
@Test
void postFreezeAttribute() {
public void postFreezeAttribute() {
assertPostFreeze(AttributeClassConfiguration.class);
}
@Test
void preFreezeTargetType() {
assertPreFreeze(TargetTypeConfiguration.class);
}
@Test
void postFreezeTargetType() {
assertPostFreeze(TargetTypeConfiguration.class);
}
@Test
void preFreezeUnresolvedGenericFactoryBean() {
public void preFreezeUnresolvedGenericFactoryBean() {
// Covers the case where a @Configuration is picked up via component scanning
// and its bean definition only has a String bean class. In such cases
// beanDefinition.hasBeanClass() returns false so we need to actually
@@ -118,13 +105,14 @@ class ConfigurationWithFactoryBeanEarlyDeductionTests {
}
}
private void assertPostFreeze(Class<?> configurationClass) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(configurationClass);
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
configurationClass);
assertContainsMyBeanName(context);
}
private void assertPreFreeze(Class<?> configurationClass, BeanFactoryPostProcessor... postProcessors) {
private void assertPreFreeze(Class<?> configurationClass,
BeanFactoryPostProcessor... postProcessors) {
NameCollectingBeanFactoryPostProcessor postProcessor = new NameCollectingBeanFactoryPostProcessor();
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
try (context) {
@@ -144,38 +132,41 @@ class ConfigurationWithFactoryBeanEarlyDeductionTests {
assertThat(names).containsExactly("myBean");
}
private static class NameCollectingBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
private static class NameCollectingBeanFactoryPostProcessor
implements BeanFactoryPostProcessor {
private String[] names;
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
ResolvableType typeToMatch = ResolvableType.forClassWithGenerics(MyBean.class, String.class);
this.names = beanFactory.getBeanNamesForType(typeToMatch, true, false);
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
this.names = beanFactory.getBeanNamesForType(MyBean.class, true, false);
}
public String[] getNames() {
return this.names;
}
}
@Configuration
static class DirectConfiguration {
@Bean
MyBean<String> myBean() {
return new MyBean<>();
MyBean myBean() {
return new MyBean();
}
}
@Configuration
static class GenericMethodConfiguration {
@Bean
FactoryBean<MyBean<String>> myBean() {
return new TestFactoryBean<>(new MyBean<>());
FactoryBean<MyBean> myBean() {
return new TestFactoryBean<>(new MyBean());
}
}
@Configuration
@@ -185,11 +176,13 @@ class ConfigurationWithFactoryBeanEarlyDeductionTests {
MyFactoryBean myBean() {
return new MyFactoryBean();
}
}
@Configuration
@Import(AttributeClassRegistrar.class)
static class AttributeClassConfiguration {
}
static class AttributeClassRegistrar implements ImportBeanDefinitionRegistrar {
@@ -198,32 +191,16 @@ class ConfigurationWithFactoryBeanEarlyDeductionTests {
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
BeanDefinition definition = BeanDefinitionBuilder.genericBeanDefinition(
RawWithAbstractObjectTypeFactoryBean.class).getBeanDefinition();
definition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE,
ResolvableType.forClassWithGenerics(MyBean.class, String.class));
definition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, MyBean.class);
registry.registerBeanDefinition("myBean", definition);
}
}
@Configuration
@Import(TargetTypeRegistrar.class)
static class TargetTypeConfiguration {
}
static class TargetTypeRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
RootBeanDefinition definition = new RootBeanDefinition(RawWithAbstractObjectTypeFactoryBean.class);
definition.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class,
ResolvableType.forClassWithGenerics(MyBean.class, String.class)));
registry.registerBeanDefinition("myBean", definition);
}
}
abstract static class AbstractMyBean {
}
static class MyBean<T> extends AbstractMyBean {
static class MyBean extends AbstractMyBean {
}
static class TestFactoryBean<T> implements FactoryBean<T> {
@@ -235,7 +212,7 @@ class ConfigurationWithFactoryBeanEarlyDeductionTests {
}
@Override
public T getObject() {
public T getObject() throws Exception {
return this.instance;
}
@@ -243,26 +220,31 @@ class ConfigurationWithFactoryBeanEarlyDeductionTests {
public Class<?> getObjectType() {
return this.instance.getClass();
}
}
static class MyFactoryBean extends TestFactoryBean<MyBean<String>> {
static class MyFactoryBean extends TestFactoryBean<MyBean> {
public MyFactoryBean() {
super(new MyBean<>());
super(new MyBean());
}
}
static class RawWithAbstractObjectTypeFactoryBean implements FactoryBean<Object> {
private final Object object = new MyBean();
@Override
public Object getObject() throws Exception {
throw new IllegalStateException();
return object;
}
@Override
public Class<?> getObjectType() {
return MyBean.class;
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,7 +39,8 @@ import org.springframework.core.annotation.AliasFor;
* @see ReflectiveRuntimeHintsRegistrar
* @see RegisterReflectionForBinding @RegisterReflectionForBinding
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD})
@Target({ ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.CONSTRUCTOR,
ElementType.FIELD, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Reflective {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,7 +36,7 @@ import org.springframework.core.annotation.AliasFor;
*
* <pre class="code">
* &#064;Configuration
* &#064;RegisterReflectionForBinding({Foo.class, Bar.class})
* &#064;RegisterReflectionForBinding({ Foo.class, Bar.class })
* public class MyConfig {
* // ...
* }</pre>
@@ -63,7 +63,7 @@ import org.springframework.core.annotation.AliasFor;
* @see org.springframework.aot.hint.BindingReflectionHintsRegistrar
* @see Reflective @Reflective
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Reflective(RegisterReflectionForBindingProcessor.class)
@@ -77,7 +77,8 @@ public @interface RegisterReflectionForBinding {
/**
* Classes for which reflection hints should be registered.
* <p>At least one class must be specified either via {@link #value} or {@code classes}.
* <p>At least one class must be specified either via {@link #value} or
* {@link #classes}.
* @see #value()
*/
@AliasFor("value")
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -47,11 +47,9 @@ class SpringFactoriesLoaderRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = (classLoader != null ? classLoader :
SpringFactoriesLoaderRuntimeHints.class.getClassLoader());
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
for (String resourceLocation : RESOURCE_LOCATIONS) {
registerHints(hints, classLoaderToUse, resourceLocation);
registerHints(hints, classLoader, resourceLocation);
}
}
@@ -65,7 +63,6 @@ class SpringFactoriesLoaderRuntimeHints implements RuntimeHintsRegistrar {
private void registerHints(RuntimeHints hints, ClassLoader classLoader,
String factoryClassName, List<String> implementationClassNames) {
Class<?> factoryClass = resolveClassName(classLoader, factoryClassName);
if (factoryClass == null) {
if (logger.isTraceEnabled()) {
@@ -103,7 +100,6 @@ class SpringFactoriesLoaderRuntimeHints implements RuntimeHintsRegistrar {
}
}
private static class ExtendedSpringFactoriesLoader extends SpringFactoriesLoader {
ExtendedSpringFactoriesLoader(@Nullable ClassLoader classLoader, Map<String, List<String>> factories) {
@@ -113,6 +109,7 @@ class SpringFactoriesLoaderRuntimeHints implements RuntimeHintsRegistrar {
static Map<String, List<String>> accessLoadFactoriesResource(ClassLoader classLoader, String resourceLocation) {
return SpringFactoriesLoader.loadFactoriesResource(classLoader, resourceLocation);
}
}
}
@@ -75,7 +75,7 @@ public class ReflectUtils {
Throwable throwable = null;
try {
classLoaderDefineClass = ClassLoader.class.getDeclaredMethod("defineClass",
String.class, byte[].class, Integer.TYPE, Integer.TYPE, ProtectionDomain.class);
String.class, byte[].class, Integer.TYPE, Integer.TYPE, ProtectionDomain.class);
}
catch (Throwable t) {
classLoaderDefineClass = null;
@@ -57,7 +57,7 @@ public class MethodProxy {
proxy.createInfo = new CreateInfo(c1, c2);
// SPRING PATCH BEGIN
if (c1 != Object.class && c1.isAssignableFrom(c2.getSuperclass()) && !Factory.class.isAssignableFrom(c2)) {
if (!c1.isInterface() && c1 != Object.class && !Factory.class.isAssignableFrom(c2)) {
// Try early initialization for overridden methods on specifically purposed subclasses
try {
proxy.init();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -418,10 +418,7 @@ final class TypeMappedAnnotations implements MergedAnnotations {
Annotation[] repeatedAnnotations = repeatableContainers.findRepeatedAnnotations(annotation);
if (repeatedAnnotations != null) {
MergedAnnotation<A> result = doWithAnnotations(type, aggregateIndex, source, repeatedAnnotations);
if (result != null) {
return result;
}
return doWithAnnotations(type, aggregateIndex, source, repeatedAnnotations);
}
AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(
annotation.annotationType(), repeatableContainers, annotationFilter);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -48,7 +48,7 @@ public class PropertiesPropertySource extends MapPropertySource {
@Override
public String[] getPropertyNames() {
synchronized (this.source) {
return ((Map<?, ?>) this.source).keySet().stream().filter(k -> k instanceof String).toArray(String[]::new);
return super.getPropertyNames();
}
}
@@ -71,18 +71,18 @@ import org.springframework.util.StringUtils;
/**
* A {@link ResourcePatternResolver} implementation that is able to resolve a
* specified resource location path into one or more matching Resources.
* The source path may be a simple path which has a one-to-one mapping to a
* target {@link org.springframework.core.io.Resource}, or alternatively
* may contain the special "{@code classpath*:}" prefix and/or
* internal Ant-style regular expressions (matched using Spring's
* {@link org.springframework.util.AntPathMatcher} utility).
* Both of the latter are effectively wildcards.
*
* <p>The source path may be a simple path which has a one-to-one mapping to a
* target {@link org.springframework.core.io.Resource}, or alternatively may
* contain the special "{@code classpath*:}" prefix and/or internal Ant-style
* path patterns (matched using Spring's {@link AntPathMatcher} utility). Both
* of the latter are effectively wildcards.
*
* <h3>No Wildcards</h3>
* <p><b>No Wildcards:</b>
*
* <p>In the simple case, if the specified location path does not start with the
* {@code "classpath*:}" prefix and does not contain a {@link PathMatcher}
* pattern, this resolver will simply return a single resource via a
* {@code "classpath*:}" prefix, and does not contain a PathMatcher pattern,
* this resolver will simply return a single resource via a
* {@code getResource()} call on the underlying {@code ResourceLoader}.
* Examples are real URLs such as "{@code file:C:/context.xml}", pseudo-URLs
* such as "{@code classpath:/context.xml}", and simple unprefixed paths
@@ -90,14 +90,14 @@ import org.springframework.util.StringUtils;
* fashion specific to the underlying {@code ResourceLoader} (e.g.
* {@code ServletContextResource} for a {@code WebApplicationContext}).
*
* <h3>Ant-style Patterns</h3>
* <p><b>Ant-style Patterns:</b>
*
* <p>When the path location contains an Ant-style pattern, for example:
* <p>When the path location contains an Ant-style pattern, e.g.:
* <pre class="code">
* /WEB-INF/*-context.xml
* com/example/**&#47;applicationContext.xml
* com/mycompany/**&#47;applicationContext.xml
* file:C:/some/path/*-context.xml
* classpath:com/example/**&#47;applicationContext.xml</pre>
* classpath:com/mycompany/**&#47;applicationContext.xml</pre>
* the resolver follows a more complex but defined procedure to try to resolve
* the wildcard. It produces a {@code Resource} for the path up to the last
* non-wildcard segment and obtains a {@code URL} from it. If this URL is not a
@@ -108,31 +108,31 @@ import org.springframework.util.StringUtils;
* {@code java.net.JarURLConnection} from it, or manually parses the jar URL, and
* then traverses the contents of the jar file, to resolve the wildcards.
*
* <h3>Implications on Portability</h3>
* <p><b>Implications on portability:</b>
*
* <p>If the specified path is already a file URL (either explicitly, or
* implicitly because the base {@code ResourceLoader} is a filesystem one),
* then wildcarding is guaranteed to work in a completely portable fashion.
*
* <p>If the specified path is a class path location, then the resolver must
* <p>If the specified path is a classpath location, then the resolver must
* obtain the last non-wildcard path segment URL via a
* {@code Classloader.getResource()} call. Since this is just a
* node of the path (not the file at the end) it is actually undefined
* (in the ClassLoader Javadocs) exactly what sort of URL is returned in
* this case. In practice, it is usually a {@code java.io.File} representing
* the directory, where the class path resource resolves to a filesystem
* location, or a jar URL of some sort, where the class path resource resolves
* the directory, where the classpath resource resolves to a filesystem
* location, or a jar URL of some sort, where the classpath resource resolves
* to a jar location. Still, there is a portability concern on this operation.
*
* <p>If a jar URL is obtained for the last non-wildcard segment, the resolver
* must be able to get a {@code java.net.JarURLConnection} from it, or
* manually parse the jar URL, to be able to walk the contents of the jar
* and resolve the wildcard. This will work in most environments but will
* manually parse the jar URL, to be able to walk the contents of the jar,
* and resolve the wildcard. This will work in most environments, but will
* fail in others, and it is strongly recommended that the wildcard
* resolution of resources coming from jars be thoroughly tested in your
* specific environment before you rely on it.
*
* <h3>{@code classpath*:} Prefix</h3>
* <p><b>{@code classpath*:} Prefix:</b>
*
* <p>There is special support for retrieving multiple class path resources with
* the same name, via the "{@code classpath*:}" prefix. For example,
@@ -142,22 +142,22 @@ import org.springframework.util.StringUtils;
* at the same location within each jar file. Internally, this happens via a
* {@code ClassLoader.getResources()} call, and is completely portable.
*
* <p>The "{@code classpath*:}" prefix can also be combined with a {@code PathMatcher}
* pattern in the rest of the location path &mdash; for example,
* "{@code classpath*:META-INF/*-beans.xml"}. In this case, the resolution strategy
* is fairly simple: a {@code ClassLoader.getResources()} call is used on the last
* non-wildcard path segment to get all the matching resources in the class loader
* hierarchy, and then off each resource the same {@code PathMatcher} resolution
* strategy described above is used for the wildcard sub pattern.
* <p>The "classpath*:" prefix can also be combined with a PathMatcher pattern in
* the rest of the location path, for example "classpath*:META-INF/*-beans.xml".
* In this case, the resolution strategy is fairly simple: a
* {@code ClassLoader.getResources()} call is used on the last non-wildcard
* path segment to get all the matching resources in the class loader hierarchy,
* and then off each resource the same PathMatcher resolution strategy described
* above is used for the wildcard sub pattern.
*
* <h3>Other Notes</h3>
* <p><b>Other notes:</b>
*
* <p>As of Spring Framework 6.0, if {@link #getResources(String)} is invoked with
* a location pattern using the "{@code classpath*:}" prefix it will first search
* <p>As of Spring Framework 6.0, if {@link #getResources(String)} is invoked
* with a location pattern using the "classpath*:" prefix it will first search
* all modules in the {@linkplain ModuleLayer#boot() boot layer}, excluding
* {@linkplain ModuleFinder#ofSystem() system modules}. It will then search the
* class path using {@link ClassLoader} APIs as described previously and return the
* combined results. Consequently, some of the limitations of class path searches
* classpath using {@link ClassLoader} APIs as described previously and return the
* combined results. Consequently, some of the limitations of classpath searches
* may not apply when applications are deployed as modules.
*
* <p><b>WARNING:</b> Note that "{@code classpath*:}" when combined with
@@ -168,26 +168,26 @@ import org.springframework.util.StringUtils;
* root of expanded directories. This originates from a limitation in the JDK's
* {@code ClassLoader.getResources()} method which only returns file system
* locations for a passed-in empty String (indicating potential roots to search).
* This {@code ResourcePatternResolver} implementation tries to mitigate the
* This {@code ResourcePatternResolver} implementation is trying to mitigate the
* jar root lookup limitation through {@link URLClassLoader} introspection and
* "{@code java.class.path}" manifest evaluation; however, without portability
* guarantees.
* "java.class.path" manifest evaluation; however, without portability guarantees.
*
* <p><b>WARNING:</b> Ant-style patterns with "{@code classpath:}" resources are not
* guaranteed to find matching resources if the base package to search is available
* <p><b>WARNING:</b> Ant-style patterns with "classpath:" resources are not
* guaranteed to find matching resources if the root package to search is available
* in multiple class path locations. This is because a resource such as
* <pre class="code">
* com/example/package1/service-context.xml</pre>
* may exist in only one class path location, but when a location pattern such as
* com/mycompany/package1/service-context.xml
* </pre>
* may be in only one location, but when a path such as
* <pre class="code">
* classpath:com/example/**&#47;service-context.xml</pre>
* classpath:com/mycompany/**&#47;service-context.xml
* </pre>
* is used to try to resolve it, the resolver will work off the (first) URL
* returned by {@code getResource("com/example")}. If the {@code com/example} base
* package node exists in multiple class path locations, the actual desired resource
* may not be present under the {@code com/example} base package in the first URL.
* Therefore, preferably, use "{@code classpath*:}" with the same Ant-style pattern
* in such a case, which will search <i>all</i> class path locations that contain
* the base package.
* returned by {@code getResource("com/mycompany");}. If this base package node
* exists in multiple classloader locations, the actual end resource may not be
* underneath. Therefore, preferably, use "{@code classpath*:}" with the same
* Ant-style pattern in such a case, which will search <i>all</i> class path
* locations that contain the root package.
*
* @author Juergen Hoeller
* @author Colin Sampaleanu
@@ -249,21 +249,19 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
/**
* Create a {@code PathMatchingResourcePatternResolver} with a
* {@link DefaultResourceLoader}.
* Create a new PathMatchingResourcePatternResolver with a DefaultResourceLoader.
* <p>ClassLoader access will happen via the thread context class loader.
* @see DefaultResourceLoader
* @see org.springframework.core.io.DefaultResourceLoader
*/
public PathMatchingResourcePatternResolver() {
this.resourceLoader = new DefaultResourceLoader();
}
/**
* Create a {@code PathMatchingResourcePatternResolver} with the supplied
* {@link ResourceLoader}.
* Create a new PathMatchingResourcePatternResolver.
* <p>ClassLoader access will happen via the thread context class loader.
* @param resourceLoader the {@code ResourceLoader} to load root directories
* and actual resources with
* @param resourceLoader the ResourceLoader to load root directories and
* actual resources with
*/
public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
Assert.notNull(resourceLoader, "ResourceLoader must not be null");
@@ -271,9 +269,8 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
}
/**
* Create a {@code PathMatchingResourcePatternResolver} with a
* {@link DefaultResourceLoader} and the supplied {@link ClassLoader}.
* @param classLoader the ClassLoader to load class path resources with,
* Create a new PathMatchingResourcePatternResolver with a DefaultResourceLoader.
* @param classLoader the ClassLoader to load classpath resources with,
* or {@code null} for using the thread context class loader
* at the time of actual resource access
* @see org.springframework.core.io.DefaultResourceLoader
@@ -284,7 +281,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
/**
* Return the {@link ResourceLoader} that this pattern resolver works with.
* Return the ResourceLoader that this pattern resolver works with.
*/
public ResourceLoader getResourceLoader() {
return this.resourceLoader;
@@ -297,10 +294,9 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
}
/**
* Set the {@link PathMatcher} implementation to use for this
* resource pattern resolver.
* <p>Default is {@link AntPathMatcher}.
* @see AntPathMatcher
* Set the PathMatcher implementation to use for this
* resource pattern resolver. Default is AntPathMatcher.
* @see org.springframework.util.AntPathMatcher
*/
public void setPathMatcher(PathMatcher pathMatcher) {
Assert.notNull(pathMatcher, "PathMatcher must not be null");
@@ -308,7 +304,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
}
/**
* Return the {@link PathMatcher} that this resource pattern resolver uses.
* Return the PathMatcher that this resource pattern resolver uses.
*/
public PathMatcher getPathMatcher() {
return this.pathMatcher;
@@ -357,8 +353,8 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
/**
* Find all class location resources with the given location via the ClassLoader.
* <p>Delegates to {@link #doFindAllClassPathResources(String)}.
* @param location the absolute path within the class path
* Delegates to {@link #doFindAllClassPathResources(String)}.
* @param location the absolute path within the classpath
* @return the result as Resource array
* @throws IOException in case of I/O errors
* @see java.lang.ClassLoader#getResources
@@ -368,16 +364,15 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
String path = stripLeadingSlash(location);
Set<Resource> result = doFindAllClassPathResources(path);
if (logger.isTraceEnabled()) {
logger.trace("Resolved class path location [" + path + "] to resources " + result);
logger.trace("Resolved classpath location [" + path + "] to resources " + result);
}
return result.toArray(new Resource[0]);
}
/**
* Find all class path resources with the given path via the configured
* {@link #getClassLoader() ClassLoader}.
* <p>Called by {@link #findAllClassPathResources(String)}.
* @param path the absolute path within the class path (never a leading slash)
* Find all class location resources with the given path via the ClassLoader.
* Called by {@link #findAllClassPathResources(String)}.
* @param path the absolute path within the classpath (never a leading slash)
* @return a mutable Set of matching Resource instances
* @since 4.1.1
*/
@@ -391,21 +386,20 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
}
if (!StringUtils.hasLength(path)) {
// The above result is likely to be incomplete, i.e. only containing file system references.
// We need to have pointers to each of the jar files on the class path as well...
// We need to have pointers to each of the jar files on the classpath as well...
addAllClassLoaderJarRoots(cl, result);
}
return result;
}
/**
* Convert the given URL as returned from the configured
* {@link #getClassLoader() ClassLoader} into a {@link Resource}, applying
* to path lookups without a pattern (see {@link #findAllClassPathResources}).
* Convert the given URL as returned from the ClassLoader into a {@link Resource},
* applying to path lookups without a pattern ({@link #findAllClassPathResources}).
* <p>As of 6.0.5, the default implementation creates a {@link FileSystemResource}
* in case of the "file" protocol or a {@link UrlResource} otherwise, matching
* the outcome of pattern-based class path traversal in the same resource layout,
* the outcome of pattern-based classpath traversal in the same resource layout,
* as well as matching the outcome of module path searches.
* @param url a URL as returned from the configured ClassLoader
* @param url a URL as returned from the ClassLoader
* @return the corresponding Resource object
* @see java.lang.ClassLoader#getResources
* @see #doFindAllClassPathResources
@@ -428,8 +422,8 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
}
/**
* Search all {@link URLClassLoader} URLs for jar file references and add each to the
* given set of resources in the form of a pointer to the root of the jar file content.
* Search all {@link URLClassLoader} URLs for jar file references and add them to the
* given set of resources in the form of pointers to the root of the jar file content.
* @param classLoader the ClassLoader to search (including its ancestors)
* @param result the set of resources to add jar roots to
* @since 4.1.1
@@ -463,7 +457,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
}
if (classLoader == ClassLoader.getSystemClassLoader()) {
// JAR "Class-Path" manifest header evaluation...
// "java.class.path" manifest evaluation...
addClassPathManifestEntries(result);
}
@@ -482,17 +476,16 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
}
/**
* Determine jar file references from {@code Class-Path} manifest entries (which
* are added to the {@code java.class.path} JVM system property by the system
* class loader) and add each to the given set of resources in the form of
* a pointer to the root of the jar file content.
* Determine jar file references from the "java.class.path." manifest property and add them
* to the given set of resources in the form of pointers to the root of the jar file content.
* @param result the set of resources to add jar roots to
* @since 4.3
*/
protected void addClassPathManifestEntries(Set<Resource> result) {
try {
String javaClassPathProperty = System.getProperty("java.class.path");
for (String path : StringUtils.delimitedListToStringArray(javaClassPathProperty, File.pathSeparator)) {
for (String path : StringUtils.delimitedListToStringArray(
javaClassPathProperty, System.getProperty("path.separator"))) {
try {
String filePath = new File(path).getAbsolutePath();
int prefixIndex = filePath.indexOf(':');
@@ -506,7 +499,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
// Build URL that points to the root of the jar file
UrlResource jarResource = new UrlResource(ResourceUtils.JAR_URL_PREFIX +
ResourceUtils.FILE_URL_PREFIX + filePath + ResourceUtils.JAR_URL_SEPARATOR);
// Potentially overlapping with URLClassLoader.getURLs() result in addAllClassLoaderJarRoots().
// Potentially overlapping with URLClassLoader.getURLs() result above!
if (!result.contains(jarResource) && !hasDuplicate(filePath, result) && jarResource.exists()) {
result.add(jarResource);
}
@@ -550,18 +543,14 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
}
/**
* Find all resources that match the given location pattern via the Ant-style
* {@link #getPathMatcher() PathMatcher}.
* <p>Supports resources in OSGi bundles, JBoss VFS, jar files, zip files,
* and file systems.
* Find all resources that match the given location pattern via the
* Ant-style PathMatcher. Supports resources in OSGi bundles, JBoss VFS,
* jar files, zip files, and file systems.
* @param locationPattern the location pattern to match
* @return the result as Resource array
* @throws IOException in case of I/O errors
* @see #determineRootDir(String)
* @see #resolveRootDirResource(Resource)
* @see #isJarResource(Resource)
* @see #doFindPathMatchingJarResources(Resource, URL, String)
* @see #doFindPathMatchingFileResources(Resource, String)
* @see #doFindPathMatchingJarResources
* @see #doFindPathMatchingFileResources
* @see org.springframework.util.PathMatcher
*/
protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
@@ -618,39 +607,29 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
}
/**
* Resolve the supplied root directory resource for path matching.
* <p>By default, {@link #findPathMatchingResources(String)} resolves Equinox
* OSGi "bundleresource:" and "bundleentry:" URLs into standard jar file URLs
* that will be traversed using Spring's standard jar file traversal algorithm.
* <p>For any custom resolution, override this template method and replace the
* supplied resource handle accordingly.
* <p>The default implementation of this method returns the supplied resource
* unmodified.
* Resolve the specified resource for path matching.
* <p>By default, Equinox OSGi "bundleresource:" / "bundleentry:" URL will be
* resolved into a standard jar file URL that be traversed using Spring's
* standard jar file traversal algorithm. For any preceding custom resolution,
* override this method and replace the resource handle accordingly.
* @param original the resource to resolve
* @return the resolved resource (may be identical to the supplied resource)
* @return the resolved resource (may be identical to the passed-in resource)
* @throws IOException in case of resolution failure
* @see #findPathMatchingResources(String)
*/
protected Resource resolveRootDirResource(Resource original) throws IOException {
return original;
}
/**
* Determine if the given resource handle indicates a jar resource that the
* {@link #doFindPathMatchingJarResources} method can handle.
* <p>{@link #findPathMatchingResources(String)} delegates to
* {@link ResourceUtils#isJarURL(URL)} to determine whether the given URL
* points to a resource in a jar file, and only invokes this method as a fallback.
* <p>This template method therefore allows for detecting further kinds of
* jar-like resources &mdash; for example, via {@code instanceof} checks on
* the resource handle type.
* <p>The default implementation of this method returns {@code false}.
* @param resource the resource handle to check (usually the root directory
* to start path matching from)
* @return {@code true} if the given resource handle indicates a jar resource
* @throws IOException in case of I/O errors
* @see #findPathMatchingResources(String)
* @see #doFindPathMatchingJarResources(Resource, URL, String)
* Return whether the given resource handle indicates a jar resource
* that the {@link #doFindPathMatchingJarResources} method can handle.
* <p>By default, the URL protocols "jar", "zip", "vfszip, and "wsjar"
* will be treated as jar resources. This template method allows for
* detecting further kinds of jar-like resources, e.g. through
* {@code instanceof} checks on the resource handle type.
* @param resource the resource handle to check
* (usually the root directory to start path matching from)
* @see #doFindPathMatchingJarResources
* @see org.springframework.util.ResourceUtils#isJarURL
*/
protected boolean isJarResource(Resource resource) throws IOException {
@@ -659,7 +638,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
/**
* Find all resources in jar files that match the given location pattern
* via the Ant-style {@link #getPathMatcher() PathMatcher}.
* via the Ant-style PathMatcher.
* @param rootDirResource the root directory as Resource
* @param rootDirUrl the pre-resolved root directory URL
* @param subPattern the sub pattern to match (below the root directory)
@@ -712,7 +691,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
}
catch (ZipException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Skipping invalid jar class path entry [" + urlFile + "]");
logger.debug("Skipping invalid jar classpath entry [" + urlFile + "]");
}
return Collections.emptySet();
}
@@ -767,8 +746,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
/**
* Find all resources in the file system of the supplied root directory that
* match the given location sub pattern via the Ant-style {@link #getPathMatcher()
* PathMatcher}.
* match the given location sub pattern via the Ant-style PathMatcher.
* @param rootDirResource the root directory as a Resource
* @param subPattern the sub pattern to match (below the root directory)
* @return a mutable Set of matching Resource instances
@@ -946,8 +924,8 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
}
/**
* If it's a "file:" URI, use {@link FileSystemResource} to avoid duplicates
* for the same path discovered via class path scanning.
* If it's a "file:" URI, use FileSystemResource to avoid duplicates
* for the same path discovered via class-path scanning.
*/
private Resource convertModuleSystemURI(URI uri) {
return (ResourceUtils.URL_PROTOCOL_FILE.equals(uri.getScheme()) ?
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -59,7 +59,7 @@ public class MultiValueMapAdapter<K, V> implements MultiValueMap<K, V>, Serializ
@Nullable
public V getFirst(K key) {
List<V> values = this.targetMap.get(key);
return (!CollectionUtils.isEmpty(values) ? values.get(0) : null);
return (values != null && !values.isEmpty() ? values.get(0) : null);
}
@Override
@@ -95,7 +95,7 @@ public class MultiValueMapAdapter<K, V> implements MultiValueMap<K, V>, Serializ
public Map<K, V> toSingleValueMap() {
Map<K, V> singleValueMap = CollectionUtils.newLinkedHashMap(this.targetMap.size());
this.targetMap.forEach((key, values) -> {
if (!CollectionUtils.isEmpty(values)) {
if (values != null && !values.isEmpty()) {
singleValueMap.put(key, values.get(0));
}
});
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -100,7 +100,6 @@ public abstract class ResourceUtils {
* @return whether the location qualifies as a URL
* @see #CLASSPATH_URL_PREFIX
* @see java.net.URL
* @see #toURL(String)
*/
public static boolean isUrl(@Nullable String resourceLocation) {
if (resourceLocation == null) {
@@ -126,7 +125,6 @@ public abstract class ResourceUtils {
* "classpath:" pseudo URL, a "file:" URL, or a plain file path
* @return a corresponding URL object
* @throws FileNotFoundException if the resource cannot be resolved to a URL
* @see #toURL(String)
*/
public static URL getURL(String resourceLocation) throws FileNotFoundException {
Assert.notNull(resourceLocation, "Resource location must not be null");
@@ -167,7 +165,6 @@ public abstract class ResourceUtils {
* @return a corresponding File object
* @throws FileNotFoundException if the resource cannot be resolved to
* a file in the file system
* @see #getFile(URL)
*/
public static File getFile(String resourceLocation) throws FileNotFoundException {
Assert.notNull(resourceLocation, "Resource location must not be null");
@@ -199,7 +196,6 @@ public abstract class ResourceUtils {
* @return a corresponding File object
* @throws FileNotFoundException if the URL cannot be resolved to
* a file in the file system
* @see #getFile(URL, String)
*/
public static File getFile(URL resourceUrl) throws FileNotFoundException {
return getFile(resourceUrl, "URL");
@@ -240,7 +236,6 @@ public abstract class ResourceUtils {
* @throws FileNotFoundException if the URL cannot be resolved to
* a file in the file system
* @since 2.5
* @see #getFile(URI, String)
*/
public static File getFile(URI resourceUri) throws FileNotFoundException {
return getFile(resourceUri, "URI");
@@ -272,7 +267,6 @@ public abstract class ResourceUtils {
* i.e. has protocol "file", "vfsfile" or "vfs".
* @param url the URL to check
* @return whether the URL has been identified as a file system URL
* @see #isJarURL(URL)
*/
public static boolean isFileURL(URL url) {
String protocol = url.getProtocol();
@@ -281,12 +275,10 @@ public abstract class ResourceUtils {
}
/**
* Determine whether the given URL points to a resource in a jar file
* &mdash; for example, whether the URL has protocol "jar", "war, "zip",
* "vfszip", or "wsjar".
* Determine whether the given URL points to a resource in a jar file.
* i.e. has protocol "jar", "war, ""zip", "vfszip" or "wsjar".
* @param url the URL to check
* @return whether the URL has been identified as a JAR URL
* @see #isJarFileURL(URL)
*/
public static boolean isJarURL(URL url) {
String protocol = url.getProtocol();
@@ -301,7 +293,6 @@ public abstract class ResourceUtils {
* @param url the URL to check
* @return whether the URL has been identified as a JAR file URL
* @since 4.1
* @see #extractJarFileURL(URL)
*/
public static boolean isJarFileURL(URL url) {
return (URL_PROTOCOL_FILE.equals(url.getProtocol()) &&
@@ -314,7 +305,6 @@ public abstract class ResourceUtils {
* @param jarUrl the original URL
* @return the URL for the actual jar file
* @throws MalformedURLException if no valid jar file URL could be extracted
* @see #extractArchiveURL(URL)
*/
public static URL extractJarFileURL(URL jarUrl) throws MalformedURLException {
String urlFile = jarUrl.getFile();
@@ -376,7 +366,6 @@ public abstract class ResourceUtils {
* @return the URI instance
* @throws URISyntaxException if the URL wasn't a valid URI
* @see java.net.URL#toURI()
* @see #toURI(String)
*/
public static URI toURI(URL url) throws URISyntaxException {
return toURI(url.toString());
@@ -388,7 +377,6 @@ public abstract class ResourceUtils {
* @param location the location String to convert into a URI instance
* @return the URI instance
* @throws URISyntaxException if the location wasn't a valid URI
* @see #toURI(URL)
*/
public static URI toURI(String location) throws URISyntaxException {
return new URI(StringUtils.replace(location, " ", "%20"));
@@ -401,7 +389,6 @@ public abstract class ResourceUtils {
* @return the URL instance
* @throws MalformedURLException if the location wasn't a valid URL
* @since 6.0
* @see java.net.URL#URL(String)
*/
public static URL toURL(String location) throws MalformedURLException {
// Equivalent without java.net.URL constructor - for building on JDK 20+
@@ -427,7 +414,6 @@ public abstract class ResourceUtils {
* @return the relative URL instance
* @throws MalformedURLException if the end result is not a valid URL
* @since 6.0
* @see java.net.URL#URL(URL, String)
*/
public static URL toRelativeURL(URL root, String relativePath) throws MalformedURLException {
// # can appear in filenames, java.net.URL should not treat it as a fragment
@@ -443,10 +429,9 @@ public abstract class ResourceUtils {
/**
* Set the {@link URLConnection#setUseCaches "useCaches"} flag on the
* given connection, preferring {@code false} but leaving the flag at
* {@code true} for JNLP based resources.
* given connection, preferring {@code false} but leaving the
* flag at {@code true} for JNLP based resources.
* @param con the URLConnection to set the flag on
* @see URLConnection#setUseCaches
*/
public static void useCachesIfNecessary(URLConnection con) {
con.setUseCaches(con.getClass().getSimpleName().startsWith("JNLP"));
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -123,7 +123,7 @@ public abstract class StringUtils {
* @see #hasText(CharSequence)
*/
public static boolean hasLength(@Nullable CharSequence str) {
return (str != null && !str.isEmpty()); // as of JDK 15
return (str != null && str.length() > 0);
}
/**
@@ -791,7 +791,6 @@ public abstract class StringUtils {
* and {@code "0"} through {@code "9"} stay the same.</li>
* <li>Special characters {@code "-"}, {@code "_"}, {@code "."}, and {@code "*"} stay the same.</li>
* <li>A sequence "{@code %<i>xy</i>}" is interpreted as a hexadecimal representation of the character.</li>
* <li>For all other characters (including those already decoded), the output is undefined.</li>
* </ul>
* @param source the encoded String
* @param charset the character set
@@ -840,7 +839,7 @@ public abstract class StringUtils {
* the {@link Locale#toString} format as well as BCP 47 language tags as
* specified by {@link Locale#forLanguageTag}.
* @param localeValue the locale value: following either {@code Locale's}
* {@code toString()} format ("en", "en_UK", etc.), also accepting spaces as
* {@code toString()} format ("en", "en_UK", etc), also accepting spaces as
* separators (as an alternative to underscores), or BCP 47 (e.g. "en-UK")
* @return a corresponding {@code Locale} instance, or {@code null} if none
* @throws IllegalArgumentException in case of an invalid locale specification
@@ -853,7 +852,7 @@ public abstract class StringUtils {
if (!localeValue.contains("_") && !localeValue.contains(" ")) {
validateLocalePart(localeValue);
Locale resolved = Locale.forLanguageTag(localeValue);
if (!resolved.getLanguage().isEmpty()) {
if (resolved.getLanguage().length() > 0) {
return resolved;
}
}
@@ -869,7 +868,7 @@ public abstract class StringUtils {
* <p><b>Note: This delegate does not accept the BCP 47 language tag format.
* Please use {@link #parseLocale} for lenient parsing of both formats.</b>
* @param localeString the locale {@code String}: following {@code Locale's}
* {@code toString()} format ("en", "en_UK", etc.), also accepting spaces as
* {@code toString()} format ("en", "en_UK", etc), also accepting spaces as
* separators (as an alternative to underscores)
* @return a corresponding {@code Locale} instance, or {@code null} if none
* @throws IllegalArgumentException in case of an invalid locale specification
@@ -877,7 +876,7 @@ public abstract class StringUtils {
@SuppressWarnings("deprecation") // for Locale constructors on JDK 19
@Nullable
public static Locale parseLocaleString(String localeString) {
if (localeString.isEmpty()) {
if (localeString.equals("")) {
return null;
}
@@ -1182,7 +1181,7 @@ public abstract class StringUtils {
if (trimTokens) {
token = token.trim();
}
if (!ignoreEmptyTokens || !token.isEmpty()) {
if (!ignoreEmptyTokens || token.length() > 0) {
tokens.add(token);
}
}
@@ -1244,7 +1243,7 @@ public abstract class StringUtils {
result.add(deleteAny(str.substring(pos, delPos), charsToDelete));
pos = delPos + delimiter.length();
}
if (!str.isEmpty() && pos <= str.length()) {
if (str.length() > 0 && pos <= str.length()) {
// Add rest of String, but not in case of empty input.
result.add(deleteAny(str.substring(pos), charsToDelete));
}
@@ -24,7 +24,6 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.AnnotatedElement;
import java.util.Arrays;
import java.util.Set;
import java.util.stream.Stream;
@@ -176,7 +175,7 @@ class MergedAnnotationsRepeatableAnnotationTests {
}
@Test
void typeHierarchyWhenOnSuperclassReturnsAnnotations() {
void typeHierarchyWhenWhenOnSuperclassReturnsAnnotations() {
Set<PeteRepeat> annotations = getAnnotations(null, PeteRepeat.class,
SearchStrategy.TYPE_HIERARCHY, SubRepeatableClass.class);
assertThat(annotations.stream().map(PeteRepeat::value)).containsExactly("A", "B",
@@ -241,44 +240,6 @@ class MergedAnnotationsRepeatableAnnotationTests {
assertThat(annotationTypes).containsExactly(WithRepeatedMetaAnnotations.class, Noninherited.class, Noninherited.class);
}
@Test // gh-32731
void searchFindsRepeatableContainerAnnotationAndRepeatedAnnotations() {
Class<?> clazz = StandardRepeatablesWithContainerWithMultipleAttributesTestCase.class;
// NO RepeatableContainers
MergedAnnotations mergedAnnotations = MergedAnnotations.from(clazz, SearchStrategy.TYPE_HIERARCHY, RepeatableContainers.none());
ContainerWithMultipleAttributes container = mergedAnnotations
.get(ContainerWithMultipleAttributes.class)
.synthesize(MergedAnnotation::isPresent).orElse(null);
assertThat(container).as("container").isNotNull();
assertThat(container.name()).isEqualTo("enigma");
RepeatableWithContainerWithMultipleAttributes[] repeatedAnnotations = container.value();
assertThat(Arrays.stream(repeatedAnnotations).map(RepeatableWithContainerWithMultipleAttributes::value))
.containsExactly("A", "B");
Set<RepeatableWithContainerWithMultipleAttributes> set =
mergedAnnotations.stream(RepeatableWithContainerWithMultipleAttributes.class)
.collect(MergedAnnotationCollectors.toAnnotationSet());
// Only finds the locally declared repeated annotation.
assertThat(set.stream().map(RepeatableWithContainerWithMultipleAttributes::value))
.containsExactly("C");
// Standard RepeatableContainers
mergedAnnotations = MergedAnnotations.from(clazz, SearchStrategy.TYPE_HIERARCHY, RepeatableContainers.standardRepeatables());
container = mergedAnnotations
.get(ContainerWithMultipleAttributes.class)
.synthesize(MergedAnnotation::isPresent).orElse(null);
assertThat(container).as("container").isNotNull();
assertThat(container.name()).isEqualTo("enigma");
repeatedAnnotations = container.value();
assertThat(Arrays.stream(repeatedAnnotations).map(RepeatableWithContainerWithMultipleAttributes::value))
.containsExactly("A", "B");
set = mergedAnnotations.stream(RepeatableWithContainerWithMultipleAttributes.class)
.collect(MergedAnnotationCollectors.toAnnotationSet());
// Finds the locally declared repeated annotation plus the 2 in the container.
assertThat(set.stream().map(RepeatableWithContainerWithMultipleAttributes::value))
.containsExactly("A", "B", "C");
}
private <A extends Annotation> Set<A> getAnnotations(Class<? extends Annotation> container,
Class<A> repeatable, SearchStrategy searchStrategy, AnnotatedElement element) {
@@ -488,27 +449,4 @@ class MergedAnnotationsRepeatableAnnotationTests {
static class WithRepeatedMetaAnnotationsClass {
}
@Retention(RetentionPolicy.RUNTIME)
@interface ContainerWithMultipleAttributes {
RepeatableWithContainerWithMultipleAttributes[] value();
String name() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(ContainerWithMultipleAttributes.class)
@interface RepeatableWithContainerWithMultipleAttributes {
String value() default "";
}
@ContainerWithMultipleAttributes(name = "enigma", value = {
@RepeatableWithContainerWithMultipleAttributes("A"),
@RepeatableWithContainerWithMultipleAttributes("B")
})
@RepeatableWithContainerWithMultipleAttributes("C")
static class StandardRepeatablesWithContainerWithMultipleAttributesTestCase {
}
}
@@ -16,9 +16,7 @@
package org.springframework.core.env;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
@@ -302,12 +300,6 @@ class StandardEnvironmentTests {
assertThat(systemProperties.get(DISALLOWED_PROPERTY_NAME)).isEqualTo(DISALLOWED_PROPERTY_VALUE);
assertThat(systemProperties.get(STRING_PROPERTY_NAME)).isEqualTo(NON_STRING_PROPERTY_VALUE);
assertThat(systemProperties.get(NON_STRING_PROPERTY_NAME)).isEqualTo(STRING_PROPERTY_VALUE);
PropertiesPropertySource systemPropertySource = (PropertiesPropertySource)
environment.getPropertySources().get(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME);
Set<String> expectedKeys = new HashSet<>(System.getProperties().stringPropertyNames());
expectedKeys.add(STRING_PROPERTY_NAME); // filtered out by stringPropertyNames due to non-String value
assertThat(Set.of(systemPropertySource.getPropertyNames())).isEqualTo(expectedKeys);
}
finally {
System.clearProperty(ALLOWED_PROPERTY_NAME);
@@ -324,7 +316,6 @@ class StandardEnvironmentTests {
assertThat(System.getenv()).isSameAs(systemEnvironment);
}
@Nested
class GetActiveProfiles {
@@ -374,7 +365,6 @@ class StandardEnvironmentTests {
}
}
@Nested
class AcceptsProfilesTests {
@@ -457,8 +447,8 @@ class StandardEnvironmentTests {
environment.addActiveProfile("p2");
assertThat(environment.acceptsProfiles(Profiles.of("p1 & p2"))).isTrue();
}
}
}
@Nested
class MatchesProfilesTests {
@@ -569,6 +559,7 @@ class StandardEnvironmentTests {
assertThat(environment.matchesProfiles("p2 & (foo | p1)")).isTrue();
assertThat(environment.matchesProfiles("foo", "(p2 & p1)")).isTrue();
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author 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,6 @@ class StreamUtilsTests {
private String string = "";
@BeforeEach
void setup() {
new Random().nextBytes(bytes);
@@ -54,7 +53,6 @@ class StreamUtilsTests {
}
}
@Test
void copyToByteArray() throws Exception {
InputStream inputStream = new ByteArrayInputStream(bytes);
@@ -129,5 +127,4 @@ class StreamUtilsTests {
ordered.verify(source).write(bytes, 1, 2);
ordered.verify(source, never()).close();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,12 +21,11 @@ import java.util.Collections;
import org.springframework.aot.hint.ExecutableMode;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.lang.Nullable;
/**
* {@link RuntimeHintsRegistrar} implementation that registers reflection hints
* for {@code EmbeddedDataSourceProxy#shutdown} in order to allow it to be used
* as a bean destroy method.
* {@link RuntimeHintsRegistrar} implementation that registers reflection hints for
* {@code EmbeddedDataSourceProxy#shutdown} in order to allow it to be used as a bean
* destroy method.
*
* @author Sebastien Deleuze
* @since 6.0
@@ -34,7 +33,7 @@ import org.springframework.lang.Nullable;
class EmbeddedDatabaseFactoryRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
hints.reflection().registerTypeIfPresent(classLoader,
"org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactory$EmbeddedDataSourceProxy",
builder -> builder
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,7 +32,7 @@ import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
/**
* The common implementation of Spring's {@link SqlRowSet} interface, wrapping a
* The default implementation of Spring's {@link SqlRowSet} interface, wrapping a
* {@link java.sql.ResultSet}, catching any {@link SQLException SQLExceptions} and
* translating them to a corresponding Spring {@link InvalidResultSetAccessException}.
*
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,168 +37,167 @@ import static org.mockito.Mockito.mock;
/**
* @author Thomas Risberg
*/
class ResultSetWrappingRowSetTests {
public class ResultSetWrappingRowSetTests {
private final ResultSet resultSet = mock();
private ResultSet resultSet = mock();
private final ResultSetWrappingSqlRowSet rowSet = new ResultSetWrappingSqlRowSet(resultSet);
private ResultSetWrappingSqlRowSet rowSet = new ResultSetWrappingSqlRowSet(resultSet);
@Test
void testGetBigDecimalInt() throws Exception {
public void testGetBigDecimalInt() throws Exception {
Method rset = ResultSet.class.getDeclaredMethod("getBigDecimal", int.class);
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getBigDecimal", int.class);
doTest(rset, rowset, 1, BigDecimal.ONE);
}
@Test
void testGetBigDecimalString() throws Exception {
public void testGetBigDecimalString() throws Exception {
Method rset = ResultSet.class.getDeclaredMethod("getBigDecimal", int.class);
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getBigDecimal", String.class);
doTest(rset, rowset, "test", BigDecimal.ONE);
}
@Test
void testGetStringInt() throws Exception {
public void testGetStringInt() throws Exception {
Method rset = ResultSet.class.getDeclaredMethod("getString", int.class);
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getString", int.class);
doTest(rset, rowset, 1, "test");
}
@Test
void testGetStringString() throws Exception {
public void testGetStringString() throws Exception {
Method rset = ResultSet.class.getDeclaredMethod("getString", int.class);
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getString", String.class);
doTest(rset, rowset, "test", "test");
}
@Test
void testGetTimestampInt() throws Exception {
public void testGetTimestampInt() throws Exception {
Method rset = ResultSet.class.getDeclaredMethod("getTimestamp", int.class);
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getTimestamp", int.class);
doTest(rset, rowset, 1, new Timestamp(1234L));
}
@Test
void testGetTimestampString() throws Exception {
public void testGetTimestampString() throws Exception {
Method rset = ResultSet.class.getDeclaredMethod("getTimestamp", int.class);
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getTimestamp", String.class);
doTest(rset, rowset, "test", new Timestamp(1234L));
}
@Test
void testGetDateInt() throws Exception {
public void testGetDateInt() throws Exception {
Method rset = ResultSet.class.getDeclaredMethod("getDate", int.class);
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getDate", int.class);
doTest(rset, rowset, 1, new Date(1234L));
}
@Test
void testGetDateString() throws Exception {
public void testGetDateString() throws Exception {
Method rset = ResultSet.class.getDeclaredMethod("getDate", int.class);
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getDate", String.class);
doTest(rset, rowset, "test", new Date(1234L));
}
@Test
void testGetTimeInt() throws Exception {
public void testGetTimeInt() throws Exception {
Method rset = ResultSet.class.getDeclaredMethod("getTime", int.class);
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getTime", int.class);
doTest(rset, rowset, 1, new Time(1234L));
}
@Test
void testGetTimeString() throws Exception {
public void testGetTimeString() throws Exception {
Method rset = ResultSet.class.getDeclaredMethod("getTime", int.class);
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getTime", String.class);
doTest(rset, rowset, "test", new Time(1234L));
}
@Test
void testGetObjectInt() throws Exception {
public void testGetObjectInt() throws Exception {
Method rset = ResultSet.class.getDeclaredMethod("getObject", int.class);
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getObject", int.class);
doTest(rset, rowset, 1, new Object());
}
@Test
void testGetObjectString() throws Exception {
public void testGetObjectString() throws Exception {
Method rset = ResultSet.class.getDeclaredMethod("getObject", int.class);
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getObject", String.class);
doTest(rset, rowset, "test", new Object());
}
@Test
void testGetIntInt() throws Exception {
public void testGetIntInt() throws Exception {
Method rset = ResultSet.class.getDeclaredMethod("getInt", int.class);
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getInt", int.class);
doTest(rset, rowset, 1, 1);
}
@Test
void testGetIntString() throws Exception {
public void testGetIntString() throws Exception {
Method rset = ResultSet.class.getDeclaredMethod("getInt", int.class);
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getInt", String.class);
doTest(rset, rowset, "test", 1);
}
@Test
void testGetFloatInt() throws Exception {
public void testGetFloatInt() throws Exception {
Method rset = ResultSet.class.getDeclaredMethod("getFloat", int.class);
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getFloat", int.class);
doTest(rset, rowset, 1, 1.0f);
}
@Test
void testGetFloatString() throws Exception {
public void testGetFloatString() throws Exception {
Method rset = ResultSet.class.getDeclaredMethod("getFloat", int.class);
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getFloat", String.class);
doTest(rset, rowset, "test", 1.0f);
}
@Test
void testGetDoubleInt() throws Exception {
public void testGetDoubleInt() throws Exception {
Method rset = ResultSet.class.getDeclaredMethod("getDouble", int.class);
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getDouble", int.class);
doTest(rset, rowset, 1, 1.0d);
}
@Test
void testGetDoubleString() throws Exception {
public void testGetDoubleString() throws Exception {
Method rset = ResultSet.class.getDeclaredMethod("getDouble", int.class);
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getDouble", String.class);
doTest(rset, rowset, "test", 1.0d);
}
@Test
void testGetLongInt() throws Exception {
public void testGetLongInt() throws Exception {
Method rset = ResultSet.class.getDeclaredMethod("getLong", int.class);
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getLong", int.class);
doTest(rset, rowset, 1, 1L);
}
@Test
void testGetLongString() throws Exception {
public void testGetLongString() throws Exception {
Method rset = ResultSet.class.getDeclaredMethod("getLong", int.class);
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getLong", String.class);
doTest(rset, rowset, "test", 1L);
}
@Test
void testGetBooleanInt() throws Exception {
public void testGetBooleanInt() throws Exception {
Method rset = ResultSet.class.getDeclaredMethod("getBoolean", int.class);
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getBoolean", int.class);
doTest(rset, rowset, 1, true);
}
@Test
void testGetBooleanString() throws Exception {
public void testGetBooleanString() throws Exception {
Method rset = ResultSet.class.getDeclaredMethod("getBoolean", int.class);
Method rowset = ResultSetWrappingSqlRowSet.class.getDeclaredMethod("getBoolean", String.class);
doTest(rset, rowset, "test", true);
}
private void doTest(Method rsetMethod, Method rowsetMethod, Object arg, Object ret) throws Exception {
if (arg instanceof String) {
given(resultSet.findColumn((String) arg)).willReturn(1);
@@ -208,9 +207,9 @@ class ResultSetWrappingRowSetTests {
given(rsetMethod.invoke(resultSet, arg)).willReturn(ret).willThrow(new SQLException("test"));
}
rowsetMethod.invoke(rowSet, arg);
assertThatExceptionOfType(InvocationTargetException.class)
.isThrownBy(() -> rowsetMethod.invoke(rowSet, arg))
.satisfies(ex -> assertThat(ex.getTargetException()).isExactlyInstanceOf(InvalidResultSetAccessException.class));
assertThatExceptionOfType(InvocationTargetException.class).isThrownBy(() ->
rowsetMethod.invoke(rowSet, arg)).
satisfies(ex -> assertThat(ex.getTargetException()).isExactlyInstanceOf(InvalidResultSetAccessException.class));
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -189,6 +189,11 @@ public class JmsMessagingTemplate extends AbstractMessagingTemplate<Destination>
}
}
@Override
public void convertAndSend(Object payload) throws MessagingException {
convertAndSend(payload, null);
}
@Override
public void convertAndSend(Object payload, @Nullable MessagePostProcessor postProcessor) throws MessagingException {
Destination defaultDestination = getDefaultDestination();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -290,7 +290,7 @@ public interface JmsOperations {
* <p>This method should be used carefully, since it will block the thread
* until the message becomes available or until the timeout value is exceeded.
* <p>This will only work with a default destination specified!
* @return the message produced for the consumer, or {@code null} if the timeout expires
* @return the message produced for the consumer or {@code null} if the timeout expires.
* @throws JmsException checked JMSException converted to unchecked
*/
@Nullable
@@ -303,7 +303,7 @@ public interface JmsOperations {
* <p>This method should be used carefully, since it will block the thread
* until the message becomes available or until the timeout value is exceeded.
* @param destination the destination to receive a message from
* @return the message produced for the consumer, or {@code null} if the timeout expires
* @return the message produced for the consumer or {@code null} if the timeout expires.
* @throws JmsException checked JMSException converted to unchecked
*/
@Nullable
@@ -317,7 +317,7 @@ public interface JmsOperations {
* until the message becomes available or until the timeout value is exceeded.
* @param destinationName the name of the destination to send this message to
* (to be resolved to an actual destination by a DestinationResolver)
* @return the message produced for the consumer, or {@code null} if the timeout expires
* @return the message produced for the consumer or {@code null} if the timeout expires.
* @throws JmsException checked JMSException converted to unchecked
*/
@Nullable
@@ -332,7 +332,7 @@ public interface JmsOperations {
* <p>This will only work with a default destination specified!
* @param messageSelector the JMS message selector expression (or {@code null} if none).
* See the JMS specification for a detailed definition of selector expressions.
* @return the message produced for the consumer, or {@code null} if the timeout expires
* @return the message produced for the consumer or {@code null} if the timeout expires.
* @throws JmsException checked JMSException converted to unchecked
*/
@Nullable
@@ -347,7 +347,7 @@ public interface JmsOperations {
* @param destination the destination to receive a message from
* @param messageSelector the JMS message selector expression (or {@code null} if none).
* See the JMS specification for a detailed definition of selector expressions.
* @return the message produced for the consumer, or {@code null} if the timeout expires
* @return the message produced for the consumer or {@code null} if the timeout expires.
* @throws JmsException checked JMSException converted to unchecked
*/
@Nullable
@@ -363,7 +363,7 @@ public interface JmsOperations {
* (to be resolved to an actual destination by a DestinationResolver)
* @param messageSelector the JMS message selector expression (or {@code null} if none).
* See the JMS specification for a detailed definition of selector expressions.
* @return the message produced for the consumer, or {@code null} if the timeout expires
* @return the message produced for the consumer or {@code null} if the timeout expires.
* @throws JmsException checked JMSException converted to unchecked
*/
@Nullable
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -168,7 +168,8 @@ public abstract class AbstractMessageSendingTemplate<D> implements MessageSendin
Map<String, Object> headersToUse = processHeadersToSend(headers);
if (headersToUse != null) {
messageHeaders = (headersToUse instanceof MessageHeaders mh ? mh : new MessageHeaders(headersToUse));
messageHeaders = (headersToUse instanceof MessageHeaders _messageHeaders ?
_messageHeaders : new MessageHeaders(headersToUse));
}
MessageConverter converter = getMessageConverter();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,7 +22,6 @@ import org.springframework.aot.hint.ExecutableMode;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.aot.hint.TypeReference;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
/**
@@ -42,9 +41,8 @@ class EntityManagerRuntimeHints implements RuntimeHintsRegistrar {
private static final String NATIVE_QUERY_IMPL_CLASS_NAME = "org.hibernate.query.sql.internal.NativeQueryImpl";
@Override
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
if (ClassUtils.isPresent(HIBERNATE_SESSION_FACTORY_CLASS_NAME, classLoader)) {
hints.proxies().registerJdkProxy(TypeReference.of(HIBERNATE_SESSION_FACTORY_CLASS_NAME),
TypeReference.of(EntityManagerFactoryInfo.class));
@@ -72,5 +70,4 @@ class EntityManagerRuntimeHints implements RuntimeHintsRegistrar {
catch (ClassNotFoundException ignored) {
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -385,9 +385,7 @@ public abstract class SharedEntityManagerCreator {
else if (targetClass.isInstance(proxy)) {
return proxy;
}
else {
return this.target.unwrap(targetClass);
}
break;
case "getOutputParameterValue":
if (this.entityManager == null) {
Object key = args[0];
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,16 +37,16 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.withSettings;
/**
* Tests for {@link SharedEntityManagerCreator}.
* Unit tests for {@link SharedEntityManagerCreator}.
*
* @author Oliver Gierke
* @author Juergen Hoeller
*/
@ExtendWith(MockitoExtension.class)
class SharedEntityManagerCreatorTests {
public class SharedEntityManagerCreatorTests {
@Test
void proxyingWorksIfInfoReturnsNullEntityManagerInterface() {
public void proxyingWorksIfInfoReturnsNullEntityManagerInterface() {
EntityManagerFactory emf = mock(EntityManagerFactory.class,
withSettings().extraInterfaces(EntityManagerFactoryInfo.class));
// EntityManagerFactoryInfo.getEntityManagerInterface returns null
@@ -54,7 +54,7 @@ class SharedEntityManagerCreatorTests {
}
@Test
void transactionRequiredExceptionOnJoinTransaction() {
public void transactionRequiredExceptionOnJoinTransaction() {
EntityManagerFactory emf = mock();
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(
@@ -62,7 +62,7 @@ class SharedEntityManagerCreatorTests {
}
@Test
void transactionRequiredExceptionOnFlush() {
public void transactionRequiredExceptionOnFlush() {
EntityManagerFactory emf = mock();
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(
@@ -70,7 +70,7 @@ class SharedEntityManagerCreatorTests {
}
@Test
void transactionRequiredExceptionOnPersist() {
public void transactionRequiredExceptionOnPersist() {
EntityManagerFactory emf = mock();
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(() ->
@@ -78,7 +78,7 @@ class SharedEntityManagerCreatorTests {
}
@Test
void transactionRequiredExceptionOnMerge() {
public void transactionRequiredExceptionOnMerge() {
EntityManagerFactory emf = mock();
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(() ->
@@ -86,7 +86,7 @@ class SharedEntityManagerCreatorTests {
}
@Test
void transactionRequiredExceptionOnRemove() {
public void transactionRequiredExceptionOnRemove() {
EntityManagerFactory emf = mock();
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(() ->
@@ -94,7 +94,7 @@ class SharedEntityManagerCreatorTests {
}
@Test
void transactionRequiredExceptionOnRefresh() {
public void transactionRequiredExceptionOnRefresh() {
EntityManagerFactory emf = mock();
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(() ->
@@ -102,98 +102,78 @@ class SharedEntityManagerCreatorTests {
}
@Test
void deferredQueryWithUpdate() {
public void deferredQueryWithUpdate() {
EntityManagerFactory emf = mock();
EntityManager targetEm = mock();
Query targetQuery = mock();
Query query = mock();
given(emf.createEntityManager()).willReturn(targetEm);
given(targetEm.createQuery("x")).willReturn(targetQuery);
given(targetEm.createQuery("x")).willReturn(query);
given(targetEm.isOpen()).willReturn(true);
given((Query) targetQuery.unwrap(targetQuery.getClass())).willReturn(targetQuery);
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
Query query = em.createQuery("x");
assertThat((Query) query.unwrap(null)).isSameAs(targetQuery);
assertThat((Query) query.unwrap(targetQuery.getClass())).isSameAs(targetQuery);
assertThat(query.unwrap(Query.class)).isSameAs(query);
query.executeUpdate();
em.createQuery("x").executeUpdate();
verify(targetQuery).executeUpdate();
verify(query).executeUpdate();
verify(targetEm).close();
}
@Test
void deferredQueryWithSingleResult() {
public void deferredQueryWithSingleResult() {
EntityManagerFactory emf = mock();
EntityManager targetEm = mock();
Query targetQuery = mock();
Query query = mock();
given(emf.createEntityManager()).willReturn(targetEm);
given(targetEm.createQuery("x")).willReturn(targetQuery);
given(targetEm.createQuery("x")).willReturn(query);
given(targetEm.isOpen()).willReturn(true);
given((Query) targetQuery.unwrap(targetQuery.getClass())).willReturn(targetQuery);
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
Query query = em.createQuery("x");
assertThat((Query) query.unwrap(null)).isSameAs(targetQuery);
assertThat((Query) query.unwrap(targetQuery.getClass())).isSameAs(targetQuery);
assertThat(query.unwrap(Query.class)).isSameAs(query);
query.getSingleResult();
em.createQuery("x").getSingleResult();
verify(targetQuery).getSingleResult();
verify(query).getSingleResult();
verify(targetEm).close();
}
@Test
void deferredQueryWithResultList() {
public void deferredQueryWithResultList() {
EntityManagerFactory emf = mock();
EntityManager targetEm = mock();
Query targetQuery = mock();
Query query = mock();
given(emf.createEntityManager()).willReturn(targetEm);
given(targetEm.createQuery("x")).willReturn(targetQuery);
given(targetEm.createQuery("x")).willReturn(query);
given(targetEm.isOpen()).willReturn(true);
given((Query) targetQuery.unwrap(targetQuery.getClass())).willReturn(targetQuery);
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
Query query = em.createQuery("x");
assertThat((Query) query.unwrap(null)).isSameAs(targetQuery);
assertThat((Query) query.unwrap(targetQuery.getClass())).isSameAs(targetQuery);
assertThat(query.unwrap(Query.class)).isSameAs(query);
query.getResultList();
em.createQuery("x").getResultList();
verify(targetQuery).getResultList();
verify(query).getResultList();
verify(targetEm).close();
}
@Test
void deferredQueryWithResultStream() {
public void deferredQueryWithResultStream() {
EntityManagerFactory emf = mock();
EntityManager targetEm = mock();
Query targetQuery = mock();
Query query = mock();
given(emf.createEntityManager()).willReturn(targetEm);
given(targetEm.createQuery("x")).willReturn(targetQuery);
given(targetEm.createQuery("x")).willReturn(query);
given(targetEm.isOpen()).willReturn(true);
given((Query) targetQuery.unwrap(targetQuery.getClass())).willReturn(targetQuery);
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
Query query = em.createQuery("x");
assertThat((Query) query.unwrap(null)).isSameAs(targetQuery);
assertThat((Query) query.unwrap(targetQuery.getClass())).isSameAs(targetQuery);
assertThat(query.unwrap(Query.class)).isSameAs(query);
query.getResultStream();
em.createQuery("x").getResultStream();
verify(targetQuery).getResultStream();
verify(query).getResultStream();
verify(targetEm).close();
}
@Test
void deferredStoredProcedureQueryWithIndexedParameters() {
public void deferredStoredProcedureQueryWithIndexedParameters() {
EntityManagerFactory emf = mock();
EntityManager targetEm = mock();
StoredProcedureQuery targetQuery = mock();
StoredProcedureQuery query = mock();
given(emf.createEntityManager()).willReturn(targetEm);
given(targetEm.createStoredProcedureQuery("x")).willReturn(targetQuery);
willReturn("y").given(targetQuery).getOutputParameterValue(0);
willReturn("z").given(targetQuery).getOutputParameterValue(2);
given(targetEm.createStoredProcedureQuery("x")).willReturn(query);
willReturn("y").given(query).getOutputParameterValue(0);
willReturn("z").given(query).getOutputParameterValue(2);
given(targetEm.isOpen()).willReturn(true);
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
@@ -207,24 +187,24 @@ class SharedEntityManagerCreatorTests {
spq.getOutputParameterValue(1));
assertThat(spq.getOutputParameterValue(2)).isEqualTo("z");
verify(targetQuery).registerStoredProcedureParameter(0, String.class, ParameterMode.OUT);
verify(targetQuery).registerStoredProcedureParameter(1, Number.class, ParameterMode.IN);
verify(targetQuery).registerStoredProcedureParameter(2, Object.class, ParameterMode.INOUT);
verify(targetQuery).execute();
verify(query).registerStoredProcedureParameter(0, String.class, ParameterMode.OUT);
verify(query).registerStoredProcedureParameter(1, Number.class, ParameterMode.IN);
verify(query).registerStoredProcedureParameter(2, Object.class, ParameterMode.INOUT);
verify(query).execute();
verify(targetEm).close();
verifyNoMoreInteractions(targetQuery);
verifyNoMoreInteractions(query);
verifyNoMoreInteractions(targetEm);
}
@Test
void deferredStoredProcedureQueryWithNamedParameters() {
public void deferredStoredProcedureQueryWithNamedParameters() {
EntityManagerFactory emf = mock();
EntityManager targetEm = mock();
StoredProcedureQuery targetQuery = mock();
StoredProcedureQuery query = mock();
given(emf.createEntityManager()).willReturn(targetEm);
given(targetEm.createStoredProcedureQuery("x")).willReturn(targetQuery);
willReturn("y").given(targetQuery).getOutputParameterValue("a");
willReturn("z").given(targetQuery).getOutputParameterValue("c");
given(targetEm.createStoredProcedureQuery("x")).willReturn(query);
willReturn("y").given(query).getOutputParameterValue("a");
willReturn("z").given(query).getOutputParameterValue("c");
given(targetEm.isOpen()).willReturn(true);
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
@@ -238,12 +218,12 @@ class SharedEntityManagerCreatorTests {
spq.getOutputParameterValue("b"));
assertThat(spq.getOutputParameterValue("c")).isEqualTo("z");
verify(targetQuery).registerStoredProcedureParameter("a", String.class, ParameterMode.OUT);
verify(targetQuery).registerStoredProcedureParameter("b", Number.class, ParameterMode.IN);
verify(targetQuery).registerStoredProcedureParameter("c", Object.class, ParameterMode.INOUT);
verify(targetQuery).execute();
verify(query).registerStoredProcedureParameter("a", String.class, ParameterMode.OUT);
verify(query).registerStoredProcedureParameter("b", Number.class, ParameterMode.IN);
verify(query).registerStoredProcedureParameter("c", Object.class, ParameterMode.INOUT);
verify(query).execute();
verify(targetEm).close();
verifyNoMoreInteractions(targetQuery);
verifyNoMoreInteractions(query);
verifyNoMoreInteractions(targetEm);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author 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,8 +17,7 @@
package org.springframework.dao;
/**
* Exception thrown when an attempt to execute an SQL statement fails to map
* the given data, typically but no limited to an insert or update data
* Exception thrown when an attempt to insert or update data
* results in violation of an integrity constraint. Note that this
* is not purely a relational concept; integrity constraints such
* as unique primary keys are required by most database types.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,12 +21,11 @@ import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.aot.hint.TypeHint;
import org.springframework.aot.hint.TypeReference;
import org.springframework.lang.Nullable;
import org.springframework.transaction.TransactionDefinition;
/**
* {@link RuntimeHintsRegistrar} implementation that registers runtime hints
* for transaction management.
* {@link RuntimeHintsRegistrar} implementation that registers runtime hints for
* transaction management.
*
* @author Sebastien Deleuze
* @since 6.0
@@ -35,9 +34,9 @@ import org.springframework.transaction.TransactionDefinition;
class TransactionRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
hints.reflection().registerTypes(
TypeReference.listOf(Isolation.class, Propagation.class, TransactionDefinition.class),
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
hints.reflection().registerTypes(TypeReference.listOf(
Isolation.class, Propagation.class, TransactionDefinition.class),
TypeHint.builtWith(MemberCategory.DECLARED_FIELDS));
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -51,7 +51,7 @@ public class DefaultTransactionAttribute extends DefaultTransactionDefinition im
/**
* Create a new {@code DefaultTransactionAttribute} with default settings.
* Create a new DefaultTransactionAttribute, with default settings.
* Can be modified through bean property setters.
* @see #setPropagationBehavior
* @see #setIsolationLevel
@@ -76,7 +76,7 @@ public class DefaultTransactionAttribute extends DefaultTransactionDefinition im
}
/**
* Create a new {@code DefaultTransactionAttribute} with the given
* Create a new DefaultTransactionAttribute with the given
* propagation behavior. Can be modified through bean property setters.
* @param propagationBehavior one of the propagation constants in the
* TransactionDefinition interface
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -210,13 +210,11 @@ public abstract class AbstractReactiveTransactionManager implements ReactiveTran
prepareSynchronization(synchronizationManager, status, definition)).thenReturn(status);
}
// PROPAGATION_REQUIRED, PROPAGATION_SUPPORTS, PROPAGATION_MANDATORY:
// regular participation in existing transaction.
// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
if (debugEnabled) {
logger.debug("Participating in existing transaction");
}
return Mono.just(prepareReactiveTransaction(
synchronizationManager, definition, transaction, false, debugEnabled, null));
return Mono.just(prepareReactiveTransaction(synchronizationManager, definition, transaction, false, debugEnabled, null));
}
/**
@@ -332,7 +330,7 @@ public abstract class AbstractReactiveTransactionManager implements ReactiveTran
if (resourcesHolder != null) {
Object suspendedResources = resourcesHolder.suspendedResources;
if (suspendedResources != null) {
resume = doResume(synchronizationManager, transaction, suspendedResources);
resume = doResume(synchronizationManager, transaction, suspendedResources);
}
List<TransactionSynchronization> suspendedSynchronizations = resourcesHolder.suspendedSynchronizations;
if (suspendedSynchronizations != null) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -451,7 +451,7 @@ public abstract class AbstractPlatformTransactionManager implements PlatformTran
if (useSavepointForNestedTransaction()) {
// Create savepoint within existing Spring-managed transaction,
// through the SavepointManager API implemented by TransactionStatus.
// Usually uses JDBC savepoints. Never activates Spring synchronization.
// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
DefaultTransactionStatus status =
prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
status.createAndHoldSavepoint();
@@ -465,8 +465,7 @@ public abstract class AbstractPlatformTransactionManager implements PlatformTran
}
}
// PROPAGATION_REQUIRED, PROPAGATION_SUPPORTS, PROPAGATION_MANDATORY:
// regular participation in existing transaction.
// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
if (debugEnabled) {
logger.debug("Participating in existing transaction");
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -65,7 +65,7 @@ public class DefaultTransactionDefinition implements TransactionDefinition, Seri
/**
* Create a new {@code DefaultTransactionDefinition} with default settings.
* Create a new DefaultTransactionDefinition, with default settings.
* Can be modified through bean property setters.
* @see #setPropagationBehavior
* @see #setIsolationLevel
@@ -93,7 +93,7 @@ public class DefaultTransactionDefinition implements TransactionDefinition, Seri
}
/**
* Create a new {@code DefaultTransactionDefinition} with the given
* Create a new DefaultTransactionDefinition with the given
* propagation behavior. Can be modified through bean property setters.
* @param propagationBehavior one of the propagation constants in the
* TransactionDefinition interface
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -110,7 +110,7 @@ public final class ResponseCookie extends HttpCookie {
/**
* Return {@code true} if the cookie has the "HttpOnly" attribute.
* @see <a href="https://owasp.org/www-community/HttpOnly">https://owasp.org/www-community/HttpOnly</a>
* @see <a href="https://www.owasp.org/index.php/HTTPOnly">https://www.owasp.org/index.php/HTTPOnly</a>
*/
public boolean isHttpOnly() {
return this.httpOnly;
@@ -268,7 +268,7 @@ public final class ResponseCookie extends HttpCookie {
/**
* Add the "HttpOnly" attribute to the cookie.
* @see <a href="https://owasp.org/www-community/HttpOnly">https://owasp.org/www-community/HttpOnly</a>
* @see <a href="https://www.owasp.org/index.php/HTTPOnly">https://www.owasp.org/index.php/HTTPOnly</a>
*/
ResponseCookieBuilder httpOnly(boolean httpOnly);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -277,6 +277,7 @@ public class ResponseEntity<T> extends HttpEntity<T> {
*/
public static HeadersBuilder<?> of(ProblemDetail body) {
return new DefaultBuilder(body.getStatus()) {
@SuppressWarnings("unchecked")
@Override
public <T> ResponseEntity<T> build() {
@@ -16,12 +16,8 @@
package org.springframework.http.client.reactive;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import reactor.core.publisher.Flux;
import org.springframework.core.io.buffer.DataBuffer;
@@ -59,7 +55,16 @@ public abstract class AbstractClientHttpResponse implements ClientHttpResponse {
this.statusCode = statusCode;
this.headers = headers;
this.cookies = cookies;
this.body = Flux.from(new SingleSubscriberPublisher<>(body));
this.body = singleSubscription(body);
}
private static Flux<DataBuffer> singleSubscription(Flux<DataBuffer> body) {
AtomicBoolean subscribed = new AtomicBoolean();
return body.doOnSubscribe(s -> {
if (!subscribed.compareAndSet(false, true)) {
throw new IllegalStateException("The client response body can only be consumed once");
}
});
}
@@ -82,39 +87,4 @@ public abstract class AbstractClientHttpResponse implements ClientHttpResponse {
public Flux<DataBuffer> getBody() {
return this.body;
}
private static final class SingleSubscriberPublisher<T> implements Publisher<T> {
private static final Subscription NO_OP_SUBSCRIPTION = new Subscription() {
@Override
public void request(long l) {
}
@Override
public void cancel() {
}
};
private final Publisher<T> delegate;
private final AtomicBoolean subscribed = new AtomicBoolean();
public SingleSubscriberPublisher(Publisher<T> delegate) {
this.delegate = delegate;
}
@Override
public void subscribe(Subscriber<? super T> subscriber) {
Objects.requireNonNull(subscriber, "Subscriber must not be null");
if (this.subscribed.compareAndSet(false, true)) {
this.delegate.subscribe(subscriber);
}
else {
subscriber.onSubscribe(NO_OP_SUBSCRIPTION);
subscriber.onError(new IllegalStateException("The client response body can only be consumed once"));
}
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -124,6 +124,7 @@ class HttpComponentsClientHttpRequest extends AbstractClientHttpRequest {
@Override
protected void applyHeaders() {
HttpHeaders headers = getHeaders();
headers.entrySet()
.stream()
.filter(entry -> !HttpHeaders.CONTENT_LENGTH.equals(entry.getKey()))
@@ -132,6 +133,7 @@ class HttpComponentsClientHttpRequest extends AbstractClientHttpRequest {
if (!this.httpRequest.containsHeader(HttpHeaders.ACCEPT)) {
this.httpRequest.addHeader(HttpHeaders.ACCEPT, MediaType.ALL_VALUE);
}
this.contentLength = headers.getContentLength();
}
@@ -142,6 +144,7 @@ class HttpComponentsClientHttpRequest extends AbstractClientHttpRequest {
}
CookieStore cookieStore = this.context.getCookieStore();
getCookies().values()
.stream()
.flatMap(Collection::stream)
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,7 +37,6 @@ import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.util.Assert;
import org.springframework.util.MultiValueMap;
/**
* {@link ClientHttpRequest} for the Java {@link HttpClient}.
@@ -109,11 +108,7 @@ class JdkClientHttpRequest extends AbstractClientHttpRequest {
@Override
protected void applyCookies() {
MultiValueMap<String, HttpCookie> cookies = getCookies();
if (cookies.isEmpty()) {
return;
}
this.builder.header(HttpHeaders.COOKIE, cookies.values().stream()
this.builder.header(HttpHeaders.COOKIE, getCookies().values().stream()
.flatMap(List::stream).map(HttpCookie::toString).collect(Collectors.joining(";")));
}
@@ -82,7 +82,6 @@ public class ResourceHttpMessageConverter extends AbstractHttpMessageConverter<R
if (this.supportsReadStreaming && InputStreamResource.class == clazz) {
return new InputStreamResource(inputMessage.getBody()) {
@Override
@Nullable
public String getFilename() {
return inputMessage.getHeaders().getContentDisposition().getFilename();
}
@@ -108,23 +107,6 @@ public class ResourceHttpMessageConverter extends AbstractHttpMessageConverter<R
}
}
@Override
protected void writeInternal(Resource resource, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
writeContent(resource, outputMessage);
}
/**
* Add the default headers for the given resource to the given message.
* @since 6.0
*/
public void addDefaultHeaders(HttpOutputMessage message, Resource resource, @Nullable MediaType contentType)
throws IOException {
addDefaultHeaders(message.getHeaders(), resource, contentType);
}
@Override
protected MediaType getDefaultContentType(Resource resource) {
return MediaTypeFactory.getMediaType(resource).orElse(MediaType.APPLICATION_OCTET_STREAM);
@@ -142,10 +124,23 @@ public class ResourceHttpMessageConverter extends AbstractHttpMessageConverter<R
return (contentLength < 0 ? null : contentLength);
}
/**
* Adds the default headers for the given resource to the given message.
* @since 6.0
*/
public void addDefaultHeaders(HttpOutputMessage message, Resource resource, @Nullable MediaType contentType) throws IOException {
addDefaultHeaders(message.getHeaders(), resource, contentType);
}
@Override
protected void writeInternal(Resource resource, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
writeContent(resource, outputMessage);
}
protected void writeContent(Resource resource, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
// We cannot use try-with-resources here for the InputStream, since we have
// custom handling of the close() method in a finally-block.
try {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -52,6 +52,22 @@ public class ResourceRegionHttpMessageConverter extends AbstractGenericHttpMessa
}
@Override
@SuppressWarnings("unchecked")
protected MediaType getDefaultContentType(Object object) {
Resource resource = null;
if (object instanceof ResourceRegion resourceRegion) {
resource = resourceRegion.getResource();
}
else {
Collection<ResourceRegion> regions = (Collection<ResourceRegion>) object;
if (!regions.isEmpty()) {
resource = regions.iterator().next().getResource();
}
}
return MediaTypeFactory.getMediaType(resource).orElse(MediaType.APPLICATION_OCTET_STREAM);
}
@Override
public boolean canRead(Class<?> clazz, @Nullable MediaType mediaType) {
return false;
@@ -103,6 +119,7 @@ public class ResourceRegionHttpMessageConverter extends AbstractGenericHttpMessa
}
@Override
@SuppressWarnings("unchecked")
protected void writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
@@ -110,7 +127,6 @@ public class ResourceRegionHttpMessageConverter extends AbstractGenericHttpMessa
writeResourceRegion(resourceRegion, outputMessage);
}
else {
@SuppressWarnings("unchecked")
Collection<ResourceRegion> regions = (Collection<ResourceRegion>) object;
if (regions.size() == 1) {
writeResourceRegion(regions.iterator().next(), outputMessage);
@@ -121,22 +137,6 @@ public class ResourceRegionHttpMessageConverter extends AbstractGenericHttpMessa
}
}
@Override
protected MediaType getDefaultContentType(Object object) {
Resource resource = null;
if (object instanceof ResourceRegion resourceRegion) {
resource = resourceRegion.getResource();
}
else {
@SuppressWarnings("unchecked")
Collection<ResourceRegion> regions = (Collection<ResourceRegion>) object;
if (!regions.isEmpty()) {
resource = regions.iterator().next().getResource();
}
}
return MediaTypeFactory.getMediaType(resource).orElse(MediaType.APPLICATION_OCTET_STREAM);
}
protected void writeResourceRegion(ResourceRegion region, HttpOutputMessage outputMessage) throws IOException {
Assert.notNull(region, "ResourceRegion must not be null");
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author 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,10 +22,9 @@ import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.aot.hint.TypeHint.Builder;
import org.springframework.lang.Nullable;
/**
* {@link RuntimeHintsRegistrar} implementation that registers reflection hints
* {@link RuntimeHintsRegistrar} implementation that registers reflection entries
* for {@link Jackson2ObjectMapperBuilder} well-known modules.
*
* @author Sebastien Deleuze
@@ -39,7 +38,7 @@ class JacksonModulesRuntimeHints implements RuntimeHintsRegistrar {
.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
@Override
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
hints.reflection()
.registerTypeIfPresent(classLoader,
"com.fasterxml.jackson.datatype.jdk8.Jdk8Module", asJacksonModule)
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author 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,11 +20,10 @@ import org.springframework.aot.hint.BindingReflectionHintsRegistrar;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.http.ProblemDetail;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
/**
* {@link RuntimeHintsRegistrar} implementation that registers binding reflection hints
* {@link RuntimeHintsRegistrar} implementation that registers binding reflection entries
* for {@link ProblemDetail} serialization support with Jackson.
*
* @author Brian Clozel
@@ -34,7 +33,7 @@ import org.springframework.util.ClassUtils;
class ProblemDetailRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
BindingReflectionHintsRegistrar bindingRegistrar = new BindingReflectionHintsRegistrar();
bindingRegistrar.registerReflectionHints(hints.reflection(), ProblemDetail.class);
if (ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader)) {
@@ -61,11 +61,6 @@ public interface ErrorResponse {
* Return the body for the response, formatted as an RFC 7807
* {@link ProblemDetail} whose {@link ProblemDetail#getStatus() status}
* should match the response status.
* <p><strong>Note:</strong> The returned {@code ProblemDetail} may be
* updated before the response is rendered, e.g. via
* {@link #updateAndGetBody(MessageSource, Locale)}. Therefore, implementing
* methods should use an instance field, and should not re-create the
* {@code ProblemDetail} on every call, nor use a static variable.
*/
ProblemDetail getBody();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,9 +37,6 @@ import org.springframework.web.ErrorResponse;
@SuppressWarnings("serial")
public class AsyncRequestTimeoutException extends RuntimeException implements ErrorResponse {
private final ProblemDetail body = ProblemDetail.forStatus(getStatusCode());
@Override
public HttpStatusCode getStatusCode() {
return HttpStatus.SERVICE_UNAVAILABLE;
@@ -47,7 +44,7 @@ public class AsyncRequestTimeoutException extends RuntimeException implements Er
@Override
public ProblemDetail getBody() {
return this.body;
return ProblemDetail.forStatus(getStatusCode());
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -230,6 +230,7 @@ public class InvocableHandlerMethod extends HandlerMethod {
/**
* Invoke the given Kotlin coroutine suspended function.
*
* <p>The default implementation invokes
* {@link CoroutinesUtils#invokeSuspendingFunction(Method, Object, Object...)},
* but subclasses can override this method to use
@@ -24,9 +24,7 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.context.ApplicationContext;
@@ -251,10 +249,13 @@ public class DefaultServerWebExchange implements ServerWebExchange {
public Mono<Void> cleanupMultipart() {
return Mono.defer(() -> {
if (this.multipartRead) {
return Mono.usingWhen(getMultipartData().onErrorComplete().map(this::collectParts),
parts -> Mono.empty(),
parts -> Flux.fromIterable(parts).flatMap(part -> part.delete().onErrorComplete())
);
return getMultipartData()
.onErrorComplete()
.flatMapIterable(Map::values)
.flatMapIterable(Function.identity())
.flatMap(part -> part.delete()
.onErrorComplete())
.then();
}
else {
return Mono.empty();
@@ -262,10 +263,6 @@ public class DefaultServerWebExchange implements ServerWebExchange {
});
}
private List<Part> collectParts(MultiValueMap<String, Part> multipartData) {
return multipartData.values().stream().flatMap(List::stream).collect(Collectors.toList());
}
@Override
public LocaleContext getLocaleContext() {
return this.localeContextResolver.resolveLocaleContext(this);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author 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,11 +19,10 @@ package org.springframework.web.util;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.core.io.ClassPathResource;
import org.springframework.lang.Nullable;
/**
* {@link RuntimeHintsRegistrar} implementation that registers resource hints
* for resources in the {@code web.util} package.
* {@link RuntimeHintsRegistrar} implementation that registers resource
* hints for web util resources.
*
* @author Sebastien Deleuze
* @since 6.0
@@ -31,7 +30,7 @@ import org.springframework.lang.Nullable;
class WebUtilRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
hints.resources().registerResource(
new ClassPathResource("HtmlCharacterEntityReferences.properties", getClass()));
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author 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,11 +19,10 @@ package org.springframework.web.reactive.socket.server.support;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.lang.Nullable;
/**
* {@link RuntimeHintsRegistrar} implementation that registers reflection hints
* related to {@link HandshakeWebSocketService}.
* {@link RuntimeHintsRegistrar} implementation that registers reflection hints related to
* {@link HandshakeWebSocketService}.
*
* @author Sebastien Deleuze
* @since 6.0
@@ -31,9 +30,8 @@ import org.springframework.lang.Nullable;
class HandshakeWebSocketServiceRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
hints.reflection().registerType(HandshakeWebSocketService.initUpgradeStrategy().getClass(),
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,9 +30,9 @@ import org.springframework.web.method.HandlerMethod;
*
* <p>A HandlerInterceptor gets called before the appropriate HandlerAdapter
* triggers the execution of the handler itself. This mechanism can be used
* for a large field of preprocessing aspects, or common handler behavior
* like locale or theme changes. Its main purpose is to allow for factoring
* out repetitive handler code.
* for a large field of preprocessing aspects, e.g. for authorization checks,
* or common handler behavior like locale or theme changes. Its main purpose
* is to allow for factoring out repetitive handler code.
*
* <p>In an asynchronous processing scenario, the handler may be executed in a
* separate thread while the main thread exits without rendering or invoking the
@@ -63,12 +63,6 @@ import org.springframework.web.method.HandlerMethod;
* forms and GZIP compression. This typically shows when one needs to map the
* filter to certain content types (e.g. images), or to all requests.
*
* <p><strong>Note:</strong> Interceptors are not ideally suited as a security
* layer due to the potential for a mismatch with annotated controller path matching.
* Generally, we recommend using Spring Security, or alternatively a similar
* approach integrated with the Servlet filter chain, and applied as early as
* possible.
*
* @author Juergen Hoeller
* @since 20.06.2003
* @see HandlerExecutionChain#getInterceptors
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -220,7 +220,7 @@ public abstract class AbstractFlashMapManager implements FlashMapManager {
@Nullable
private String decodeAndNormalizePath(@Nullable String path, HttpServletRequest request) {
if (StringUtils.hasLength(path)) {
if (path != null && !path.isEmpty()) {
path = getUrlPathHelper().decodeRequestString(request, path);
if (path.charAt(0) != '/') {
String requestUri = getUrlPathHelper().getRequestUri(request);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -248,31 +248,30 @@ public class ConcurrentWebSocketSessionDecorator extends WebSocketSessionDecorat
@Override
public void close(CloseStatus status) throws IOException {
if (this.closeLock.tryLock()) {
try {
if (this.closeInProgress) {
return;
}
if (!CloseStatus.SESSION_NOT_RELIABLE.equals(status)) {
try {
checkSessionLimits();
}
catch (SessionLimitExceededException ex) {
// Ignore
}
if (this.limitExceeded) {
if (logger.isDebugEnabled()) {
logger.debug("Changing close status " + status + " to SESSION_NOT_RELIABLE.");
}
status = CloseStatus.SESSION_NOT_RELIABLE;
}
}
this.closeInProgress = true;
super.close(status);
this.closeLock.lock();
try {
if (this.closeInProgress) {
return;
}
finally {
this.closeLock.unlock();
if (!CloseStatus.SESSION_NOT_RELIABLE.equals(status)) {
try {
checkSessionLimits();
}
catch (SessionLimitExceededException ex) {
// Ignore
}
if (this.limitExceeded) {
if (logger.isDebugEnabled()) {
logger.debug("Changing close status " + status + " to SESSION_NOT_RELIABLE.");
}
status = CloseStatus.SESSION_NOT_RELIABLE;
}
}
this.closeInProgress = true;
super.close(status);
}
finally {
this.closeLock.unlock();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,11 +21,10 @@ import org.springframework.aot.hint.ReflectionHints;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.aot.hint.TypeReference;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
/**
* {@link RuntimeHintsRegistrar} implementation that registers reflection hints
* {@link RuntimeHintsRegistrar} implementation that registers reflection entries
* for {@link AbstractHandshakeHandler}.
*
* @author Sebastien Deleuze
@@ -61,9 +60,8 @@ class HandshakeHandlerRuntimeHints implements RuntimeHintsRegistrar {
"com.ibm.websphere.wsoc.WsWsocServerContainer", classLoader);
}
@Override
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
ReflectionHints reflectionHints = hints.reflection();
if (tomcatWsPresent) {
registerType(reflectionHints, "org.springframework.web.socket.server.standard.TomcatRequestUpgradeStrategy");
@@ -89,5 +87,4 @@ class HandshakeHandlerRuntimeHints implements RuntimeHintsRegistrar {
reflectionHints.registerType(TypeReference.of(className),
builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS));
}
}