Compare commits

..

1 Commits

Author SHA1 Message Date
Spring Builds 8c85c3166c Release v5.3.31 2023-11-16 08:00:15 +00:00
110 changed files with 1289 additions and 1938 deletions
+6 -6
View File
@@ -28,11 +28,11 @@ configure(allprojects) { project ->
dependencyManagement {
imports {
mavenBom "com.fasterxml.jackson:jackson-bom:2.12.7"
mavenBom "io.netty:netty-bom:4.1.107.Final"
mavenBom "io.projectreactor:reactor-bom:2020.0.41"
mavenBom "io.netty:netty-bom:4.1.101.Final"
mavenBom "io.projectreactor:reactor-bom:2020.0.38"
mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR13"
mavenBom "io.rsocket:rsocket-bom:1.1.3"
mavenBom "org.eclipse.jetty:jetty-bom:9.4.54.v20240208"
mavenBom "org.eclipse.jetty:jetty-bom:9.4.53.v20231009"
mavenBom "org.jetbrains.kotlin:kotlin-bom:1.5.32"
mavenBom "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.5.2"
mavenBom "org.jetbrains.kotlinx:kotlinx-serialization-bom:1.2.2"
@@ -139,7 +139,7 @@ configure(allprojects) { project ->
entry 'tomcat-embed-core'
entry 'tomcat-embed-websocket'
}
dependencySet(group: 'io.undertow', version: '2.2.29.Final') {
dependencySet(group: 'io.undertow', version: '2.2.28.Final') {
entry 'undertow-core'
entry('undertow-servlet') {
exclude group: "org.jboss.spec.javax.servlet", name: "jboss-servlet-api_4.0_spec"
@@ -340,7 +340,7 @@ configure([rootProject] + javaProjects) { project ->
}
checkstyle {
toolVersion = "10.12.7"
toolVersion = "10.12.5"
configDirectory.set(rootProject.file("src/checkstyle"))
}
@@ -362,7 +362,7 @@ configure([rootProject] + javaProjects) { project ->
// JSR-305 only used for non-required meta-annotations
compileOnly("com.google.code.findbugs:jsr305")
testCompileOnly("com.google.code.findbugs:jsr305")
checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:0.0.41")
checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:0.0.31")
}
ext.javadocLinks = [
+1 -1
View File
@@ -1,4 +1,4 @@
FROM ubuntu:jammy-20240111
FROM ubuntu:jammy-20230624
ADD setup.sh /setup.sh
ADD get-jdk-url.sh /get-jdk-url.sh
+2 -2
View File
@@ -3,10 +3,10 @@ set -e
case "$1" in
java8)
echo "https://github.com/bell-sw/Liberica/releases/download/8u402%2B7/bellsoft-jdk8u402+7-linux-amd64.tar.gz"
echo "https://github.com/bell-sw/Liberica/releases/download/8u372+7/bellsoft-jdk8u372+7-linux-amd64.tar.gz"
;;
java17)
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.10%2B13/bellsoft-jdk17.0.10+13-linux-amd64.tar.gz"
echo "https://github.com/bell-sw/Liberica/releases/download/17.0.7+7/bellsoft-jdk17.0.7+7-linux-amd64.tar.gz"
;;
*)
echo $"Unknown java version"
+1 -1
View File
@@ -1,4 +1,4 @@
version=5.3.32
version=5.3.31
org.gradle.jvmargs=-Xmx2048m
org.gradle.caching=true
org.gradle.parallel=true
@@ -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.
@@ -35,8 +35,7 @@ import org.springframework.lang.Nullable;
/**
* Internal implementation of AspectJPointcutAdvisor.
*
* <p>Note that there will be one instance of this advisor for each target method.
* Note that there will be one instance of this advisor for each target method.
*
* @author Rod Johnson
* @author Juergen Hoeller
@@ -294,7 +293,7 @@ final class InstantiationModelAwarePointcutAdvisorImpl
@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
// This can match only on declared pointcut.
return (isAspectMaterialized() && this.declaredPointcut.matches(method, targetClass, args));
return (isAspectMaterialized() && this.declaredPointcut.matches(method, targetClass));
}
private boolean isAspectMaterialized() {
@@ -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.
@@ -38,67 +38,56 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
* @author Adrian Colyer
* @author Juergen Hoeller
* @author Chris Beams
* @author Sam Brannen
*/
class ArgumentBindingTests {
public class ArgumentBindingTests {
@Test
void annotationArgumentNameBinding() {
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new TransactionalBean());
proxyFactory.addAspect(PointcutWithAnnotationArgument.class);
ITransactionalBean proxiedTestBean = proxyFactory.getProxy();
assertThatIllegalStateException()
.isThrownBy(proxiedTestBean::doInTransaction)
.withMessage("Invoked with @Transactional");
}
@Test
void bindingInPointcutUsedByAdvice() {
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new TestBean());
public void testBindingInPointcutUsedByAdvice() {
TestBean tb = new TestBean();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(tb);
proxyFactory.addAspect(NamedPointcutWithArgs.class);
ITestBean proxiedTestBean = proxyFactory.getProxy();
assertThatIllegalArgumentException()
.isThrownBy(() -> proxiedTestBean.setName("enigma"))
.withMessage("enigma");
ITestBean proxiedTestBean = proxyFactory.getProxy();
assertThatIllegalArgumentException().isThrownBy(() ->
proxiedTestBean.setName("Supercalifragalisticexpialidocious"));
}
@Test
void bindingWithDynamicAdvice() {
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(new TestBean());
proxyFactory.addAspect(DynamicPointcutWithArgs.class);
ITestBean proxiedTestBean = proxyFactory.getProxy();
public void testAnnotationArgumentNameBinding() {
TransactionalBean tb = new TransactionalBean();
AspectJProxyFactory proxyFactory = new AspectJProxyFactory(tb);
proxyFactory.addAspect(PointcutWithAnnotationArgument.class);
proxiedTestBean.applyName(1);
assertThatIllegalArgumentException()
.isThrownBy(() -> proxiedTestBean.applyName("enigma"))
.withMessage("enigma");
ITransactionalBean proxiedTestBean = proxyFactory.getProxy();
assertThatIllegalStateException().isThrownBy(
proxiedTestBean::doInTransaction);
}
@Test
void parameterNameDiscoverWithReferencePointcut() throws Exception {
public void testParameterNameDiscoverWithReferencePointcut() throws Exception {
AspectJAdviceParameterNameDiscoverer discoverer =
new AspectJAdviceParameterNameDiscoverer("somepc(formal) && set(* *)");
discoverer.setRaiseExceptions(true);
Method method = getClass().getDeclaredMethod("methodWithOneParam", String.class);
assertThat(discoverer.getParameterNames(method)).containsExactly("formal");
Method methodUsedForParameterTypeDiscovery =
getClass().getMethod("methodWithOneParam", String.class);
String[] pnames = discoverer.getParameterNames(methodUsedForParameterTypeDiscovery);
assertThat(pnames.length).as("one parameter name").isEqualTo(1);
assertThat(pnames[0]).isEqualTo("formal");
}
@SuppressWarnings("unused")
private void methodWithOneParam(String aParam) {
public void methodWithOneParam(String aParam) {
}
interface ITransactionalBean {
public interface ITransactionalBean {
@Transactional
void doInTransaction();
}
static class TransactionalBean implements ITransactionalBean {
public static class TransactionalBean implements ITransactionalBean {
@Override
@Transactional
@@ -106,46 +95,38 @@ class ArgumentBindingTests {
}
}
}
/**
* Mimics Spring's @Transactional annotation without actually introducing the dependency.
*/
@Retention(RetentionPolicy.RUNTIME)
@interface Transactional {
}
/**
* Represents Spring's Transactional annotation without actually introducing the dependency
*/
@Retention(RetentionPolicy.RUNTIME)
@interface Transactional {
}
@Aspect
static class PointcutWithAnnotationArgument {
@Aspect
class PointcutWithAnnotationArgument {
@Around("execution(* org.springframework..*.*(..)) && @annotation(transactional)")
public Object around(ProceedingJoinPoint pjp, Transactional transactional) throws Throwable {
throw new IllegalStateException("Invoked with @Transactional");
}
}
@Aspect
static class NamedPointcutWithArgs {
@Pointcut("execution(* *(..)) && args(s,..)")
public void pointcutWithArgs(String s) {}
@Around("pointcutWithArgs(aString)")
public Object doAround(ProceedingJoinPoint pjp, String aString) throws Throwable {
throw new IllegalArgumentException(aString);
}
}
@Aspect("pertarget(execution(* *(..)))")
static class DynamicPointcutWithArgs {
@Around("execution(* *(..)) && args(java.lang.String)")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
throw new IllegalArgumentException(String.valueOf(pjp.getArgs()[0]));
}
@Around(value = "execution(* org.springframework..*.*(..)) && @annotation(transaction)")
public Object around(ProceedingJoinPoint pjp, Transactional transaction) throws Throwable {
System.out.println("Invoked with transaction " + transaction);
throw new IllegalStateException();
}
}
@Aspect
class NamedPointcutWithArgs {
@Pointcut("execution(* *(..)) && args(s,..)")
public void pointcutWithArgs(String s) {}
@Around("pointcutWithArgs(aString)")
public Object doAround(ProceedingJoinPoint pjp, String aString) throws Throwable {
System.out.println("got '" + aString + "' at '" + pjp + "'");
throw new IllegalArgumentException(aString);
}
}
@@ -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.
@@ -292,7 +292,7 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa
if (actualValue != null) {
actualValue = typeConverter.convertIfNecessary(actualValue, expectedValue.getClass());
}
if (!ObjectUtils.nullSafeEquals(expectedValue, actualValue)) {
if (!expectedValue.equals(actualValue)) {
return false;
}
}
@@ -97,10 +97,10 @@ import org.springframework.util.StringUtils;
* Supports autowiring constructors, properties by name, and properties by type.
*
* <p>The main template method to be implemented by subclasses is
* {@link #resolveDependency(DependencyDescriptor, String, Set, TypeConverter)}, used for
* autowiring. In case of a {@link org.springframework.beans.factory.ListableBeanFactory}
* which is capable of searching its bean definitions, matching beans will typically be
* implemented through such a search. Otherwise, simplified matching can be implemented.
* {@link #resolveDependency(DependencyDescriptor, String, Set, TypeConverter)},
* used for autowiring by type. In case of a factory which is capable of searching
* its bean definitions, matching beans will typically be implemented through such
* a search. For other factory styles, simplified matching algorithms can be implemented.
*
* <p>Note that this class does <i>not</i> assume or implement bean definition
* registry capabilities. See {@link DefaultListableBeanFactory} for an implementation
@@ -675,7 +675,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
// Apply SmartInstantiationAwareBeanPostProcessors to predict the
// eventual type after a before-instantiation shortcut.
if (targetType != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
boolean matchingOnlyFactoryBean = (typesToMatch.length == 1 && typesToMatch[0] == FactoryBean.class);
boolean matchingOnlyFactoryBean = typesToMatch.length == 1 && typesToMatch[0] == FactoryBean.class;
for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
Class<?> predicted = bp.predictBeanType(targetType, beanName);
if (predicted != null &&
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 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.
@@ -749,7 +749,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
aliases.add(fullBeanName);
}
String[] retrievedAliases = super.getAliases(beanName);
String prefix = (factoryPrefix ? FACTORY_BEAN_PREFIX : "");
String prefix = factoryPrefix ? FACTORY_BEAN_PREFIX : "";
for (String retrievedAlias : retrievedAliases) {
String alias = prefix + retrievedAlias;
if (!alias.equals(name)) {
@@ -107,7 +107,11 @@ class ConstructorResolver {
/**
* "autowire constructor" (with constructor arguments by type) behavior.
* Also applied if explicit constructor argument values are specified.
* Also applied if explicit constructor argument values are specified,
* matching all remaining arguments with beans from the bean factory.
* <p>This corresponds to constructor injection: In this mode, a Spring
* bean factory is able to host components that expect constructor-based
* dependency resolution.
* @param beanName the name of the bean
* @param mbd the merged bean definition for the bean
* @param chosenCtors chosen candidate constructors (or {@code null} if none)
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 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.
@@ -78,10 +78,8 @@ public class PathEditor extends PropertyEditorSupport {
if (nioPathCandidate && !text.startsWith("/")) {
try {
URI uri = new URI(text);
String scheme = uri.getScheme();
if (scheme != null) {
// No NIO candidate except for "C:" style drive letters
nioPathCandidate = (scheme.length() == 1);
if (uri.getScheme() != null) {
nioPathCandidate = false;
// Let's try NIO file system providers via Paths.get(URI)
setValue(Paths.get(uri).normalize());
return;
@@ -111,8 +109,7 @@ public class PathEditor extends PropertyEditorSupport {
setValue(resource.getFile().toPath());
}
catch (IOException ex) {
throw new IllegalArgumentException(
"Could not retrieve file for " + resource + ": " + ex.getMessage());
throw new IllegalArgumentException("Failed to retrieve file for " + resource, ex);
}
}
}
@@ -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.
@@ -33,10 +33,6 @@ public interface ITestBean extends AgeHolder {
void setName(String name);
default void applyName(Object name) {
setName(String.valueOf(name));
}
ITestBean getSpouse();
void setSpouse(ITestBean spouse);
@@ -236,7 +236,7 @@ public class CaffeineCacheManager implements CacheManager {
* Build a common {@link CaffeineCache} instance for the specified cache name,
* using the common Caffeine configuration specified on this cache manager.
* <p>Delegates to {@link #adaptCaffeineCache} as the adaptation method to
* Spring's cache abstraction (allowing for centralized decoration etc.),
* Spring's cache abstraction (allowing for centralized decoration etc),
* passing in a freshly built native Caffeine Cache instance.
* @param name the name of the cache
* @return the Spring CaffeineCache adapter (or a decorator thereof)
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2015 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,6 @@ import java.lang.annotation.Target;
* @author Stephane Nicoll
* @author Sam Brannen
* @since 4.1
* @see Cacheable
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@@ -43,10 +42,8 @@ public @interface CacheConfig {
* Names of the default caches to consider for caching operations defined
* in the annotated class.
* <p>If none is set at the operation level, these are used instead of the default.
* <p>Names may be used to determine the target cache(s), to be resolved via the
* configured {@link #cacheResolver()} which typically delegates to
* {@link org.springframework.cache.CacheManager#getCache}.
* For further details see {@link Cacheable#cacheNames()}.
* <p>May be used to determine the target cache (or caches), matching the
* qualifier value or the bean names of a specific bean definition.
*/
String[] cacheNames() default {};
@@ -68,12 +68,8 @@ public @interface Cacheable {
/**
* Names of the caches in which method invocation results are stored.
* <p>Names may be used to determine the target cache(s), to be resolved via the
* configured {@link #cacheResolver()} which typically delegates to
* {@link org.springframework.cache.CacheManager#getCache}.
* <p>This will usually be a single cache name. If multiple names are specified,
* they will be consulted for a cache hit in the order of definition, and they
* will all receive a put/evict request for the same newly cached value.
* <p>Names may be used to determine the target cache (or caches), matching
* the qualifier value or bean name of a specific bean definition.
* @since 4.2
* @see #value
* @see CacheConfig#cacheNames
@@ -547,10 +547,10 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
}
/**
* Collect a {@link CachePutRequest} for every {@link CacheOperation}
* using the specified result value.
* Collect the {@link CachePutRequest} for all {@link CacheOperation} using
* the specified result value.
* @param contexts the contexts to handle
* @param result the result value
* @param result the result value (never {@code null})
* @param putRequests the collection to update
*/
private void collectPutRequests(Collection<CacheOperationContext> contexts,
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 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.
@@ -22,7 +22,7 @@ import org.springframework.lang.Nullable;
* Abstract the invocation of a cache operation.
*
* <p>Does not provide a way to transmit checked exceptions but
* provides a special exception that should be used to wrap any
* provide a special exception that should be used to wrap any
* exception that was thrown by the underlying invocation.
* Callers are expected to handle this issue type specifically.
*
@@ -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.
@@ -121,7 +121,7 @@ public interface SmartLifecycle extends Lifecycle, Phased {
/**
* Return the phase that this lifecycle object is supposed to run in.
* <p>The default implementation returns {@link #DEFAULT_PHASE} in order to
* let {@code stop()} callbacks execute before regular {@code Lifecycle}
* let {@code stop()} callbacks execute after regular {@code Lifecycle}
* implementations.
* @see #isAutoStartup()
* @see #start()
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 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.
@@ -62,13 +62,11 @@ import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* A component provider that scans for candidate components starting from a
* specified base package. Can use the {@linkplain CandidateComponentsIndex component
* index}, if it is available, and scans the classpath otherwise.
*
* <p>Candidate components are identified by applying exclude and include filters.
* {@link AnnotationTypeFilter} and {@link AssignableTypeFilter} include filters
* for an annotation/target-type that is annotated with {@link Indexed} are
* A component provider that provides candidate components from a base package. Can
* use {@link CandidateComponentsIndex the index} if it is available of scans the
* classpath otherwise. Candidate components are identified by applying exclude and
* include filters. {@link AnnotationTypeFilter}, {@link AssignableTypeFilter} include
* filters on an annotation/superclass that are annotated with {@link Indexed} are
* supported: if any other include filter is specified, the index is ignored and
* classpath scanning is used instead.
*
@@ -306,7 +304,7 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
/**
* Scan the component index or class path for candidate components.
* Scan the class path for candidate components.
* @param basePackage the package to check for annotated classes
* @return a corresponding Set of autodetected bean definitions
*/
@@ -320,7 +318,7 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
}
/**
* Determine if the component index can be used by this instance.
* Determine if the index can be used by this instance.
* @return {@code true} if the index is available and the configuration of this
* instance is supported by it, {@code false} otherwise
* @since 5.0
@@ -456,7 +454,8 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
}
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException("Failed to read candidate component class: " + resource, ex);
throw new BeanDefinitionStoreException(
"Failed to read candidate component class: " + resource, ex);
}
}
}
@@ -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.
@@ -402,8 +402,8 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
if (webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)) {
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)) {
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@WebServiceRef annotation is not supported on static methods");
}
@@ -413,9 +413,7 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new WebServiceRefElement(method, bridgedMethod, pd));
}
}
else if (ejbClass != null && bridgedMethod.isAnnotationPresent(ejbClass)) {
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
else if (ejbClass != null && bridgedMethod.isAnnotationPresent(ejbClass)) {
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@EJB annotation is not supported on static methods");
}
@@ -425,9 +423,7 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new EjbRefElement(method, bridgedMethod, pd));
}
}
else if (bridgedMethod.isAnnotationPresent(Resource.class)) {
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
else if (bridgedMethod.isAnnotationPresent(Resource.class)) {
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@Resource annotation is not supported on static methods");
}
@@ -211,7 +211,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
/** Flag that indicates whether this context has been closed already. */
private final AtomicBoolean closed = new AtomicBoolean();
/** Synchronization monitor for "refresh" and "close". */
/** Synchronization monitor for the "refresh" and "destroy". */
private final Object startupShutdownMonitor = new Object();
/** Reference to the JVM shutdown hook, if registered. */
@@ -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.
@@ -27,7 +27,6 @@ import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.context.ResourceLoaderAware;
@@ -144,7 +143,7 @@ public class ReloadableResourceBundleMessageSource extends AbstractResourceBased
/**
* Set the PropertiesPersister to use for parsing properties files.
* <p>The default is {@code ResourcePropertiesPersister}.
* <p>The default is ResourcePropertiesPersister.
* @see ResourcePropertiesPersister#INSTANCE
*/
public void setPropertiesPersister(@Nullable PropertiesPersister propertiesPersister) {
@@ -402,7 +401,7 @@ public class ReloadableResourceBundleMessageSource extends AbstractResourceBased
/**
* Refresh the PropertiesHolder for the given bundle filename.
* <p>The holder can be {@code null} if not cached before, or a timed-out cache entry
* The holder can be {@code null} if not cached before, or a timed-out cache entry
* (potentially getting re-validated against the current last-modified timestamp).
* @param filename the bundle filename (basename + Locale)
* @param propHolder the current PropertiesHolder for the bundle
@@ -563,7 +562,7 @@ public class ReloadableResourceBundleMessageSource extends AbstractResourceBased
private volatile long refreshTimestamp = -2;
private final Lock refreshLock = new ReentrantLock();
private final ReentrantLock refreshLock = new ReentrantLock();
/** Cache to hold already generated MessageFormats per message code. */
private final ConcurrentMap<String, Map<Locale, MessageFormat>> cachedMessageFormats =
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,7 +29,7 @@ import org.springframework.lang.Nullable;
* {@link Runnable Runnables} based on different kinds of triggers.
*
* <p>This interface is separate from {@link SchedulingTaskExecutor} since it
* usually represents a different kind of backend, i.e. a thread pool with
* usually represents for a different kind of backend, i.e. a thread pool with
* different characteristics and capabilities. Implementations may implement
* both interfaces if they can handle both kinds of execution characteristics.
*
@@ -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,9 +100,9 @@ public @interface Scheduled {
/**
* A time zone for which the cron expression will be resolved. By default, this
* attribute is the empty String (i.e. the scheduler's time zone will be used).
* attribute is the empty String (i.e. the server's local time zone will be used).
* @return a zone id accepted by {@link java.util.TimeZone#getTimeZone(String)},
* or an empty String to indicate the scheduler's default time zone
* or an empty String to indicate the server's default time zone
* @since 4.0
* @see org.springframework.scheduling.support.CronTrigger#CronTrigger(String, java.util.TimeZone)
* @see java.util.TimeZone
@@ -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.
@@ -27,6 +27,7 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@@ -83,7 +84,7 @@ import org.springframework.util.StringValueResolver;
* "fixedRate", "fixedDelay", or "cron" expression provided via the annotation.
*
* <p>This post-processor is automatically registered by Spring's
* {@code <task:annotation-driven>} XML element and also by the
* {@code <task:annotation-driven>} XML element, and also by the
* {@link EnableScheduling @EnableScheduling} annotation.
*
* <p>Autodetects any {@link SchedulingConfigurer} instances in the container,
@@ -433,14 +434,14 @@ public class ScheduledAnnotationBeanPostProcessor
Assert.isTrue(initialDelay == -1, "'initialDelay' not supported for cron triggers");
processedSchedule = true;
if (!Scheduled.CRON_DISABLED.equals(cron)) {
CronTrigger trigger;
TimeZone timeZone;
if (StringUtils.hasText(zone)) {
trigger = new CronTrigger(cron, StringUtils.parseTimeZoneString(zone));
timeZone = StringUtils.parseTimeZoneString(zone);
}
else {
trigger = new CronTrigger(cron);
timeZone = TimeZone.getDefault();
}
tasks.add(this.registrar.scheduleCronTask(new CronTask(runnable, trigger)));
tasks.add(this.registrar.scheduleCronTask(new CronTask(runnable, new CronTrigger(cron, timeZone))));
}
}
}
@@ -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.
@@ -29,15 +29,17 @@ import org.springframework.util.StringUtils;
* Created using the {@code parse*} methods.
*
* @author Arjen Poutsma
* @author Juergen Hoeller
* @since 5.3
*/
final class BitsCronField extends CronField {
public static final BitsCronField ZERO_NANOS = forZeroNanos();
private static final long MASK = 0xFFFFFFFFFFFFFFFFL;
@Nullable
private static BitsCronField zeroNanos = null;
// we store at most 60 bits, for seconds and minutes, so a 64-bit long suffices
private long bits;
@@ -46,14 +48,16 @@ final class BitsCronField extends CronField {
super(type);
}
/**
* Return a {@code BitsCronField} enabled for 0 nanoseconds.
*/
private static BitsCronField forZeroNanos() {
BitsCronField field = new BitsCronField(Type.NANO);
field.setBit(0);
return field;
public static BitsCronField zeroNanos() {
if (zeroNanos == null) {
BitsCronField field = new BitsCronField(Type.NANO);
field.setBit(0);
zeroNanos = field;
}
return zeroNanos;
}
/**
@@ -104,6 +108,7 @@ final class BitsCronField extends CronField {
return result;
}
private static BitsCronField parseDate(String value, BitsCronField.Type type) {
if (value.equals("?")) {
value = "*";
@@ -169,7 +174,6 @@ final class BitsCronField extends CronField {
}
}
@Nullable
@Override
public <T extends Temporal & Comparable<? super T>> T nextOrSame(T temporal) {
@@ -213,6 +217,7 @@ final class BitsCronField extends CronField {
else {
return -1;
}
}
private void setBits(ValueRange range) {
@@ -242,20 +247,7 @@ final class BitsCronField extends CronField {
}
private void clearBit(int index) {
this.bits &= ~(1L << index);
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof BitsCronField)) {
return false;
}
BitsCronField otherField = (BitsCronField) other;
return (type() == otherField.type() && this.bits == otherField.bits);
this.bits &= ~(1L << index);
}
@Override
@@ -263,6 +255,18 @@ final class BitsCronField extends CronField {
return Long.hashCode(this.bits);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof BitsCronField)) {
return false;
}
BitsCronField other = (BitsCronField) o;
return type() == other.type() && this.bits == other.bits;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder(type().toString());
@@ -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.
@@ -29,14 +29,9 @@ import org.springframework.util.StringUtils;
* <a href="https://www.manpagez.com/man/5/crontab/">crontab expression</a>
* that can calculate the next time it matches.
*
* <p>{@code CronExpression} instances are created through {@link #parse(String)};
* the next match is determined with {@link #next(Temporal)}.
*
* <p>Supports a Quartz day-of-month/week field with an L/# expression. Follows
* common cron conventions in every other respect, including 0-6 for SUN-SAT
* (plus 7 for SUN as well). Note that Quartz deviates from the day-of-week
* convention in cron through 1-7 for SUN-SAT whereas Spring strictly follows
* cron even in combination with the optional Quartz-specific L/# expressions.
* <p>{@code CronExpression} instances are created through
* {@link #parse(String)}; the next match is determined with
* {@link #next(Temporal)}.
*
* @author Arjen Poutsma
* @since 5.3
@@ -62,12 +57,18 @@ public final class CronExpression {
private final String expression;
private CronExpression(CronField seconds, CronField minutes, CronField hours,
CronField daysOfMonth, CronField months, CronField daysOfWeek, String expression) {
private CronExpression(
CronField seconds,
CronField minutes,
CronField hours,
CronField daysOfMonth,
CronField months,
CronField daysOfWeek,
String expression) {
// Reverse order, to make big changes first.
// To make sure we end up at 0 nanos, we add an extra field.
this.fields = new CronField[] {daysOfWeek, months, daysOfMonth, hours, minutes, seconds, CronField.zeroNanos()};
// reverse order, to make big changes first
// to make sure we end up at 0 nanos, we add an extra field
this.fields = new CronField[]{daysOfWeek, months, daysOfMonth, hours, minutes, seconds, CronField.zeroNanos()};
this.expression = expression;
}
@@ -120,8 +121,11 @@ public final class CronExpression {
* {@code LW}), it means "the last weekday of the month".
* </li>
* <li>
* In the "day of week" field, {@code dL} or {@code DDDL} stands for
* "the last day of week {@code d} (or {@code DDD}) in the month".
* In the "day of week" field, {@code L} stands for "the last day of the
* week".
* If prefixed by a number or three-letter name (i.e. {@code dL} or
* {@code DDDL}), it means "the last day of week {@code d} (or {@code DDD})
* in the month".
* </li>
* </ul>
* </li>
@@ -173,7 +177,7 @@ public final class CronExpression {
* the cron format
*/
public static CronExpression parse(String expression) {
Assert.hasLength(expression, "Expression must not be empty");
Assert.hasLength(expression, "Expression string must not be empty");
expression = resolveMacros(expression);
@@ -266,19 +270,28 @@ public final class CronExpression {
}
@Override
public boolean equals(@Nullable Object other) {
return (this == other || (other instanceof CronExpression &&
Arrays.equals(this.fields, ((CronExpression) other).fields)));
}
@Override
public int hashCode() {
return Arrays.hashCode(this.fields);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o instanceof CronExpression) {
CronExpression other = (CronExpression) o;
return Arrays.equals(this.fields, other.fields);
}
else {
return false;
}
}
/**
* Return the expression string used to create this {@code CronExpression}.
* @return the expression string
*/
@Override
public String toString() {
@@ -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.
@@ -29,24 +29,17 @@ import org.springframework.util.StringUtils;
/**
* Single field in a cron pattern. Created using the {@code parse*} methods,
* the main and only entry point is {@link #nextOrSame(Temporal)}.
*
* <p>Supports a Quartz day-of-month/week field with an L/# expression. Follows
* common cron conventions in every other respect, including 0-6 for SUN-SAT
* (plus 7 for SUN as well). Note that Quartz deviates from the day-of-week
* convention in cron through 1-7 for SUN-SAT whereas Spring strictly follows
* cron even in combination with the optional Quartz-specific L/# expressions.
* main and only entry point is {@link #nextOrSame(Temporal)}.
*
* @author Arjen Poutsma
* @since 5.3
*/
abstract class CronField {
private static final String[] MONTHS = new String[]
{"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};
private static final String[] MONTHS = new String[]{"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP",
"OCT", "NOV", "DEC"};
private static final String[] DAYS = new String[]
{"MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"};
private static final String[] DAYS = new String[]{"MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"};
private final Type type;
@@ -55,12 +48,11 @@ abstract class CronField {
this.type = type;
}
/**
* Return a {@code CronField} enabled for 0 nanoseconds.
*/
public static CronField zeroNanos() {
return BitsCronField.ZERO_NANOS;
return BitsCronField.zeroNanos();
}
/**
@@ -177,7 +169,6 @@ abstract class CronField {
* day-of-month, month, day-of-week.
*/
protected enum Type {
NANO(ChronoField.NANO_OF_SECOND, ChronoUnit.SECONDS),
SECOND(ChronoField.SECOND_OF_MINUTE, ChronoUnit.MINUTES, ChronoField.NANO_OF_SECOND),
MINUTE(ChronoField.MINUTE_OF_HOUR, ChronoUnit.HOURS, ChronoField.SECOND_OF_MINUTE, ChronoField.NANO_OF_SECOND),
@@ -186,18 +177,21 @@ abstract class CronField {
MONTH(ChronoField.MONTH_OF_YEAR, ChronoUnit.YEARS, ChronoField.DAY_OF_MONTH, ChronoField.HOUR_OF_DAY, ChronoField.MINUTE_OF_HOUR, ChronoField.SECOND_OF_MINUTE, ChronoField.NANO_OF_SECOND),
DAY_OF_WEEK(ChronoField.DAY_OF_WEEK, ChronoUnit.WEEKS, ChronoField.HOUR_OF_DAY, ChronoField.MINUTE_OF_HOUR, ChronoField.SECOND_OF_MINUTE, ChronoField.NANO_OF_SECOND);
private final ChronoField field;
private final ChronoUnit higherOrder;
private final ChronoField[] lowerOrders;
Type(ChronoField field, ChronoUnit higherOrder, ChronoField... lowerOrders) {
this.field = field;
this.higherOrder = higherOrder;
this.lowerOrders = lowerOrders;
}
/**
* Return the value of this type for the given temporal.
* @return the value of this type
@@ -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.
@@ -27,14 +27,8 @@ import org.springframework.scheduling.TriggerContext;
import org.springframework.util.Assert;
/**
* {@link Trigger} implementation for cron expressions. Wraps a
* {@link CronExpression} which parses according to common crontab conventions.
*
* <p>Supports a Quartz day-of-month/week field with an L/# expression. Follows
* common cron conventions in every other respect, including 0-6 for SUN-SAT
* (plus 7 for SUN as well). Note that Quartz deviates from the day-of-week
* convention in cron through 1-7 for SUN-SAT whereas Spring strictly follows
* cron even in combination with the optional Quartz-specific L/# expressions.
* {@link Trigger} implementation for cron expressions.
* Wraps a {@link CronExpression}.
*
* @author Juergen Hoeller
* @author Arjen Poutsma
@@ -45,7 +39,6 @@ public class CronTrigger implements Trigger {
private final CronExpression expression;
@Nullable
private final ZoneId zoneId;
@@ -55,8 +48,7 @@ public class CronTrigger implements Trigger {
* expression conventions
*/
public CronTrigger(String expression) {
this.expression = CronExpression.parse(expression);
this.zoneId = null;
this(expression, ZoneId.systemDefault());
}
/**
@@ -66,9 +58,7 @@ public class CronTrigger implements Trigger {
* @param timeZone a time zone in which the trigger times will be generated
*/
public CronTrigger(String expression, TimeZone timeZone) {
this.expression = CronExpression.parse(expression);
Assert.notNull(timeZone, "TimeZone must not be null");
this.zoneId = timeZone.toZoneId();
this(expression, timeZone.toZoneId());
}
/**
@@ -80,8 +70,10 @@ public class CronTrigger implements Trigger {
* @see CronExpression#parse(String)
*/
public CronTrigger(String expression, ZoneId zoneId) {
this.expression = CronExpression.parse(expression);
Assert.hasLength(expression, "Expression must not be empty");
Assert.notNull(zoneId, "ZoneId must not be null");
this.expression = CronExpression.parse(expression);
this.zoneId = zoneId;
}
@@ -102,23 +94,22 @@ public class CronTrigger implements Trigger {
*/
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
Date timestamp = triggerContext.lastCompletionTime();
if (timestamp != null) {
Date date = triggerContext.lastCompletionTime();
if (date != null) {
Date scheduled = triggerContext.lastScheduledExecutionTime();
if (scheduled != null && timestamp.before(scheduled)) {
if (scheduled != null && date.before(scheduled)) {
// Previous task apparently executed too early...
// Let's simply use the last calculated execution time then,
// in order to prevent accidental re-fires in the same second.
timestamp = scheduled;
date = scheduled;
}
}
else {
timestamp = new Date(triggerContext.getClock().millis());
date = new Date(triggerContext.getClock().millis());
}
ZoneId zone = (this.zoneId != null ? this.zoneId : triggerContext.getClock().getZone());
ZonedDateTime zonedTimestamp = ZonedDateTime.ofInstant(timestamp.toInstant(), zone);
ZonedDateTime nextTimestamp = this.expression.next(zonedTimestamp);
return (nextTimestamp != null ? Date.from(nextTimestamp.toInstant()) : null);
ZonedDateTime dateTime = ZonedDateTime.ofInstant(date.toInstant(), this.zoneId);
ZonedDateTime next = this.expression.next(dateTime);
return (next != null ? Date.from(next.toInstant()) : 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.
@@ -29,16 +29,10 @@ import org.springframework.util.Assert;
/**
* Extension of {@link CronField} for
* <a href="https://www.quartz-scheduler.org">Quartz</a>-specific fields.
* <a href="https://www.quartz-scheduler.org>Quartz</a> -specific fields.
* Created using the {@code parse*} methods, uses a {@link TemporalAdjuster}
* internally.
*
* <p>Supports a Quartz day-of-month/week field with an L/# expression. Follows
* common cron conventions in every other respect, including 0-6 for SUN-SAT
* (plus 7 for SUN as well). Note that Quartz deviates from the day-of-week
* convention in cron through 1-7 for SUN-SAT whereas Spring strictly follows
* cron even in combination with the optional Quartz-specific L/# expressions.
*
* @author Arjen Poutsma
* @since 5.3
*/
@@ -66,18 +60,16 @@ final class QuartzCronField extends CronField {
this.rollForwardType = rollForwardType;
}
/**
* Determine whether the given value is a Quartz day-of-month field.
* Returns whether the given value is a Quartz day-of-month field.
*/
public static boolean isQuartzDaysOfMonthField(String value) {
return value.contains("L") || value.contains("W");
}
/**
* Parse the given value into a days of months {@code QuartzCronField},
* the fourth entry of a cron expression.
* <p>Expects a "L" or "W" in the given value.
* Parse the given value into a days of months {@code QuartzCronField}, the fourth entry of a cron expression.
* Expects a "L" or "W" in the given value.
*/
public static QuartzCronField parseDaysOfMonth(String value) {
int idx = value.lastIndexOf('L');
@@ -86,14 +78,14 @@ final class QuartzCronField extends CronField {
if (idx != 0) {
throw new IllegalArgumentException("Unrecognized characters before 'L' in '" + value + "'");
}
else if (value.length() == 2 && value.charAt(1) == 'W') { // "LW"
else if (value.length() == 2 && value.charAt(1) == 'W') { // "LW"
adjuster = lastWeekdayOfMonth();
}
else {
if (value.length() == 1) { // "L"
if (value.length() == 1) { // "L"
adjuster = lastDayOfMonth();
}
else { // "L-[0-9]+"
else { // "L-[0-9]+"
int offset = Integer.parseInt(value.substring(idx + 1));
if (offset >= 0) {
throw new IllegalArgumentException("Offset '" + offset + " should be < 0 '" + value + "'");
@@ -111,7 +103,7 @@ final class QuartzCronField extends CronField {
else if (idx != value.length() - 1) {
throw new IllegalArgumentException("Unrecognized characters after 'W' in '" + value + "'");
}
else { // "[0-9]+W"
else { // "[0-9]+W"
int dayOfMonth = Integer.parseInt(value.substring(0, idx));
dayOfMonth = Type.DAY_OF_MONTH.checkValidValue(dayOfMonth);
TemporalAdjuster adjuster = weekdayNearestTo(dayOfMonth);
@@ -122,16 +114,15 @@ final class QuartzCronField extends CronField {
}
/**
* Determine whether the given value is a Quartz day-of-week field.
* Returns whether the given value is a Quartz day-of-week field.
*/
public static boolean isQuartzDaysOfWeekField(String value) {
return value.contains("L") || value.contains("#");
}
/**
* Parse the given value into a days of week {@code QuartzCronField},
* the sixth entry of a cron expression.
* <p>Expects a "L" or "#" in the given value.
* Parse the given value into a days of week {@code QuartzCronField}, the sixth entry of a cron expression.
* Expects a "L" or "#" in the given value.
*/
public static QuartzCronField parseDaysOfWeek(String value) {
int idx = value.lastIndexOf('L');
@@ -144,7 +135,7 @@ final class QuartzCronField extends CronField {
if (idx == 0) {
throw new IllegalArgumentException("No day-of-week before 'L' in '" + value + "'");
}
else { // "[0-7]L"
else { // "[0-7]L"
DayOfWeek dayOfWeek = parseDayOfWeek(value.substring(0, idx));
adjuster = lastInMonth(dayOfWeek);
}
@@ -166,6 +157,7 @@ final class QuartzCronField extends CronField {
throw new IllegalArgumentException("Ordinal '" + ordinal + "' in '" + value +
"' must be positive number ");
}
TemporalAdjuster adjuster = dayOfWeekInMonth(ordinal, dayOfWeek);
return new QuartzCronField(Type.DAY_OF_WEEK, Type.DAY_OF_MONTH, adjuster, value);
}
@@ -175,13 +167,14 @@ final class QuartzCronField extends CronField {
private static DayOfWeek parseDayOfWeek(String value) {
int dayOfWeek = Integer.parseInt(value);
if (dayOfWeek == 0) {
dayOfWeek = 7; // cron is 0 based; java.time 1 based
dayOfWeek = 7; // cron is 0 based; java.time 1 based
}
try {
return DayOfWeek.of(dayOfWeek);
}
catch (DateTimeException ex) {
throw new IllegalArgumentException(ex.getMessage() + " '" + value + "'", ex);
String msg = ex.getMessage() + " '" + value + "'";
throw new IllegalArgumentException(msg, ex);
}
}
@@ -220,10 +213,10 @@ final class QuartzCronField extends CronField {
Temporal lastDom = adjuster.adjustInto(temporal);
Temporal result;
int dow = lastDom.get(ChronoField.DAY_OF_WEEK);
if (dow == 6) { // Saturday
if (dow == 6) { // Saturday
result = lastDom.minus(1, ChronoUnit.DAYS);
}
else if (dow == 7) { // Sunday
else if (dow == 7) { // Sunday
result = lastDom.minus(2, ChronoUnit.DAYS);
}
else {
@@ -234,7 +227,7 @@ final class QuartzCronField extends CronField {
}
/**
* Returns a temporal adjuster that finds the nth-to-last day of the month.
* Return a temporal adjuster that finds the nth-to-last day of the month.
* @param offset the negative offset, i.e. -3 means third-to-last
* @return a nth-to-last day-of-month adjuster
*/
@@ -248,7 +241,7 @@ final class QuartzCronField extends CronField {
}
/**
* Returns a temporal adjuster that finds the weekday nearest to the given
* Return a temporal adjuster that finds the weekday nearest to the given
* day-of-month. If {@code dayOfMonth} falls on a Saturday, the date is
* moved back to Friday; if it falls on a Sunday (or if {@code dayOfMonth}
* is 1 and it falls on a Saturday), it is moved forward to Monday.
@@ -260,10 +253,10 @@ final class QuartzCronField extends CronField {
int current = Type.DAY_OF_MONTH.get(temporal);
DayOfWeek dayOfWeek = DayOfWeek.from(temporal);
if ((current == dayOfMonth && isWeekday(dayOfWeek)) || // dayOfMonth is a weekday
(dayOfWeek == DayOfWeek.FRIDAY && current == dayOfMonth - 1) || // dayOfMonth is a Saturday, so Friday before
(dayOfWeek == DayOfWeek.MONDAY && current == dayOfMonth + 1) || // dayOfMonth is a Sunday, so Monday after
(dayOfWeek == DayOfWeek.MONDAY && dayOfMonth == 1 && current == 3)) { // dayOfMonth is Saturday 1st, so Monday 3rd
if ((current == dayOfMonth && isWeekday(dayOfWeek)) || // dayOfMonth is a weekday
(dayOfWeek == DayOfWeek.FRIDAY && current == dayOfMonth - 1) || // dayOfMonth is a Saturday, so Friday before
(dayOfWeek == DayOfWeek.MONDAY && current == dayOfMonth + 1) || // dayOfMonth is a Sunday, so Monday after
(dayOfWeek == DayOfWeek.MONDAY && dayOfMonth == 1 && current == 3)) { // dayOfMonth is Saturday 1st, so Monday 3rd
return temporal;
}
int count = 0;
@@ -299,7 +292,7 @@ final class QuartzCronField extends CronField {
}
/**
* Returns a temporal adjuster that finds the last of the given day-of-week
* Return a temporal adjuster that finds the last of the given doy-of-week
* in a month.
*/
private static TemporalAdjuster lastInMonth(DayOfWeek dayOfWeek) {
@@ -336,7 +329,6 @@ final class QuartzCronField extends CronField {
}
}
@Override
public <T extends Temporal & Comparable<? super T>> T nextOrSame(T temporal) {
T result = adjust(temporal);
@@ -353,6 +345,7 @@ final class QuartzCronField extends CronField {
return result;
}
@Nullable
@SuppressWarnings("unchecked")
private <T extends Temporal & Comparable<? super T>> T adjust(T temporal) {
@@ -360,26 +353,28 @@ final class QuartzCronField extends CronField {
}
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof QuartzCronField)) {
return false;
}
QuartzCronField otherField = (QuartzCronField) other;
return (type() == otherField.type() && this.value.equals(otherField.value));
}
@Override
public int hashCode() {
return this.value.hashCode();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof QuartzCronField)) {
return false;
}
QuartzCronField other = (QuartzCronField) o;
return type() == other.type() &&
this.value.equals(other.value);
}
@Override
public String toString() {
return type() + " '" + this.value + "'";
}
}
@@ -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.
@@ -39,7 +39,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Juergen Hoeller
* @author Chris Beams
*/
class ScopedProxyTests {
public class ScopedProxyTests {
private static final Class<?> CLASS = ScopedProxyTests.class;
private static final String CLASSNAME = CLASS.getSimpleName();
@@ -51,24 +51,27 @@ class ScopedProxyTests {
@Test // SPR-2108
void testProxyAssignable() {
public void testProxyAssignable() throws Exception {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(MAP_CONTEXT);
Object baseMap = bf.getBean("singletonMap");
assertThat(baseMap instanceof Map).isTrue();
boolean condition = baseMap instanceof Map;
assertThat(condition).isTrue();
}
@Test
void testSimpleProxy() {
public void testSimpleProxy() throws Exception {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(MAP_CONTEXT);
Object simpleMap = bf.getBean("simpleMap");
assertThat(simpleMap instanceof Map).isTrue();
assertThat(simpleMap instanceof HashMap).isTrue();
boolean condition1 = simpleMap instanceof Map;
assertThat(condition1).isTrue();
boolean condition = simpleMap instanceof HashMap;
assertThat(condition).isTrue();
}
@Test
void testScopedOverride() {
public void testScopedOverride() throws Exception {
GenericApplicationContext ctx = new GenericApplicationContext();
new XmlBeanDefinitionReader(ctx).loadBeanDefinitions(OVERRIDE_CONTEXT);
SimpleMapScope scope = new SimpleMapScope();
@@ -84,7 +87,7 @@ class ScopedProxyTests {
}
@Test
void testJdkScopedProxy() throws Exception {
public void testJdkScopedProxy() throws Exception {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(TESTBEAN_CONTEXT);
bf.setSerializationId("X");
@@ -94,7 +97,8 @@ class ScopedProxyTests {
ITestBean bean = (ITestBean) bf.getBean("testBean");
assertThat(bean).isNotNull();
assertThat(AopUtils.isJdkDynamicProxy(bean)).isTrue();
assertThat(bean instanceof ScopedObject).isTrue();
boolean condition1 = bean instanceof ScopedObject;
assertThat(condition1).isTrue();
ScopedObject scoped = (ScopedObject) bean;
assertThat(scoped.getTargetObject().getClass()).isEqualTo(TestBean.class);
bean.setAge(101);
@@ -106,7 +110,8 @@ class ScopedProxyTests {
assertThat(deserialized).isNotNull();
assertThat(AopUtils.isJdkDynamicProxy(deserialized)).isTrue();
assertThat(bean.getAge()).isEqualTo(101);
assertThat(deserialized instanceof ScopedObject).isTrue();
boolean condition = deserialized instanceof ScopedObject;
assertThat(condition).isTrue();
ScopedObject scopedDeserialized = (ScopedObject) deserialized;
assertThat(scopedDeserialized.getTargetObject().getClass()).isEqualTo(TestBean.class);
@@ -114,7 +119,7 @@ class ScopedProxyTests {
}
@Test
void testCglibScopedProxy() throws Exception {
public void testCglibScopedProxy() throws Exception {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(LIST_CONTEXT);
bf.setSerializationId("Y");
@@ -123,7 +128,8 @@ class ScopedProxyTests {
TestBean tb = (TestBean) bf.getBean("testBean");
assertThat(AopUtils.isCglibProxy(tb.getFriends())).isTrue();
assertThat(tb.getFriends() instanceof ScopedObject).isTrue();
boolean condition1 = tb.getFriends() instanceof ScopedObject;
assertThat(condition1).isTrue();
ScopedObject scoped = (ScopedObject) tb.getFriends();
assertThat(scoped.getTargetObject().getClass()).isEqualTo(ArrayList.class);
tb.getFriends().add("myFriend");
@@ -131,11 +137,12 @@ class ScopedProxyTests {
assertThat(scope.getMap().containsKey("scopedTarget.scopedList")).isTrue();
assertThat(scope.getMap().get("scopedTarget.scopedList").getClass()).isEqualTo(ArrayList.class);
ArrayList<Object> deserialized = (ArrayList<Object>) SerializationTestUtils.serializeAndDeserialize(tb.getFriends());
ArrayList<?> deserialized = (ArrayList<?>) SerializationTestUtils.serializeAndDeserialize(tb.getFriends());
assertThat(deserialized).isNotNull();
assertThat(AopUtils.isCglibProxy(deserialized)).isTrue();
assertThat(deserialized).contains("myFriend");
assertThat(deserialized instanceof ScopedObject).isTrue();
assertThat(deserialized.contains("myFriend")).isTrue();
boolean condition = deserialized instanceof ScopedObject;
assertThat(condition).isTrue();
ScopedObject scopedDeserialized = (ScopedObject) deserialized;
assertThat(scopedDeserialized.getTargetObject().getClass()).isEqualTo(ArrayList.class);
@@ -63,12 +63,12 @@ public class QualifierAnnotationAutowireContextTests {
new RootBeanDefinition(QualifiedFieldTestBean.class));
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(context::refresh)
.satisfies(ex -> {
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
assertThat(ex.getBeanName()).isEqualTo("autowired");
});
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
context::refresh)
.satisfies(ex -> {
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
assertThat(ex.getBeanName()).isEqualTo("autowired");
});
}
@Test
@@ -81,13 +81,12 @@ public class QualifierAnnotationAutowireContextTests {
context.registerBeanDefinition("autowired",
new RootBeanDefinition(QualifiedMethodParameterTestBean.class));
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(context::refresh)
.satisfies(ex -> {
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
assertThat(ex.getBeanName()).isEqualTo("autowired");
});
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
context::refresh)
.satisfies(ex -> {
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
assertThat(ex.getBeanName()).isEqualTo("autowired");
});
}
@@ -101,10 +100,9 @@ public class QualifierAnnotationAutowireContextTests {
context.registerBeanDefinition("autowired",
new RootBeanDefinition(QualifiedConstructorArgumentTestBean.class));
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
.isThrownBy(context::refresh)
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("autowired"));
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(
context::refresh)
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("autowired"));
}
@Test
@@ -207,13 +205,12 @@ public class QualifierAnnotationAutowireContextTests {
context.registerBeanDefinition("autowired",
new RootBeanDefinition(QualifiedFieldTestBean.class));
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(context::refresh)
.satisfies(ex -> {
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
assertThat(ex.getBeanName()).isEqualTo("autowired");
});
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
context::refresh)
.satisfies(ex -> {
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
assertThat(ex.getBeanName()).isEqualTo("autowired");
});
}
@Test
@@ -230,13 +227,12 @@ public class QualifierAnnotationAutowireContextTests {
context.registerBeanDefinition("autowired",
new RootBeanDefinition(QualifiedMethodParameterTestBean.class));
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(context::refresh)
.satisfies(ex -> {
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
assertThat(ex.getBeanName()).isEqualTo("autowired");
});
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
context::refresh)
.satisfies(ex -> {
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
assertThat(ex.getBeanName()).isEqualTo("autowired");
});
}
@Test
@@ -253,10 +249,9 @@ public class QualifierAnnotationAutowireContextTests {
context.registerBeanDefinition("autowired",
new RootBeanDefinition(QualifiedConstructorArgumentTestBean.class));
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
.isThrownBy(context::refresh)
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("autowired"));
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(
context::refresh)
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("autowired"));
}
@Test
@@ -379,13 +374,12 @@ public class QualifierAnnotationAutowireContextTests {
context.registerBeanDefinition("autowired",
new RootBeanDefinition(QualifiedFieldWithDefaultValueTestBean.class));
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(context::refresh)
.satisfies(ex -> {
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
assertThat(ex.getBeanName()).isEqualTo("autowired");
});
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
context::refresh)
.satisfies(ex -> {
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
assertThat(ex.getBeanName()).isEqualTo("autowired");
});
}
@Test
@@ -457,13 +451,12 @@ public class QualifierAnnotationAutowireContextTests {
context.registerBeanDefinition("autowired",
new RootBeanDefinition(QualifiedFieldWithMultipleAttributesTestBean.class));
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(context::refresh)
.satisfies(ex -> {
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
assertThat(ex.getBeanName()).isEqualTo("autowired");
});
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
context::refresh)
.satisfies(ex -> {
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
assertThat(ex.getBeanName()).isEqualTo("autowired");
});
}
@Test
@@ -514,13 +507,12 @@ public class QualifierAnnotationAutowireContextTests {
context.registerBeanDefinition("autowired",
new RootBeanDefinition(QualifiedFieldWithMultipleAttributesTestBean.class));
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(context::refresh)
.satisfies(ex -> {
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
assertThat(ex.getBeanName()).isEqualTo("autowired");
});
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
context::refresh)
.satisfies(ex -> {
assertThat(ex.getRootCause()).isInstanceOf(NoSuchBeanDefinitionException.class);
assertThat(ex.getBeanName()).isEqualTo("autowired");
});
}
@Test
@@ -582,10 +574,9 @@ public class QualifierAnnotationAutowireContextTests {
context.registerBeanDefinition("autowired",
new RootBeanDefinition(QualifiedConstructorArgumentWithBaseQualifierNonDefaultValueTestBean.class));
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
.isThrownBy(context::refresh)
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("autowired"));
assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(
context::refresh)
.satisfies(ex -> assertThat(ex.getBeanName()).isEqualTo("autowired"));
}
@@ -761,7 +752,7 @@ public class QualifierAnnotationAutowireContextTests {
@Qualifier
@interface TestQualifierWithMultipleAttributes {
String[] value() default "default";
String value() default "default";
int number();
}
@@ -58,10 +58,9 @@ public class QualifierAnnotationTests {
BeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
reader.loadBeanDefinitions(CONFIG_LOCATION);
context.registerSingleton("testBean", NonQualifiedTestBean.class);
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(context::refresh)
.withMessageContaining("found 6");
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
context::refresh)
.withMessageContaining("found 6");
}
@Test
@@ -192,10 +191,9 @@ public class QualifierAnnotationTests {
BeanDefinitionReader reader = new XmlBeanDefinitionReader(context);
reader.loadBeanDefinitions(CONFIG_LOCATION);
context.registerSingleton("testBean", QualifiedByAttributesTestBean.class);
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(context::refresh)
.withMessageContaining("found 6");
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
context::refresh)
.withMessageContaining("found 6");
}
@Test
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-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.
@@ -138,8 +138,8 @@ class PropertySourceAnnotationTests {
@Test
void withUnresolvablePlaceholder() {
assertThatExceptionOfType(BeanDefinitionStoreException.class)
.isThrownBy(() -> new AnnotationConfigApplicationContext(ConfigWithUnresolvablePlaceholder.class))
.withCauseInstanceOf(IllegalArgumentException.class);
.isThrownBy(() -> new AnnotationConfigApplicationContext(ConfigWithUnresolvablePlaceholder.class))
.withCauseInstanceOf(IllegalArgumentException.class);
}
@Test
@@ -170,8 +170,8 @@ class PropertySourceAnnotationTests {
@Test
void withEmptyResourceLocations() {
assertThatExceptionOfType(BeanDefinitionStoreException.class)
.isThrownBy(() -> new AnnotationConfigApplicationContext(ConfigWithEmptyResourceLocations.class))
.withCauseInstanceOf(IllegalArgumentException.class);
.isThrownBy(() -> new AnnotationConfigApplicationContext(ConfigWithEmptyResourceLocations.class))
.withCauseInstanceOf(IllegalArgumentException.class);
}
@Test
@@ -253,8 +253,8 @@ class PropertySourceAnnotationTests {
@Test
void withMissingPropertySource() {
assertThatExceptionOfType(BeanDefinitionStoreException.class)
.isThrownBy(() -> new AnnotationConfigApplicationContext(ConfigWithMissingPropertySource.class))
.withCauseInstanceOf(FileNotFoundException.class);
.isThrownBy(() -> new AnnotationConfigApplicationContext(ConfigWithMissingPropertySource.class))
.withCauseInstanceOf(FileNotFoundException.class);
}
@Test
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 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.
@@ -102,16 +102,16 @@ class GenericApplicationContextTests {
assertThat(context.getBean(String.class)).isSameAs(context.getBean("testBean"));
assertThat(context.getAutowireCapableBeanFactory().getBean(String.class))
.isSameAs(context.getAutowireCapableBeanFactory().getBean("testBean"));
.isSameAs(context.getAutowireCapableBeanFactory().getBean("testBean"));
context.close();
assertThatIllegalStateException()
.isThrownBy(() -> context.getBean(String.class));
.isThrownBy(() -> context.getBean(String.class));
assertThatIllegalStateException()
.isThrownBy(() -> context.getAutowireCapableBeanFactory().getBean(String.class));
.isThrownBy(() -> context.getAutowireCapableBeanFactory().getBean(String.class));
assertThatIllegalStateException()
.isThrownBy(() -> context.getAutowireCapableBeanFactory().getBean("testBean"));
.isThrownBy(() -> context.getAutowireCapableBeanFactory().getBean("testBean"));
}
@Test
@@ -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.
@@ -35,32 +35,27 @@ class BitsCronFieldTests {
@Test
void parse() {
assertThat(BitsCronField.parseSeconds("42")).has(clearRange(0, 41)).has(set(42)).has(clearRange(43, 59));
assertThat(BitsCronField.parseSeconds("0-4,8-12")).has(setRange(0, 4)).has(clearRange(5,7))
.has(setRange(8, 12)).has(clearRange(13,59));
assertThat(BitsCronField.parseSeconds("57/2")).has(clearRange(0, 56)).has(set(57))
.has(clear(58)).has(set(59));
assertThat(BitsCronField.parseSeconds("0-4,8-12")).has(setRange(0, 4)).has(clearRange(5,7)).has(setRange(8, 12)).has(clearRange(13,59));
assertThat(BitsCronField.parseSeconds("57/2")).has(clearRange(0, 56)).has(set(57)).has(clear(58)).has(set(59));
assertThat(BitsCronField.parseMinutes("30")).has(set(30)).has(clearRange(1, 29)).has(clearRange(31, 59));
assertThat(BitsCronField.parseHours("23")).has(set(23)).has(clearRange(0, 23));
assertThat(BitsCronField.parseHours("0-23/2")).has(set(0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22))
.has(clear(1,3,5,7,9,11,13,15,17,19,21,23));
assertThat(BitsCronField.parseHours("0-23/2")).has(set(0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22)).has(clear(1,3,5,7,9,11,13,15,17,19,21,23));
assertThat(BitsCronField.parseDaysOfMonth("1")).has(set(1)).has(clearRange(2, 31));
assertThat(BitsCronField.parseMonth("1")).has(set(1)).has(clearRange(2, 12));
assertThat(BitsCronField.parseDaysOfWeek("0")).has(set(7, 7)).has(clearRange(0, 6));
assertThat(BitsCronField.parseDaysOfWeek("7-5")).has(clear(0)).has(setRange(1, 5))
.has(clear(6)).has(set(7));
assertThat(BitsCronField.parseDaysOfWeek("7-5")).has(clear(0)).has(setRange(1, 5)).has(clear(6)).has(set(7));
}
@Test
void parseLists() {
assertThat(BitsCronField.parseSeconds("15,30")).has(set(15, 30)).has(clearRange(1, 15))
.has(clearRange(31, 59));
assertThat(BitsCronField.parseMinutes("1,2,5,9")).has(set(1, 2, 5, 9)).has(clear(0))
.has(clearRange(3, 4)).has(clearRange(6, 8)).has(clearRange(10, 59));
assertThat(BitsCronField.parseSeconds("15,30")).has(set(15, 30)).has(clearRange(1, 15)).has(clearRange(31, 59));
assertThat(BitsCronField.parseMinutes("1,2,5,9")).has(set(1, 2, 5, 9)).has(clear(0)).has(clearRange(3, 4)).has(clearRange(6, 8)).has(clearRange(10, 59));
assertThat(BitsCronField.parseHours("1,2,3")).has(set(1, 2, 3)).has(clearRange(4, 23));
assertThat(BitsCronField.parseDaysOfMonth("1,2,3")).has(set(1, 2, 3)).has(clearRange(4, 31));
assertThat(BitsCronField.parseMonth("1,2,3")).has(set(1, 2, 3)).has(clearRange(4, 12));
@@ -112,7 +107,6 @@ class BitsCronFieldTests {
.has(clear(0)).has(setRange(1, 7));
}
private static Condition<BitsCronField> set(int... indices) {
return new Condition<BitsCronField>(String.format("set bits %s", Arrays.toString(indices))) {
@Override
@@ -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.
@@ -848,7 +848,6 @@ class CronTriggerTests {
assertThat(nextExecutionTime).isEqualTo(this.calendar.getTime());
}
private static void roundup(Calendar calendar) {
calendar.add(Calendar.SECOND, 1);
calendar.set(Calendar.MILLISECOND, 0);
@@ -862,7 +861,9 @@ class CronTriggerTests {
}
private static TriggerContext getTriggerContext(Date lastCompletionTime) {
return new SimpleTriggerContext(null, null, lastCompletionTime);
SimpleTriggerContext context = new SimpleTriggerContext();
context.update(null, null, lastCompletionTime);
return context;
}
@@ -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.
@@ -28,7 +28,6 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
* Unit tests for {@link QuartzCronField}.
*
* @author Arjen Poutsma
* @author Juergen Hoeller
*/
class QuartzCronFieldTests {
@@ -72,46 +71,6 @@ class QuartzCronFieldTests {
assertThat(field.nextOrSame(last)).isEqualTo(expected);
}
@Test
void dayOfWeek_0(){
// third Sunday (0) of the month
QuartzCronField field = QuartzCronField.parseDaysOfWeek("0#3");
LocalDate last = LocalDate.of(2024, 1, 1);
LocalDate expected = LocalDate.of(2024, 1, 21);
assertThat(field.nextOrSame(last)).isEqualTo(expected);
}
@Test
void dayOfWeek_1(){
// third Monday (1) of the month
QuartzCronField field = QuartzCronField.parseDaysOfWeek("1#3");
LocalDate last = LocalDate.of(2024, 1, 1);
LocalDate expected = LocalDate.of(2024, 1, 15);
assertThat(field.nextOrSame(last)).isEqualTo(expected);
}
@Test
void dayOfWeek_2(){
// third Tuesday (2) of the month
QuartzCronField field = QuartzCronField.parseDaysOfWeek("2#3");
LocalDate last = LocalDate.of(2024, 1, 1);
LocalDate expected = LocalDate.of(2024, 1, 16);
assertThat(field.nextOrSame(last)).isEqualTo(expected);
}
@Test
void dayOfWeek_7() {
// third Sunday (7 as alternative to 0) of the month
QuartzCronField field = QuartzCronField.parseDaysOfWeek("7#3");
LocalDate last = LocalDate.of(2024, 1, 1);
LocalDate expected = LocalDate.of(2024, 1, 21);
assertThat(field.nextOrSame(last)).isEqualTo(expected);
}
@Test
void invalidValues() {
assertThatIllegalArgumentException().isThrownBy(() -> QuartzCronField.parseDaysOfMonth(""));
@@ -3,6 +3,9 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<!--
Not yet in use: illustration of possible approach
-->
<bean id="overrideOneMethod" class="org.springframework.beans.factory.xml.OverrideOneMethod">
<lookup-method name="getPrototypeDependency" bean="jenny"/>
@@ -24,34 +27,39 @@
<lookup-method name="protectedOverrideSingleton" bean="david"/>
<!-- This method is not overloaded, so we don't need to specify any arg types -->
<!--
This method is not overloaded, so we don't need to specify any arg types
-->
<replaced-method name="doSomething" replacer="doSomethingReplacer"/>
</bean>
<bean id="replaceVoidMethod" parent="someParent" class="org.springframework.beans.factory.xml.OverrideOneMethodSubclass"/>
<bean id="replaceVoidMethod" parent="someParent"
class="org.springframework.beans.factory.xml.OverrideOneMethodSubclass">
<bean id="replaceEchoMethod" class="org.springframework.beans.factory.xml.EchoService">
<!-- This method is not overloaded, so we don't need to specify any arg types -->
<replaced-method name="echo" replacer="reverseArrayReplacer" />
</bean>
<bean id="reverseReplacer" class="org.springframework.beans.factory.xml.ReverseMethodReplacer"/>
<bean id="reverseReplacer"
class="org.springframework.beans.factory.xml.ReverseMethodReplacer"/>
<bean id="reverseArrayReplacer" class="org.springframework.beans.factory.xml.ReverseArrayMethodReplacer"/>
<bean id="fixedReplacer"
class="org.springframework.beans.factory.xml.FixedMethodReplacer"/>
<bean id="fixedReplacer" class="org.springframework.beans.factory.xml.FixedMethodReplacer"/>
<bean id="doSomethingReplacer"
class="org.springframework.beans.factory.xml.XmlBeanFactoryTests$DoSomethingReplacer"/>
<bean id="doSomethingReplacer" class="org.springframework.beans.factory.xml.XmlBeanFactoryTests$DoSomethingReplacer"/>
<bean id="serializableReplacer"
class="org.springframework.beans.factory.xml.SerializableMethodReplacerCandidate">
<bean id="serializableReplacer" class="org.springframework.beans.factory.xml.SerializableMethodReplacerCandidate">
<!-- Arbitrary method replacer -->
<replaced-method name="replaceMe" replacer="reverseReplacer">
<arg-type>String</arg-type>
</replaced-method>
</bean>
<bean id="jenny" class="org.springframework.beans.testfixture.beans.TestBean" scope="prototype">
<bean id="jenny" class="org.springframework.beans.testfixture.beans.TestBean"
scope="prototype">
<property name="name"><value>Jenny</value></property>
<property name="age"><value>30</value></property>
<property name="spouse">
@@ -60,7 +68,8 @@
</property>
</bean>
<bean id="david" class="org.springframework.beans.testfixture.beans.TestBean" scope="singleton">
<bean id="david" class="org.springframework.beans.testfixture.beans.TestBean"
scope="singleton">
<description>
Simple bean, without any collections.
</description>
@@ -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.
@@ -159,7 +159,7 @@ final class SerializableTypeWrapper {
/**
* Return the source of the type, or {@code null} if not known.
* <p>The default implementation returns {@code null}.
* <p>The default implementations returns {@code null}.
*/
@Nullable
default Object getSource() {
@@ -214,12 +214,7 @@ final class SerializableTypeWrapper {
return result;
}
Type type = this.provider.getType();
if (type instanceof TypeVariable<?> && method.getName().equals("getName")) {
// Avoid reflection for common comparison of type variables
return ((TypeVariable<?>) type).getName();
}
return ReflectionUtils.invokeMethod(method, type, args);
return ReflectionUtils.invokeMethod(method, this.provider.getType(), args);
}
}
@@ -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.
@@ -757,7 +757,7 @@ public abstract class AnnotationUtils {
* Google App Engine's late arrival of {@code TypeNotPresentExceptionProxy} for
* {@code Class} values (instead of early {@code Class.getAnnotations() failure}).
* <p>This method not failing indicates that {@link #getAnnotationAttributes(Annotation)}
* won't fail either (when attempted later on).
* won't failure either (when attempted later on).
* @param annotation the annotation to validate
* @throws IllegalStateException if a declared {@code Class} attribute could not be read
* @since 4.3.15
@@ -1059,7 +1059,8 @@ public abstract class AnnotationUtils {
return null;
}
catch (Throwable ex) {
handleValueRetrievalFailure(annotation, ex);
rethrowAnnotationConfigurationException(ex);
handleIntrospectionFailure(annotation.getClass(), ex);
return null;
}
}
@@ -1075,18 +1076,14 @@ public abstract class AnnotationUtils {
* @return the value returned from the method invocation
* @since 5.3.24
*/
@Nullable
static Object invokeAnnotationMethod(Method method, @Nullable Object annotation) {
if (annotation == null) {
return null;
}
static Object invokeAnnotationMethod(Method method, Object annotation) {
if (Proxy.isProxyClass(annotation.getClass())) {
try {
InvocationHandler handler = Proxy.getInvocationHandler(annotation);
return handler.invoke(annotation, method, null);
}
catch (Throwable ex) {
// Ignore and fall back to reflection below
// ignore and fall back to reflection below
}
}
return ReflectionUtils.invokeMethod(method, annotation);
@@ -1120,32 +1117,20 @@ public abstract class AnnotationUtils {
* @see #rethrowAnnotationConfigurationException
* @see IntrospectionFailureLogger
*/
static void handleIntrospectionFailure(AnnotatedElement element, Throwable ex) {
static void handleIntrospectionFailure(@Nullable AnnotatedElement element, Throwable ex) {
rethrowAnnotationConfigurationException(ex);
IntrospectionFailureLogger logger = IntrospectionFailureLogger.INFO;
boolean meta = false;
if (element instanceof Class && Annotation.class.isAssignableFrom((Class<?>) element)) {
// Meta-annotation introspection failure
// Meta-annotation or (default) value lookup on an annotation type
logger = IntrospectionFailureLogger.DEBUG;
meta = true;
}
if (logger.isEnabled()) {
logger.log("Failed to " + (meta ? "meta-introspect annotation " : "introspect annotations on ") +
element + ": " + ex);
}
}
/**
* Handle the supplied value retrieval exception.
* @param annotation the annotation instance from which to retrieve the value
* @param ex the exception that we encountered
* @see #handleIntrospectionFailure
*/
private static void handleValueRetrievalFailure(Annotation annotation, Throwable ex) {
rethrowAnnotationConfigurationException(ex);
IntrospectionFailureLogger logger = IntrospectionFailureLogger.INFO;
if (logger.isEnabled()) {
logger.log("Failed to retrieve value from " + annotation + ": " + ex);
String message = meta ?
"Failed to meta-introspect annotation " :
"Failed to introspect annotations on ";
logger.log(message + element + ": " + ex);
}
}
@@ -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.
@@ -339,10 +339,11 @@ abstract class AnnotationsScanner {
Method[] methods = baseTypeMethodsCache.get(baseType);
if (methods == null) {
methods = ReflectionUtils.getDeclaredMethods(baseType);
boolean isInterface = baseType.isInterface();
methods = isInterface ? baseType.getMethods() : ReflectionUtils.getDeclaredMethods(baseType);
int cleared = 0;
for (int i = 0; i < methods.length; i++) {
if (Modifier.isPrivate(methods[i].getModifiers()) ||
if ((!isInterface && Modifier.isPrivate(methods[i].getModifiers())) ||
hasPlainJavaAnnotationsOnly(methods[i]) ||
getDeclaredAnnotations(methods[i], false).length == 0) {
methods[i] = null;
@@ -456,7 +457,7 @@ abstract class AnnotationsScanner {
for (int i = 0; i < annotations.length; i++) {
Annotation annotation = annotations[i];
if (isIgnorable(annotation.annotationType()) ||
!AttributeMethods.forAnnotationType(annotation.annotationType()).canLoad(annotation)) {
!AttributeMethods.forAnnotationType(annotation.annotationType()).isValid(annotation)) {
annotations[i] = null;
}
else {
@@ -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,7 @@ final class AttributeMethods {
if (m1 != null && m2 != null) {
return m1.getName().compareTo(m2.getName());
}
return (m1 != null ? -1 : 1);
return m1 != null ? -1 : 1;
};
@@ -87,26 +87,18 @@ final class AttributeMethods {
/**
* Determine if values from the given annotation can be safely accessed without
* causing any {@link TypeNotPresentException TypeNotPresentExceptions}.
* <p>This method is designed to cover Google App Engine's late arrival of such
* exceptions for {@code Class} values (instead of the more typical early
* {@code Class.getAnnotations() failure} on a regular JVM).
* @param annotation the annotation to check
* @return {@code true} if all values are present
* @see #validate(Annotation)
*/
boolean canLoad(Annotation annotation) {
boolean isValid(Annotation annotation) {
assertAnnotation(annotation);
for (int i = 0; i < size(); i++) {
if (canThrowTypeNotPresentException(i)) {
try {
AnnotationUtils.invokeAnnotationMethod(get(i), annotation);
}
catch (IllegalStateException ex) {
// Plain invocation failure to expose -> leave up to attribute retrieval
// (if any) where such invocation failure will be logged eventually.
}
catch (Throwable ex) {
// TypeNotPresentException etc. -> annotation type not actually loadable.
return false;
}
}
@@ -116,13 +108,13 @@ final class AttributeMethods {
/**
* Check if values from the given annotation can be safely accessed without causing
* any {@link TypeNotPresentException TypeNotPresentExceptions}.
* <p>This method is designed to cover Google App Engine's late arrival of such
* any {@link TypeNotPresentException TypeNotPresentExceptions}. In particular,
* this method is designed to cover Google App Engine's late arrival of such
* exceptions for {@code Class} values (instead of the more typical early
* {@code Class.getAnnotations() failure} on a regular JVM).
* {@code Class.getAnnotations() failure}).
* @param annotation the annotation to validate
* @throws IllegalStateException if a declared {@code Class} attribute could not be read
* @see #canLoad(Annotation)
* @see #isValid(Annotation)
*/
void validate(Annotation annotation) {
assertAnnotation(annotation);
@@ -131,9 +123,6 @@ final class AttributeMethods {
try {
AnnotationUtils.invokeAnnotationMethod(get(i), annotation);
}
catch (IllegalStateException ex) {
throw ex;
}
catch (Throwable ex) {
throw new IllegalStateException("Could not obtain annotation attribute value for " +
get(i).getName() + " declared on " + annotation.annotationType(), ex);
@@ -158,7 +147,7 @@ final class AttributeMethods {
@Nullable
Method get(String name) {
int index = indexOf(name);
return (index != -1 ? this.attributeMethods[index] : null);
return index != -1 ? this.attributeMethods[index] : null;
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 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.
@@ -476,7 +476,7 @@ public class TypeDescriptor implements Serializable {
ObjectUtils.nullSafeEquals(getMapValueTypeDescriptor(), otherDesc.getMapValueTypeDescriptor()));
}
else {
return Arrays.equals(getResolvableType().getGenerics(), otherDesc.getResolvableType().getGenerics());
return true;
}
}
@@ -523,7 +523,7 @@ public class TypeDescriptor implements Serializable {
/**
* Create a new type descriptor for an object.
* <p>Use this factory method to introspect a source object before asking the
* conversion system to convert it to some other type.
* conversion system to convert it to some another type.
* <p>If the provided object is {@code null}, returns {@code null}, else calls
* {@link #valueOf(Class)} to build a TypeDescriptor from the object's class.
* @param source the source object
@@ -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.
@@ -30,8 +30,6 @@ import org.springframework.util.DefaultPropertiesPersister;
* "spring.xml.ignore" property.
*
* <p>This is the standard implementation used in Spring's resource support.
* Only intended for internal use within the framework. For other purposes,
* use its base class {@link DefaultPropertiesPersister} instead.
*
* @author Juergen Hoeller
* @author Sebastien Deleuze
@@ -42,6 +40,7 @@ public class ResourcePropertiesPersister extends DefaultPropertiesPersister {
/**
* A convenient constant for a default {@code ResourcePropertiesPersister} instance,
* as used in Spring's common resource support.
* @since 5.3
*/
public static final ResourcePropertiesPersister INSTANCE = new ResourcePropertiesPersister();
@@ -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.
@@ -43,45 +43,55 @@ class AnnotationIntrospectionFailureTests {
@Test
void filteredTypeThrowsTypeNotPresentException() throws Exception {
FilteringClassLoader classLoader = new FilteringClassLoader(getClass().getClassLoader());
Class<?> withAnnotation = ClassUtils.forName(WithExampleAnnotation.class.getName(), classLoader);
Annotation annotation = withAnnotation.getAnnotations()[0];
FilteringClassLoader classLoader = new FilteringClassLoader(
getClass().getClassLoader());
Class<?> withExampleAnnotation = ClassUtils.forName(
WithExampleAnnotation.class.getName(), classLoader);
Annotation annotation = withExampleAnnotation.getAnnotations()[0];
Method method = annotation.annotationType().getMethod("value");
method.setAccessible(true);
assertThatExceptionOfType(TypeNotPresentException.class)
.isThrownBy(() -> ReflectionUtils.invokeMethod(method, annotation))
.withCauseInstanceOf(ClassNotFoundException.class);
assertThatExceptionOfType(TypeNotPresentException.class).isThrownBy(() ->
ReflectionUtils.invokeMethod(method, annotation))
.withCauseInstanceOf(ClassNotFoundException.class);
}
@Test
@SuppressWarnings("unchecked")
void filteredTypeInMetaAnnotationWhenUsingAnnotatedElementUtilsHandlesException() throws Exception {
FilteringClassLoader classLoader = new FilteringClassLoader(getClass().getClassLoader());
Class<?> withAnnotation = ClassUtils.forName(WithExampleMetaAnnotation.class.getName(), classLoader);
Class<Annotation> annotationClass = (Class<Annotation>)
ClassUtils.forName(ExampleAnnotation.class.getName(), classLoader);
Class<Annotation> metaAnnotationClass = (Class<Annotation>)
ClassUtils.forName(ExampleMetaAnnotation.class.getName(), classLoader);
assertThat(AnnotatedElementUtils.getMergedAnnotationAttributes(withAnnotation, annotationClass)).isNull();
assertThat(AnnotatedElementUtils.getMergedAnnotationAttributes(withAnnotation, metaAnnotationClass)).isNull();
assertThat(AnnotatedElementUtils.hasAnnotation(withAnnotation, annotationClass)).isFalse();
assertThat(AnnotatedElementUtils.hasAnnotation(withAnnotation, metaAnnotationClass)).isFalse();
FilteringClassLoader classLoader = new FilteringClassLoader(
getClass().getClassLoader());
Class<?> withExampleMetaAnnotation = ClassUtils.forName(
WithExampleMetaAnnotation.class.getName(), classLoader);
Class<Annotation> exampleAnnotationClass = (Class<Annotation>) ClassUtils.forName(
ExampleAnnotation.class.getName(), classLoader);
Class<Annotation> exampleMetaAnnotationClass = (Class<Annotation>) ClassUtils.forName(
ExampleMetaAnnotation.class.getName(), classLoader);
assertThat(AnnotatedElementUtils.getMergedAnnotationAttributes(
withExampleMetaAnnotation, exampleAnnotationClass)).isNull();
assertThat(AnnotatedElementUtils.getMergedAnnotationAttributes(
withExampleMetaAnnotation, exampleMetaAnnotationClass)).isNull();
assertThat(AnnotatedElementUtils.hasAnnotation(withExampleMetaAnnotation,
exampleAnnotationClass)).isFalse();
assertThat(AnnotatedElementUtils.hasAnnotation(withExampleMetaAnnotation,
exampleMetaAnnotationClass)).isFalse();
}
@Test
@SuppressWarnings("unchecked")
void filteredTypeInMetaAnnotationWhenUsingMergedAnnotationsHandlesException() throws Exception {
FilteringClassLoader classLoader = new FilteringClassLoader(getClass().getClassLoader());
Class<?> withAnnotation = ClassUtils.forName(WithExampleMetaAnnotation.class.getName(), classLoader);
Class<Annotation> annotationClass = (Class<Annotation>)
ClassUtils.forName(ExampleAnnotation.class.getName(), classLoader);
Class<Annotation> metaAnnotationClass = (Class<Annotation>)
ClassUtils.forName(ExampleMetaAnnotation.class.getName(), classLoader);
MergedAnnotations annotations = MergedAnnotations.from(withAnnotation);
assertThat(annotations.get(annotationClass).isPresent()).isFalse();
assertThat(annotations.get(metaAnnotationClass).isPresent()).isFalse();
assertThat(annotations.isPresent(metaAnnotationClass)).isFalse();
assertThat(annotations.isPresent(annotationClass)).isFalse();
FilteringClassLoader classLoader = new FilteringClassLoader(
getClass().getClassLoader());
Class<?> withExampleMetaAnnotation = ClassUtils.forName(
WithExampleMetaAnnotation.class.getName(), classLoader);
Class<Annotation> exampleAnnotationClass = (Class<Annotation>) ClassUtils.forName(
ExampleAnnotation.class.getName(), classLoader);
Class<Annotation> exampleMetaAnnotationClass = (Class<Annotation>) ClassUtils.forName(
ExampleMetaAnnotation.class.getName(), classLoader);
MergedAnnotations annotations = MergedAnnotations.from(withExampleMetaAnnotation);
assertThat(annotations.get(exampleAnnotationClass).isPresent()).isFalse();
assertThat(annotations.get(exampleMetaAnnotationClass).isPresent()).isFalse();
assertThat(annotations.isPresent(exampleMetaAnnotationClass)).isFalse();
assertThat(annotations.isPresent(exampleAnnotationClass)).isFalse();
}
@@ -93,16 +103,17 @@ class AnnotationIntrospectionFailureTests {
@Override
protected boolean isEligibleForOverriding(String className) {
return className.startsWith(AnnotationIntrospectionFailureTests.class.getName()) ||
className.startsWith("jdk.internal");
return className.startsWith(
AnnotationIntrospectionFailureTests.class.getName());
}
@Override
protected Class<?> loadClassForOverriding(String name) throws ClassNotFoundException {
if (name.contains("Filtered") || name.startsWith("jdk.internal")) {
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (name.startsWith(AnnotationIntrospectionFailureTests.class.getName()) &&
name.contains("Filtered")) {
throw new ClassNotFoundException(name);
}
return super.loadClassForOverriding(name);
return super.loadClass(name, resolve);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 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.
@@ -194,7 +194,7 @@ class AnnotationsScannerTests {
}
@Test
void typeHierarchyStrategyOnClassWhenHasSingleInterfaceScansInterfaces() {
void typeHierarchyStrategyOnClassWhenHasInterfaceDoesNotIncludeInterfaces() {
Class<?> source = WithSingleInterface.class;
assertThat(scan(source, SearchStrategy.TYPE_HIERARCHY)).containsExactly(
"0:TestAnnotation1", "1:TestAnnotation2", "1:TestInheritedAnnotation2");
@@ -350,19 +350,10 @@ class AnnotationsScannerTests {
}
@Test
void typeHierarchyStrategyOnMethodWhenHasInterfaceScansInterfaces() {
void typeHierarchyStrategyOnMethodWhenHasInterfaceDoesNotIncludeInterfaces() {
Method source = methodFrom(WithSingleInterface.class);
assertThat(scan(source, SearchStrategy.TYPE_HIERARCHY)).containsExactly(
"0:TestAnnotation1", "1:TestAnnotation2", "1:TestInheritedAnnotation2");
source = methodFrom(Hello1Impl.class);
assertThat(scan(source, SearchStrategy.TYPE_HIERARCHY)).containsExactly("1:TestAnnotation1");
}
@Test // gh-31803
void typeHierarchyStrategyOnMethodWhenHasInterfaceHierarchyScansInterfacesOnlyOnce() {
Method source = methodFrom(Hello2Impl.class);
assertThat(scan(source, SearchStrategy.TYPE_HIERARCHY)).containsExactly("1:TestAnnotation1");
}
@Test
@@ -672,30 +663,6 @@ class AnnotationsScannerTests {
}
}
interface Hello1 {
@TestAnnotation1
void method();
}
interface Hello2 extends Hello1 {
}
static class Hello1Impl implements Hello1 {
@Override
public void method() {
}
}
static class Hello2Impl implements Hello2 {
@Override
public void method() {
}
}
@TestAnnotation2
@TestInheritedAnnotation2
static class HierarchySuperclass extends HierarchySuperSuperclass {
@@ -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.
@@ -112,7 +112,7 @@ class AttributeMethodsTests {
ClassValue annotation = mockAnnotation(ClassValue.class);
given(annotation.value()).willThrow(TypeNotPresentException.class);
AttributeMethods attributes = AttributeMethods.forAnnotationType(annotation.annotationType());
assertThat(attributes.canLoad(annotation)).isFalse();
assertThat(attributes.isValid(annotation)).isFalse();
}
@Test
@@ -121,7 +121,7 @@ class AttributeMethodsTests {
ClassValue annotation = mock(ClassValue.class);
given(annotation.value()).willReturn((Class) InputStream.class);
AttributeMethods attributes = AttributeMethods.forAnnotationType(annotation.annotationType());
assertThat(attributes.canLoad(annotation)).isTrue();
assertThat(attributes.isValid(annotation)).isTrue();
}
@Test
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 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.
@@ -34,13 +34,11 @@ import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.junit.jupiter.api.Test;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
@@ -67,7 +65,7 @@ class TypeDescriptorTests {
assertThat(desc.getName()).isEqualTo("int");
assertThat(desc.toString()).isEqualTo("int");
assertThat(desc.isPrimitive()).isTrue();
assertThat(desc.getAnnotations()).isEmpty();
assertThat(desc.getAnnotations().length).isEqualTo(0);
assertThat(desc.isCollection()).isFalse();
assertThat(desc.isMap()).isFalse();
}
@@ -79,8 +77,8 @@ class TypeDescriptorTests {
assertThat(desc.getObjectType()).isEqualTo(String.class);
assertThat(desc.getName()).isEqualTo("java.lang.String");
assertThat(desc.toString()).isEqualTo("java.lang.String");
assertThat(desc.isPrimitive()).isFalse();
assertThat(desc.getAnnotations()).isEmpty();
assertThat(!desc.isPrimitive()).isTrue();
assertThat(desc.getAnnotations().length).isEqualTo(0);
assertThat(desc.isCollection()).isFalse();
assertThat(desc.isArray()).isFalse();
assertThat(desc.isMap()).isFalse();
@@ -94,8 +92,8 @@ class TypeDescriptorTests {
assertThat(desc.getObjectType()).isEqualTo(List.class);
assertThat(desc.getName()).isEqualTo("java.util.List");
assertThat(desc.toString()).isEqualTo("java.util.List<java.util.List<java.util.Map<java.lang.Integer, java.lang.Enum<?>>>>");
assertThat(desc.isPrimitive()).isFalse();
assertThat(desc.getAnnotations()).isEmpty();
assertThat(!desc.isPrimitive()).isTrue();
assertThat(desc.getAnnotations().length).isEqualTo(0);
assertThat(desc.isCollection()).isTrue();
assertThat(desc.isArray()).isFalse();
assertThat(desc.getElementTypeDescriptor().getType()).isEqualTo(List.class);
@@ -115,8 +113,8 @@ class TypeDescriptorTests {
assertThat(desc.getObjectType()).isEqualTo(List.class);
assertThat(desc.getName()).isEqualTo("java.util.List");
assertThat(desc.toString()).isEqualTo("java.util.List<?>");
assertThat(desc.isPrimitive()).isFalse();
assertThat(desc.getAnnotations()).isEmpty();
assertThat(!desc.isPrimitive()).isTrue();
assertThat(desc.getAnnotations().length).isEqualTo(0);
assertThat(desc.isCollection()).isTrue();
assertThat(desc.isArray()).isFalse();
assertThat((Object) desc.getElementTypeDescriptor()).isNull();
@@ -131,8 +129,8 @@ class TypeDescriptorTests {
assertThat(desc.getObjectType()).isEqualTo(Integer[].class);
assertThat(desc.getName()).isEqualTo("java.lang.Integer[]");
assertThat(desc.toString()).isEqualTo("java.lang.Integer[]");
assertThat(desc.isPrimitive()).isFalse();
assertThat(desc.getAnnotations()).isEmpty();
assertThat(!desc.isPrimitive()).isTrue();
assertThat(desc.getAnnotations().length).isEqualTo(0);
assertThat(desc.isCollection()).isFalse();
assertThat(desc.isArray()).isTrue();
assertThat(desc.getElementTypeDescriptor().getType()).isEqualTo(Integer.class);
@@ -148,8 +146,8 @@ class TypeDescriptorTests {
assertThat(desc.getObjectType()).isEqualTo(Map.class);
assertThat(desc.getName()).isEqualTo("java.util.Map");
assertThat(desc.toString()).isEqualTo("java.util.Map<java.lang.Integer, java.util.List<java.lang.String>>");
assertThat(desc.isPrimitive()).isFalse();
assertThat(desc.getAnnotations()).isEmpty();
assertThat(!desc.isPrimitive()).isTrue();
assertThat(desc.getAnnotations().length).isEqualTo(0);
assertThat(desc.isCollection()).isFalse();
assertThat(desc.isArray()).isFalse();
assertThat(desc.isMap()).isTrue();
@@ -164,7 +162,7 @@ class TypeDescriptorTests {
void parameterAnnotated() throws Exception {
TypeDescriptor t1 = new TypeDescriptor(new MethodParameter(getClass().getMethod("testAnnotatedMethod", String.class), 0));
assertThat(t1.getType()).isEqualTo(String.class);
assertThat(t1.getAnnotations()).hasSize(1);
assertThat(t1.getAnnotations().length).isEqualTo(1);
assertThat(t1.getAnnotation(ParameterAnnotation.class)).isNotNull();
assertThat(t1.hasAnnotation(ParameterAnnotation.class)).isTrue();
assertThat(t1.getAnnotation(ParameterAnnotation.class).value()).isEqualTo(123);
@@ -337,7 +335,7 @@ class TypeDescriptorTests {
@Test
void fieldAnnotated() throws Exception {
TypeDescriptor typeDescriptor = new TypeDescriptor(getClass().getField("fieldAnnotated"));
assertThat(typeDescriptor.getAnnotations()).hasSize(1);
assertThat(typeDescriptor.getAnnotations().length).isEqualTo(1);
assertThat(typeDescriptor.getAnnotation(FieldAnnotation.class)).isNotNull();
}
@@ -464,8 +462,8 @@ class TypeDescriptorTests {
assertThat(desc.getObjectType()).isEqualTo(List.class);
assertThat(desc.getName()).isEqualTo("java.util.List");
assertThat(desc.toString()).isEqualTo("java.util.List<java.lang.Integer>");
assertThat(desc.isPrimitive()).isFalse();
assertThat(desc.getAnnotations()).isEmpty();
assertThat(!desc.isPrimitive()).isTrue();
assertThat(desc.getAnnotations().length).isEqualTo(0);
assertThat(desc.isCollection()).isTrue();
assertThat(desc.isArray()).isFalse();
assertThat(desc.getElementTypeDescriptor().getType()).isEqualTo(Integer.class);
@@ -480,8 +478,8 @@ class TypeDescriptorTests {
assertThat(desc.getObjectType()).isEqualTo(List.class);
assertThat(desc.getName()).isEqualTo("java.util.List");
assertThat(desc.toString()).isEqualTo("java.util.List<java.util.List<java.lang.Integer>>");
assertThat(desc.isPrimitive()).isFalse();
assertThat(desc.getAnnotations()).isEmpty();
assertThat(!desc.isPrimitive()).isTrue();
assertThat(desc.getAnnotations().length).isEqualTo(0);
assertThat(desc.isCollection()).isTrue();
assertThat(desc.isArray()).isFalse();
assertThat(desc.getElementTypeDescriptor().getType()).isEqualTo(List.class);
@@ -496,8 +494,8 @@ class TypeDescriptorTests {
assertThat(desc.getObjectType()).isEqualTo(Map.class);
assertThat(desc.getName()).isEqualTo("java.util.Map");
assertThat(desc.toString()).isEqualTo("java.util.Map<java.lang.String, java.lang.Integer>");
assertThat(desc.isPrimitive()).isFalse();
assertThat(desc.getAnnotations()).isEmpty();
assertThat(!desc.isPrimitive()).isTrue();
assertThat(desc.getAnnotations().length).isEqualTo(0);
assertThat(desc.isCollection()).isFalse();
assertThat(desc.isArray()).isFalse();
assertThat(desc.isMap()).isTrue();
@@ -513,8 +511,8 @@ class TypeDescriptorTests {
assertThat(desc.getObjectType()).isEqualTo(Map.class);
assertThat(desc.getName()).isEqualTo("java.util.Map");
assertThat(desc.toString()).isEqualTo("java.util.Map<java.lang.String, java.util.Map<java.lang.String, java.lang.Integer>>");
assertThat(desc.isPrimitive()).isFalse();
assertThat(desc.getAnnotations()).isEmpty();
assertThat(!desc.isPrimitive()).isTrue();
assertThat(desc.getAnnotations().length).isEqualTo(0);
assertThat(desc.isCollection()).isFalse();
assertThat(desc.isArray()).isFalse();
assertThat(desc.isMap()).isTrue();
@@ -526,7 +524,7 @@ class TypeDescriptorTests {
@Test
void narrow() {
TypeDescriptor desc = TypeDescriptor.valueOf(Number.class);
Integer value = 3;
Integer value = Integer.valueOf(3);
desc = desc.narrow(value);
assertThat(desc.getType()).isEqualTo(Integer.class);
}
@@ -534,7 +532,7 @@ class TypeDescriptorTests {
@Test
void elementType() {
TypeDescriptor desc = TypeDescriptor.valueOf(List.class);
Integer value = 3;
Integer value = Integer.valueOf(3);
desc = desc.elementTypeDescriptor(value);
assertThat(desc.getType()).isEqualTo(Integer.class);
}
@@ -552,7 +550,7 @@ class TypeDescriptorTests {
@Test
void mapKeyType() {
TypeDescriptor desc = TypeDescriptor.valueOf(Map.class);
Integer value = 3;
Integer value = Integer.valueOf(3);
desc = desc.getMapKeyTypeDescriptor(value);
assertThat(desc.getType()).isEqualTo(Integer.class);
}
@@ -570,7 +568,7 @@ class TypeDescriptorTests {
@Test
void mapValueType() {
TypeDescriptor desc = TypeDescriptor.valueOf(Map.class);
Integer value = 3;
Integer value = Integer.valueOf(3);
desc = desc.getMapValueTypeDescriptor(value);
assertThat(desc.getType()).isEqualTo(Integer.class);
}
@@ -665,12 +663,12 @@ class TypeDescriptorTests {
}
@Test
void upcast() throws Exception {
void upCast() throws Exception {
Property property = new Property(getClass(), getClass().getMethod("getProperty"),
getClass().getMethod("setProperty", Map.class));
TypeDescriptor typeDescriptor = new TypeDescriptor(property);
TypeDescriptor upcast = typeDescriptor.upcast(Object.class);
assertThat(upcast.getAnnotation(MethodAnnotation1.class)).isNotNull();
TypeDescriptor upCast = typeDescriptor.upcast(Object.class);
assertThat(upCast.getAnnotation(MethodAnnotation1.class) != null).isTrue();
}
@Test
@@ -684,7 +682,7 @@ class TypeDescriptorTests {
}
@Test
void elementTypeForCollectionSubclass() {
void elementTypeForCollectionSubclass() throws Exception {
@SuppressWarnings("serial")
class CustomSet extends HashSet<String> {
}
@@ -694,7 +692,7 @@ class TypeDescriptorTests {
}
@Test
void elementTypeForMapSubclass() {
void elementTypeForMapSubclass() throws Exception {
@SuppressWarnings("serial")
class CustomMap extends HashMap<String, Integer> {
}
@@ -706,7 +704,7 @@ class TypeDescriptorTests {
}
@Test
void createMapArray() {
void createMapArray() throws Exception {
TypeDescriptor mapType = TypeDescriptor.map(
LinkedHashMap.class, TypeDescriptor.valueOf(String.class), TypeDescriptor.valueOf(Integer.class));
TypeDescriptor arrayType = TypeDescriptor.array(mapType);
@@ -715,13 +713,13 @@ class TypeDescriptorTests {
}
@Test
void createStringArray() {
void createStringArray() throws Exception {
TypeDescriptor arrayType = TypeDescriptor.array(TypeDescriptor.valueOf(String.class));
assertThat(TypeDescriptor.valueOf(String[].class)).isEqualTo(arrayType);
}
@Test
void createNullArray() {
void createNullArray() throws Exception {
assertThat((Object) TypeDescriptor.array(null)).isNull();
}
@@ -738,13 +736,13 @@ class TypeDescriptorTests {
}
@Test
void createCollectionWithNullElement() {
void createCollectionWithNullElement() throws Exception {
TypeDescriptor typeDescriptor = TypeDescriptor.collection(List.class, null);
assertThat(typeDescriptor.getElementTypeDescriptor()).isNull();
}
@Test
void createMapWithNullElements() {
void createMapWithNullElements() throws Exception {
TypeDescriptor typeDescriptor = TypeDescriptor.map(LinkedHashMap.class, null, null);
assertThat(typeDescriptor.getMapKeyTypeDescriptor()).isNull();
assertThat(typeDescriptor.getMapValueTypeDescriptor()).isNull();
@@ -759,17 +757,6 @@ class TypeDescriptorTests {
assertThat(TypeDescriptor.valueOf(Integer.class).getSource()).isEqualTo(Integer.class);
}
@Test // gh-31672
void equalityWithGenerics() {
ResolvableType rt1 = ResolvableType.forClassWithGenerics(Optional.class, Integer.class);
ResolvableType rt2 = ResolvableType.forClassWithGenerics(Optional.class, String.class);
TypeDescriptor td1 = new TypeDescriptor(rt1, null, null);
TypeDescriptor td2 = new TypeDescriptor(rt2, null, null);
assertThat(td1).isNotEqualTo(td2);
}
// Methods designed for test introspection
@@ -117,7 +117,6 @@ public class ReflectiveMethodResolver implements MethodResolver {
TypeConverter typeConverter = context.getTypeConverter();
Class<?> type = (targetObject instanceof Class ? (Class<?>) targetObject : targetObject.getClass());
ArrayList<Method> methods = new ArrayList<>(getMethods(type, targetObject));
methods.removeIf(method -> !method.getName().equals(name));
// If a filter is registered for this type, call it
MethodFilter filter = (this.filters != null ? this.filters.get(type) : null);
@@ -161,46 +160,48 @@ public class ReflectiveMethodResolver implements MethodResolver {
boolean multipleOptions = false;
for (Method method : methodsToIterate) {
int paramCount = method.getParameterCount();
List<TypeDescriptor> paramDescriptors = new ArrayList<>(paramCount);
for (int i = 0; i < paramCount; i++) {
paramDescriptors.add(new TypeDescriptor(new MethodParameter(method, i)));
}
ReflectionHelper.ArgumentsMatchInfo matchInfo = null;
if (method.isVarArgs() && argumentTypes.size() >= (paramCount - 1)) {
// *sigh* complicated
matchInfo = ReflectionHelper.compareArgumentsVarargs(paramDescriptors, argumentTypes, typeConverter);
}
else if (paramCount == argumentTypes.size()) {
// Name and parameter number match, check the arguments
matchInfo = ReflectionHelper.compareArguments(paramDescriptors, argumentTypes, typeConverter);
}
if (matchInfo != null) {
if (matchInfo.isExactMatch()) {
return new ReflectiveMethodExecutor(method, type);
if (method.getName().equals(name)) {
int paramCount = method.getParameterCount();
List<TypeDescriptor> paramDescriptors = new ArrayList<>(paramCount);
for (int i = 0; i < paramCount; i++) {
paramDescriptors.add(new TypeDescriptor(new MethodParameter(method, i)));
}
else if (matchInfo.isCloseMatch()) {
if (this.useDistance) {
int matchDistance = ReflectionHelper.getTypeDifferenceWeight(paramDescriptors, argumentTypes);
if (closeMatch == null || matchDistance < closeMatchDistance) {
// This is a better match...
closeMatch = method;
closeMatchDistance = matchDistance;
ReflectionHelper.ArgumentsMatchInfo matchInfo = null;
if (method.isVarArgs() && argumentTypes.size() >= (paramCount - 1)) {
// *sigh* complicated
matchInfo = ReflectionHelper.compareArgumentsVarargs(paramDescriptors, argumentTypes, typeConverter);
}
else if (paramCount == argumentTypes.size()) {
// Name and parameter number match, check the arguments
matchInfo = ReflectionHelper.compareArguments(paramDescriptors, argumentTypes, typeConverter);
}
if (matchInfo != null) {
if (matchInfo.isExactMatch()) {
return new ReflectiveMethodExecutor(method, type);
}
else if (matchInfo.isCloseMatch()) {
if (this.useDistance) {
int matchDistance = ReflectionHelper.getTypeDifferenceWeight(paramDescriptors, argumentTypes);
if (closeMatch == null || matchDistance < closeMatchDistance) {
// This is a better match...
closeMatch = method;
closeMatchDistance = matchDistance;
}
}
else {
// Take this as a close match if there isn't one already
if (closeMatch == null) {
closeMatch = method;
}
}
}
else {
// Take this as a close match if there isn't one already
if (closeMatch == null) {
closeMatch = method;
else if (matchInfo.isMatchRequiringConversion()) {
if (matchRequiringConversion != null) {
multipleOptions = true;
}
matchRequiringConversion = method;
}
}
else if (matchInfo.isMatchRequiringConversion()) {
if (matchRequiringConversion != null) {
multipleOptions = true;
}
matchRequiringConversion = method;
}
}
}
if (closeMatch != null) {
@@ -60,7 +60,7 @@ import org.springframework.util.LinkedCaseInsensitiveMap;
import org.springframework.util.StringUtils;
/**
* <b>This is the central delegate in the JDBC core package.</b>
* <b>This is the central class in the JDBC core package.</b>
* It simplifies the use of JDBC and helps to avoid common errors.
* It executes core JDBC workflow, leaving application code to provide SQL
* and extract results. This class executes SQL queries or updates, initiating
@@ -417,7 +417,9 @@ public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
logger.debug("Executing SQL statement [" + sql + "]");
}
// Callback to execute the statement.
/**
* Callback to execute the statement.
*/
class ExecuteStatementCallback implements StatementCallback<Object>, SqlProvider {
@Override
@Nullable
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 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.
@@ -43,7 +43,7 @@ public interface CallMetaDataProvider {
/**
* Initialize the database specific management of procedure column meta-data.
* <p>This is only called for databases that are supported. This initialization
* This is only called for databases that are supported. This initialization
* can be turned off by specifying that column meta-data should not be used.
* @param databaseMetaData used to retrieve database specific information
* @param catalogName name of catalog to use (or {@code null} if none)
@@ -55,36 +55,30 @@ public interface CallMetaDataProvider {
void initializeWithProcedureColumnMetaData(DatabaseMetaData databaseMetaData, @Nullable String catalogName,
@Nullable String schemaName, @Nullable String procedureName) throws SQLException;
/**
* Get the call parameter meta-data that is currently used.
* @return a List of {@link CallParameterMetaData}
*/
List<CallParameterMetaData> getCallParameterMetaData();
/**
* Provide any modification of the procedure name passed in to match the meta-data currently used.
* <p>This could include altering the case.
* This could include altering the case.
*/
@Nullable
String procedureNameToUse(@Nullable String procedureName);
/**
* Provide any modification of the catalog name passed in to match the meta-data currently used.
* <p>This could include altering the case.
* This could include altering the case.
*/
@Nullable
String catalogNameToUse(@Nullable String catalogName);
/**
* Provide any modification of the schema name passed in to match the meta-data currently used.
* <p>This could include altering the case.
* This could include altering the case.
*/
@Nullable
String schemaNameToUse(@Nullable String schemaName);
/**
* Provide any modification of the catalog name passed in to match the meta-data currently used.
* <p>The returned value will be used for meta-data lookups. This could include altering the case
* The returned value will be used for meta-data lookups. This could include altering the case
* used or providing a base catalog if none is provided.
*/
@Nullable
@@ -92,7 +86,7 @@ public interface CallMetaDataProvider {
/**
* Provide any modification of the schema name passed in to match the meta-data currently used.
* <p>The returned value will be used for meta-data lookups. This could include altering the case
* The returned value will be used for meta-data lookups. This could include altering the case
* used or providing a base schema if none is provided.
*/
@Nullable
@@ -100,7 +94,7 @@ public interface CallMetaDataProvider {
/**
* Provide any modification of the column name passed in to match the meta-data currently used.
* <p>This could include altering the case.
* This could include altering the case.
* @param parameterName name of the parameter of column
*/
@Nullable
@@ -108,7 +102,7 @@ public interface CallMetaDataProvider {
/**
* Create a default out parameter based on the provided meta-data.
* <p>This is used when no explicit parameter declaration has been made.
* This is used when no explicit parameter declaration has been made.
* @param parameterName the name of the parameter
* @param meta meta-data used for this call
* @return the configured SqlOutParameter
@@ -117,7 +111,7 @@ public interface CallMetaDataProvider {
/**
* Create a default in/out parameter based on the provided meta-data.
* <p>This is used when no explicit parameter declaration has been made.
* This is used when no explicit parameter declaration has been made.
* @param parameterName the name of the parameter
* @param meta meta-data used for this call
* @return the configured SqlInOutParameter
@@ -126,7 +120,7 @@ public interface CallMetaDataProvider {
/**
* Create a default in parameter based on the provided meta-data.
* <p>This is used when no explicit parameter declaration has been made.
* This is used when no explicit parameter declaration has been made.
* @param parameterName the name of the parameter
* @param meta meta-data used for this call
* @return the configured SqlParameter
@@ -148,7 +142,7 @@ public interface CallMetaDataProvider {
/**
* Does this database support returning ResultSets as ref cursors to be retrieved with
* {@link java.sql.CallableStatement#getObject(int)} for the specified column?
* {@link java.sql.CallableStatement#getObject(int)} for the specified column.
*/
boolean isRefCursorSupported();
@@ -164,12 +158,18 @@ public interface CallMetaDataProvider {
boolean isProcedureColumnMetaDataUsed();
/**
* Should we bypass the return parameter with the specified name?
* <p>This allows the database specific implementation to skip the processing
* Should we bypass the return parameter with the specified name.
* This allows the database specific implementation to skip the processing
* for specific results returned by the database call.
*/
boolean byPassReturnParameter(String parameterName);
/**
* Get the call parameter meta-data that is currently used.
* @return a List of {@link CallParameterMetaData}
*/
List<CallParameterMetaData> getCallParameterMetaData();
/**
* Does the database support the use of catalog name in procedure calls?
*/
@@ -168,6 +168,11 @@ public class GenericCallMetaDataProvider implements CallMetaDataProvider {
return identifierNameToUse(parameterName);
}
@Override
public boolean byPassReturnParameter(String parameterName) {
return false;
}
@Override
public SqlParameter createDefaultOutParameter(String parameterName, CallParameterMetaData meta) {
return new SqlOutParameter(parameterName, meta.getSqlType());
@@ -208,11 +213,6 @@ public class GenericCallMetaDataProvider implements CallMetaDataProvider {
return this.procedureColumnMetaDataUsed;
}
@Override
public boolean byPassReturnParameter(String parameterName) {
return false;
}
/**
* Specify whether the database supports the use of catalog name in procedure calls.
@@ -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.
@@ -45,8 +45,8 @@ import org.springframework.util.Assert;
* lobCreator.setBlobAsBinaryStream(ps, 2, contentStream, contentLength);
* lobCreator.setClobAsString(ps, 3, description);
* }
* });
* </pre>
* }
* );</pre>
*
* @author Juergen Hoeller
* @since 1.0.2
@@ -1,5 +1,5 @@
/*
* Copyright 2002-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.
@@ -41,13 +41,13 @@ import org.springframework.lang.Nullable;
* final LobHandler lobHandler = new DefaultLobHandler(); // reusable object
*
* jdbcTemplate.query(
* "SELECT content FROM imagedb WHERE image_name=?", new Object[] {name},
* new AbstractLobStreamingResultSetExtractor() {
* public void streamData(ResultSet rs) throws SQLException, IOException {
* FileCopyUtils.copy(lobHandler.getBlobAsBinaryStream(rs, 1), contentStream);
* }
* });
* </pre>
* "SELECT content FROM imagedb WHERE image_name=?", new Object[] {name},
* new AbstractLobStreamingResultSetExtractor() {
* public void streamData(ResultSet rs) throws SQLException, IOException {
* FileCopyUtils.copy(lobHandler.getBlobAsBinaryStream(rs, 1), contentStream);
* }
* }
* );</pre>
*
* @author Juergen Hoeller
* @since 1.0.2
@@ -1,5 +1,5 @@
/*
* Copyright 2002-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.
@@ -209,7 +209,7 @@ public class SqlLobValue implements DisposableSqlTypeValue {
}
/**
* Close the LobCreator.
* Close the LobCreator, if any.
*/
@Override
public void cleanup() {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 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.
@@ -75,10 +75,10 @@ public abstract class AbstractDataSource implements DataSource {
throw new UnsupportedOperationException("setLogWriter");
}
@Override
public Logger getParentLogger() {
return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
}
//---------------------------------------------------------------------
// Implementation of JDBC 4.0's Wrapper interface
//---------------------------------------------------------------------
@Override
@SuppressWarnings("unchecked")
@@ -95,4 +95,14 @@ public abstract class AbstractDataSource implements DataSource {
return iface.isInstance(this);
}
//---------------------------------------------------------------------
// Implementation of JDBC 4.1's getParentLogger method
//---------------------------------------------------------------------
@Override
public Logger getParentLogger() {
return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 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.
@@ -104,16 +104,6 @@ public class DelegatingDataSource implements DataSource, InitializingBean {
return obtainTargetDataSource().getConnection(username, password);
}
@Override
public int getLoginTimeout() throws SQLException {
return obtainTargetDataSource().getLoginTimeout();
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
obtainTargetDataSource().setLoginTimeout(seconds);
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return obtainTargetDataSource().getLogWriter();
@@ -125,10 +115,20 @@ public class DelegatingDataSource implements DataSource, InitializingBean {
}
@Override
public Logger getParentLogger() {
return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
public int getLoginTimeout() throws SQLException {
return obtainTargetDataSource().getLoginTimeout();
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
obtainTargetDataSource().setLoginTimeout(seconds);
}
//---------------------------------------------------------------------
// Implementation of JDBC 4.0's Wrapper interface
//---------------------------------------------------------------------
@Override
@SuppressWarnings("unchecked")
public <T> T unwrap(Class<T> iface) throws SQLException {
@@ -143,4 +143,14 @@ public class DelegatingDataSource implements DataSource, InitializingBean {
return (iface.isInstance(this) || obtainTargetDataSource().isWrapperFor(iface));
}
//---------------------------------------------------------------------
// Implementation of JDBC 4.1's getParentLogger method
//---------------------------------------------------------------------
@Override
public Logger getParentLogger() {
return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 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.
@@ -186,7 +186,7 @@ public class TransactionAwareDataSourceProxy extends DelegatingDataSource {
// Allow for differentiating between the proxy and the raw Connection.
StringBuilder sb = new StringBuilder("Transaction-aware proxy for target Connection ");
if (this.target != null) {
sb.append('[').append(this.target).append(']');
sb.append('[').append(this.target.toString()).append(']');
}
else {
sb.append(" from DataSource [").append(this.targetDataSource).append(']');
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 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.
@@ -61,7 +61,7 @@ public abstract class AbstractRoutingDataSource extends AbstractDataSource imple
/**
* Specify the map of target DataSources, with the lookup key as key.
* <p>The mapped value can either be a corresponding {@link javax.sql.DataSource}
* The mapped value can either be a corresponding {@link javax.sql.DataSource}
* instance or a data source name String (to be resolved via a
* {@link #setDataSourceLookup DataSourceLookup}).
* <p>The key can be of arbitrary type; this class implements the
@@ -213,7 +213,6 @@ public abstract class AbstractRoutingDataSource extends AbstractDataSource imple
return (iface.isInstance(this) || determineTargetDataSource().isWrapperFor(iface));
}
/**
* Retrieve the current target DataSource. Determines the
* {@link #determineCurrentLookupKey() current lookup key}, performs
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +20,9 @@ import org.springframework.jdbc.support.SqlValue;
/**
* Subinterface of {@link org.springframework.jdbc.support.SqlValue}
* that specifically indicates passing in XML data to a specified column.
* that supports passing in XML data to specified column and adds a
* cleanup callback, to be invoked after the value has been set and
* the corresponding statement has been executed.
*
* @author Thomas Risberg
* @since 2.5.5
@@ -281,10 +281,6 @@ class ChannelSendOperator<T> extends Mono<Void> implements Scannable {
return;
}
synchronized (this) {
if (this.state == State.READY_TO_WRITE) {
s.request(n);
return;
}
if (this.writeSubscriber != null) {
if (this.state == State.EMITTING_CACHED_SIGNALS) {
this.demandBeforeReadyToWrite = n;
@@ -77,7 +77,7 @@ public abstract class SharedEntityManagerCreator {
private static final Set<String> transactionRequiringMethods = new HashSet<>(6);
private static final Set<String> queryTerminatingMethods = new HashSet<>(10);
private static final Set<String> queryTerminatingMethods = new HashSet<>(9);
static {
transactionRequiringMethods.add("joinTransaction");
@@ -93,7 +93,6 @@ public abstract class SharedEntityManagerCreator {
queryTerminatingMethods.add("getResultStream"); // javax.persistence.Query.getResultStream()
queryTerminatingMethods.add("getResultList"); // javax.persistence.Query.getResultList()
queryTerminatingMethods.add("list"); // org.hibernate.query.Query.list()
queryTerminatingMethods.add("scroll"); // org.hibernate.query.Query.scroll()
queryTerminatingMethods.add("stream"); // org.hibernate.query.Query.stream()
queryTerminatingMethods.add("uniqueResult"); // org.hibernate.query.Query.uniqueResult()
queryTerminatingMethods.add("uniqueResultOptional"); // org.hibernate.query.Query.uniqueResultOptional()
@@ -35,6 +35,7 @@ import org.springframework.util.Assert;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.request;
/**
* A {@link ClientHttpRequestFactory} for requests executed via {@link MockMvc}.
*
@@ -1,5 +1,5 @@
/*
* Copyright 2002-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.
@@ -34,13 +34,13 @@ public interface MvcResult {
/**
* Return the performed request.
* @return the request (never {@code null})
* @return the request, never {@code null}
*/
MockHttpServletRequest getRequest();
/**
* Return the resulting response.
* @return the response (never {@code null})
* @return the response, never {@code null}
*/
MockHttpServletResponse getResponse();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 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.
@@ -355,8 +355,8 @@ public abstract class TransactionAspectSupport implements BeanFactoryAware, Init
(isSuspendingFunction ? (hasSuspendingFlowReturnType ? Flux.class : Mono.class) : method.getReturnType());
ReactiveAdapter adapter = this.reactiveAdapterRegistry.getAdapter(reactiveType);
if (adapter == null) {
throw new IllegalStateException("Cannot apply reactive transaction to non-reactive return type [" +
method.getReturnType() + "] with specified transaction manager: " + tm);
throw new IllegalStateException("Cannot apply reactive transaction to non-reactive return type: " +
method.getReturnType());
}
return new ReactiveTransactionSupport(adapter);
});
@@ -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.
@@ -39,8 +39,7 @@ import org.springframework.util.function.SingletonSupplier;
*/
public class TransactionContext {
@Nullable
private final TransactionContext parent;
private final @Nullable TransactionContext parent;
private final SingletonSupplier<UUID> contextId = SingletonSupplier.of(UUID::randomUUID);
@@ -49,13 +48,11 @@ public class TransactionContext {
@Nullable
private Set<TransactionSynchronization> synchronizations;
@Nullable
private volatile String currentTransactionName;
private volatile @Nullable String currentTransactionName;
private volatile boolean currentTransactionReadOnly;
@Nullable
private volatile Integer currentTransactionIsolationLevel;
private volatile @Nullable Integer currentTransactionIsolationLevel;
private volatile boolean actualTransactionActive;
-1
View File
@@ -66,7 +66,6 @@ dependencies {
testImplementation("com.fasterxml.jackson.datatype:jackson-datatype-joda")
testImplementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
testImplementation("com.fasterxml.jackson.module:jackson-module-kotlin")
testImplementation("com.fasterxml.jackson.dataformat:jackson-dataformat-csv")
testImplementation("org.apache.tomcat:tomcat-util")
testImplementation("org.apache.tomcat.embed:tomcat-embed-core")
testImplementation("org.eclipse.jetty:jetty-server")
@@ -1,94 +0,0 @@
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.http.client.reactive;
import java.util.concurrent.atomic.AtomicBoolean;
import reactor.core.publisher.Flux;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseCookie;
import org.springframework.util.Assert;
import org.springframework.util.MultiValueMap;
/**
* Base class for {@link ClientHttpResponse} implementations.
*
* @author Arjen Poutsma
* @since 5.3.32
*/
public abstract class AbstractClientHttpResponse implements ClientHttpResponse {
private final int statusCode;
private final HttpHeaders headers;
private final MultiValueMap<String, ResponseCookie> cookies;
private final Flux<DataBuffer> body;
protected AbstractClientHttpResponse(int statusCode, HttpHeaders headers,
MultiValueMap<String, ResponseCookie> cookies, Flux<DataBuffer> body) {
Assert.notNull(headers, "Headers must not be null");
Assert.notNull(body, "Body must not be null");
this.statusCode = statusCode;
this.headers = headers;
this.cookies = cookies;
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");
}
});
}
@Override
public HttpStatus getStatusCode() {
return HttpStatus.valueOf(this.statusCode);
}
@Override
public int getRawStatusCode() {
return this.statusCode;
}
@Override
public HttpHeaders getHeaders() {
return this.headers;
}
@Override
public MultiValueMap<String, ResponseCookie> getCookies() {
return this.cookies;
}
@Override
public Flux<DataBuffer> getBody() {
return this.body;
}
}
@@ -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.
@@ -17,6 +17,7 @@
package org.springframework.http.client.reactive;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.hc.client5.http.cookie.Cookie;
import org.apache.hc.client5.http.protocol.HttpClientContext;
@@ -25,8 +26,10 @@ import org.apache.hc.core5.http.Message;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseCookie;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
@@ -39,22 +42,45 @@ import org.springframework.util.MultiValueMap;
* @since 5.3
* @see <a href="https://hc.apache.org/index.html">Apache HttpComponents</a>
*/
class HttpComponentsClientHttpResponse extends AbstractClientHttpResponse {
class HttpComponentsClientHttpResponse implements ClientHttpResponse {
private final DataBufferFactory dataBufferFactory;
private final Message<HttpResponse, Publisher<ByteBuffer>> message;
private final HttpHeaders headers;
private final HttpClientContext context;
private final AtomicBoolean rejectSubscribers = new AtomicBoolean();
public HttpComponentsClientHttpResponse(DataBufferFactory dataBufferFactory,
Message<HttpResponse, Publisher<ByteBuffer>> message, HttpClientContext context) {
super(message.getHead().getCode(),
HttpHeaders.readOnlyHttpHeaders(new HttpComponentsHeadersAdapter(message.getHead())),
adaptCookies(context),
Flux.from(message.getBody()).map(dataBufferFactory::wrap)
);
this.dataBufferFactory = dataBufferFactory;
this.message = message;
this.context = context;
MultiValueMap<String, String> adapter = new HttpComponentsHeadersAdapter(message.getHead());
this.headers = HttpHeaders.readOnlyHttpHeaders(adapter);
}
private static MultiValueMap<String, ResponseCookie> adaptCookies(HttpClientContext context) {
@Override
public HttpStatus getStatusCode() {
return HttpStatus.valueOf(this.message.getHead().getCode());
}
@Override
public int getRawStatusCode() {
return this.message.getHead().getCode();
}
@Override
public MultiValueMap<String, ResponseCookie> getCookies() {
LinkedMultiValueMap<String, ResponseCookie> result = new LinkedMultiValueMap<>();
context.getCookieStore().getCookies().forEach(cookie ->
this.context.getCookieStore().getCookies().forEach(cookie ->
result.add(cookie.getName(),
ResponseCookie.fromClientResponse(cookie.getName(), cookie.getValue())
.domain(cookie.getDomain())
@@ -67,10 +93,25 @@ class HttpComponentsClientHttpResponse extends AbstractClientHttpResponse {
return result;
}
private static long getMaxAgeSeconds(Cookie cookie) {
private long getMaxAgeSeconds(Cookie cookie) {
String maxAgeAttribute = cookie.getAttribute(Cookie.MAX_AGE_ATTR);
return (maxAgeAttribute != null ? Long.parseLong(maxAgeAttribute) : -1);
}
@Override
public Flux<DataBuffer> getBody() {
return Flux.from(this.message.getBody())
.doOnSubscribe(s -> {
if (!this.rejectSubscribers.compareAndSet(false, true)) {
throw new IllegalStateException("The client response body can only be consumed once.");
}
})
.map(this.dataBufferFactory::wrap);
}
@Override
public HttpHeaders getHeaders() {
return this.headers;
}
}
@@ -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.
@@ -21,12 +21,13 @@ import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.reactive.client.ReactiveResponse;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseCookie;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
@@ -41,37 +42,57 @@ import org.springframework.util.MultiValueMap;
* @see <a href="https://github.com/jetty-project/jetty-reactive-httpclient">
* Jetty ReactiveStreams HttpClient</a>
*/
class JettyClientHttpResponse extends AbstractClientHttpResponse {
class JettyClientHttpResponse implements ClientHttpResponse {
private static final Pattern SAMESITE_PATTERN = Pattern.compile("(?i).*SameSite=(Strict|Lax|None).*");
public JettyClientHttpResponse(ReactiveResponse reactiveResponse, Flux<DataBuffer> content) {
private final ReactiveResponse reactiveResponse;
super(reactiveResponse.getStatus(),
adaptHeaders(reactiveResponse),
adaptCookies(reactiveResponse),
content);
private final Flux<DataBuffer> content;
private final HttpHeaders headers;
public JettyClientHttpResponse(ReactiveResponse reactiveResponse, Publisher<DataBuffer> content) {
this.reactiveResponse = reactiveResponse;
this.content = Flux.from(content);
MultiValueMap<String, String> headers = (Jetty10HttpFieldsHelper.jetty10Present() ?
Jetty10HttpFieldsHelper.getHttpHeaders(reactiveResponse.getResponse()) :
new JettyHeadersAdapter(reactiveResponse.getHeaders()));
this.headers = HttpHeaders.readOnlyHttpHeaders(headers);
}
private static HttpHeaders adaptHeaders(ReactiveResponse response) {
MultiValueMap<String, String> headers = new JettyHeadersAdapter(response.getHeaders());
return HttpHeaders.readOnlyHttpHeaders(headers);
@Override
public HttpStatus getStatusCode() {
return HttpStatus.valueOf(getRawStatusCode());
}
private static MultiValueMap<String, ResponseCookie> adaptCookies(ReactiveResponse response) {
@Override
public int getRawStatusCode() {
return this.reactiveResponse.getStatus();
}
@Override
public MultiValueMap<String, ResponseCookie> getCookies() {
MultiValueMap<String, ResponseCookie> result = new LinkedMultiValueMap<>();
List<HttpField> cookieHeaders = response.getHeaders().getFields(HttpHeaders.SET_COOKIE);
cookieHeaders.forEach(header ->
HttpCookie.parse(header.getValue()).forEach(cookie -> result.add(cookie.getName(),
List<String> cookieHeader = getHeaders().get(HttpHeaders.SET_COOKIE);
if (cookieHeader != null) {
cookieHeader.forEach(header ->
HttpCookie.parse(header).forEach(cookie -> result.add(cookie.getName(),
ResponseCookie.fromClientResponse(cookie.getName(), cookie.getValue())
.domain(cookie.getDomain())
.path(cookie.getPath())
.maxAge(cookie.getMaxAge())
.secure(cookie.getSecure())
.httpOnly(cookie.isHttpOnly())
.sameSite(parseSameSite(header.getValue()))
.sameSite(parseSameSite(header))
.build()))
);
}
return CollectionUtils.unmodifiableMultiValueMap(result);
}
@@ -81,4 +102,15 @@ class JettyClientHttpResponse extends AbstractClientHttpResponse {
return (matcher.matches() ? matcher.group(1) : null);
}
@Override
public Flux<DataBuffer> getBody() {
return this.content;
}
@Override
public HttpHeaders getHeaders() {
return this.headers;
}
}
@@ -169,8 +169,8 @@ public abstract class AbstractJackson2Encoder extends Jackson2CodecSupport imple
separator))
.doAfterTerminate(() -> {
try {
generator.close();
byteBuilder.release();
generator.close();
}
catch (IOException ex) {
logger.error("Could not close Encoder resources", ex);
@@ -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.
@@ -106,9 +106,10 @@ public class StringHttpMessageConverter extends AbstractHttpMessageConverter<Str
@Override
protected void addDefaultHeaders(HttpHeaders headers, String s, @Nullable MediaType type) throws IOException {
if (headers.getContentType() == null ) {
if (type != null && type.isConcrete() && (type.isCompatibleWith(MediaType.APPLICATION_JSON) ||
if (type != null && type.isConcrete() &&
(type.isCompatibleWith(MediaType.APPLICATION_JSON) ||
type.isCompatibleWith(APPLICATION_PLUS_JSON))) {
// Prevent charset parameter for JSON.
// Prevent charset parameter for JSON..
headers.setContentType(type);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 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.
@@ -115,11 +115,13 @@ public class ServletServerHttpAsyncRequestControl implements ServerHttpAsyncRequ
// ---------------------------------------------------------------------
@Override
public void onStartAsync(AsyncEvent event) throws IOException {
public void onComplete(AsyncEvent event) throws IOException {
this.asyncContext = null;
this.asyncCompleted.set(true);
}
@Override
public void onTimeout(AsyncEvent event) throws IOException {
public void onStartAsync(AsyncEvent event) throws IOException {
}
@Override
@@ -127,9 +129,7 @@ public class ServletServerHttpAsyncRequestControl implements ServerHttpAsyncRequ
}
@Override
public void onComplete(AsyncEvent event) throws IOException {
this.asyncContext = null;
this.asyncCompleted.set(true);
public void onTimeout(AsyncEvent event) throws IOException {
}
}
@@ -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.
@@ -39,7 +39,7 @@ import org.springframework.util.Assert;
* to defer the invocation of the write function, until we know if the source
* publisher will begin publishing without an error. If the first emission is
* an error, the write function is bypassed, and the error is sent directly
* through the result publisher. Otherwise, the write function is invoked.
* through the result publisher. Otherwise the write function is invoked.
*
* @author Rossen Stoyanchev
* @author Stephane Maldini
@@ -273,10 +273,6 @@ public class ChannelSendOperator<T> extends Mono<Void> implements Scannable {
return;
}
synchronized (this) {
if (this.state == State.READY_TO_WRITE) {
s.request(n);
return;
}
if (this.writeSubscriber != null) {
if (this.state == State.EMITTING_CACHED_SIGNALS) {
this.demandBeforeReadyToWrite = n;
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-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.
@@ -276,11 +276,6 @@ public class ServletHttpHandlerAdapter implements Servlet {
this.logPrefix = logPrefix;
}
@Override
public void onStartAsync(AsyncEvent event) {
// no-op
}
@Override
public void onTimeout(AsyncEvent event) {
// Should never happen since we call asyncContext.setTimeout(-1)
@@ -347,6 +342,11 @@ public class ServletHttpHandlerAdapter implements Servlet {
}
});
}
@Override
public void onStartAsync(AsyncEvent event) {
// no-op
}
}
@@ -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.
@@ -133,14 +133,6 @@ public @interface CrossOrigin {
*/
String allowCredentials() default "";
/**
* Whether private network access is supported. Please, see
* {@link CorsConfiguration#setAllowPrivateNetwork(Boolean)} for details.
* <p>By default this is not set (i.e. private network access is not supported).
* @since 5.3.32
*/
String allowPrivateNetwork() default "";
/**
* The maximum age (in seconds) of the cache duration for preflight responses.
* <p>This property controls the value of the {@code Access-Control-Max-Age}
@@ -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,8 +30,7 @@ import org.springframework.lang.Nullable;
/**
* Interface specifying a basic set of RESTful operations.
*
* <p>Implemented by {@link RestTemplate}. Not often used directly, but a useful
* Implemented by {@link RestTemplate}. Not often used directly, but a useful
* option to enhance testability, as it can easily be mocked or stubbed.
*
* @author Arjen Poutsma
@@ -68,7 +67,7 @@ public interface RestOperations {
<T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException;
/**
* Retrieve a representation by doing a GET on the URL.
* Retrieve a representation by doing a GET on the URL .
* The response (if any) is converted and returned.
* @param url the URL
* @param responseType the type of the return value
@@ -79,7 +78,7 @@ public interface RestOperations {
/**
* Retrieve an entity by doing a GET on the specified URL.
* The response is converted and stored in a {@link ResponseEntity}.
* The response is converted and stored in an {@link ResponseEntity}.
* <p>URI Template variables are expanded using the given URI variables, if any.
* @param url the URL
* @param responseType the type of the return value
@@ -92,7 +91,7 @@ public interface RestOperations {
/**
* Retrieve a representation by doing a GET on the URI template.
* The response is converted and stored in a {@link ResponseEntity}.
* The response is converted and stored in an {@link ResponseEntity}.
* <p>URI Template variables are expanded using the given map.
* @param url the URL
* @param responseType the type of the return value
@@ -104,8 +103,8 @@ public interface RestOperations {
throws RestClientException;
/**
* Retrieve a representation by doing a GET on the URL.
* The response is converted and stored in a {@link ResponseEntity}.
* Retrieve a representation by doing a GET on the URL .
* The response is converted and stored in an {@link ResponseEntity}.
* @param url the URL
* @param responseType the type of the return value
* @return the converted object
@@ -145,7 +144,7 @@ public interface RestOperations {
// POST
/**
* Create a new resource by POSTing the given object to the URI template, and return the value of
* Create a new resource by POSTing the given object to the URI template, and returns the value of
* the {@code Location} header. This header typically indicates where the new resource is stored.
* <p>URI Template variables are expanded using the given URI variables, if any.
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
@@ -165,7 +164,7 @@ public interface RestOperations {
URI postForLocation(String url, @Nullable Object request, Object... uriVariables) throws RestClientException;
/**
* Create a new resource by POSTing the given object to the URI template, and return the value of
* Create a new resource by POSTing the given object to the URI template, and returns the value of
* the {@code Location} header. This header typically indicates where the new resource is stored.
* <p>URI Template variables are expanded using the given map.
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
@@ -186,7 +185,7 @@ public interface RestOperations {
throws RestClientException;
/**
* Create a new resource by POSTing the given object to the URL, and return the value of the
* Create a new resource by POSTing the given object to the URL, and returns the value of the
* {@code Location} header. This header typically indicates where the new resource is stored.
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
* add additional HTTP headers to the request.
@@ -205,7 +204,7 @@ public interface RestOperations {
/**
* Create a new resource by POSTing the given object to the URI template,
* and return the representation found in the response.
* and returns the representation found in the response.
* <p>URI Template variables are expanded using the given URI variables, if any.
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
* add additional HTTP headers to the request.
@@ -227,7 +226,7 @@ public interface RestOperations {
/**
* Create a new resource by POSTing the given object to the URI template,
* and return the representation found in the response.
* and returns the representation found in the response.
* <p>URI Template variables are expanded using the given map.
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
* add additional HTTP headers to the request.
@@ -249,7 +248,7 @@ public interface RestOperations {
/**
* Create a new resource by POSTing the given object to the URL,
* and return the representation found in the response.
* and returns the representation found in the response.
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
* add additional HTTP headers to the request.
* <p>The body of the entity, or {@code request} itself, can be a
@@ -268,7 +267,7 @@ public interface RestOperations {
/**
* Create a new resource by POSTing the given object to the URI template,
* and return the response as {@link ResponseEntity}.
* and returns the response as {@link ResponseEntity}.
* <p>URI Template variables are expanded using the given URI variables, if any.
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
* add additional HTTP headers to the request.
@@ -289,7 +288,7 @@ public interface RestOperations {
/**
* Create a new resource by POSTing the given object to the URI template,
* and return the response as {@link HttpEntity}.
* and returns the response as {@link HttpEntity}.
* <p>URI Template variables are expanded using the given map.
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
* add additional HTTP headers to the request.
@@ -310,7 +309,7 @@ public interface RestOperations {
/**
* Create a new resource by POSTing the given object to the URL,
* and return the response as {@link ResponseEntity}.
* and returns the response as {@link ResponseEntity}.
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
* add additional HTTP headers to the request.
* <p>The body of the entity, or {@code request} itself, can be a
@@ -374,7 +373,7 @@ public interface RestOperations {
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
* add additional HTTP headers to the request.
* <p><b>NOTE: The standard JDK HTTP library does not support HTTP PATCH.
* You need to use e.g. the Apache HttpComponents request factory.</b>
* You need to use the Apache HttpComponents or OkHttp request factory.</b>
* @param url the URL
* @param request the object to be PATCHed (may be {@code null})
* @param responseType the type of the return value
@@ -383,7 +382,8 @@ public interface RestOperations {
* @since 4.3.5
* @see HttpEntity
* @see RestTemplate#setRequestFactory
* @see org.springframework.http.client.HttpComponentsClientHttpRequestFactory
* @see org.springframework.http.client.HttpComponentsAsyncClientHttpRequestFactory
* @see org.springframework.http.client.OkHttp3ClientHttpRequestFactory
*/
@Nullable
<T> T patchForObject(String url, @Nullable Object request, Class<T> responseType, Object... uriVariables)
@@ -396,7 +396,7 @@ public interface RestOperations {
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
* add additional HTTP headers to the request.
* <p><b>NOTE: The standard JDK HTTP library does not support HTTP PATCH.
* You need to use e.g. the Apache HttpComponents request factory.</b>
* You need to use the Apache HttpComponents or OkHttp request factory.</b>
* @param url the URL
* @param request the object to be PATCHed (may be {@code null})
* @param responseType the type of the return value
@@ -405,7 +405,8 @@ public interface RestOperations {
* @since 4.3.5
* @see HttpEntity
* @see RestTemplate#setRequestFactory
* @see org.springframework.http.client.HttpComponentsClientHttpRequestFactory
* @see org.springframework.http.client.HttpComponentsAsyncClientHttpRequestFactory
* @see org.springframework.http.client.OkHttp3ClientHttpRequestFactory
*/
@Nullable
<T> T patchForObject(String url, @Nullable Object request, Class<T> responseType,
@@ -417,7 +418,7 @@ public interface RestOperations {
* <p>The {@code request} parameter can be a {@link HttpEntity} in order to
* add additional HTTP headers to the request.
* <p><b>NOTE: The standard JDK HTTP library does not support HTTP PATCH.
* You need to use e.g. the Apache HttpComponents request factory.</b>
* You need to use the Apache HttpComponents or OkHttp request factory.</b>
* @param url the URL
* @param request the object to be PATCHed (may be {@code null})
* @param responseType the type of the return value
@@ -425,7 +426,8 @@ public interface RestOperations {
* @since 4.3.5
* @see HttpEntity
* @see RestTemplate#setRequestFactory
* @see org.springframework.http.client.HttpComponentsClientHttpRequestFactory
* @see org.springframework.http.client.HttpComponentsAsyncClientHttpRequestFactory
* @see org.springframework.http.client.OkHttp3ClientHttpRequestFactory
*/
@Nullable
<T> T patchForObject(URI url, @Nullable Object request, Class<T> responseType)
@@ -489,8 +491,8 @@ public interface RestOperations {
// exchange
/**
* Execute the HTTP method to the given URI template, writing the given request entity to the request,
* and return the response as {@link ResponseEntity}.
* Execute the HTTP method to the given URI template, writing the given request entity to the request, and
* returns the response as {@link ResponseEntity}.
* <p>URI Template variables are expanded using the given URI variables, if any.
* @param url the URL
* @param method the HTTP method (GET, POST, etc)
@@ -505,8 +507,8 @@ public interface RestOperations {
Class<T> responseType, Object... uriVariables) throws RestClientException;
/**
* Execute the HTTP method to the given URI template, writing the given request entity to the request,
* and return the response as {@link ResponseEntity}.
* Execute the HTTP method to the given URI template, writing the given request entity to the request, and
* returns the response as {@link ResponseEntity}.
* <p>URI Template variables are expanded using the given URI variables, if any.
* @param url the URL
* @param method the HTTP method (GET, POST, etc)
@@ -521,8 +523,8 @@ public interface RestOperations {
Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException;
/**
* Execute the HTTP method to the given URI template, writing the given request entity to the request,
* and return the response as {@link ResponseEntity}.
* Execute the HTTP method to the given URI template, writing the given request entity to the request, and
* returns the response as {@link ResponseEntity}.
* @param url the URL
* @param method the HTTP method (GET, POST, etc)
* @param requestEntity the entity (headers and/or body) to write to the request
@@ -536,7 +538,7 @@ public interface RestOperations {
/**
* Execute the HTTP method to the given URI template, writing the given
* request entity to the request, and return the response as {@link ResponseEntity}.
* request entity to the request, and returns the response as {@link ResponseEntity}.
* The given {@link ParameterizedTypeReference} is used to pass generic type information:
* <pre class="code">
* ParameterizedTypeReference&lt;List&lt;MyBean&gt;&gt; myBean =
@@ -559,7 +561,7 @@ public interface RestOperations {
/**
* Execute the HTTP method to the given URI template, writing the given
* request entity to the request, and return the response as {@link ResponseEntity}.
* request entity to the request, and returns the response as {@link ResponseEntity}.
* The given {@link ParameterizedTypeReference} is used to pass generic type information:
* <pre class="code">
* ParameterizedTypeReference&lt;List&lt;MyBean&gt;&gt; myBean =
@@ -582,7 +584,7 @@ public interface RestOperations {
/**
* Execute the HTTP method to the given URI template, writing the given
* request entity to the request, and return the response as {@link ResponseEntity}.
* request entity to the request, and returns the response as {@link ResponseEntity}.
* The given {@link ParameterizedTypeReference} is used to pass generic type information:
* <pre class="code">
* ParameterizedTypeReference&lt;List&lt;MyBean&gt;&gt; myBean =
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 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.
@@ -128,7 +128,7 @@ public class StandardServletAsyncWebRequest extends ServletWebRequest implements
@Override
public void dispatch() {
Assert.state(this.asyncContext != null, "Cannot dispatch without an AsyncContext");
Assert.notNull(this.asyncContext, "Cannot dispatch without an AsyncContext");
this.asyncContext.dispatch();
}
@@ -142,13 +142,13 @@ public class StandardServletAsyncWebRequest extends ServletWebRequest implements
}
@Override
public void onTimeout(AsyncEvent event) throws IOException {
this.timeoutHandlers.forEach(Runnable::run);
public void onError(AsyncEvent event) throws IOException {
this.exceptionHandlers.forEach(consumer -> consumer.accept(event.getThrowable()));
}
@Override
public void onError(AsyncEvent event) throws IOException {
this.exceptionHandlers.forEach(consumer -> consumer.accept(event.getThrowable()));
public void onTimeout(AsyncEvent event) throws IOException {
this.timeoutHandlers.forEach(Runnable::run);
}
@Override
@@ -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.
@@ -53,7 +53,6 @@ import org.springframework.web.context.request.async.DeferredResult.DeferredResu
*
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @author Sam Brannen
* @since 3.2
* @see org.springframework.web.context.request.AsyncWebRequestInterceptor
* @see org.springframework.web.servlet.AsyncHandlerInterceptor
@@ -78,15 +77,12 @@ public final class WebAsyncManager {
private static Boolean taskExecutorWarning = true;
@Nullable
private AsyncWebRequest asyncWebRequest;
private AsyncTaskExecutor taskExecutor = DEFAULT_TASK_EXECUTOR;
@Nullable
private volatile Object concurrentResult = RESULT_NONE;
@Nullable
private volatile Object[] concurrentResultContext;
/*
@@ -160,7 +156,6 @@ public final class WebAsyncManager {
* concurrent handling raised one.
* @see #clearConcurrentResult()
*/
@Nullable
public Object getConcurrentResult() {
return this.concurrentResult;
}
@@ -170,7 +165,6 @@ public final class WebAsyncManager {
* concurrent handling.
* @see #clearConcurrentResult()
*/
@Nullable
public Object[] getConcurrentResultContext() {
return this.concurrentResultContext;
}
@@ -384,7 +378,7 @@ public final class WebAsyncManager {
return request != null ? request.getRequestURI() : "servlet container";
}
private void setConcurrentResultAndDispatch(@Nullable Object result) {
private void setConcurrentResultAndDispatch(Object result) {
synchronized (WebAsyncManager.this) {
if (this.concurrentResult != RESULT_NONE) {
return;
@@ -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.
@@ -93,9 +93,6 @@ public class CorsConfiguration {
@Nullable
private Boolean allowCredentials;
@Nullable
private Boolean allowPrivateNetwork;
@Nullable
private Long maxAge;
@@ -120,7 +117,6 @@ public class CorsConfiguration {
this.allowedHeaders = other.allowedHeaders;
this.exposedHeaders = other.exposedHeaders;
this.allowCredentials = other.allowCredentials;
this.allowPrivateNetwork = other.allowPrivateNetwork;
this.maxAge = other.maxAge;
}
@@ -133,10 +129,9 @@ public class CorsConfiguration {
* {@code Access-Control-Allow-Origin} response header is set either to the
* matched domain value or to {@code "*"}. Keep in mind however that the
* CORS spec does not allow {@code "*"} when {@link #setAllowCredentials
* allowCredentials} is set to {@code true}, and does not recommend {@code "*"}
* when {@link #setAllowPrivateNetwork allowPrivateNetwork} is set to {@code true}.
* As a consequence, those combinations are rejected in favor of using
* {@link #setAllowedOriginPatterns allowedOriginPatterns} instead.
* allowCredentials} is set to {@code true} and as of 5.3 that combination
* is rejected in favor of using {@link #setAllowedOriginPatterns
* allowedOriginPatterns} instead.
* <p>By default this is not set which means that no origins are allowed.
* However, an instance of this class is often initialized further, e.g. for
* {@code @CrossOrigin}, via {@link #applyPermitDefaultValues()}.
@@ -187,13 +182,11 @@ public class CorsConfiguration {
* domain1.com on any port, including the default port
* </ul>
* <p>In contrast to {@link #setAllowedOrigins(List) allowedOrigins} which
* only supports "*" and cannot be used with {@code allowCredentials} or
* {@code allowPrivateNetwork}, when an {@code allowedOriginPattern} is matched,
* the {@code Access-Control-Allow-Origin} response header is set to the
* matched origin and not to {@code "*"} nor to the pattern.
* Therefore, {@code allowedOriginPatterns} can be used in combination with
* {@link #setAllowCredentials} and {@link #setAllowPrivateNetwork} set to
* {@code true}.
* only supports "*" and cannot be used with {@code allowCredentials}, when
* an allowedOriginPattern is matched, the {@code Access-Control-Allow-Origin}
* response header is set to the matched origin and not to {@code "*"} nor
* to the pattern. Therefore allowedOriginPatterns can be used in combination
* with {@link #setAllowCredentials} set to {@code true}.
* <p>By default this is not set.
* @since 5.3
*/
@@ -425,33 +418,6 @@ public class CorsConfiguration {
return this.allowCredentials;
}
/**
* Whether private network access is supported for user-agents restricting such access by default.
* <p>Private network requests are requests whose target server's IP address is more private than
* that from which the request initiator was fetched. For example, a request from a public website
* (https://example.com) to a private website (https://router.local), or a request from a private
* website to localhost.
* <p>Setting this property has an impact on how {@link #setAllowedOrigins(List)
* origins} and {@link #setAllowedOriginPatterns(List) originPatterns} are processed,
* see related API documentation for more details.
* <p>By default this is not set (i.e. private network access is not supported).
* @since 5.3.32
* @see <a href="https://wicg.github.io/private-network-access/">Private network access specifications</a>
*/
public void setAllowPrivateNetwork(@Nullable Boolean allowPrivateNetwork) {
this.allowPrivateNetwork = allowPrivateNetwork;
}
/**
* Return the configured {@code allowPrivateNetwork} flag, or {@code null} if none.
* @since 5.3.32
* @see #setAllowPrivateNetwork(Boolean)
*/
@Nullable
public Boolean getAllowPrivateNetwork() {
return this.allowPrivateNetwork;
}
/**
* Configure how long, as a duration, the response from a pre-flight request
* can be cached by clients.
@@ -534,25 +500,6 @@ public class CorsConfiguration {
}
}
/**
* Validate that when {@link #setAllowPrivateNetwork allowPrivateNetwork} is {@code true},
* {@link #setAllowedOrigins allowedOrigins} does not contain the special
* value {@code "*"} since this is insecure.
* @throws IllegalArgumentException if the validation fails
* @since 5.3.32
*/
public void validateAllowPrivateNetwork() {
if (this.allowPrivateNetwork == Boolean.TRUE &&
this.allowedOrigins != null && this.allowedOrigins.contains(ALL)) {
throw new IllegalArgumentException(
"When allowPrivateNetwork is true, allowedOrigins cannot contain the special value \"*\" " +
"as it is not recommended from a security perspective. " +
"To allow private network access to a set of origins, list them explicitly " +
"or consider using \"allowedOriginPatterns\" instead.");
}
}
/**
* Combine the non-null properties of the supplied
* {@code CorsConfiguration} with this one.
@@ -587,10 +534,6 @@ public class CorsConfiguration {
if (allowCredentials != null) {
config.setAllowCredentials(allowCredentials);
}
Boolean allowPrivateNetwork = other.getAllowPrivateNetwork();
if (allowPrivateNetwork != null) {
config.setAllowPrivateNetwork(allowPrivateNetwork);
}
Long maxAge = other.getMaxAge();
if (maxAge != null) {
config.setMaxAge(maxAge);
@@ -654,7 +597,6 @@ public class CorsConfiguration {
if (!ObjectUtils.isEmpty(this.allowedOrigins)) {
if (this.allowedOrigins.contains(ALL)) {
validateAllowCredentials();
validateAllowPrivateNetwork();
return ALL;
}
for (String allowedOrigin : this.allowedOrigins) {
@@ -55,18 +55,6 @@ public class DefaultCorsProcessor implements CorsProcessor {
private static final Log logger = LogFactory.getLog(DefaultCorsProcessor.class);
/**
* The {@code Access-Control-Request-Private-Network} request header field name.
* @see <a href="https://wicg.github.io/private-network-access/">Private Network Access specification</a>
*/
static final String ACCESS_CONTROL_REQUEST_PRIVATE_NETWORK = "Access-Control-Request-Private-Network";
/**
* The {@code Access-Control-Allow-Private-Network} response header field name.
* @see <a href="https://wicg.github.io/private-network-access/">Private Network Access specification</a>
*/
static final String ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK = "Access-Control-Allow-Private-Network";
@Override
@SuppressWarnings("resource")
@@ -168,11 +156,6 @@ public class DefaultCorsProcessor implements CorsProcessor {
responseHeaders.setAccessControlAllowCredentials(true);
}
if (Boolean.TRUE.equals(config.getAllowPrivateNetwork()) &&
Boolean.parseBoolean(request.getHeaders().getFirst(ACCESS_CONTROL_REQUEST_PRIVATE_NETWORK))) {
responseHeaders.set(ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK, Boolean.toString(true));
}
if (preFlightRequest && config.getMaxAge() != null) {
responseHeaders.setAccessControlMaxAge(config.getMaxAge());
}
@@ -53,18 +53,6 @@ public class DefaultCorsProcessor implements CorsProcessor {
private static final List<String> VARY_HEADERS = Arrays.asList(
HttpHeaders.ORIGIN, HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS);
/**
* The {@code Access-Control-Request-Private-Network} request header field name.
* @see <a href="https://wicg.github.io/private-network-access/">Private Network Access specification</a>
*/
static final String ACCESS_CONTROL_REQUEST_PRIVATE_NETWORK = "Access-Control-Request-Private-Network";
/**
* The {@code Access-Control-Allow-Private-Network} response header field name.
* @see <a href="https://wicg.github.io/private-network-access/">Private Network Access specification</a>
*/
static final String ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK = "Access-Control-Allow-Private-Network";
@Override
public boolean process(@Nullable CorsConfiguration config, ServerWebExchange exchange) {
@@ -167,11 +155,6 @@ public class DefaultCorsProcessor implements CorsProcessor {
responseHeaders.setAccessControlAllowCredentials(true);
}
if (Boolean.TRUE.equals(config.getAllowPrivateNetwork()) &&
Boolean.parseBoolean(request.getHeaders().getFirst(ACCESS_CONTROL_REQUEST_PRIVATE_NETWORK))) {
responseHeaders.set(ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK, Boolean.toString(true));
}
if (preFlightRequest && config.getMaxAge() != null) {
responseHeaders.setAccessControlMaxAge(config.getMaxAge());
}
@@ -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.
@@ -26,7 +26,6 @@ import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import reactor.core.publisher.Mono;
@@ -311,10 +310,12 @@ public class InMemoryWebSessionStore implements WebSessionStore {
/** Max time between expiration checks. */
private static final int CHECK_PERIOD = 60 * 1000;
private final Lock lock = new ReentrantLock();
private final ReentrantLock lock = new ReentrantLock();
private Instant checkTime = clock.instant().plus(CHECK_PERIOD, ChronoUnit.MILLIS);
public void checkIfNecessary(Instant now) {
if (this.checkTime.isBefore(now)) {
removeExpiredSessions(now);
@@ -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.
@@ -21,10 +21,6 @@ import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
@@ -60,9 +56,6 @@ public class ContentCachingResponseWrapper extends HttpServletResponseWrapper {
@Nullable
private Integer contentLength;
@Nullable
private String contentType;
/**
* Create a new ContentCachingResponseWrapper for the given servlet response.
@@ -149,122 +142,6 @@ public class ContentCachingResponseWrapper extends HttpServletResponseWrapper {
this.contentLength = lenInt;
}
@Override
public void setContentType(String type) {
this.contentType = type;
}
@Override
@Nullable
public String getContentType() {
return this.contentType;
}
@Override
public boolean containsHeader(String name) {
if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name)) {
return this.contentLength != null;
}
else if (HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(name)) {
return this.contentType != null;
}
else {
return super.containsHeader(name);
}
}
@Override
public void setHeader(String name, String value) {
if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name)) {
this.contentLength = Integer.valueOf(value);
}
else if (HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(name)) {
this.contentType = value;
}
else {
super.setHeader(name, value);
}
}
@Override
public void addHeader(String name, String value) {
if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name)) {
this.contentLength = Integer.valueOf(value);
}
else if (HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(name)) {
this.contentType = value;
}
else {
super.addHeader(name, value);
}
}
@Override
public void setIntHeader(String name, int value) {
if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name)) {
this.contentLength = Integer.valueOf(value);
}
else {
super.setIntHeader(name, value);
}
}
@Override
public void addIntHeader(String name, int value) {
if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name)) {
this.contentLength = Integer.valueOf(value);
}
else {
super.addIntHeader(name, value);
}
}
@Override
@Nullable
public String getHeader(String name) {
if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name)) {
return (this.contentLength != null) ? this.contentLength.toString() : null;
}
else if (HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(name)) {
return this.contentType;
}
else {
return super.getHeader(name);
}
}
@Override
public Collection<String> getHeaders(String name) {
if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name)) {
return this.contentLength != null ? Collections.singleton(this.contentLength.toString()) :
Collections.emptySet();
}
else if (HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(name)) {
return this.contentType != null ? Collections.singleton(this.contentType) : Collections.emptySet();
}
else {
return super.getHeaders(name);
}
}
@Override
public Collection<String> getHeaderNames() {
Collection<String> headerNames = super.getHeaderNames();
if (this.contentLength != null || this.contentType != null) {
List<String> result = new ArrayList<>(headerNames);
if (this.contentLength != null) {
result.add(HttpHeaders.CONTENT_LENGTH);
}
if (this.contentType != null) {
result.add(HttpHeaders.CONTENT_TYPE);
}
return result;
}
else {
return headerNames;
}
}
@Override
public void setBufferSize(int size) {
if (size > this.content.size()) {
@@ -332,17 +209,11 @@ public class ContentCachingResponseWrapper extends HttpServletResponseWrapper {
protected void copyBodyToResponse(boolean complete) throws IOException {
if (this.content.size() > 0) {
HttpServletResponse rawResponse = (HttpServletResponse) getResponse();
if (!rawResponse.isCommitted()) {
if (complete || this.contentLength != null) {
if (rawResponse.getHeader(HttpHeaders.TRANSFER_ENCODING) == null) {
rawResponse.setContentLength(complete ? this.content.size() : this.contentLength);
}
this.contentLength = null;
}
if (complete || this.contentType != null) {
rawResponse.setContentType(this.contentType);
this.contentType = null;
if ((complete || this.contentLength != null) && !rawResponse.isCommitted()) {
if (rawResponse.getHeader(HttpHeaders.TRANSFER_ENCODING) == null) {
rawResponse.setContentLength(complete ? this.content.size() : this.contentLength);
}
this.contentLength = null;
}
this.content.writeTo(rawResponse.getOutputStream());
this.content.reset();
@@ -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.
@@ -77,7 +77,7 @@ public class UriComponentsBuilder implements UriBuilder, Cloneable {
private static final String HTTP_PATTERN = "(?i)(http|https):";
private static final String USERINFO_PATTERN = "([^@/?#]*)";
private static final String USERINFO_PATTERN = "([^@\\[/?#]*)";
private static final String HOST_IPV4_PATTERN = "[^\\[/?#:]*";
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 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.
@@ -32,15 +32,14 @@ import static org.mockito.Mockito.verify;
*/
public class SimpleClientHttpRequestFactoryTests {
@Test // SPR-13225
@Test // SPR-13225
public void headerWithNullValue() {
HttpURLConnection urlConnection = mock(HttpURLConnection.class);
given(urlConnection.getRequestMethod()).willReturn("GET");
HttpHeaders headers = new HttpHeaders();
headers.set("foo", null);
SimpleBufferingClientHttpRequest.addHeaders(urlConnection, headers);
verify(urlConnection, times(1)).addRequestProperty("foo", "");
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 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.
@@ -51,6 +51,7 @@ import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ReactiveHttpOutputMessage;
import org.springframework.lang.NonNull;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
@@ -172,6 +173,7 @@ public class ClientHttpConnectorTests {
.verify();
}
@NonNull
private Buffer randomBody(int size) {
Buffer responseBody = new Buffer();
Random rnd = new Random();
@@ -1,101 +0,0 @@
/*
* 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.http.codec.json;
import java.util.Arrays;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.dataformat.csv.CsvMapper;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Flux;
import org.springframework.core.ResolvableType;
import org.springframework.core.testfixture.codec.AbstractEncoderTests;
import org.springframework.http.MediaType;
import org.springframework.util.Assert;
import org.springframework.util.MimeType;
import org.springframework.web.testfixture.xml.Pojo;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link AbstractJackson2Encoder} for the CSV variant and how resources are managed.
* @author Brian Clozel
*/
class JacksonCsvEncoderTests extends AbstractEncoderTests<org.springframework.http.codec.json.JacksonCsvEncoderTests.JacksonCsvEncoder> {
public JacksonCsvEncoderTests() {
super(new JacksonCsvEncoder());
}
@Test
@Override
public void canEncode() throws Exception {
ResolvableType pojoType = ResolvableType.forClass(Pojo.class);
assertThat(this.encoder.canEncode(pojoType, JacksonCsvEncoder.TEXT_CSV)).isTrue();
}
@Test
@Override
public void encode() throws Exception {
Flux<Object> input = Flux.just(new Pojo("spring", "framework"),
new Pojo("spring", "data"),
new Pojo("spring", "boot"));
testEncode(input, Pojo.class, step -> step
.consumeNextWith(expectString("bar,foo\nframework,spring\n"))
.consumeNextWith(expectString("data,spring\n"))
.consumeNextWith(expectString("boot,spring\n"))
.verifyComplete());
}
@Test
// See gh-30493
// this test did not fail directly but logged a NullPointerException dropped by the reactive pipeline
void encodeEmptyFlux() {
Flux<Object> input = Flux.empty();
testEncode(input, Pojo.class, step -> step.verifyComplete());
}
static class JacksonCsvEncoder extends AbstractJackson2Encoder {
public static final MediaType TEXT_CSV = new MediaType("text", "csv");
public JacksonCsvEncoder() {
this(CsvMapper.builder().build(), TEXT_CSV);
}
@Override
protected byte[] getStreamingMediaTypeSeparator(MimeType mimeType) {
// CsvMapper emits newlines
return new byte[0];
}
public JacksonCsvEncoder(ObjectMapper mapper, MimeType... mimeTypes) {
super(mapper, mimeTypes);
Assert.isInstanceOf(CsvMapper.class, mapper);
setStreamingMediaTypes(Arrays.asList(TEXT_CSV));
}
@Override
protected ObjectWriter customizeWriter(ObjectWriter writer, MimeType mimeType, ResolvableType elementType, Map<String, Object> hints) {
CsvMapper mapper = (CsvMapper) getObjectMapper();
return writer.with(mapper.schemaFor(elementType.toClass()).withHeader());
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 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.
@@ -50,8 +50,6 @@ class CorsConfigurationTests {
assertThat(config.getExposedHeaders()).isNull();
config.setAllowCredentials(null);
assertThat(config.getAllowCredentials()).isNull();
config.setAllowPrivateNetwork(null);
assertThat(config.getAllowPrivateNetwork()).isNull();
config.setMaxAge((Long) null);
assertThat(config.getMaxAge()).isNull();
}
@@ -65,7 +63,6 @@ class CorsConfigurationTests {
config.addAllowedMethod("*");
config.addExposedHeader("*");
config.setAllowCredentials(true);
config.setAllowPrivateNetwork(true);
config.setMaxAge(123L);
assertThat(config.getAllowedOrigins()).containsExactly("*");
@@ -74,7 +71,6 @@ class CorsConfigurationTests {
assertThat(config.getAllowedMethods()).containsExactly("*");
assertThat(config.getExposedHeaders()).containsExactly("*");
assertThat(config.getAllowCredentials()).isTrue();
assertThat(config.getAllowPrivateNetwork()).isTrue();
assertThat(config.getMaxAge()).isEqualTo(Long.valueOf(123));
}
@@ -97,7 +93,6 @@ class CorsConfigurationTests {
config.addAllowedMethod(HttpMethod.GET.name());
config.setMaxAge(123L);
config.setAllowCredentials(true);
config.setAllowPrivateNetwork(true);
CorsConfiguration other = new CorsConfiguration();
config = config.combine(other);
@@ -110,7 +105,6 @@ class CorsConfigurationTests {
assertThat(config.getAllowedMethods()).containsExactly(HttpMethod.GET.name());
assertThat(config.getMaxAge()).isEqualTo(Long.valueOf(123));
assertThat(config.getAllowCredentials()).isTrue();
assertThat(config.getAllowPrivateNetwork()).isTrue();
}
@Test // SPR-15772
@@ -264,7 +258,6 @@ class CorsConfigurationTests {
config.addAllowedMethod(HttpMethod.GET.name());
config.setMaxAge(123L);
config.setAllowCredentials(true);
config.setAllowPrivateNetwork(true);
CorsConfiguration other = new CorsConfiguration();
other.addAllowedOrigin("https://domain2.com");
@@ -274,7 +267,6 @@ class CorsConfigurationTests {
other.addAllowedMethod(HttpMethod.PUT.name());
other.setMaxAge(456L);
other.setAllowCredentials(false);
other.setAllowPrivateNetwork(false);
config = config.combine(other);
assertThat(config).isNotNull();
@@ -285,7 +277,6 @@ class CorsConfigurationTests {
assertThat(config.getMaxAge()).isEqualTo(Long.valueOf(456));
assertThat(config).isNotNull();
assertThat(config.getAllowCredentials()).isFalse();
assertThat(config.getAllowPrivateNetwork()).isFalse();
assertThat(config.getAllowedOriginPatterns()).containsExactly("http://*.domain1.com", "http://*.domain2.com");
}
@@ -352,32 +352,6 @@ public class DefaultCorsProcessorTests {
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
}
@Test
public void preflightRequestPrivateNetworkWithWildcardOrigin() throws Exception {
this.request.setMethod(HttpMethod.OPTIONS.name());
this.request.addHeader(HttpHeaders.ORIGIN, "https://domain2.com");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1");
this.request.addHeader(DefaultCorsProcessor.ACCESS_CONTROL_REQUEST_PRIVATE_NETWORK, "true");
this.conf.setAllowedOrigins(Arrays.asList("https://domain1.com", "*", "http://domain3.example"));
this.conf.addAllowedHeader("Header1");
this.conf.setAllowPrivateNetwork(true);
assertThatIllegalArgumentException().isThrownBy(() ->
this.processor.processRequest(this.conf, this.request, this.response));
this.conf.setAllowedOrigins(null);
this.conf.addAllowedOriginPattern("*");
this.processor.processRequest(this.conf, this.request, this.response);
assertThat(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
assertThat(this.response.containsHeader(DefaultCorsProcessor.ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK)).isTrue();
assertThat(this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)).isEqualTo("https://domain2.com");
assertThat(this.response.getHeaders(HttpHeaders.VARY)).contains(HttpHeaders.ORIGIN,
HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
}
@Test
public void preflightRequestAllowedHeaders() throws Exception {
this.request.setMethod(HttpMethod.OPTIONS.name());
@@ -461,49 +435,4 @@ public class DefaultCorsProcessorTests {
HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS);
}
@Test
public void preflightRequestWithoutAccessControlRequestPrivateNetwork() throws Exception {
this.request.setMethod(HttpMethod.OPTIONS.name());
this.request.addHeader(HttpHeaders.ORIGIN, "https://domain2.com");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
this.conf.addAllowedHeader("*");
this.conf.addAllowedOrigin("https://domain2.com");
this.processor.processRequest(this.conf, this.request, this.response);
assertThat(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
assertThat(this.response.containsHeader(DefaultCorsProcessor.ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK)).isFalse();
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
}
@Test
public void preflightRequestWithAccessControlRequestPrivateNetworkNotAllowed() throws Exception {
this.request.setMethod(HttpMethod.OPTIONS.name());
this.request.addHeader(HttpHeaders.ORIGIN, "https://domain2.com");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
this.request.addHeader(DefaultCorsProcessor.ACCESS_CONTROL_REQUEST_PRIVATE_NETWORK, "true");
this.conf.addAllowedHeader("*");
this.conf.addAllowedOrigin("https://domain2.com");
this.processor.processRequest(this.conf, this.request, this.response);
assertThat(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
assertThat(this.response.containsHeader(DefaultCorsProcessor.ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK)).isFalse();
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
}
@Test
public void preflightRequestWithAccessControlRequestPrivateNetworkAllowed() throws Exception {
this.request.setMethod(HttpMethod.OPTIONS.name());
this.request.addHeader(HttpHeaders.ORIGIN, "https://domain2.com");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
this.request.addHeader(DefaultCorsProcessor.ACCESS_CONTROL_REQUEST_PRIVATE_NETWORK, "true");
this.conf.addAllowedHeader("*");
this.conf.addAllowedOrigin("https://domain2.com");
this.conf.setAllowPrivateNetwork(true);
this.processor.processRequest(this.conf, this.request, this.response);
assertThat(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
assertThat(this.response.containsHeader(DefaultCorsProcessor.ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK)).isTrue();
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
}
}
@@ -364,33 +364,6 @@ public class DefaultCorsProcessorTests {
assertThat((Object) response.getStatusCode()).isNull();
}
@Test
public void preflightRequestPrivateNetworkWithWildcardOrigin() {
ServerWebExchange exchange = MockServerWebExchange.from(preFlightRequest()
.header(ACCESS_CONTROL_REQUEST_METHOD, "GET")
.header(ACCESS_CONTROL_REQUEST_HEADERS, "Header1")
.header(DefaultCorsProcessor.ACCESS_CONTROL_REQUEST_PRIVATE_NETWORK, "true"));
this.conf.addAllowedOrigin("https://domain1.com");
this.conf.addAllowedOrigin("*");
this.conf.addAllowedOrigin("http://domain3.example");
this.conf.addAllowedHeader("Header1");
this.conf.setAllowPrivateNetwork(true);
assertThatIllegalArgumentException().isThrownBy(() -> this.processor.process(this.conf, exchange));
this.conf.setAllowedOrigins(null);
this.conf.addAllowedOriginPattern("*");
this.processor.process(this.conf, exchange);
ServerHttpResponse response = exchange.getResponse();
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
assertThat(response.getHeaders().containsKey(DefaultCorsProcessor.ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK)).isTrue();
assertThat(response.getHeaders().getFirst(ACCESS_CONTROL_ALLOW_ORIGIN)).isEqualTo("https://domain2.com");
assertThat(response.getHeaders().get(VARY)).contains(ORIGIN,
ACCESS_CONTROL_REQUEST_METHOD, ACCESS_CONTROL_REQUEST_HEADERS);
assertThat(response.getStatusCode()).isNull();
}
@Test
public void preflightRequestAllowedHeaders() {
ServerWebExchange exchange = MockServerWebExchange.from(preFlightRequest()
@@ -487,57 +460,6 @@ public class DefaultCorsProcessorTests {
ACCESS_CONTROL_REQUEST_METHOD, ACCESS_CONTROL_REQUEST_HEADERS);
}
@Test
public void preflightRequestWithoutAccessControlRequestPrivateNetwork() {
ServerWebExchange exchange = MockServerWebExchange.from(preFlightRequest()
.header(ACCESS_CONTROL_REQUEST_METHOD, "GET"));
this.conf.addAllowedHeader("*");
this.conf.addAllowedOrigin("https://domain2.com");
this.processor.process(this.conf, exchange);
ServerHttpResponse response = exchange.getResponse();
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
assertThat(response.getHeaders().containsKey(DefaultCorsProcessor.ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK)).isFalse();
assertThat(response.getStatusCode()).isNull();
}
@Test
public void preflightRequestWithAccessControlRequestPrivateNetworkNotAllowed() {
ServerWebExchange exchange = MockServerWebExchange.from(preFlightRequest()
.header(ACCESS_CONTROL_REQUEST_METHOD, "GET")
.header(DefaultCorsProcessor.ACCESS_CONTROL_REQUEST_PRIVATE_NETWORK, "true"));
this.conf.addAllowedHeader("*");
this.conf.addAllowedOrigin("https://domain2.com");
this.processor.process(this.conf, exchange);
ServerHttpResponse response = exchange.getResponse();
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
assertThat(response.getHeaders().containsKey(DefaultCorsProcessor.ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK)).isFalse();
assertThat(response.getStatusCode()).isNull();
}
@Test
public void preflightRequestWithAccessControlRequestPrivateNetworkAllowed() {
ServerWebExchange exchange = MockServerWebExchange.from(preFlightRequest()
.header(ACCESS_CONTROL_REQUEST_METHOD, "GET")
.header(DefaultCorsProcessor.ACCESS_CONTROL_REQUEST_PRIVATE_NETWORK, "true"));
this.conf.addAllowedHeader("*");
this.conf.addAllowedOrigin("https://domain2.com");
this.conf.setAllowPrivateNetwork(true);
this.processor.process(this.conf, exchange);
ServerHttpResponse response = exchange.getResponse();
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
assertThat(response.getHeaders().containsKey(DefaultCorsProcessor.ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK)).isTrue();
assertThat(response.getStatusCode()).isNull();
}
private ServerWebExchange actualRequest() {
return MockServerWebExchange.from(corsRequest(HttpMethod.GET));
@@ -23,7 +23,6 @@ import javax.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.Test;
import org.springframework.http.MediaType;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StreamUtils;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
@@ -69,7 +68,6 @@ public class ShallowEtagHeaderFilterTests {
FilterChain filterChain = (filterRequest, filterResponse) -> {
assertThat(filterRequest).as("Invalid request passed").isEqualTo(request);
((HttpServletResponse) filterResponse).setStatus(HttpServletResponse.SC_OK);
filterResponse.setContentType(MediaType.TEXT_PLAIN_VALUE);
FileCopyUtils.copy(responseBody, filterResponse.getOutputStream());
};
filter.doFilter(request, response, filterChain);
@@ -77,7 +75,6 @@ public class ShallowEtagHeaderFilterTests {
assertThat(response.getStatus()).as("Invalid status").isEqualTo(200);
assertThat(response.getHeader("ETag")).as("Invalid ETag").isEqualTo("\"0b10a8db164e0754105b7a99be72e3fe5\"");
assertThat(response.getContentLength() > 0).as("Invalid Content-Length header").isTrue();
assertThat(response.getContentType()).as("Invalid Content-Type header").isEqualTo(MediaType.TEXT_PLAIN_VALUE);
assertThat(response.getContentAsByteArray()).as("Invalid content").isEqualTo(responseBody);
}
@@ -91,7 +88,6 @@ public class ShallowEtagHeaderFilterTests {
FilterChain filterChain = (filterRequest, filterResponse) -> {
assertThat(filterRequest).as("Invalid request passed").isEqualTo(request);
((HttpServletResponse) filterResponse).setStatus(HttpServletResponse.SC_OK);
filterResponse.setContentType(MediaType.TEXT_PLAIN_VALUE);
FileCopyUtils.copy(responseBody, filterResponse.getOutputStream());
};
filter.doFilter(request, response, filterChain);
@@ -99,7 +95,6 @@ public class ShallowEtagHeaderFilterTests {
assertThat(response.getStatus()).as("Invalid status").isEqualTo(200);
assertThat(response.getHeader("ETag")).as("Invalid ETag").isEqualTo("W/\"0b10a8db164e0754105b7a99be72e3fe5\"");
assertThat(response.getContentLength() > 0).as("Invalid Content-Length header").isTrue();
assertThat(response.getContentType()).as("Invalid Content-Type header").isEqualTo(MediaType.TEXT_PLAIN_VALUE);
assertThat(response.getContentAsByteArray()).as("Invalid content").isEqualTo(responseBody);
}
@@ -113,16 +108,14 @@ public class ShallowEtagHeaderFilterTests {
FilterChain filterChain = (filterRequest, filterResponse) -> {
assertThat(filterRequest).as("Invalid request passed").isEqualTo(request);
byte[] responseBody = "Hello World".getBytes(StandardCharsets.UTF_8);
filterResponse.setContentLength(responseBody.length);
filterResponse.setContentType(MediaType.TEXT_PLAIN_VALUE);
FileCopyUtils.copy(responseBody, filterResponse.getOutputStream());
filterResponse.setContentLength(responseBody.length);
};
filter.doFilter(request, response, filterChain);
assertThat(response.getStatus()).as("Invalid status").isEqualTo(304);
assertThat(response.getHeader("ETag")).as("Invalid ETag").isEqualTo("\"0b10a8db164e0754105b7a99be72e3fe5\"");
assertThat(response.containsHeader("Content-Length")).as("Response has Content-Length header").isFalse();
assertThat(response.containsHeader("Content-Type")).as("Response has Content-Type header").isFalse();
byte[] expecteds = new byte[0];
assertThat(response.getContentAsByteArray()).as("Invalid content").isEqualTo(expecteds);
}
@@ -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.
@@ -131,17 +131,6 @@ public class CorsRegistration {
return this;
}
/**
* Whether private network access is supported.
* <p>Please, see {@link CorsConfiguration#setAllowPrivateNetwork(Boolean)} for details.
* <p>By default this is not set (i.e. private network access is not supported).
* @since 5.3.32
*/
public CorsRegistration allowPrivateNetwork(boolean allowPrivateNetwork) {
this.config.setAllowPrivateNetwork(allowPrivateNetwork);
return this;
}
/**
* Configure how long in seconds the response from a pre-flight request
* can be cached by clients.
@@ -196,7 +196,6 @@ public abstract class AbstractHandlerMapping extends ApplicationObjectSupport
config = (config != null ? config.combine(handlerConfig) : handlerConfig);
if (config != null) {
config.validateAllowCredentials();
config.validateAllowPrivateNetwork();
}
if (!this.corsProcessor.process(config, exchange) || CorsUtils.isPreFlightRequest(request)) {
return NO_OP_HANDLER;
@@ -534,7 +534,6 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
corsConfig.validateAllowCredentials();
corsConfig.validateAllowPrivateNetwork();
this.corsLookup.put(handlerMethod, corsConfig);
}
@@ -341,18 +341,6 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
"or an empty string (\"\"): current value is [" + allowCredentials + "]");
}
String allowPrivateNetwork = resolveCorsAnnotationValue(annotation.allowPrivateNetwork());
if ("true".equalsIgnoreCase(allowPrivateNetwork)) {
config.setAllowPrivateNetwork(true);
}
else if ("false".equalsIgnoreCase(allowPrivateNetwork)) {
config.setAllowPrivateNetwork(false);
}
else if (!allowPrivateNetwork.isEmpty()) {
throw new IllegalStateException("@CrossOrigin's allowPrivateNetwork value must be \"true\", \"false\", " +
"or an empty string (\"\"): current value is [" + allowPrivateNetwork + "]");
}
if (annotation.maxAge() >= 0) {
config.setMaxAge(annotation.maxAge());
}
@@ -51,8 +51,8 @@ public class CorsRegistryTests {
@Test
public void customizedMapping() {
this.registry.addMapping("/foo").allowedOrigins("https://domain2.com", "https://domain2.com")
.allowedMethods("DELETE").allowCredentials(true).allowPrivateNetwork(true)
.allowedHeaders("header1", "header2").exposedHeaders("header3", "header4").maxAge(3600);
.allowedMethods("DELETE").allowCredentials(false).allowedHeaders("header1", "header2")
.exposedHeaders("header3", "header4").maxAge(3600);
Map<String, CorsConfiguration> configs = this.registry.getCorsConfigurations();
assertThat(configs.size()).isEqualTo(1);
CorsConfiguration config = configs.get("/foo");
@@ -60,8 +60,7 @@ public class CorsRegistryTests {
assertThat(config.getAllowedMethods()).isEqualTo(Collections.singletonList("DELETE"));
assertThat(config.getAllowedHeaders()).isEqualTo(Arrays.asList("header1", "header2"));
assertThat(config.getExposedHeaders()).isEqualTo(Arrays.asList("header3", "header4"));
assertThat(config.getAllowCredentials()).isTrue();
assertThat(config.getAllowPrivateNetwork()).isTrue();
assertThat(config.getAllowCredentials()).isFalse();
assertThat(config.getMaxAge()).isEqualTo(Long.valueOf(3600));
}
@@ -91,7 +90,6 @@ public class CorsRegistryTests {
assertThat(config.getAllowedHeaders()).isEqualTo(Collections.singletonList("*"));
assertThat(config.getExposedHeaders()).isEmpty();
assertThat(config.getAllowCredentials()).isNull();
assertThat(config.getAllowPrivateNetwork()).isNull();
assertThat(config.getMaxAge()).isEqualTo(Long.valueOf(1800));
}
@@ -475,7 +475,7 @@ class WebClientIntegrationTests {
.retrieve()
.bodyToMono(Map.class);
StepVerifier.create(result).expectComplete().verify(Duration.ofSeconds(3));
StepVerifier.create(result).verifyComplete();
}
@ParameterizedWebClientTest // SPR-15946
@@ -800,7 +800,7 @@ class WebClientIntegrationTests {
MyException error = (MyException) throwable;
assertThat(error.getMessage()).isEqualTo("foofoo");
})
.verify(Duration.ofSeconds(3));
.verify();
}
@ParameterizedWebClientTest
@@ -842,7 +842,7 @@ class WebClientIntegrationTests {
StepVerifier.create(result)
.expectNext("Internal Server error")
.expectComplete().verify(Duration.ofSeconds(3));
.verifyComplete();
expectRequestCount(1);
expectRequest(request -> {
@@ -866,7 +866,7 @@ class WebClientIntegrationTests {
StepVerifier.create(result)
.expectNext("Internal Server error")
.expectComplete().verify(Duration.ofSeconds(3));
.verifyComplete();
expectRequestCount(1);
expectRequest(request -> {
@@ -1024,7 +1024,7 @@ class WebClientIntegrationTests {
StepVerifier.create(result)
.assertNext(r -> assertThat(r.getStatusCode().is2xxSuccessful()).isTrue())
.expectComplete().verify(Duration.ofSeconds(3));
.verifyComplete();
}
@ParameterizedWebClientTest
@@ -1209,7 +1209,7 @@ class WebClientIntegrationTests {
WebClientException ex = (WebClientException) throwable;
assertThat(ex.getCause()).isInstanceOf(IOException.class);
})
.verify(Duration.ofSeconds(3));
.verify();
}
@ParameterizedWebClientTest
@@ -1221,7 +1221,7 @@ class WebClientIntegrationTests {
WebClientException ex = (WebClientException) throwable;
assertThat(ex.getCause()).isInstanceOf(IOException.class);
})
.verify(Duration.ofSeconds(3));
.verify();
}
private <T> Mono<T> doMalformedChunkedResponseTest(

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