Compare commits

..

1 Commits

Author SHA1 Message Date
Spring Builds 5d6f2c84c8 Release v6.1.7 2024-05-16 08:52:46 +00:00
35 changed files with 223 additions and 497 deletions
-2
View File
@@ -51,5 +51,3 @@ atlassian-ide-plugin.xml
.vscode/
cached-antora-playbook.yml
node_modules
+2
View File
@@ -89,6 +89,8 @@ configure([rootProject] + javaProjects) { project ->
ext.javadocLinks = [
"https://docs.oracle.com/en/java/javase/17/docs/api/",
"https://jakarta.ee/specifications/platform/9/apidocs/",
"https://docs.oracle.com/cd/E13222_01/wls/docs90/javadocs/", // CommonJ and weblogic.* packages
"https://docs.jboss.org/jbossas/javadoc/4.0.5/connector/", // org.jboss.resource.*
"https://docs.jboss.org/hibernate/orm/5.6/javadocs/",
"https://eclipse.dev/aspectj/doc/released/aspectj5rt-api",
"https://www.quartz-scheduler.org/api/2.3.0/",
-39
View File
@@ -1,39 +0,0 @@
antora:
extensions:
- require: '@springio/antora-extensions'
root_component_name: 'framework'
site:
title: Spring Framework
url: https://docs.spring.io/spring-framework/reference
robots: allow
git:
ensure_git_suffix: false
content:
sources:
- url: https://github.com/spring-projects/spring-framework
# Refname matching:
# https://docs.antora.org/antora/latest/playbook/content-refname-matching/
branches: ['main', '{6..9}.+({0..9}).x']
tags: ['v{6..9}.+({0..9}).+({0..9})?(-{RC,M}*)', '!(v6.0.{0..8})', '!(v6.0.0-{RC,M}{0..9})']
start_path: framework-docs
asciidoc:
extensions:
- '@asciidoctor/tabs'
- '@springio/asciidoctor-extensions'
- '@springio/asciidoctor-extensions/include-code-extension'
attributes:
page-stackoverflow-url: https://stackoverflow.com/questions/tagged/spring
page-pagination: ''
hide-uri-scheme: '@'
tabs-sync-option: '@'
include-java: 'example$docs-src/main/java/org/springframework/docs'
urls:
latest_version_segment_strategy: redirect:to
latest_version_segment: ''
redirect_facility: httpd
runtime:
log:
failure_level: warn
ui:
bundle:
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.15/ui-bundle.zip
+20 -3
View File
@@ -10,10 +10,27 @@ apply from: "${rootDir}/gradle/ide.gradle"
apply from: "${rootDir}/gradle/publications.gradle"
antora {
options = [clean: true, fetch: !project.gradle.startParameter.offline, stacktrace: true]
version = '3.2.0-alpha.2'
playbook = 'cached-antora-playbook.yml'
playbookProvider {
repository = 'spring-projects/spring-framework'
branch = 'docs-build'
path = 'lib/antora/templates/per-branch-antora-playbook.yml'
checkLocalBranch = true
}
options = ['--clean', '--stacktrace']
environment = [
'BUILD_REFNAME': 'HEAD',
'BUILD_VERSION': project.version,
'ALGOLIA_API_KEY': '82c7ead946afbac3cf98c32446154691',
'ALGOLIA_APP_ID': '244V8V9FGG',
'ALGOLIA_INDEX_NAME': 'framework-docs'
]
dependencies = [
'@antora/atlas-extension': '1.0.0-alpha.1',
'@antora/collector-extension': '1.0.0-alpha.3',
'@asciidoctor/tabs': '1.0.0-beta.3',
'@opendevise/antora-release-line-extension': '1.0.0',
'@springio/antora-extensions': '1.8.2',
'@springio/asciidoctor-extensions': '1.0.0-alpha.9'
]
}
@@ -24,7 +24,7 @@ To create the archive, two additional JVM flags must be specified:
* `-Dspring.context.exit=onRefresh`: starts and then immediately exits your Spring
application as described above
To create a CDS archive, your JDK/JRE must have a base image. If you add the flags above to
To create a CDS archive, your JDK must have a base image. If you add the flags above to
your startup script, you may get a warning that looks like this:
[source,shell,indent=0,subs="verbatim"]
@@ -32,8 +32,7 @@ your startup script, you may get a warning that looks like this:
-XX:ArchiveClassesAtExit is unsupported when base CDS archive is not loaded. Run with -Xlog:cds for more info.
----
The base CDS archive is usually provided out-of-the-box, but can also be created if needed by issuing the following
command:
The base CDS archive can be created by issuing the following command:
[source,shell,indent=0,subs="verbatim"]
----
@@ -45,9 +44,6 @@ command:
Once the archive is available, add `-XX:SharedArchiveFile=application.jsa` to your startup
script to use it, assuming an `application.jsa` file in the working directory.
To check if the CDS cache is effective, you can use (for testing purposes only, not in production) `-Xshare:on` which
prints an error message and exits if CDS can't be enabled.
To figure out how effective the cache is, you can enable class loading logs by adding
an extra attribute: `-Xlog:class+load:file=cds.log`. This creates a `cds.log` with every
attempt to load a class and its source. Classes that are loaded from the cache should have
@@ -62,11 +58,8 @@ a "shared objects file" source, as shown in the following example:
[0.065s][info][class,load] org.springframework.context.MessageSource source: shared objects file (top)
----
If CDS can't be enabled or if you have a large number of classes that are not loaded from the cache, make sure that
the following conditions are fulfilled when creating and using the archive:
- The very same JVM must used.
- The classpath must be specified as a list of JARs, and avoid the usage of directories and `*` wildcard characters.
- The timestamps of the JARs must be preserved.
- When using the archive, the classpath must be the same than the one used to create the archive, in the same order.
Additional JARs or directories can be specified *at the end* (but won't be cached).
TIP: If you have a large number of classes that are not loaded from the cache, make sure that
the JDK and classpath used by the commands that create the archive and start the application
are identical. Note also that to effectively cache classes, the classpath should be specified
as a list of JARs containing those classes, and avoid the usage of directories and `*`
wildcard characters.
@@ -3,40 +3,28 @@
[.small]#xref:web/webmvc/mvc-controller/ann-validation.adoc[See equivalent in the Servlet stack]#
Spring WebFlux has built-in xref:core/validation/validator.adoc[Validation] for
`@RequestMapping` methods, including xref:core/validation/beanvalidation.adoc[Java Bean Validation].
Validation may be applied at one of two levels:
Spring WebFlux has built-in xref:core/validation/validator.adoc[Validation] support for
`@RequestMapping` methods, including the option to use
xref:core/validation/beanvalidation.adoc[Java Bean Validation].
The validation support works on two levels.
1. xref:web/webflux/controller/ann-methods/modelattrib-method-args.adoc[@ModelAttribute],
First, resolvers for
xref:web/webflux/controller/ann-methods/modelattrib-method-args.adoc[@ModelAttribute],
xref:web/webflux/controller/ann-methods/requestbody.adoc[@RequestBody], and
xref:web/webflux/controller/ann-methods/multipart-forms.adoc[@RequestPart] argument
resolvers validate a method argument individually if the method parameter is annotated
with Jakarta `@Valid` or Spring's `@Validated`, _AND_ there is no `Errors` or
`BindingResult` parameter immediately after, _AND_ method validation is not needed (to be
discussed next). The exception raised in this case is `MethodArgumentNotValidException`.
xref:web/webflux/controller/ann-methods/multipart-forms.adoc[@RequestPart] method
parameters perform validation if the parameter has Jakarta's `@Valid` or Spring's
`@Validated` annotation, and raise `MethodArgumentNotValidException` if necessary.
Alternatively, you can handle the errors in the controller method by adding an
`Errors` or `BindingResult` method parameter immediately after the validated one.
2. When `@Constraint` annotations such as `@Min`, `@NotBlank` and others are declared
directly on method parameters, or on the method (for the return value), then method
validation must be applied, and that supersedes validation at the method argument level
because method validation covers both method parameter constraints and nested constraints
via `@Valid`. The exception raised in this case is `HandlerMethodValidationException`.
Applications must handle both `MethodArgumentNotValidException` and
`HandlerMethodValidationException` as either may be raised depending on the controller
method signature. The two exceptions, however are designed to be very similar, and can be
handled with almost identical code. The main difference is that the former is for a single
object while the latter is for a list of method parameters.
NOTE: `@Valid` is not a constraint annotation, but rather for nested constraints within
an Object. Therefore, by itself `@Valid` does not lead to method validation. `@NotNull`
on the other hand is a constraint, and adding it to an `@Valid` parameter leads to method
validation. For nullability specifically, you may also use the `required` flag of
`@RequestBody` or `@ModelAttribute`.
Method validation may be used in combination with `Errors` or `BindingResult` method
parameters. However, the controller method is called only if all validation errors are on
method parameters with an `Errors` immediately after. If there are validation errors on
any other method parameter then `HandlerMethodValidationException` is raised.
Second, if {bean-validation-site}[Java Bean Validation] is present _AND_ any method
parameter has `@Constraint` annotations, then method validation is applied instead,
raising `HandlerMethodValidationException` if necessary. For this case you can still add
an `Errors` or `BindingResult` method parameter to handle validation errors within the
controller method, but if other method arguments have validation errors then
`HandlerMethodValidationException` is raised instead. Method validation can apply
to the return value if the method is annotated with `@Valid` or with `@Constraint`
annotations.
You can configure a `Validator` globally through the
xref:web/webflux/config.adoc#webflux-config-validation[WebMvc config], or locally
@@ -3,40 +3,28 @@
[.small]#xref:web/webflux/controller/ann-validation.adoc[See equivalent in the Reactive stack]#
Spring MVC has built-in xref:core/validation/validator.adoc[validation] for
`@RequestMapping` methods, including xref:core/validation/beanvalidation.adoc[Java Bean Validation].
Validation may be applied at one of two levels:
Spring MVC has built-in xref:core/validation/validator.adoc[Validation] support for
`@RequestMapping` methods, including the option to use
xref:core/validation/beanvalidation.adoc[Java Bean Validation].
The validation support works on two levels.
1. xref:web/webmvc/mvc-controller/ann-methods/modelattrib-method-args.adoc[@ModelAttribute],
First, resolvers for
xref:web/webmvc/mvc-controller/ann-methods/modelattrib-method-args.adoc[@ModelAttribute],
xref:web/webmvc/mvc-controller/ann-methods/requestbody.adoc[@RequestBody], and
xref:web/webmvc/mvc-controller/ann-methods/multipart-forms.adoc[@RequestPart] argument
resolvers validate a method argument individually if the method parameter is annotated
with Jakarta `@Valid` or Spring's `@Validated`, _AND_ there is no `Errors` or
`BindingResult` parameter immediately after, _AND_ method validation is not needed (to be
discussed next). The exception raised in this case is `MethodArgumentNotValidException`.
xref:web/webmvc/mvc-controller/ann-methods/multipart-forms.adoc[@RequestPart] method
parameters perform validation if the parameter has Jakarta's `@Valid` or Spring's
`@Validated` annotation, and raise `MethodArgumentNotValidException` if necessary.
Alternatively, you can handle the errors in the controller method by adding an
`Errors` or `BindingResult` method parameter immediately after the validated one.
2. When `@Constraint` annotations such as `@Min`, `@NotBlank` and others are declared
directly on method parameters, or on the method (for the return value), then method
validation must be applied, and that supersedes validation at the method argument level
because method validation covers both method parameter constraints and nested constraints
via `@Valid`. The exception raised in this case is `HandlerMethodValidationException`.
Applications must handle both `MethodArgumentNotValidException` and
`HandlerMethodValidationException` as either may be raised depending on the controller
method signature. The two exceptions, however are designed to be very similar, and can be
handled with almost identical code. The main difference is that the former is for a single
object while the latter is for a list of method parameters.
NOTE: `@Valid` is not a constraint annotation, but rather for nested constraints within
an Object. Therefore, by itself `@Valid` does not lead to method validation. `@NotNull`
on the other hand is a constraint, and adding it to an `@Valid` parameter leads to method
validation. For nullability specifically, you may also use the `required` flag of
`@RequestBody` or `@ModelAttribute`.
Method validation may be used in combination with `Errors` or `BindingResult` method
parameters. However, the controller method is called only if all validation errors are on
method parameters with an `Errors` immediately after. If there are validation errors on
any other method parameter then `HandlerMethodValidationException` is raised.
Second, if {bean-validation-site}[Java Bean Validation] is present _AND_ any method
parameter has `@Constraint` annotations, then method validation is applied instead,
raising `HandlerMethodValidationException` if necessary. For this case you can still add
an `Errors` or `BindingResult` method parameter to handle validation errors within the
controller method, but if other method arguments have validation errors then
`HandlerMethodValidationException` is raised instead. Method validation can apply
to the return value if the method is annotated with `@Valid` or with `@Constraint`
annotations.
You can configure a `Validator` globally through the
xref:web/webmvc/mvc-config/validation.adoc[WebMvc config], or locally through an
-10
View File
@@ -1,10 +0,0 @@
{
"dependencies": {
"antora": "3.2.0-alpha.4",
"@antora/atlas-extension": "1.0.0-alpha.2",
"@antora/collector-extension": "1.0.0-alpha.3",
"@asciidoctor/tabs": "1.0.0-beta.6",
"@springio/antora-extensions": "1.11.1",
"@springio/asciidoctor-extensions": "1.0.0-alpha.10"
}
}
+1 -1
View File
@@ -115,7 +115,7 @@ dependencies {
api("org.codehaus.jettison:jettison:1.5.4")
api("org.crac:crac:1.4.0")
api("org.dom4j:dom4j:2.1.4")
api("org.eclipse.jetty:jetty-reactive-httpclient:4.0.4")
api("org.eclipse.jetty:jetty-reactive-httpclient:4.0.3")
api("org.eclipse.persistence:org.eclipse.persistence.jpa:3.0.4")
api("org.eclipse:yasson:2.0.4")
api("org.ehcache:ehcache:3.10.8")
+1 -1
View File
@@ -1,4 +1,4 @@
version=6.1.8
version=6.1.7
org.gradle.caching=true
org.gradle.jvmargs=-Xmx2048m
@@ -41,7 +41,6 @@ import org.aspectj.weaver.tools.PointcutParameter;
import org.aspectj.weaver.tools.PointcutParser;
import org.aspectj.weaver.tools.PointcutPrimitive;
import org.aspectj.weaver.tools.ShadowMatch;
import org.aspectj.weaver.tools.UnsupportedPointcutPrimitiveException;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.IntroductionAwareMethodMatcher;
@@ -116,8 +115,6 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
@Nullable
private transient PointcutExpression pointcutExpression;
private transient boolean pointcutParsingFailed = false;
private transient Map<Method, ShadowMatch> shadowMatchCache = new ConcurrentHashMap<>(32);
@@ -273,10 +270,6 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
@Override
public boolean matches(Class<?> targetClass) {
if (this.pointcutParsingFailed) {
return false;
}
try {
try {
return obtainPointcutExpression().couldMatchJoinPointsInType(targetClass);
@@ -290,11 +283,8 @@ public class AspectJExpressionPointcut extends AbstractExpressionPointcut
}
}
}
catch (IllegalArgumentException | IllegalStateException | UnsupportedPointcutPrimitiveException ex) {
this.pointcutParsingFailed = true;
if (logger.isDebugEnabled()) {
logger.debug("Pointcut parser rejected expression [" + getExpression() + "]: " + ex);
}
catch (IllegalArgumentException | IllegalStateException ex) {
throw ex;
}
catch (Throwable ex) {
logger.debug("PointcutExpression matching rejected target class", ex);
@@ -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.
@@ -50,7 +50,6 @@ import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConvertingComparator;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.MethodFilter;
import org.springframework.util.StringUtils;
@@ -134,19 +133,17 @@ public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFacto
List<Advisor> advisors = new ArrayList<>();
for (Method method : getAdvisorMethods(aspectClass)) {
if (method.equals(ClassUtils.getMostSpecificMethod(method, aspectClass))) {
// Prior to Spring Framework 5.2.7, advisors.size() was supplied as the declarationOrderInAspect
// to getAdvisor(...) to represent the "current position" in the declared methods list.
// However, since Java 7 the "current position" is not valid since the JDK no longer
// returns declared methods in the order in which they are declared in the source code.
// Thus, we now hard code the declarationOrderInAspect to 0 for all advice methods
// discovered via reflection in order to support reliable advice ordering across JVM launches.
// Specifically, a value of 0 aligns with the default value used in
// AspectJPrecedenceComparator.getAspectDeclarationOrder(Advisor).
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
if (advisor != null) {
advisors.add(advisor);
}
// Prior to Spring Framework 5.2.7, advisors.size() was supplied as the declarationOrderInAspect
// to getAdvisor(...) to represent the "current position" in the declared methods list.
// However, since Java 7 the "current position" is not valid since the JDK no longer
// returns declared methods in the order in which they are declared in the source code.
// Thus, we now hard code the declarationOrderInAspect to 0 for all advice methods
// discovered via reflection in order to support reliable advice ordering across JVM launches.
// Specifically, a value of 0 aligns with the default value used in
// AspectJPrecedenceComparator.getAspectDeclarationOrder(Advisor).
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
if (advisor != null) {
advisors.add(advisor);
}
}
@@ -39,13 +39,13 @@ 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.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
/**
* @author Rob Harrop
* @author Rod Johnson
* @author Chris Beams
* @author Juergen Hoeller
* @author Yanming Zhou
*/
class AspectJExpressionPointcutTests {
@@ -243,7 +243,7 @@ class AspectJExpressionPointcutTests {
@Test
void testInvalidExpression() {
String expression = "execution(void org.springframework.beans.testfixture.beans.TestBean.setSomeNumber(Number) && args(Double)";
assertThat(getPointcut(expression).getClassFilter().matches(Object.class)).isFalse();
assertThatIllegalArgumentException().isThrownBy(() -> getPointcut(expression).getClassFilter().matches(Object.class));
}
private TestBean getAdvisedProxy(String pointcutExpression, CallCountingInterceptor interceptor) {
@@ -83,15 +83,15 @@ abstract class AbstractAspectJAdvisorFactoryTests {
@Test
void rejectsPerCflowAspect() {
assertThatExceptionOfType(AopConfigException.class)
.isThrownBy(() -> getAdvisorFactory().getAdvisors(aspectInstanceFactory(new PerCflowAspect(), "someBean")))
.withMessageContaining("PERCFLOW");
.isThrownBy(() -> getAdvisorFactory().getAdvisors(aspectInstanceFactory(new PerCflowAspect(), "someBean")))
.withMessageContaining("PERCFLOW");
}
@Test
void rejectsPerCflowBelowAspect() {
assertThatExceptionOfType(AopConfigException.class)
.isThrownBy(() -> getAdvisorFactory().getAdvisors(aspectInstanceFactory(new PerCflowBelowAspect(), "someBean")))
.withMessageContaining("PERCFLOWBELOW");
.isThrownBy(() -> getAdvisorFactory().getAdvisors(aspectInstanceFactory(new PerCflowBelowAspect(), "someBean")))
.withMessageContaining("PERCFLOWBELOW");
}
@Test
@@ -770,15 +770,9 @@ abstract class AbstractAspectJAdvisorFactoryTests {
}
}
@Aspect
static class IncrementingAspect extends DoublingAspect {
@Override
public Object doubleAge(ProceedingJoinPoint pjp) throws Throwable {
return ((int) pjp.proceed()) * 2;
}
@Around("execution(* getAge())")
public int incrementAge(ProceedingJoinPoint pjp) throws Throwable {
return ((int) pjp.proceed()) + 1;
@@ -786,6 +780,7 @@ abstract class AbstractAspectJAdvisorFactoryTests {
}
@Aspect
private static class InvocationTrackingAspect {
@@ -22,12 +22,11 @@ import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author Adrian Colyer
* @author Juergen Hoeller
*/
class AutoProxyWithCodeStyleAspectsTests {
@Test
void noAutoProxyingOfAjcCompiledAspects() {
void noAutoproxyingOfAjcCompiledAspects() {
new ClassPathXmlApplicationContext("org/springframework/aop/aspectj/autoproxy/ajcAutoproxyTests.xml");
}
@@ -20,14 +20,11 @@ import org.junit.jupiter.api.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author Ramnivas Laddad
* @author Juergen Hoeller
*/
class SpringConfiguredWithAutoProxyingTests {
public class SpringConfiguredWithAutoProxyingTests {
@Test
void springConfiguredAndAutoProxyUsedTogether() {
// instantiation is sufficient to trigger failure if this is going to fail...
new ClassPathXmlApplicationContext("org/springframework/beans/factory/aspectj/springConfigured.xml");
}
@@ -2,21 +2,16 @@
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context-2.5.xsd">
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<aop:aspectj-autoproxy/>
<context:spring-configured/>
<bean id="myAspect" class="org.springframework.aop.aspectj.autoproxy.CodeStyleAspect" factory-method="aspectOf">
<bean id="myAspect" class="org.springframework.aop.aspectj.autoproxy.CodeStyleAspect"
factory-method="aspectOf">
<property name="foo" value="bar"/>
</bean>
<bean id="otherBean" class="java.lang.Object"/>
<bean id="yetAnotherBean" class="java.lang.Object"/>
</beans>
@@ -165,7 +165,7 @@ class BeanDefinitionMethodGenerator {
this.aotContributions.forEach(aotContribution -> aotContribution.applyTo(generationContext, codeGenerator));
CodeWarnings codeWarnings = new CodeWarnings();
codeWarnings.detectDeprecation(this.registeredBean.getBeanType());
codeWarnings.detectDeprecation(this.registeredBean.getBeanClass());
return generatedMethods.add("getBeanDefinition", method -> {
method.addJavadoc("Get the $L definition for '$L'.",
(this.registeredBean.isInnerBean() ? "inner-bean" : "bean"),
@@ -23,12 +23,10 @@ import java.util.Set;
import java.util.StringJoiner;
import java.util.stream.Stream;
import org.springframework.core.ResolvableType;
import org.springframework.javapoet.AnnotationSpec;
import org.springframework.javapoet.CodeBlock;
import org.springframework.javapoet.MethodSpec;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
/**
* Helper class to register warnings that the compiler may trigger on
@@ -74,26 +72,6 @@ class CodeWarnings {
return this;
}
/**
* Detect the presence of {@link Deprecated} on the signature of the
* specified {@link ResolvableType}.
* @param resolvableType a type signature
* @return {@code this} instance
*/
public CodeWarnings detectDeprecation(ResolvableType resolvableType) {
if (ResolvableType.NONE.equals(resolvableType)) {
return this;
}
Class<?> type = ClassUtils.getUserClass(resolvableType.toClass());
detectDeprecation(type);
if (resolvableType.hasGenerics() && !resolvableType.hasUnresolvableGenerics()) {
for (ResolvableType generic : resolvableType.getGenerics()) {
detectDeprecation(generic);
}
}
return this;
}
/**
* Include {@link SuppressWarnings} on the specified method if necessary.
* @param method the method to update
@@ -782,19 +782,6 @@ class BeanDefinitionMethodGeneratorTests {
compileAndCheckWarnings(method);
}
@Test
void generateBeanDefinitionMethodWithDeprecatedGenericElementInTargetClass() {
RootBeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setTargetType(ResolvableType.forClassWithGenerics(GenericBean.class, DeprecatedBean.class));
RegisteredBean registeredBean = registerBean(beanDefinition);
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
methodGeneratorFactory, registeredBean, null,
Collections.emptyList());
MethodReference method = generator.generateBeanDefinitionMethod(
generationContext, beanRegistrationsCode);
compileAndCheckWarnings(method);
}
private void compileAndCheckWarnings(MethodReference methodReference) {
assertThatNoException().isThrownBy(() -> compile(TEST_COMPILER, methodReference,
((instanceSupplier, compiled) -> {})));
@@ -17,21 +17,15 @@
package org.springframework.beans.factory.aot;
import java.util.function.Consumer;
import java.util.stream.Stream;
import javax.lang.model.element.Modifier;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.aot.test.generate.TestGenerationContext;
import org.springframework.beans.testfixture.beans.GenericBean;
import org.springframework.beans.testfixture.beans.factory.aot.DeferredTypeBuilder;
import org.springframework.beans.testfixture.beans.factory.generator.deprecation.DeprecatedBean;
import org.springframework.beans.testfixture.beans.factory.generator.deprecation.DeprecatedForRemovalBean;
import org.springframework.core.ResolvableType;
import org.springframework.core.test.tools.Compiled;
import org.springframework.core.test.tools.TestCompiler;
import org.springframework.javapoet.MethodSpec;
@@ -104,40 +98,6 @@ class CodeWarningsTests {
assertThat(this.codeWarnings.getWarnings()).containsExactly("removal");
}
@ParameterizedTest
@MethodSource("resolvableTypesWithDeprecated")
void detectDeprecationOnResolvableTypeWithDeprecated(ResolvableType resolvableType) {
this.codeWarnings.detectDeprecation(resolvableType);
assertThat(this.codeWarnings.getWarnings()).containsExactly("deprecation");
}
@SuppressWarnings("deprecation")
static Stream<Arguments> resolvableTypesWithDeprecated() {
return Stream.of(
Arguments.of(ResolvableType.forClass(DeprecatedBean.class)),
Arguments.of(ResolvableType.forClassWithGenerics(GenericBean.class, DeprecatedBean.class)),
Arguments.of(ResolvableType.forClassWithGenerics(GenericBean.class,
ResolvableType.forClassWithGenerics(GenericBean.class, DeprecatedBean.class)))
);
}
@ParameterizedTest
@MethodSource("resolvableTypesWithDeprecatedForRemoval")
void detectDeprecationOnResolvableTypeWithDeprecatedForRemoval(ResolvableType resolvableType) {
this.codeWarnings.detectDeprecation(resolvableType);
assertThat(this.codeWarnings.getWarnings()).containsExactly("removal");
}
@SuppressWarnings("removal")
static Stream<Arguments> resolvableTypesWithDeprecatedForRemoval() {
return Stream.of(
Arguments.of(ResolvableType.forClass(DeprecatedForRemovalBean.class)),
Arguments.of(ResolvableType.forClassWithGenerics(GenericBean.class, DeprecatedForRemovalBean.class)),
Arguments.of(ResolvableType.forClassWithGenerics(GenericBean.class,
ResolvableType.forClassWithGenerics(GenericBean.class, DeprecatedForRemovalBean.class)))
);
}
@Test
void toStringIncludeWarnings() {
this.codeWarnings.register("deprecation");
@@ -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,10 +22,8 @@ import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.EnumMap;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import org.springframework.format.Formatter;
@@ -37,14 +35,9 @@ import org.springframework.util.StringUtils;
/**
* A formatter for {@link java.util.Date} types.
*
* <p>Supports the configuration of an explicit date time pattern, timezone,
* locale, and fallback date time patterns for lenient parsing.
*
* <p>Common ISO patterns for UTC instants are applied at millisecond precision.
* Note that {@link org.springframework.format.datetime.standard.InstantFormatter}
* is recommended for flexible UTC parsing into a {@link java.time.Instant} instead.
*
* @author Keith Donald
* @author Juergen Hoeller
* @author Phillip Webb
@@ -56,23 +49,15 @@ public class DateFormatter implements Formatter<Date> {
private static final TimeZone UTC = TimeZone.getTimeZone("UTC");
// We use an EnumMap instead of Map.of(...) since the former provides better performance.
private static final Map<ISO, String> ISO_PATTERNS;
private static final Map<ISO, String> ISO_FALLBACK_PATTERNS;
static {
// We use an EnumMap instead of Map.of(...) since the former provides better performance.
Map<ISO, String> formats = new EnumMap<>(ISO.class);
formats.put(ISO.DATE, "yyyy-MM-dd");
formats.put(ISO.TIME, "HH:mm:ss.SSSXXX");
formats.put(ISO.DATE_TIME, "yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
ISO_PATTERNS = Collections.unmodifiableMap(formats);
// Fallback format for the time part without milliseconds.
Map<ISO, String> fallbackFormats = new EnumMap<>(ISO.class);
fallbackFormats.put(ISO.TIME, "HH:mm:ssXXX");
fallbackFormats.put(ISO.DATE_TIME, "yyyy-MM-dd'T'HH:mm:ssXXX");
ISO_FALLBACK_PATTERNS = Collections.unmodifiableMap(fallbackFormats);
}
@@ -217,16 +202,8 @@ public class DateFormatter implements Formatter<Date> {
return getDateFormat(locale).parse(text);
}
catch (ParseException ex) {
Set<String> fallbackPatterns = new LinkedHashSet<>();
String isoPattern = ISO_FALLBACK_PATTERNS.get(this.iso);
if (isoPattern != null) {
fallbackPatterns.add(isoPattern);
}
if (!ObjectUtils.isEmpty(this.fallbackPatterns)) {
Collections.addAll(fallbackPatterns, this.fallbackPatterns);
}
if (!fallbackPatterns.isEmpty()) {
for (String pattern : fallbackPatterns) {
for (String pattern : this.fallbackPatterns) {
try {
DateFormat dateFormat = configureDateFormat(new SimpleDateFormat(pattern, locale));
// Align timezone for parsing format with printing format if ISO is set.
@@ -244,8 +221,8 @@ public class DateFormatter implements Formatter<Date> {
}
if (this.source != null) {
ParseException parseException = new ParseException(
String.format("Unable to parse date time value \"%s\" using configuration from %s", text, this.source),
ex.getErrorOffset());
String.format("Unable to parse date time value \"%s\" using configuration from %s", text, this.source),
ex.getErrorOffset());
parseException.initCause(ex);
throw parseException;
}
@@ -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,12 +45,12 @@ public class InstantFormatter implements Formatter<Instant> {
return Instant.ofEpochMilli(Long.parseLong(text));
}
catch (NumberFormatException ex) {
if (!text.isEmpty() && Character.isAlphabetic(text.charAt(0))) {
if (text.length() > 0 && Character.isAlphabetic(text.charAt(0))) {
// assuming RFC-1123 value a la "Tue, 3 Jun 2008 11:05:30 GMT"
return Instant.from(DateTimeFormatter.RFC_1123_DATE_TIME.parse(text));
}
else {
// assuming UTC instant a la "2007-12-03T10:15:30.000Z"
// assuming UTC instant a la "2007-12-03T10:15:30.00Z"
return Instant.parse(text);
}
}
@@ -28,14 +28,17 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
*
* @author Adrian Colyer
* @author Chris Beams
* @author Juergen Hoeller
*/
class OverloadedAdviceTests {
@Test
@SuppressWarnings("resource")
void testConfigParsingWithMismatchedAdviceMethod() {
new ClassPathXmlApplicationContext(getClass().getSimpleName() + ".xml", getClass());
void testExceptionOnConfigParsingWithMismatchedAdviceMethod() {
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(() -> new ClassPathXmlApplicationContext(getClass().getSimpleName() + ".xml", getClass()))
.havingRootCause()
.isInstanceOf(IllegalArgumentException.class)
.as("invalidAbsoluteTypeName should be detected by AJ").withMessageContaining("invalidAbsoluteTypeName");
}
@Test
@@ -35,7 +35,6 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
*
* @author Keith Donald
* @author Phillip Webb
* @author Juergen Hoeller
*/
class DateFormatterTests {
@@ -46,7 +45,6 @@ class DateFormatterTests {
void shouldPrintAndParseDefault() throws Exception {
DateFormatter formatter = new DateFormatter();
formatter.setTimeZone(UTC);
Date date = getDate(2009, Calendar.JUNE, 1);
assertThat(formatter.print(date, Locale.US)).isEqualTo("Jun 1, 2009");
assertThat(formatter.parse("Jun 1, 2009", Locale.US)).isEqualTo(date);
@@ -56,7 +54,6 @@ class DateFormatterTests {
void shouldPrintAndParseFromPattern() throws ParseException {
DateFormatter formatter = new DateFormatter("yyyy-MM-dd");
formatter.setTimeZone(UTC);
Date date = getDate(2009, Calendar.JUNE, 1);
assertThat(formatter.print(date, Locale.US)).isEqualTo("2009-06-01");
assertThat(formatter.parse("2009-06-01", Locale.US)).isEqualTo(date);
@@ -67,7 +64,6 @@ class DateFormatterTests {
DateFormatter formatter = new DateFormatter();
formatter.setTimeZone(UTC);
formatter.setStyle(DateFormat.SHORT);
Date date = getDate(2009, Calendar.JUNE, 1);
assertThat(formatter.print(date, Locale.US)).isEqualTo("6/1/09");
assertThat(formatter.parse("6/1/09", Locale.US)).isEqualTo(date);
@@ -78,7 +74,6 @@ class DateFormatterTests {
DateFormatter formatter = new DateFormatter();
formatter.setTimeZone(UTC);
formatter.setStyle(DateFormat.MEDIUM);
Date date = getDate(2009, Calendar.JUNE, 1);
assertThat(formatter.print(date, Locale.US)).isEqualTo("Jun 1, 2009");
assertThat(formatter.parse("Jun 1, 2009", Locale.US)).isEqualTo(date);
@@ -89,7 +84,6 @@ class DateFormatterTests {
DateFormatter formatter = new DateFormatter();
formatter.setTimeZone(UTC);
formatter.setStyle(DateFormat.LONG);
Date date = getDate(2009, Calendar.JUNE, 1);
assertThat(formatter.print(date, Locale.US)).isEqualTo("June 1, 2009");
assertThat(formatter.parse("June 1, 2009", Locale.US)).isEqualTo(date);
@@ -100,18 +94,16 @@ class DateFormatterTests {
DateFormatter formatter = new DateFormatter();
formatter.setTimeZone(UTC);
formatter.setStyle(DateFormat.FULL);
Date date = getDate(2009, Calendar.JUNE, 1);
assertThat(formatter.print(date, Locale.US)).isEqualTo("Monday, June 1, 2009");
assertThat(formatter.parse("Monday, June 1, 2009", Locale.US)).isEqualTo(date);
}
@Test
void shouldPrintAndParseIsoDate() throws Exception {
void shouldPrintAndParseISODate() throws Exception {
DateFormatter formatter = new DateFormatter();
formatter.setTimeZone(UTC);
formatter.setIso(ISO.DATE);
Date date = getDate(2009, Calendar.JUNE, 1, 14, 23, 5, 3);
assertThat(formatter.print(date, Locale.US)).isEqualTo("2009-06-01");
assertThat(formatter.parse("2009-6-01", Locale.US))
@@ -119,44 +111,33 @@ class DateFormatterTests {
}
@Test
void shouldPrintAndParseIsoTime() throws Exception {
void shouldPrintAndParseISOTime() throws Exception {
DateFormatter formatter = new DateFormatter();
formatter.setTimeZone(UTC);
formatter.setIso(ISO.TIME);
Date date = getDate(2009, Calendar.JANUARY, 1, 14, 23, 5, 3);
assertThat(formatter.print(date, Locale.US)).isEqualTo("14:23:05.003Z");
assertThat(formatter.parse("14:23:05.003Z", Locale.US))
.isEqualTo(getDate(1970, Calendar.JANUARY, 1, 14, 23, 5, 3));
date = getDate(2009, Calendar.JANUARY, 1, 14, 23, 5, 0);
assertThat(formatter.print(date, Locale.US)).isEqualTo("14:23:05.000Z");
assertThat(formatter.parse("14:23:05Z", Locale.US))
.isEqualTo(getDate(1970, Calendar.JANUARY, 1, 14, 23, 5, 0).toInstant());
}
@Test
void shouldPrintAndParseIsoDateTime() throws Exception {
void shouldPrintAndParseISODateTime() throws Exception {
DateFormatter formatter = new DateFormatter();
formatter.setTimeZone(UTC);
formatter.setIso(ISO.DATE_TIME);
Date date = getDate(2009, Calendar.JUNE, 1, 14, 23, 5, 3);
assertThat(formatter.print(date, Locale.US)).isEqualTo("2009-06-01T14:23:05.003Z");
assertThat(formatter.parse("2009-06-01T14:23:05.003Z", Locale.US)).isEqualTo(date);
date = getDate(2009, Calendar.JUNE, 1, 14, 23, 5, 0);
assertThat(formatter.print(date, Locale.US)).isEqualTo("2009-06-01T14:23:05.000Z");
assertThat(formatter.parse("2009-06-01T14:23:05Z", Locale.US)).isEqualTo(date.toInstant());
}
@Test
void shouldThrowOnUnsupportedStylePattern() {
DateFormatter formatter = new DateFormatter();
formatter.setStylePattern("OO");
assertThatIllegalStateException().isThrownBy(() -> formatter.parse("2009", Locale.US))
.withMessageContaining("Unsupported style pattern 'OO'");
assertThatIllegalStateException().isThrownBy(() ->
formatter.parse("2009", Locale.US))
.withMessageContaining("Unsupported style pattern 'OO'");
}
@Test
@@ -167,8 +148,8 @@ class DateFormatterTests {
formatter.setStylePattern("L-");
formatter.setIso(ISO.DATE_TIME);
formatter.setPattern("yyyy");
Date date = getDate(2009, Calendar.JUNE, 1, 14, 23, 5, 3);
assertThat(formatter.print(date, Locale.US)).as("uses pattern").isEqualTo("2009");
formatter.setPattern("");
@@ -274,7 +274,9 @@ class DateTimeFormattingTests {
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
String value = binder.getBindingResult().getFieldValue("localDateTime").toString();
assertThat(value).startsWith("10/31/09").endsWith("12:00 PM");
assertThat(value)
.startsWith("10/31/09")
.endsWith("12:00 PM");
}
@Test
@@ -284,7 +286,9 @@ class DateTimeFormattingTests {
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
String value = binder.getBindingResult().getFieldValue("localDateTime").toString();
assertThat(value).startsWith("10/31/09").endsWith("12:00 PM");
assertThat(value)
.startsWith("10/31/09")
.endsWith("12:00 PM");
}
@Test
@@ -294,7 +298,9 @@ class DateTimeFormattingTests {
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
String value = binder.getBindingResult().getFieldValue("styleLocalDateTime").toString();
assertThat(value).startsWith("Oct 31, 2009").endsWith("12:00:00 PM");
assertThat(value)
.startsWith("Oct 31, 2009")
.endsWith("12:00:00 PM");
}
@Test
@@ -304,7 +310,9 @@ class DateTimeFormattingTests {
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
String value = binder.getBindingResult().getFieldValue("localDateTime").toString();
assertThat(value).startsWith("10/31/09").endsWith("12:00 PM");
assertThat(value)
.startsWith("10/31/09")
.endsWith("12:00 PM");
}
@Test
@@ -317,7 +325,9 @@ class DateTimeFormattingTests {
binder.bind(propertyValues);
assertThat(binder.getBindingResult().getErrorCount()).isZero();
String value = binder.getBindingResult().getFieldValue("localDateTime").toString();
assertThat(value).startsWith("Oct 31, 2009").endsWith("12:00:00 PM");
assertThat(value)
.startsWith("Oct 31, 2009")
.endsWith("12:00:00 PM");
}
@Test
@@ -530,7 +540,6 @@ class DateTimeFormattingTests {
assertThat(binder.getBindingResult().getRawFieldValue("monthDayAnnotatedPattern")).isEqualTo(MonthDay.parse("--01-03"));
}
@Nested
class FallbackPatternTests {
@@ -635,10 +644,10 @@ class DateTimeFormattingTests {
@DateTimeFormat(style = "M-")
private LocalDate styleLocalDate;
@DateTimeFormat(style = "S-", fallbackPatterns = {"yyyy-MM-dd", "yyyyMMdd", "yyyy.MM.dd"})
@DateTimeFormat(style = "S-", fallbackPatterns = { "yyyy-MM-dd", "yyyyMMdd", "yyyy.MM.dd" })
private LocalDate styleLocalDateWithFallbackPatterns;
@DateTimeFormat(pattern = "yyyy-MM-dd", fallbackPatterns = {"M/d/yy", "yyyyMMdd", "yyyy.MM.dd"})
@DateTimeFormat(pattern = "yyyy-MM-dd", fallbackPatterns = { "M/d/yy", "yyyyMMdd", "yyyy.MM.dd" })
private LocalDate patternLocalDateWithFallbackPatterns;
private LocalTime localTime;
@@ -646,7 +655,7 @@ class DateTimeFormattingTests {
@DateTimeFormat(style = "-M")
private LocalTime styleLocalTime;
@DateTimeFormat(style = "-M", fallbackPatterns = {"HH:mm:ss", "HH:mm"})
@DateTimeFormat(style = "-M", fallbackPatterns = { "HH:mm:ss", "HH:mm"})
private LocalTime styleLocalTimeWithFallbackPatterns;
private LocalDateTime localDateTime;
@@ -666,7 +675,7 @@ class DateTimeFormattingTests {
@DateTimeFormat(iso = ISO.DATE_TIME)
private LocalDateTime isoLocalDateTime;
@DateTimeFormat(iso = ISO.DATE_TIME, fallbackPatterns = {"yyyy-MM-dd HH:mm:ss", "M/d/yy HH:mm"})
@DateTimeFormat(iso = ISO.DATE_TIME, fallbackPatterns = { "yyyy-MM-dd HH:mm:ss", "M/d/yy HH:mm"})
private LocalDateTime isoLocalDateTimeWithFallbackPatterns;
private Instant instant;
@@ -694,6 +703,7 @@ class DateTimeFormattingTests {
private final List<DateTimeBean> children = new ArrayList<>();
public LocalDate getLocalDate() {
return this.localDate;
}
@@ -20,7 +20,6 @@ import java.text.ParseException;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Locale;
import java.util.Random;
import java.util.stream.Stream;
@@ -51,12 +50,13 @@ class InstantFormatterTests {
private final InstantFormatter instantFormatter = new InstantFormatter();
@ParameterizedTest
@ArgumentsSource(ISOSerializedInstantProvider.class)
void should_parse_an_ISO_formatted_string_representation_of_an_Instant(String input) throws ParseException {
Instant expected = DateTimeFormatter.ISO_INSTANT.parse(input, Instant::from);
Instant actual = instantFormatter.parse(input, Locale.US);
Instant actual = instantFormatter.parse(input, null);
assertThat(actual).isEqualTo(expected);
}
@@ -64,7 +64,9 @@ class InstantFormatterTests {
@ArgumentsSource(RFC1123SerializedInstantProvider.class)
void should_parse_an_RFC1123_formatted_string_representation_of_an_Instant(String input) throws ParseException {
Instant expected = DateTimeFormatter.RFC_1123_DATE_TIME.parse(input, Instant::from);
Instant actual = instantFormatter.parse(input, Locale.US);
Instant actual = instantFormatter.parse(input, null);
assertThat(actual).isEqualTo(expected);
}
@@ -72,18 +74,20 @@ class InstantFormatterTests {
@ArgumentsSource(RandomInstantProvider.class)
void should_serialize_an_Instant_using_ISO_format_and_ignoring_Locale(Instant input) {
String expected = DateTimeFormatter.ISO_INSTANT.format(input);
String actual = instantFormatter.print(input, Locale.US);
String actual = instantFormatter.print(input, null);
assertThat(actual).isEqualTo(expected);
}
@ParameterizedTest
@ArgumentsSource(RandomEpochMillisProvider.class)
void should_parse_into_an_Instant_from_epoch_milli(Instant input) throws ParseException {
Instant actual = instantFormatter.parse(Long.toString(input.toEpochMilli()), Locale.US);
Instant actual = instantFormatter.parse(Long.toString(input.toEpochMilli()), null);
assertThat(actual).isEqualTo(input);
}
private static class RandomInstantProvider implements ArgumentsProvider {
private static final long DATA_SET_SIZE = 10;
@@ -105,7 +109,6 @@ class InstantFormatterTests {
}
}
private static class ISOSerializedInstantProvider extends RandomInstantProvider {
@Override
@@ -114,7 +117,6 @@ class InstantFormatterTests {
}
}
private static class RFC1123SerializedInstantProvider extends RandomInstantProvider {
// RFC-1123 supports only 4-digit years
@@ -128,8 +130,6 @@ class InstantFormatterTests {
.map(DateTimeFormatter.RFC_1123_DATE_TIME.withZone(systemDefault())::format);
}
}
private static final class RandomEpochMillisProvider implements ArgumentsProvider {
private static final long DATA_SET_SIZE = 10;
@@ -18,6 +18,4 @@
<bean id="testBean" class="org.springframework.beans.testfixture.beans.TestBean"/>
<bean id="testBean2" class="org.springframework.beans.testfixture.beans.TestBean"/>
</beans>
@@ -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.
@@ -154,7 +154,7 @@ final class PersistenceUnitReader {
/**
* Validate the given stream and return a valid DOM document for parsing.
*/
Document buildDocument(ErrorHandler handler, InputStream stream)
protected Document buildDocument(ErrorHandler handler, InputStream stream)
throws ParserConfigurationException, SAXException, IOException {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
@@ -168,7 +168,9 @@ final class PersistenceUnitReader {
/**
* Parse the validated document and add entries to the given unit info list.
*/
void parseDocument(Resource resource, Document document, List<SpringPersistenceUnitInfo> infos) throws IOException {
protected List<SpringPersistenceUnitInfo> parseDocument(
Resource resource, Document document, List<SpringPersistenceUnitInfo> infos) throws IOException {
Element persistence = document.getDocumentElement();
String version = persistence.getAttribute(PERSISTENCE_VERSION);
URL rootUrl = determinePersistenceUnitRootUrl(resource);
@@ -177,12 +179,14 @@ final class PersistenceUnitReader {
for (Element unit : units) {
infos.add(parsePersistenceUnitInfo(unit, version, rootUrl));
}
return infos;
}
/**
* Parse the unit info DOM element.
*/
SpringPersistenceUnitInfo parsePersistenceUnitInfo(
protected SpringPersistenceUnitInfo parsePersistenceUnitInfo(
Element persistenceUnit, String version, @Nullable URL rootUrl) throws IOException {
SpringPersistenceUnitInfo unitInfo = new SpringPersistenceUnitInfo();
@@ -249,7 +253,7 @@ final class PersistenceUnitReader {
/**
* Parse the {@code property} XML elements.
*/
void parseProperties(Element persistenceUnit, SpringPersistenceUnitInfo unitInfo) {
protected void parseProperties(Element persistenceUnit, SpringPersistenceUnitInfo unitInfo) {
Element propRoot = DomUtils.getChildElementByTagName(persistenceUnit, PROPERTIES);
if (propRoot == null) {
return;
@@ -265,7 +269,7 @@ final class PersistenceUnitReader {
/**
* Parse the {@code class} XML elements.
*/
void parseManagedClasses(Element persistenceUnit, SpringPersistenceUnitInfo unitInfo) {
protected void parseManagedClasses(Element persistenceUnit, SpringPersistenceUnitInfo unitInfo) {
List<Element> classes = DomUtils.getChildElementsByTagName(persistenceUnit, MANAGED_CLASS_NAME);
for (Element element : classes) {
String value = DomUtils.getTextValue(element).trim();
@@ -278,7 +282,7 @@ final class PersistenceUnitReader {
/**
* Parse the {@code mapping-file} XML elements.
*/
void parseMappingFiles(Element persistenceUnit, SpringPersistenceUnitInfo unitInfo) {
protected void parseMappingFiles(Element persistenceUnit, SpringPersistenceUnitInfo unitInfo) {
List<Element> files = DomUtils.getChildElementsByTagName(persistenceUnit, MAPPING_FILE_NAME);
for (Element element : files) {
String value = DomUtils.getTextValue(element).trim();
@@ -291,7 +295,7 @@ final class PersistenceUnitReader {
/**
* Parse the {@code jar-file} XML elements.
*/
void parseJarFiles(Element persistenceUnit, SpringPersistenceUnitInfo unitInfo) throws IOException {
protected void parseJarFiles(Element persistenceUnit, SpringPersistenceUnitInfo unitInfo) throws IOException {
List<Element> jars = DomUtils.getChildElementsByTagName(persistenceUnit, JAR_FILE_URL);
for (Element element : jars) {
String value = DomUtils.getTextValue(element).trim();
@@ -37,8 +37,6 @@ import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.xml.XMLConstants;
import javax.xml.datatype.Duration;
@@ -194,7 +192,7 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, Generi
@Nullable
private ClassLoader beanClassLoader;
private final Lock jaxbContextLock = new ReentrantLock();
private final Object jaxbContextMonitor = new Object();
@Nullable
private volatile JAXBContext jaxbContext;
@@ -206,12 +204,6 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, Generi
private boolean processExternalEntities = false;
@Nullable
private volatile SAXParserFactory schemaParserFactory;
@Nullable
private volatile SAXParserFactory sourceParserFactory;
/**
* Set multiple JAXB context paths. The given array of context paths gets
@@ -434,7 +426,6 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, Generi
*/
public void setSupportDtd(boolean supportDtd) {
this.supportDtd = supportDtd;
this.sourceParserFactory = null;
}
/**
@@ -459,7 +450,6 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, Generi
if (processExternalEntities) {
this.supportDtd = true;
}
this.sourceParserFactory = null;
}
/**
@@ -507,9 +497,7 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, Generi
if (context != null) {
return context;
}
this.jaxbContextLock.lock();
try {
synchronized (this.jaxbContextMonitor) {
context = this.jaxbContext;
if (context == null) {
try {
@@ -533,9 +521,6 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, Generi
}
return context;
}
finally {
this.jaxbContextLock.unlock();
}
}
private JAXBContext createJaxbContextFromContextPath(String contextPath) throws JAXBException {
@@ -602,24 +587,17 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, Generi
Assert.notEmpty(resources, "No resources given");
Assert.hasLength(schemaLanguage, "No schema language provided");
Source[] schemaSources = new Source[resources.length];
SAXParserFactory saxParserFactory = this.schemaParserFactory;
if (saxParserFactory == null) {
saxParserFactory = SAXParserFactory.newInstance();
saxParserFactory.setNamespaceAware(true);
saxParserFactory.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
this.schemaParserFactory = saxParserFactory;
}
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
saxParserFactory.setNamespaceAware(true);
saxParserFactory.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
SAXParser saxParser = saxParserFactory.newSAXParser();
XMLReader xmlReader = saxParser.getXMLReader();
for (int i = 0; i < resources.length; i++) {
Resource resource = resources[i];
Assert.isTrue(resource != null && resource.exists(), () -> "Resource does not exist: " + resource);
InputSource inputSource = SaxResourceUtils.createInputSource(resource);
schemaSources[i] = new SAXSource(xmlReader, inputSource);
}
SchemaFactory schemaFactory = SchemaFactory.newInstance(schemaLanguage);
if (this.schemaResourceResolver != null) {
schemaFactory.setResourceResolver(this.schemaResourceResolver);
@@ -908,16 +886,11 @@ public class Jaxb2Marshaller implements MimeMarshaller, MimeUnmarshaller, Generi
try {
if (xmlReader == null) {
SAXParserFactory saxParserFactory = this.sourceParserFactory;
if (saxParserFactory == null) {
saxParserFactory = SAXParserFactory.newInstance();
saxParserFactory.setNamespaceAware(true);
saxParserFactory.setFeature(
"http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd());
saxParserFactory.setFeature(
"http://xml.org/sax/features/external-general-entities", isProcessExternalEntities());
this.sourceParserFactory = saxParserFactory;
}
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
saxParserFactory.setNamespaceAware(true);
saxParserFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd());
String name = "http://xml.org/sax/features/external-general-entities";
saxParserFactory.setFeature(name, isProcessExternalEntities());
SAXParser saxParser = saxParserFactory.newSAXParser();
xmlReader = saxParser.getXMLReader();
}
@@ -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.
@@ -83,10 +83,9 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
private boolean processExternalEntities = false;
@Nullable
private volatile DocumentBuilderFactory documentBuilderFactory;
private DocumentBuilderFactory documentBuilderFactory;
@Nullable
private volatile SAXParserFactory saxParserFactory;
private final Object documentBuilderFactoryMonitor = new Object();
/**
@@ -95,8 +94,6 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
*/
public void setSupportDtd(boolean supportDtd) {
this.supportDtd = supportDtd;
this.documentBuilderFactory = null;
this.saxParserFactory = null;
}
/**
@@ -121,8 +118,6 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
if (processExternalEntities) {
this.supportDtd = true;
}
this.documentBuilderFactory = null;
this.saxParserFactory = null;
}
/**
@@ -142,13 +137,14 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
*/
protected Document buildDocument() {
try {
DocumentBuilderFactory builderFactory = this.documentBuilderFactory;
if (builderFactory == null) {
builderFactory = createDocumentBuilderFactory();
this.documentBuilderFactory = builderFactory;
DocumentBuilder documentBuilder;
synchronized (this.documentBuilderFactoryMonitor) {
if (this.documentBuilderFactory == null) {
this.documentBuilderFactory = createDocumentBuilderFactory();
}
documentBuilder = createDocumentBuilder(this.documentBuilderFactory);
}
DocumentBuilder builder = createDocumentBuilder(builderFactory);
return builder.newDocument();
return documentBuilder.newDocument();
}
catch (ParserConfigurationException ex) {
throw new UnmarshallingFailureException("Could not create document placeholder: " + ex.getMessage(), ex);
@@ -183,11 +179,11 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
protected DocumentBuilder createDocumentBuilder(DocumentBuilderFactory factory)
throws ParserConfigurationException {
DocumentBuilder builder = factory.newDocumentBuilder();
DocumentBuilder documentBuilder = factory.newDocumentBuilder();
if (!isProcessExternalEntities()) {
builder.setEntityResolver(NO_OP_ENTITY_RESOLVER);
documentBuilder.setEntityResolver(NO_OP_ENTITY_RESOLVER);
}
return builder;
return documentBuilder;
}
/**
@@ -197,17 +193,11 @@ public abstract class AbstractMarshaller implements Marshaller, Unmarshaller {
* @throws ParserConfigurationException if thrown by JAXP methods
*/
protected XMLReader createXmlReader() throws SAXException, ParserConfigurationException {
SAXParserFactory parserFactory = this.saxParserFactory;
if (parserFactory == null) {
parserFactory = SAXParserFactory.newInstance();
parserFactory.setNamespaceAware(true);
parserFactory.setFeature(
"http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd());
parserFactory.setFeature(
"http://xml.org/sax/features/external-general-entities", isProcessExternalEntities());
this.saxParserFactory = parserFactory;
}
SAXParser saxParser = parserFactory.newSAXParser();
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
saxParserFactory.setNamespaceAware(true);
saxParserFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd());
saxParserFactory.setFeature("http://xml.org/sax/features/external-general-entities", isProcessExternalEntities());
SAXParser saxParser = saxParserFactory.newSAXParser();
XMLReader xmlReader = saxParser.getXMLReader();
if (!isProcessExternalEntities()) {
xmlReader.setEntityResolver(NO_OP_ENTITY_RESOLVER);
@@ -100,6 +100,9 @@ public class MockHttpServletRequest implements HttpServletRequest {
private static final TimeZone GMT = TimeZone.getTimeZone("GMT");
private static final BufferedReader EMPTY_BUFFERED_READER =
new BufferedReader(new StringReader(""));
/**
* Date formats as specified in the HTTP RFC.
* @see <a href="https://tools.ietf.org/html/rfc7231#section-7.1.1.1">Section 7.1.1.1 of RFC 7231</a>
@@ -735,7 +738,7 @@ public class MockHttpServletRequest implements HttpServletRequest {
this.reader = new BufferedReader(sourceReader);
}
else {
this.reader = new BufferedReader(new StringReader(""));
this.reader = EMPTY_BUFFERED_READER;
}
return this.reader;
}
@@ -93,17 +93,6 @@ class MockHttpServletRequestTests {
secondRequest.getInputStream().close();
}
@Test // gh-32820
void readEmptyReaderWorksAcrossRequests() throws IOException {
MockHttpServletRequest firstRequest = new MockHttpServletRequest();
firstRequest.getReader().read(new char[256]);
firstRequest.getReader().close();
MockHttpServletRequest secondRequest = new MockHttpServletRequest();
secondRequest.getReader().read(new char[256]);
secondRequest.getReader().close();
}
@Test
void setContentAndGetReader() throws IOException {
byte[] bytes = "body".getBytes(Charset.defaultCharset());
@@ -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.
@@ -61,7 +61,6 @@ import org.springframework.util.ClassUtils;
* @author Arjen Poutsma
* @author Sebastien Deleuze
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @since 3.0
* @see MarshallingHttpMessageConverter
*/
@@ -71,9 +70,6 @@ public class Jaxb2RootElementHttpMessageConverter extends AbstractJaxb2HttpMessa
private boolean processExternalEntities = false;
@Nullable
private volatile SAXParserFactory sourceParserFactory;
/**
* Indicate whether DTD parsing should be supported.
@@ -81,7 +77,6 @@ public class Jaxb2RootElementHttpMessageConverter extends AbstractJaxb2HttpMessa
*/
public void setSupportDtd(boolean supportDtd) {
this.supportDtd = supportDtd;
this.sourceParserFactory = null;
}
/**
@@ -102,7 +97,6 @@ public class Jaxb2RootElementHttpMessageConverter extends AbstractJaxb2HttpMessa
if (processExternalEntities) {
this.supportDtd = true;
}
this.sourceParserFactory = null;
}
/**
@@ -162,16 +156,11 @@ public class Jaxb2RootElementHttpMessageConverter extends AbstractJaxb2HttpMessa
if (source instanceof StreamSource streamSource) {
InputSource inputSource = new InputSource(streamSource.getInputStream());
try {
SAXParserFactory saxParserFactory = this.sourceParserFactory;
if (saxParserFactory == null) {
saxParserFactory = SAXParserFactory.newInstance();
saxParserFactory.setNamespaceAware(true);
saxParserFactory.setFeature(
"http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd());
saxParserFactory.setFeature(
"http://xml.org/sax/features/external-general-entities", isProcessExternalEntities());
this.sourceParserFactory = saxParserFactory;
}
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
saxParserFactory.setNamespaceAware(true);
saxParserFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd());
String featureName = "http://xml.org/sax/features/external-general-entities";
saxParserFactory.setFeature(featureName, isProcessExternalEntities());
SAXParser saxParser = saxParserFactory.newSAXParser();
XMLReader xmlReader = saxParser.getXMLReader();
if (!isProcessExternalEntities()) {
@@ -63,7 +63,6 @@ import org.springframework.util.StreamUtils;
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @since 3.0
* @param <T> the converted object type
*/
@@ -76,7 +75,11 @@ public class SourceHttpMessageConverter<T extends Source> extends AbstractHttpMe
(publicID, systemID, base, ns) -> InputStream.nullInputStream();
private static final Set<Class<?>> SUPPORTED_CLASSES = Set.of(
DOMSource.class, SAXSource.class, StAXSource.class, StreamSource.class, Source.class);
DOMSource.class,
SAXSource.class,
StAXSource.class,
StreamSource.class,
Source.class);
private final TransformerFactory transformerFactory = TransformerFactory.newInstance();
@@ -85,15 +88,6 @@ public class SourceHttpMessageConverter<T extends Source> extends AbstractHttpMe
private boolean processExternalEntities = false;
@Nullable
private volatile DocumentBuilderFactory documentBuilderFactory;
@Nullable
private volatile SAXParserFactory saxParserFactory;
@Nullable
private volatile XMLInputFactory xmlInputFactory;
/**
* Sets the {@link #setSupportedMediaTypes(java.util.List) supportedMediaTypes}
@@ -110,9 +104,6 @@ public class SourceHttpMessageConverter<T extends Source> extends AbstractHttpMe
*/
public void setSupportDtd(boolean supportDtd) {
this.supportDtd = supportDtd;
this.documentBuilderFactory = null;
this.saxParserFactory = null;
this.xmlInputFactory = null;
}
/**
@@ -133,9 +124,6 @@ public class SourceHttpMessageConverter<T extends Source> extends AbstractHttpMe
if (processExternalEntities) {
this.supportDtd = true;
}
this.documentBuilderFactory = null;
this.saxParserFactory = null;
this.xmlInputFactory = null;
}
/**
@@ -177,21 +165,17 @@ public class SourceHttpMessageConverter<T extends Source> extends AbstractHttpMe
private DOMSource readDOMSource(InputStream body, HttpInputMessage inputMessage) throws IOException {
try {
DocumentBuilderFactory builderFactory = this.documentBuilderFactory;
if (builderFactory == null) {
builderFactory = DocumentBuilderFactory.newInstance();
builderFactory.setNamespaceAware(true);
builderFactory.setFeature(
"http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd());
builderFactory.setFeature(
"http://xml.org/sax/features/external-general-entities", isProcessExternalEntities());
this.documentBuilderFactory = builderFactory;
}
DocumentBuilder builder = builderFactory.newDocumentBuilder();
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setNamespaceAware(true);
documentBuilderFactory.setFeature(
"http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd());
documentBuilderFactory.setFeature(
"http://xml.org/sax/features/external-general-entities", isProcessExternalEntities());
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
if (!isProcessExternalEntities()) {
builder.setEntityResolver(NO_OP_ENTITY_RESOLVER);
documentBuilder.setEntityResolver(NO_OP_ENTITY_RESOLVER);
}
Document document = builder.parse(body);
Document document = documentBuilder.parse(body);
return new DOMSource(document);
}
catch (NullPointerException ex) {
@@ -213,17 +197,11 @@ public class SourceHttpMessageConverter<T extends Source> extends AbstractHttpMe
private SAXSource readSAXSource(InputStream body, HttpInputMessage inputMessage) throws IOException {
try {
SAXParserFactory parserFactory = this.saxParserFactory;
if (parserFactory == null) {
parserFactory = SAXParserFactory.newInstance();
parserFactory.setNamespaceAware(true);
parserFactory.setFeature(
"http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd());
parserFactory.setFeature(
"http://xml.org/sax/features/external-general-entities", isProcessExternalEntities());
this.saxParserFactory = parserFactory;
}
SAXParser saxParser = parserFactory.newSAXParser();
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
saxParserFactory.setNamespaceAware(true);
saxParserFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", !isSupportDtd());
saxParserFactory.setFeature("http://xml.org/sax/features/external-general-entities", isProcessExternalEntities());
SAXParser saxParser = saxParserFactory.newSAXParser();
XMLReader xmlReader = saxParser.getXMLReader();
if (!isProcessExternalEntities()) {
xmlReader.setEntityResolver(NO_OP_ENTITY_RESOLVER);
@@ -239,15 +217,11 @@ public class SourceHttpMessageConverter<T extends Source> extends AbstractHttpMe
private Source readStAXSource(InputStream body, HttpInputMessage inputMessage) {
try {
XMLInputFactory inputFactory = this.xmlInputFactory;
if (inputFactory == null) {
inputFactory = XMLInputFactory.newInstance();
inputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, isSupportDtd());
inputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, isProcessExternalEntities());
if (!isProcessExternalEntities()) {
inputFactory.setXMLResolver(NO_OP_XML_RESOLVER);
}
this.xmlInputFactory = inputFactory;
XMLInputFactory inputFactory = XMLInputFactory.newInstance();
inputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, isSupportDtd());
inputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, isProcessExternalEntities());
if (!isProcessExternalEntities()) {
inputFactory.setXMLResolver(NO_OP_XML_RESOLVER);
}
XMLStreamReader streamReader = inputFactory.createXMLStreamReader(body);
return new StAXSource(streamReader);