Compare commits

..

1 Commits

Author SHA1 Message Date
Spring Builds e5d99ecf98 Release v5.3.30 2023-09-14 07:43:24 +00:00
164 changed files with 2430 additions and 3361 deletions
+1 -1
View File
@@ -1,3 +1,3 @@
# Enable auto-env through the sdkman_auto_env config
# Add key=value pairs of SDKs to use below
java=8.0.382-librca
java=8.0.372-librca
+13 -13
View File
@@ -28,18 +28,18 @@ 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.95.Final"
mavenBom "io.projectreactor:reactor-bom:2020.0.34"
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.51.v20230217"
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"
mavenBom "org.junit:junit-bom:5.8.2"
}
dependencies {
dependencySet(group: 'org.apache.logging.log4j', version: '2.21.1') {
dependencySet(group: 'org.apache.logging.log4j', version: '2.20.0') {
entry 'log4j-api'
entry 'log4j-core'
entry 'log4j-jul'
@@ -67,9 +67,9 @@ configure(allprojects) { project ->
dependency "io.reactivex:rxjava:1.3.8"
dependency "io.reactivex:rxjava-reactive-streams:1.2.1"
dependency "io.reactivex.rxjava2:rxjava:2.2.21"
dependency "io.reactivex.rxjava3:rxjava:3.1.8"
dependency "io.smallrye.reactive:mutiny:1.9.0"
dependency "io.projectreactor.tools:blockhound:1.0.8.RELEASE"
dependency "io.reactivex.rxjava3:rxjava:3.1.5"
dependency "io.smallrye.reactive:mutiny:1.8.0"
dependency "io.projectreactor.tools:blockhound:1.0.6.RELEASE"
dependency "com.caucho:hessian:4.0.63"
dependency "com.fasterxml:aalto-xml:1.3.1"
@@ -96,7 +96,7 @@ configure(allprojects) { project ->
dependency "com.h2database:h2:2.1.214"
dependency "com.github.ben-manes.caffeine:caffeine:2.9.3"
dependency "com.github.librepdf:openpdf:1.3.33"
dependency "com.github.librepdf:openpdf:1.3.30"
dependency "com.rometools:rome:1.18.0"
dependency "commons-io:commons-io:2.5"
dependency "io.vavr:vavr:0.10.4"
@@ -128,18 +128,18 @@ configure(allprojects) { project ->
dependency "org.webjars:webjars-locator-core:0.48"
dependency "org.webjars:underscorejs:1.8.3"
dependencySet(group: 'org.apache.tomcat', version: '9.0.82') {
dependencySet(group: 'org.apache.tomcat', version: '9.0.78') {
entry 'tomcat-util'
entry('tomcat-websocket') {
exclude group: "org.apache.tomcat", name: "tomcat-servlet-api"
exclude group: "org.apache.tomcat", name: "tomcat-websocket-api"
}
}
dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.82') {
dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.78') {
entry 'tomcat-embed-core'
entry 'tomcat-embed-websocket'
}
dependencySet(group: 'io.undertow', version: '2.2.29.Final') {
dependencySet(group: 'io.undertow', version: '2.2.25.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.1"
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.30
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-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.
@@ -1004,20 +1004,18 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA
*/
protected abstract static class PropertyHandler {
@Nullable
private final Class<?> propertyType;
private final boolean readable;
private final boolean writable;
public PropertyHandler(@Nullable Class<?> propertyType, boolean readable, boolean writable) {
public PropertyHandler(Class<?> propertyType, boolean readable, boolean writable) {
this.propertyType = propertyType;
this.readable = readable;
this.writable = writable;
}
@Nullable
public Class<?> getPropertyType() {
return this.propertyType;
}
@@ -23,7 +23,6 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.net.URI;
import java.net.URL;
import java.time.temporal.Temporal;
@@ -31,7 +30,6 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -789,28 +787,38 @@ public abstract class BeanUtils {
if (editable != null) {
if (!editable.isInstance(target)) {
throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
"] not assignable to editable class [" + editable.getName() + "]");
"] not assignable to Editable class [" + editable.getName() + "]");
}
actualEditable = editable;
}
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
Set<String> ignoredProps = (ignoreProperties != null ? new HashSet<>(Arrays.asList(ignoreProperties)) : null);
CachedIntrospectionResults sourceResults = (actualEditable != source.getClass() ?
CachedIntrospectionResults.forClass(source.getClass()) : null);
List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);
for (PropertyDescriptor targetPd : targetPds) {
Method writeMethod = targetPd.getWriteMethod();
if (writeMethod != null && (ignoredProps == null || !ignoredProps.contains(targetPd.getName()))) {
PropertyDescriptor sourcePd = (sourceResults != null ?
sourceResults.getPropertyDescriptor(targetPd.getName()) : targetPd);
if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
if (sourcePd != null) {
Method readMethod = sourcePd.getReadMethod();
if (readMethod != null) {
if (isAssignable(writeMethod, readMethod)) {
ResolvableType sourceResolvableType = ResolvableType.forMethodReturnType(readMethod);
ResolvableType targetResolvableType = ResolvableType.forMethodParameter(writeMethod, 0);
// Ignore generic types in assignable check if either ResolvableType has unresolvable generics.
boolean isAssignable =
(sourceResolvableType.hasUnresolvableGenerics() || targetResolvableType.hasUnresolvableGenerics() ?
ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType()) :
targetResolvableType.isAssignableFrom(sourceResolvableType));
if (isAssignable) {
try {
ReflectionUtils.makeAccessible(readMethod);
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
readMethod.setAccessible(true);
}
Object value = readMethod.invoke(source);
ReflectionUtils.makeAccessible(writeMethod);
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
writeMethod.setAccessible(true);
}
writeMethod.invoke(target, value);
}
catch (Throwable ex) {
@@ -824,24 +832,6 @@ public abstract class BeanUtils {
}
}
private static boolean isAssignable(Method writeMethod, Method readMethod) {
Type paramType = writeMethod.getGenericParameterTypes()[0];
if (paramType instanceof Class) {
return ClassUtils.isAssignable((Class<?>) paramType, readMethod.getReturnType());
}
else if (paramType.equals(readMethod.getGenericReturnType())) {
return true;
}
else {
ResolvableType sourceType = ResolvableType.forMethodReturnType(readMethod);
ResolvableType targetType = ResolvableType.forMethodParameter(writeMethod, 0);
// Ignore generic types in assignable check if either ResolvableType has unresolvable generics.
return (sourceType.hasUnresolvableGenerics() || targetType.hasUnresolvableGenerics() ?
ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType()) :
targetType.isAssignableFrom(sourceType));
}
}
/**
* Inner class to avoid a hard dependency on Kotlin at runtime.
@@ -902,6 +892,7 @@ public abstract class BeanUtils {
}
return kotlinConstructor.callBy(argParameters);
}
}
}
@@ -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.
@@ -129,6 +129,7 @@ public class DirectFieldAccessor extends AbstractNestablePropertyAccessor {
ReflectionUtils.makeAccessible(this.field);
return this.field.get(getWrappedInstance());
}
catch (IllegalAccessException ex) {
throw new InvalidPropertyException(getWrappedClass(),
this.field.getName(), "Field is not accessible", ex);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -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");
}
@@ -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.
@@ -104,8 +104,8 @@ import org.springframework.stereotype.Component;
*
* }</pre>
*
* <p>{@code @Configuration} classes may not only be bootstrapped using component
* scanning, but may also themselves <em>configure</em> component scanning using
* <p>{@code @Configuration} classes may not only be bootstrapped using
* component scanning, but may also themselves <em>configure</em> component scanning using
* the {@link ComponentScan @ComponentScan} annotation:
*
* <pre class="code">
@@ -137,39 +137,31 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
/**
* The name of the {@link LifecycleProcessor} bean in the context.
* If none is supplied, a {@link DefaultLifecycleProcessor} is used.
* @since 3.0
* @see org.springframework.context.LifecycleProcessor
* @see org.springframework.context.support.DefaultLifecycleProcessor
* @see #start()
* @see #stop()
*/
public static final String LIFECYCLE_PROCESSOR_BEAN_NAME = "lifecycleProcessor";
/**
* The name of the {@link MessageSource} bean in the context.
* Name of the MessageSource bean in the factory.
* If none is supplied, message resolution is delegated to the parent.
* @see org.springframework.context.MessageSource
* @see org.springframework.context.support.ResourceBundleMessageSource
* @see org.springframework.context.support.ReloadableResourceBundleMessageSource
* @see #getMessage(MessageSourceResolvable, Locale)
* @see MessageSource
*/
public static final String MESSAGE_SOURCE_BEAN_NAME = "messageSource";
/**
* The name of the {@link ApplicationEventMulticaster} bean in the context.
* If none is supplied, a {@link SimpleApplicationEventMulticaster} is used.
* Name of the LifecycleProcessor bean in the factory.
* If none is supplied, a DefaultLifecycleProcessor is used.
* @see org.springframework.context.LifecycleProcessor
* @see org.springframework.context.support.DefaultLifecycleProcessor
*/
public static final String LIFECYCLE_PROCESSOR_BEAN_NAME = "lifecycleProcessor";
/**
* Name of the ApplicationEventMulticaster bean in the factory.
* If none is supplied, a default SimpleApplicationEventMulticaster is used.
* @see org.springframework.context.event.ApplicationEventMulticaster
* @see org.springframework.context.event.SimpleApplicationEventMulticaster
* @see #publishEvent(ApplicationEvent)
* @see #addApplicationListener(ApplicationListener)
*/
public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
/**
* Boolean flag controlled by a {@code spring.spel.ignore} system property that
* instructs Spring to ignore SpEL, i.e. to not initialize the SpEL infrastructure.
* Boolean flag controlled by a {@code spring.spel.ignore} system property that instructs Spring to
* ignore SpEL, i.e. to not initialize the SpEL infrastructure.
* <p>The default is "false".
*/
private static final boolean shouldIgnoreSpel = SpringProperties.getFlag("spring.spel.ignore");
@@ -211,7 +203,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. */
@@ -219,7 +211,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
private Thread shutdownHook;
/** ResourcePatternResolver used by this context. */
private final ResourcePatternResolver resourcePatternResolver;
private ResourcePatternResolver resourcePatternResolver;
/** LifecycleProcessor for managing the lifecycle of beans within this context. */
@Nullable
@@ -55,7 +55,7 @@ class ContextTypeMatchClassLoader extends DecoratingClassLoader implements Smart
// override classes that have not been loaded yet. If not accessible, we will
// always override requested classes, even when the classes have been loaded
// by the parent ClassLoader already and cannot be transformed anymore anyway.
Method method;
Method method = null;
try {
method = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
ReflectionUtils.makeAccessible(method);
@@ -63,7 +63,6 @@ class ContextTypeMatchClassLoader extends DecoratingClassLoader implements Smart
catch (Throwable ex) {
// Typically a JDK 9+ InaccessibleObjectException...
// Avoid through JVM startup with --add-opens=java.base/java.lang=ALL-UNNAMED
method = null;
LogFactory.getLog(ContextTypeMatchClassLoader.class).debug(
"ClassLoader.findLoadedClass not accessible -> will always override requested class", 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.
@@ -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-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.
@@ -92,7 +92,7 @@ public class ProxyFactoryBeanTests {
@BeforeEach
public void setup() throws Exception {
public void setUp() throws Exception {
DefaultListableBeanFactory parent = new DefaultListableBeanFactory();
parent.registerBeanDefinition("target2", new RootBeanDefinition(TestApplicationListener.class));
this.factory = new DefaultListableBeanFactory(parent);
@@ -139,24 +139,22 @@ public class ProxyFactoryBeanTests {
private void testDoubleTargetSourceIsRejected(String name) {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(new ClassPathResource(DBL_TARGETSOURCE_CONTEXT, CLASS));
assertThatExceptionOfType(BeanCreationException.class).as("Should not allow TargetSource to be specified in interceptorNames as well as targetSource property")
.isThrownBy(() -> bf.getBean(name))
.havingCause()
.isInstanceOf(AopConfigException.class)
.withMessageContaining("TargetSource");
.isThrownBy(() -> bf.getBean(name))
.havingCause()
.isInstanceOf(AopConfigException.class)
.withMessageContaining("TargetSource");
}
@Test
public void testTargetSourceNotAtEndOfInterceptorNamesIsRejected() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(new ClassPathResource(NOTLAST_TARGETSOURCE_CONTEXT, CLASS));
assertThatExceptionOfType(BeanCreationException.class).as("TargetSource or non-advised object must be last in interceptorNames")
.isThrownBy(() -> bf.getBean("targetSourceNotLast"))
.havingCause()
.isInstanceOf(AopConfigException.class)
.withMessageContaining("interceptorNames");
.isThrownBy(() -> bf.getBean("targetSourceNotLast"))
.havingCause()
.isInstanceOf(AopConfigException.class)
.withMessageContaining("interceptorNames");
}
@Test
@@ -169,11 +167,11 @@ public class ProxyFactoryBeanTests {
assertThat(cba.getCalls()).isEqualTo(0);
ITestBean tb = (ITestBean) bf.getBean("directTarget");
assertThat(tb.getName()).isEqualTo("Adam");
assertThat(tb.getName().equals("Adam")).isTrue();
assertThat(cba.getCalls()).isEqualTo(1);
ProxyFactoryBean pfb = (ProxyFactoryBean) bf.getBean("&directTarget");
assertThat(pfb.getObjectType()).isAssignableTo(TestBean.class);
assertThat(TestBean.class.isAssignableFrom(pfb.getObjectType())).as("Has correct object type").isTrue();
}
@Test
@@ -181,9 +179,9 @@ public class ProxyFactoryBeanTests {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(new ClassPathResource(TARGETSOURCE_CONTEXT, CLASS));
ITestBean tb = (ITestBean) bf.getBean("viaTargetSource");
assertThat(tb.getName()).isEqualTo("Adam");
assertThat(tb.getName().equals("Adam")).isTrue();
ProxyFactoryBean pfb = (ProxyFactoryBean) bf.getBean("&viaTargetSource");
assertThat(pfb.getObjectType()).isAssignableTo(TestBean.class);
assertThat(TestBean.class.isAssignableFrom(pfb.getObjectType())).as("Has correct object type").isTrue();
}
@Test
@@ -192,15 +190,11 @@ public class ProxyFactoryBeanTests {
new XmlBeanDefinitionReader(bf).loadBeanDefinitions(new ClassPathResource(TARGETSOURCE_CONTEXT, CLASS));
ITestBean tb = (ITestBean) bf.getBean("noTarget");
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(tb::getName).withMessage("getName");
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() ->
tb.getName())
.withMessage("getName");
FactoryBean<?> pfb = (ProxyFactoryBean) bf.getBean("&noTarget");
assertThat(pfb.getObjectType()).isAssignableTo(ITestBean.class);
}
@Test
public void testGetObjectTypeOnUninitializedFactoryBean() {
ProxyFactoryBean pfb = new ProxyFactoryBean();
assertThat(pfb.getObjectType()).isNull();
assertThat(ITestBean.class.isAssignableFrom(pfb.getObjectType())).as("Has correct object type").isTrue();
}
/**
@@ -225,20 +219,20 @@ public class ProxyFactoryBeanTests {
pc1.addAdvice(1, di);
assertThat(pc2.getAdvisors()).isEqualTo(pc1.getAdvisors());
assertThat(pc2.getAdvisors().length).as("Now have one more advisor").isEqualTo((oldLength + 1));
assertThat(di.getCount()).isEqualTo(0);
assertThat(0).isEqualTo(di.getCount());
test1.setAge(5);
assertThat(test1.getAge()).isEqualTo(test1_1.getAge());
assertThat(di.getCount()).isEqualTo(3);
assertThat(3).isEqualTo(di.getCount());
}
@Test
public void testPrototypeInstancesAreNotEqual() {
assertThat(factory.getType("prototype")).isAssignableTo(ITestBean.class);
assertThat(ITestBean.class.isAssignableFrom(factory.getType("prototype"))).as("Has correct object type").isTrue();
ITestBean test2 = (ITestBean) factory.getBean("prototype");
ITestBean test2_1 = (ITestBean) factory.getBean("prototype");
assertThat(test2).as("Prototype instances !=").isNotSameAs(test2_1);
assertThat(test2).as("Prototype instances equal").isEqualTo(test2_1);
assertThat(factory.getType("prototype")).isAssignableTo(ITestBean.class);
assertThat(test2 != test2_1).as("Prototype instances !=").isTrue();
assertThat(test2.equals(test2_1)).as("Prototype instances equal").isTrue();
assertThat(ITestBean.class.isAssignableFrom(factory.getType("prototype"))).as("Has correct object type").isTrue();
}
/**
@@ -268,7 +262,7 @@ public class ProxyFactoryBeanTests {
assertThat(prototype2FirstInstance.getCount()).isEqualTo(INITIAL_COUNT + 1);
SideEffectBean prototype2SecondInstance = (SideEffectBean) bf.getBean(beanName);
assertThat(prototype2FirstInstance).as("Prototypes are not ==").isNotSameAs(prototype2SecondInstance);
assertThat(prototype2FirstInstance == prototype2SecondInstance).as("Prototypes are not ==").isFalse();
assertThat(prototype2SecondInstance.getCount()).isEqualTo(INITIAL_COUNT);
assertThat(prototype2FirstInstance.getCount()).isEqualTo(INITIAL_COUNT + 1);
@@ -291,19 +285,19 @@ public class ProxyFactoryBeanTests {
TestBean target = (TestBean) factory.getBean("test");
target.setName(name);
ITestBean autoInvoker = (ITestBean) factory.getBean("autoInvoker");
assertThat(autoInvoker.getName()).isEqualTo(name);
assertThat(autoInvoker.getName().equals(name)).isTrue();
}
@Test
public void testCanGetFactoryReferenceAndManipulate() {
ProxyFactoryBean config = (ProxyFactoryBean) factory.getBean("&test1");
assertThat(config.getObjectType()).isAssignableTo(ITestBean.class);
assertThat(factory.getType("test1")).isAssignableTo(ITestBean.class);
assertThat(ITestBean.class.isAssignableFrom(config.getObjectType())).as("Has correct object type").isTrue();
assertThat(ITestBean.class.isAssignableFrom(factory.getType("test1"))).as("Has correct object type").isTrue();
// Trigger lazy initialization.
config.getObject();
assertThat(config.getAdvisors().length).as("Have one advisors").isEqualTo(1);
assertThat(config.getObjectType()).isAssignableTo(ITestBean.class);
assertThat(factory.getType("test1")).isAssignableTo(ITestBean.class);
assertThat(ITestBean.class.isAssignableFrom(config.getObjectType())).as("Has correct object type").isTrue();
assertThat(ITestBean.class.isAssignableFrom(factory.getType("test1"))).as("Has correct object type").isTrue();
ITestBean tb = (ITestBean) factory.getBean("test1");
// no exception
@@ -314,7 +308,7 @@ public class ProxyFactoryBeanTests {
config.addAdvice(0, (MethodInterceptor) invocation -> {
throw ex;
});
assertThat(config.getAdvisors()).as("Have correct advisor count").hasSize(2);
assertThat(config.getAdvisors().length).as("Have correct advisor count").isEqualTo(2);
ITestBean tb1 = (ITestBean) factory.getBean("test1");
assertThatException()
@@ -354,31 +348,31 @@ public class ProxyFactoryBeanTests {
// Add to head of interceptor chain
int oldCount = config.getAdvisors().length;
config.addAdvisor(0, new DefaultIntroductionAdvisor(ti, TimeStamped.class));
assertThat(config.getAdvisors()).hasSize(oldCount + 1);
assertThat(config.getAdvisors().length == oldCount + 1).isTrue();
TimeStamped ts = (TimeStamped) factory.getBean("test2");
assertThat(ts.getTimeStamp()).isEqualTo(time);
// Can remove
config.removeAdvice(ti);
assertThat(config.getAdvisors()).hasSize(oldCount);
assertThat(config.getAdvisors().length == oldCount).isTrue();
// Check no change on existing object reference
assertThat(ts.getTimeStamp()).isEqualTo(time);
assertThat(ts.getTimeStamp() == time).isTrue();
assertThat(factory.getBean("test2")).as("Should no longer implement TimeStamped")
.isNotInstanceOf(TimeStamped.class);
// Now check non-effect of removing interceptor that isn't there
config.removeAdvice(new DebugInterceptor());
assertThat(config.getAdvisors()).hasSize(oldCount);
assertThat(config.getAdvisors().length == oldCount).isTrue();
ITestBean it = (ITestBean) ts;
DebugInterceptor debugInterceptor = new DebugInterceptor();
config.addAdvice(0, debugInterceptor);
it.getSpouse();
// Won't affect existing reference
assertThat(debugInterceptor.getCount()).isEqualTo(0);
assertThat(debugInterceptor.getCount() == 0).isTrue();
it = (ITestBean) factory.getBean("test2");
it.getSpouse();
assertThat(debugInterceptor.getCount()).isEqualTo(1);
@@ -418,16 +412,16 @@ public class ProxyFactoryBeanTests {
public void testMethodPointcuts() {
ITestBean tb = (ITestBean) factory.getBean("pointcuts");
PointcutForVoid.reset();
assertThat(PointcutForVoid.methodNames).as("No methods intercepted").isEmpty();
assertThat(PointcutForVoid.methodNames.isEmpty()).as("No methods intercepted").isTrue();
tb.getAge();
assertThat(PointcutForVoid.methodNames).as("Not void: shouldn't have intercepted").isEmpty();
assertThat(PointcutForVoid.methodNames.isEmpty()).as("Not void: shouldn't have intercepted").isTrue();
tb.setAge(1);
tb.getAge();
tb.setName("Tristan");
tb.toString();
assertThat(PointcutForVoid.methodNames).as("Recorded wrong number of invocations").hasSize(2);
assertThat(PointcutForVoid.methodNames.get(0)).isEqualTo("setAge");
assertThat(PointcutForVoid.methodNames.get(1)).isEqualTo("setName");
assertThat(PointcutForVoid.methodNames.size()).as("Recorded wrong number of invocations").isEqualTo(2);
assertThat(PointcutForVoid.methodNames.get(0).equals("setAge")).isTrue();
assertThat(PointcutForVoid.methodNames.get(1).equals("setName")).isTrue();
}
@Test
@@ -504,17 +498,17 @@ public class ProxyFactoryBeanTests {
@Test
public void testGlobalsCanAddAspectInterfaces() {
AddedGlobalInterface agi = (AddedGlobalInterface) factory.getBean("autoInvoker");
assertThat(agi.globalsAdded()).isEqualTo(-1);
assertThat(agi.globalsAdded() == -1).isTrue();
ProxyFactoryBean pfb = (ProxyFactoryBean) factory.getBean("&validGlobals");
// Trigger lazy initialization.
pfb.getObject();
// 2 globals + 2 explicit
assertThat(pfb.getAdvisors()).as("Have 2 globals and 2 explicit advisors").hasSize(3);
assertThat(pfb.getAdvisors().length).as("Have 2 globals and 2 explicit advisors").isEqualTo(3);
ApplicationListener<?> l = (ApplicationListener<?>) factory.getBean("validGlobals");
agi = (AddedGlobalInterface) l;
assertThat(agi.globalsAdded()).isEqualTo(-1);
assertThat(agi.globalsAdded() == -1).isTrue();
assertThat(factory.getBean("test1")).as("Aspect interface shouldn't be implemented without globals")
.isNotInstanceOf(AddedGlobalInterface.class);
@@ -715,7 +709,6 @@ public class ProxyFactoryBeanTests {
}
}
/**
* Aspect interface
*/
@@ -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-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.
@@ -215,7 +215,7 @@ public class CommonAnnotationBeanPostProcessorTests {
bf.registerBeanDefinition("testBean4", tbd);
bf.registerResolvableDependency(BeanFactory.class, bf);
bf.registerResolvableDependency(INestedTestBean.class, (ObjectFactory<Object>) NestedTestBean::new);
bf.registerResolvableDependency(INestedTestBean.class, (ObjectFactory<Object>) () -> new NestedTestBean());
@SuppressWarnings("deprecation")
org.springframework.beans.factory.config.PropertyPlaceholderConfigurer ppc = new org.springframework.beans.factory.config.PropertyPlaceholderConfigurer();
@@ -233,7 +233,7 @@ public class CommonAnnotationBeanPostProcessorTests {
assertThat(tb).isNotSameAs(anotherBean.getTestBean6());
String[] depBeans = bf.getDependenciesForBean("annotatedBean");
assertThat(depBeans).hasSize(1);
assertThat(depBeans.length).isEqualTo(1);
assertThat(depBeans[0]).isEqualTo("testBean4");
}
@@ -508,25 +508,6 @@ public class CommonAnnotationBeanPostProcessorTests {
assertThat(tb.getName()).isEqualTo("notLazyAnymore");
}
@Test
public void testLazyResolutionWithFallbackTypeMatch() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
CommonAnnotationBeanPostProcessor bpp = new CommonAnnotationBeanPostProcessor();
bpp.setBeanFactory(bf);
bf.addBeanPostProcessor(bpp);
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(LazyResourceCglibInjectionBean.class));
bf.registerBeanDefinition("tb", new RootBeanDefinition(TestBean.class));
LazyResourceCglibInjectionBean bean = (LazyResourceCglibInjectionBean) bf.getBean("annotatedBean");
assertThat(bf.containsSingleton("tb")).isFalse();
bean.testBean.setName("notLazyAnymore");
assertThat(bf.containsSingleton("tb")).isTrue();
TestBean tb = (TestBean) bf.getBean("tb");
assertThat(tb.getName()).isEqualTo("notLazyAnymore");
}
public static class AnnotatedInitDestroyBean {
@@ -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-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.
@@ -36,10 +36,6 @@ public abstract class AbstractApplicationEventListenerTests {
}
}
protected <T> GenericTestEvent<T> createGenericTestEvent(T payload) {
return new GenericTestEvent<>(this, payload);
}
protected static class GenericTestEvent<T> extends ApplicationEvent {
@@ -55,7 +51,6 @@ public abstract class AbstractApplicationEventListenerTests {
}
}
protected static class SmartGenericTestEvent<T> extends GenericTestEvent<T> implements ResolvableTypeProvider {
private final ResolvableType resolvableType;
@@ -72,7 +67,6 @@ public abstract class AbstractApplicationEventListenerTests {
}
}
protected static class StringEvent extends GenericTestEvent<String> {
public StringEvent(Object source, String payload) {
@@ -80,7 +74,6 @@ public abstract class AbstractApplicationEventListenerTests {
}
}
protected static class LongEvent extends GenericTestEvent<Long> {
public LongEvent(Object source, Long payload) {
@@ -88,31 +81,31 @@ public abstract class AbstractApplicationEventListenerTests {
}
}
protected <T> GenericTestEvent<T> createGenericTestEvent(T payload) {
return new GenericTestEvent<>(this, payload);
}
static class GenericEventListener implements ApplicationListener<GenericTestEvent<?>> {
@Override
public void onApplicationEvent(GenericTestEvent<?> event) {
}
}
static class ObjectEventListener implements ApplicationListener<GenericTestEvent<Object>> {
@Override
public void onApplicationEvent(GenericTestEvent<Object> event) {
}
}
static class UpperBoundEventListener implements ApplicationListener<GenericTestEvent<? extends RuntimeException>> {
static class UpperBoundEventListener
implements ApplicationListener<GenericTestEvent<? extends RuntimeException>> {
@Override
public void onApplicationEvent(GenericTestEvent<? extends RuntimeException> event) {
}
}
static class StringEventListener implements ApplicationListener<GenericTestEvent<String>> {
@Override
@@ -120,7 +113,6 @@ public abstract class AbstractApplicationEventListenerTests {
}
}
@SuppressWarnings("rawtypes")
static class RawApplicationListener implements ApplicationListener {
@@ -129,10 +121,10 @@ public abstract class AbstractApplicationEventListenerTests {
}
}
static class TestEvents {
public GenericTestEvent<?> wildcardEvent;
}
}
@@ -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.
@@ -104,8 +104,7 @@ public class GenericApplicationListenerAdapterTests extends AbstractApplicationE
@Test
public void genericListenerStrictTypeSubClass() {
supportsEventType(false, ObjectEventListener.class,
ResolvableType.forClassWithGenerics(GenericTestEvent.class, Long.class));
supportsEventType(false, ObjectEventListener.class, ResolvableType.forClassWithGenerics(GenericTestEvent.class, Long.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>
@@ -16,24 +16,12 @@
package org.springframework.cglib.beans;
import java.beans.PropertyDescriptor;
import java.lang.reflect.*;
import java.security.ProtectionDomain;
import java.util.HashMap;
import java.util.Map;
import org.springframework.cglib.core.*;
import org.springframework.asm.ClassVisitor;
import org.springframework.asm.Type;
import org.springframework.cglib.core.AbstractClassGenerator;
import org.springframework.cglib.core.ClassEmitter;
import org.springframework.cglib.core.CodeEmitter;
import org.springframework.cglib.core.Constants;
import org.springframework.cglib.core.Converter;
import org.springframework.cglib.core.EmitUtils;
import org.springframework.cglib.core.KeyFactory;
import org.springframework.cglib.core.Local;
import org.springframework.cglib.core.MethodInfo;
import org.springframework.cglib.core.ReflectUtils;
import org.springframework.cglib.core.Signature;
import org.springframework.cglib.core.TypeUtils;
import java.util.*;
/**
* @author Chris Nokleberg
@@ -41,154 +29,151 @@ import org.springframework.cglib.core.TypeUtils;
@SuppressWarnings({"rawtypes", "unchecked"})
abstract public class BeanCopier
{
private static final BeanCopierKey KEY_FACTORY =
(BeanCopierKey)KeyFactory.create(BeanCopierKey.class);
private static final Type CONVERTER =
TypeUtils.parseType("org.springframework.cglib.core.Converter");
private static final Type BEAN_COPIER =
TypeUtils.parseType("org.springframework.cglib.beans.BeanCopier");
private static final Signature COPY =
new Signature("copy", Type.VOID_TYPE, new Type[]{ Constants.TYPE_OBJECT, Constants.TYPE_OBJECT, CONVERTER });
private static final Signature CONVERT =
TypeUtils.parseSignature("Object convert(Object, Class, Object)");
private static final BeanCopierKey KEY_FACTORY =
(BeanCopierKey)KeyFactory.create(BeanCopierKey.class);
private static final Type CONVERTER =
TypeUtils.parseType("org.springframework.cglib.core.Converter");
private static final Type BEAN_COPIER =
TypeUtils.parseType("org.springframework.cglib.beans.BeanCopier");
private static final Signature COPY =
new Signature("copy", Type.VOID_TYPE, new Type[]{ Constants.TYPE_OBJECT, Constants.TYPE_OBJECT, CONVERTER });
private static final Signature CONVERT =
TypeUtils.parseSignature("Object convert(Object, Class, Object)");
interface BeanCopierKey {
public Object newInstance(String source, String target, boolean useConverter);
}
interface BeanCopierKey {
public Object newInstance(String source, String target, boolean useConverter);
}
public static BeanCopier create(Class source, Class target, boolean useConverter) {
Generator gen = new Generator();
gen.setSource(source);
gen.setTarget(target);
gen.setUseConverter(useConverter);
return gen.create();
}
public static BeanCopier create(Class source, Class target, boolean useConverter) {
Generator gen = new Generator();
gen.setSource(source);
gen.setTarget(target);
gen.setUseConverter(useConverter);
return gen.create();
}
abstract public void copy(Object from, Object to, Converter converter);
abstract public void copy(Object from, Object to, Converter converter);
public static class Generator extends AbstractClassGenerator {
private static final Source SOURCE = new Source(BeanCopier.class.getName());
private Class source;
private Class target;
private boolean useConverter;
public static class Generator extends AbstractClassGenerator {
private static final Source SOURCE = new Source(BeanCopier.class.getName());
private Class source;
private Class target;
private boolean useConverter;
public Generator() {
super(SOURCE);
}
public Generator() {
super(SOURCE);
}
public void setSource(Class source) {
this.source = source;
// SPRING PATCH BEGIN
setContextClass(source);
setNamePrefix(source.getName());
// SPRING PATCH END
}
public void setTarget(Class target) {
this.target = target;
public void setSource(Class source) {
if(!Modifier.isPublic(source.getModifiers())){
setNamePrefix(source.getName());
}
this.source = source;
}
public void setTarget(Class target) {
if(!Modifier.isPublic(target.getModifiers())){
setNamePrefix(target.getName());
}
this.target = target;
// SPRING PATCH BEGIN
setContextClass(target);
setNamePrefix(target.getName());
// SPRING PATCH END
}
}
public void setUseConverter(boolean useConverter) {
this.useConverter = useConverter;
}
public void setUseConverter(boolean useConverter) {
this.useConverter = useConverter;
}
@Override
protected ClassLoader getDefaultClassLoader() {
return source.getClassLoader();
}
protected ClassLoader getDefaultClassLoader() {
return source.getClassLoader();
}
@Override
protected ProtectionDomain getProtectionDomain() {
return ReflectUtils.getProtectionDomain(source);
}
protected ProtectionDomain getProtectionDomain() {
return ReflectUtils.getProtectionDomain(source);
}
public BeanCopier create() {
Object key = KEY_FACTORY.newInstance(source.getName(), target.getName(), useConverter);
return (BeanCopier)super.create(key);
}
public BeanCopier create() {
Object key = KEY_FACTORY.newInstance(source.getName(), target.getName(), useConverter);
return (BeanCopier)super.create(key);
}
@Override
public void generateClass(ClassVisitor v) {
Type sourceType = Type.getType(source);
Type targetType = Type.getType(target);
ClassEmitter ce = new ClassEmitter(v);
ce.begin_class(Constants.V1_8,
Constants.ACC_PUBLIC,
getClassName(),
BEAN_COPIER,
null,
Constants.SOURCE_FILE);
public void generateClass(ClassVisitor v) {
Type sourceType = Type.getType(source);
Type targetType = Type.getType(target);
ClassEmitter ce = new ClassEmitter(v);
ce.begin_class(Constants.V1_8,
Constants.ACC_PUBLIC,
getClassName(),
BEAN_COPIER,
null,
Constants.SOURCE_FILE);
EmitUtils.null_constructor(ce);
CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, COPY, null);
PropertyDescriptor[] getters = ReflectUtils.getBeanGetters(source);
PropertyDescriptor[] setters = ReflectUtils.getBeanSetters(target);
EmitUtils.null_constructor(ce);
CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, COPY, null);
PropertyDescriptor[] getters = ReflectUtils.getBeanGetters(source);
PropertyDescriptor[] setters = ReflectUtils.getBeanSetters(target);
Map names = new HashMap();
for (PropertyDescriptor getter : getters) {
names.put(getter.getName(), getter);
}
Local targetLocal = e.make_local();
Local sourceLocal = e.make_local();
if (useConverter) {
e.load_arg(1);
e.checkcast(targetType);
e.store_local(targetLocal);
e.load_arg(0);
e.checkcast(sourceType);
e.store_local(sourceLocal);
} else {
e.load_arg(1);
e.checkcast(targetType);
e.load_arg(0);
e.checkcast(sourceType);
}
for (PropertyDescriptor setter : setters) {
PropertyDescriptor getter = (PropertyDescriptor)names.get(setter.getName());
if (getter != null) {
MethodInfo read = ReflectUtils.getMethodInfo(getter.getReadMethod());
MethodInfo write = ReflectUtils.getMethodInfo(setter.getWriteMethod());
if (useConverter) {
Type setterType = write.getSignature().getArgumentTypes()[0];
e.load_local(targetLocal);
e.load_arg(2);
e.load_local(sourceLocal);
e.invoke(read);
e.box(read.getSignature().getReturnType());
EmitUtils.load_class(e, setterType);
e.push(write.getSignature().getName());
e.invoke_interface(CONVERTER, CONVERT);
e.unbox_or_zero(setterType);
e.invoke(write);
} else if (compatible(getter, setter)) {
e.dup2();
e.invoke(read);
e.invoke(write);
}
}
}
e.return_value();
e.end_method();
ce.end_class();
}
Map names = new HashMap();
for (int i = 0; i < getters.length; i++) {
names.put(getters[i].getName(), getters[i]);
}
Local targetLocal = e.make_local();
Local sourceLocal = e.make_local();
if (useConverter) {
e.load_arg(1);
e.checkcast(targetType);
e.store_local(targetLocal);
e.load_arg(0);
e.checkcast(sourceType);
e.store_local(sourceLocal);
} else {
e.load_arg(1);
e.checkcast(targetType);
e.load_arg(0);
e.checkcast(sourceType);
}
for (int i = 0; i < setters.length; i++) {
PropertyDescriptor setter = setters[i];
PropertyDescriptor getter = (PropertyDescriptor)names.get(setter.getName());
if (getter != null) {
MethodInfo read = ReflectUtils.getMethodInfo(getter.getReadMethod());
MethodInfo write = ReflectUtils.getMethodInfo(setter.getWriteMethod());
if (useConverter) {
Type setterType = write.getSignature().getArgumentTypes()[0];
e.load_local(targetLocal);
e.load_arg(2);
e.load_local(sourceLocal);
e.invoke(read);
e.box(read.getSignature().getReturnType());
EmitUtils.load_class(e, setterType);
e.push(write.getSignature().getName());
e.invoke_interface(CONVERTER, CONVERT);
e.unbox_or_zero(setterType);
e.invoke(write);
} else if (compatible(getter, setter)) {
e.dup2();
e.invoke(read);
e.invoke(write);
}
}
}
e.return_value();
e.end_method();
ce.end_class();
}
private static boolean compatible(PropertyDescriptor getter, PropertyDescriptor setter) {
// TODO: allow automatic widening conversions?
return setter.getPropertyType().isAssignableFrom(getter.getPropertyType());
}
private static boolean compatible(PropertyDescriptor getter, PropertyDescriptor setter) {
// TODO: allow automatic widening conversions?
return setter.getPropertyType().isAssignableFrom(getter.getPropertyType());
}
@Override
protected Object firstInstance(Class type) {
return ReflectUtils.newInstance(type);
}
protected Object firstInstance(Class type) {
return ReflectUtils.newInstance(type);
}
@Override
protected Object nextInstance(Object instance) {
return instance;
}
}
protected Object nextInstance(Object instance) {
return instance;
}
}
}
@@ -17,18 +17,10 @@ package org.springframework.cglib.beans;
import java.beans.PropertyDescriptor;
import java.security.ProtectionDomain;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.*;
import org.springframework.cglib.core.*;
import org.springframework.asm.ClassVisitor;
import org.springframework.asm.Type;
import org.springframework.cglib.core.AbstractClassGenerator;
import org.springframework.cglib.core.ClassEmitter;
import org.springframework.cglib.core.Constants;
import org.springframework.cglib.core.EmitUtils;
import org.springframework.cglib.core.KeyFactory;
import org.springframework.cglib.core.ReflectUtils;
/**
* @author Juozas Baliuka, Chris Nokleberg
@@ -36,131 +28,126 @@ import org.springframework.cglib.core.ReflectUtils;
@SuppressWarnings({"rawtypes", "unchecked"})
public class BeanGenerator extends AbstractClassGenerator
{
private static final Source SOURCE = new Source(BeanGenerator.class.getName());
private static final BeanGeneratorKey KEY_FACTORY =
(BeanGeneratorKey)KeyFactory.create(BeanGeneratorKey.class);
private static final Source SOURCE = new Source(BeanGenerator.class.getName());
private static final BeanGeneratorKey KEY_FACTORY =
(BeanGeneratorKey)KeyFactory.create(BeanGeneratorKey.class);
interface BeanGeneratorKey {
public Object newInstance(String superclass, Map props);
}
interface BeanGeneratorKey {
public Object newInstance(String superclass, Map props);
}
private Class superclass;
private Map props = new HashMap();
private boolean classOnly;
private Class superclass;
private Map props = new HashMap();
private boolean classOnly;
public BeanGenerator() {
super(SOURCE);
}
public BeanGenerator() {
super(SOURCE);
}
/**
* Set the class which the generated class will extend. The class
* must not be declared as final, and must have a non-private
* no-argument constructor.
* @param superclass class to extend, or null to extend Object
*/
public void setSuperclass(Class superclass) {
if (superclass != null && superclass.equals(Object.class)) {
superclass = null;
}
this.superclass = superclass;
/**
* Set the class which the generated class will extend. The class
* must not be declared as final, and must have a non-private
* no-argument constructor.
* @param superclass class to extend, or null to extend Object
*/
public void setSuperclass(Class superclass) {
if (superclass != null && superclass.equals(Object.class)) {
superclass = null;
}
this.superclass = superclass;
// SPRING PATCH BEGIN
setContextClass(superclass);
// SPRING PATCH END
}
}
public void addProperty(String name, Class type) {
if (props.containsKey(name)) {
throw new IllegalArgumentException("Duplicate property name \"" + name + "\"");
}
props.put(name, Type.getType(type));
}
public void addProperty(String name, Class type) {
if (props.containsKey(name)) {
throw new IllegalArgumentException("Duplicate property name \"" + name + "\"");
}
props.put(name, Type.getType(type));
}
@Override
protected ClassLoader getDefaultClassLoader() {
if (superclass != null) {
return superclass.getClassLoader();
} else {
return null;
}
}
protected ClassLoader getDefaultClassLoader() {
if (superclass != null) {
return superclass.getClassLoader();
} else {
return null;
}
}
@Override
protected ProtectionDomain getProtectionDomain() {
return ReflectUtils.getProtectionDomain(superclass);
}
protected ProtectionDomain getProtectionDomain() {
return ReflectUtils.getProtectionDomain(superclass);
}
public Object create() {
classOnly = false;
return createHelper();
}
public Object create() {
classOnly = false;
return createHelper();
}
public Object createClass() {
classOnly = true;
return createHelper();
}
public Object createClass() {
classOnly = true;
return createHelper();
}
private Object createHelper() {
if (superclass != null) {
setNamePrefix(superclass.getName());
}
String superName = (superclass != null) ? superclass.getName() : "java.lang.Object";
Object key = KEY_FACTORY.newInstance(superName, props);
return super.create(key);
}
private Object createHelper() {
if (superclass != null) {
setNamePrefix(superclass.getName());
}
String superName = (superclass != null) ? superclass.getName() : "java.lang.Object";
Object key = KEY_FACTORY.newInstance(superName, props);
return super.create(key);
}
@Override
public void generateClass(ClassVisitor v) throws Exception {
int size = props.size();
String[] names = (String[])props.keySet().toArray(new String[size]);
Type[] types = new Type[size];
for (int i = 0; i < size; i++) {
types[i] = (Type)props.get(names[i]);
}
ClassEmitter ce = new ClassEmitter(v);
ce.begin_class(Constants.V1_8,
Constants.ACC_PUBLIC,
getClassName(),
superclass != null ? Type.getType(superclass) : Constants.TYPE_OBJECT,
null,
null);
EmitUtils.null_constructor(ce);
EmitUtils.add_properties(ce, names, types);
ce.end_class();
}
public void generateClass(ClassVisitor v) throws Exception {
int size = props.size();
String[] names = (String[])props.keySet().toArray(new String[size]);
Type[] types = new Type[size];
for (int i = 0; i < size; i++) {
types[i] = (Type)props.get(names[i]);
}
ClassEmitter ce = new ClassEmitter(v);
ce.begin_class(Constants.V1_8,
Constants.ACC_PUBLIC,
getClassName(),
superclass != null ? Type.getType(superclass) : Constants.TYPE_OBJECT,
null,
null);
EmitUtils.null_constructor(ce);
EmitUtils.add_properties(ce, names, types);
ce.end_class();
}
@Override
protected Object firstInstance(Class type) {
if (classOnly) {
return type;
} else {
return ReflectUtils.newInstance(type);
}
}
protected Object firstInstance(Class type) {
if (classOnly) {
return type;
} else {
return ReflectUtils.newInstance(type);
}
}
@Override
protected Object nextInstance(Object instance) {
Class protoclass = (instance instanceof Class) ? (Class)instance : instance.getClass();
if (classOnly) {
return protoclass;
} else {
return ReflectUtils.newInstance(protoclass);
}
}
protected Object nextInstance(Object instance) {
Class protoclass = (instance instanceof Class) ? (Class)instance : instance.getClass();
if (classOnly) {
return protoclass;
} else {
return ReflectUtils.newInstance(protoclass);
}
}
public static void addProperties(BeanGenerator gen, Map props) {
for (Iterator it = props.keySet().iterator(); it.hasNext();) {
String name = (String)it.next();
gen.addProperty(name, (Class)props.get(name));
}
}
public static void addProperties(BeanGenerator gen, Map props) {
for (Iterator it = props.keySet().iterator(); it.hasNext();) {
String name = (String)it.next();
gen.addProperty(name, (Class)props.get(name));
}
}
public static void addProperties(BeanGenerator gen, Class type) {
addProperties(gen, ReflectUtils.getBeanProperties(type));
}
public static void addProperties(BeanGenerator gen, Class type) {
addProperties(gen, ReflectUtils.getBeanProperties(type));
}
public static void addProperties(BeanGenerator gen, PropertyDescriptor[] descriptors) {
for (PropertyDescriptor descriptor : descriptors) {
gen.addProperty(descriptor.getName(), descriptor.getPropertyType());
}
}
public static void addProperties(BeanGenerator gen, PropertyDescriptor[] descriptors) {
for (int i = 0; i < descriptors.length; i++) {
gen.addProperty(descriptors[i].getName(), descriptors[i].getPropertyType());
}
}
}
@@ -41,310 +41,293 @@ import org.springframework.cglib.core.ReflectUtils;
*/
@SuppressWarnings({"rawtypes", "unchecked"})
abstract public class BeanMap implements Map {
/**
* Limit the properties reflected in the key set of the map
* to readable properties.
* @see BeanMap.Generator#setRequire
*/
public static final int REQUIRE_GETTER = 1;
/**
* Limit the properties reflected in the key set of the map
* to readable properties.
* @see BeanMap.Generator#setRequire
*/
public static final int REQUIRE_GETTER = 1;
/**
* Limit the properties reflected in the key set of the map
* to writable properties.
* @see BeanMap.Generator#setRequire
*/
public static final int REQUIRE_SETTER = 2;
/**
* Limit the properties reflected in the key set of the map
* to writable properties.
* @see BeanMap.Generator#setRequire
*/
public static final int REQUIRE_SETTER = 2;
/**
* Helper method to create a new <code>BeanMap</code>. For finer
* control over the generated instance, use a new instance of
* <code>BeanMap.Generator</code> instead of this static method.
* @param bean the JavaBean underlying the map
* @return a new <code>BeanMap</code> instance
*/
public static BeanMap create(Object bean) {
Generator gen = new Generator();
gen.setBean(bean);
return gen.create();
}
/**
* Helper method to create a new <code>BeanMap</code>. For finer
* control over the generated instance, use a new instance of
* <code>BeanMap.Generator</code> instead of this static method.
* @param bean the JavaBean underlying the map
* @return a new <code>BeanMap</code> instance
*/
public static BeanMap create(Object bean) {
Generator gen = new Generator();
gen.setBean(bean);
return gen.create();
}
public static class Generator extends AbstractClassGenerator {
private static final Source SOURCE = new Source(BeanMap.class.getName());
public static class Generator extends AbstractClassGenerator {
private static final Source SOURCE = new Source(BeanMap.class.getName());
private static final BeanMapKey KEY_FACTORY =
(BeanMapKey)KeyFactory.create(BeanMapKey.class, KeyFactory.CLASS_BY_NAME);
private static final BeanMapKey KEY_FACTORY =
(BeanMapKey)KeyFactory.create(BeanMapKey.class, KeyFactory.CLASS_BY_NAME);
interface BeanMapKey {
public Object newInstance(Class type, int require);
}
private Object bean;
private Class beanClass;
private int require;
public Generator() {
super(SOURCE);
}
interface BeanMapKey {
public Object newInstance(Class type, int require);
}
private Object bean;
private Class beanClass;
private int require;
public Generator() {
super(SOURCE);
}
/**
* Set the bean that the generated map should reflect. The bean may be swapped
* out for another bean of the same type using {@link #setBean}.
* Calling this method overrides any value previously set using {@link #setBeanClass}.
* You must call either this method or {@link #setBeanClass} before {@link #create}.
* @param bean the initial bean
*/
public void setBean(Object bean) {
this.bean = bean;
if (bean != null) {
/**
* Set the bean that the generated map should reflect. The bean may be swapped
* out for another bean of the same type using {@link #setBean}.
* Calling this method overrides any value previously set using {@link #setBeanClass}.
* You must call either this method or {@link #setBeanClass} before {@link #create}.
* @param bean the initial bean
*/
public void setBean(Object bean) {
this.bean = bean;
if (bean != null) {
beanClass = bean.getClass();
// SPRING PATCH BEGIN
setContextClass(beanClass);
// SPRING PATCH END
}
}
}
/**
* Set the class of the bean that the generated map should support.
* You must call either this method or {@link #setBeanClass} before {@link #create}.
* @param beanClass the class of the bean
*/
public void setBeanClass(Class beanClass) {
this.beanClass = beanClass;
}
/**
* Set the class of the bean that the generated map should support.
* You must call either this method or {@link #setBeanClass} before {@link #create}.
* @param beanClass the class of the bean
*/
public void setBeanClass(Class beanClass) {
this.beanClass = beanClass;
}
/**
* Limit the properties reflected by the generated map.
* @param require any combination of {@link #REQUIRE_GETTER} and
* {@link #REQUIRE_SETTER}; default is zero (any property allowed)
*/
public void setRequire(int require) {
this.require = require;
}
/**
* Limit the properties reflected by the generated map.
* @param require any combination of {@link #REQUIRE_GETTER} and
* {@link #REQUIRE_SETTER}; default is zero (any property allowed)
*/
public void setRequire(int require) {
this.require = require;
}
@Override
protected ClassLoader getDefaultClassLoader() {
return beanClass.getClassLoader();
}
protected ClassLoader getDefaultClassLoader() {
return beanClass.getClassLoader();
}
@Override
protected ProtectionDomain getProtectionDomain() {
return ReflectUtils.getProtectionDomain(beanClass);
}
protected ProtectionDomain getProtectionDomain() {
return ReflectUtils.getProtectionDomain(beanClass);
}
/**
* Create a new instance of the <code>BeanMap</code>. An existing
* generated class will be reused if possible.
*/
public BeanMap create() {
if (beanClass == null) {
throw new IllegalArgumentException("Class of bean unknown");
}
setNamePrefix(beanClass.getName());
return (BeanMap)super.create(KEY_FACTORY.newInstance(beanClass, require));
}
/**
* Create a new instance of the <code>BeanMap</code>. An existing
* generated class will be reused if possible.
*/
public BeanMap create() {
if (beanClass == null)
throw new IllegalArgumentException("Class of bean unknown");
setNamePrefix(beanClass.getName());
return (BeanMap)super.create(KEY_FACTORY.newInstance(beanClass, require));
}
@Override
public void generateClass(ClassVisitor v) throws Exception {
new BeanMapEmitter(v, getClassName(), beanClass, require);
}
public void generateClass(ClassVisitor v) throws Exception {
new BeanMapEmitter(v, getClassName(), beanClass, require);
}
@Override
protected Object firstInstance(Class type) {
return ((BeanMap)ReflectUtils.newInstance(type)).newInstance(bean);
}
protected Object firstInstance(Class type) {
return ((BeanMap)ReflectUtils.newInstance(type)).newInstance(bean);
}
@Override
protected Object nextInstance(Object instance) {
return ((BeanMap)instance).newInstance(bean);
}
}
protected Object nextInstance(Object instance) {
return ((BeanMap)instance).newInstance(bean);
}
}
/**
* Create a new <code>BeanMap</code> instance using the specified bean.
* This is faster than using the {@link #create} static method.
* @param bean the JavaBean underlying the map
* @return a new <code>BeanMap</code> instance
*/
abstract public BeanMap newInstance(Object bean);
/**
* Create a new <code>BeanMap</code> instance using the specified bean.
* This is faster than using the {@link #create} static method.
* @param bean the JavaBean underlying the map
* @return a new <code>BeanMap</code> instance
*/
abstract public BeanMap newInstance(Object bean);
/**
* Get the type of a property.
* @param name the name of the JavaBean property
* @return the type of the property, or null if the property does not exist
*/
abstract public Class getPropertyType(String name);
/**
* Get the type of a property.
* @param name the name of the JavaBean property
* @return the type of the property, or null if the property does not exist
*/
abstract public Class getPropertyType(String name);
protected Object bean;
protected Object bean;
protected BeanMap() {
}
protected BeanMap() {
}
protected BeanMap(Object bean) {
setBean(bean);
}
protected BeanMap(Object bean) {
setBean(bean);
}
@Override
public Object get(Object key) {
return get(bean, key);
}
public Object get(Object key) {
return get(bean, key);
}
@Override
public Object put(Object key, Object value) {
return put(bean, key, value);
}
public Object put(Object key, Object value) {
return put(bean, key, value);
}
/**
* Get the property of a bean. This allows a <code>BeanMap</code>
* to be used statically for multiple beans--the bean instance tied to the
* map is ignored and the bean passed to this method is used instead.
* @param bean the bean to query; must be compatible with the type of
* this <code>BeanMap</code>
* @param key must be a String
* @return the current value, or null if there is no matching property
*/
abstract public Object get(Object bean, Object key);
/**
* Get the property of a bean. This allows a <code>BeanMap</code>
* to be used statically for multiple beans--the bean instance tied to the
* map is ignored and the bean passed to this method is used instead.
* @param bean the bean to query; must be compatible with the type of
* this <code>BeanMap</code>
* @param key must be a String
* @return the current value, or null if there is no matching property
*/
abstract public Object get(Object bean, Object key);
/**
* Set the property of a bean. This allows a <code>BeanMap</code>
* to be used statically for multiple beans--the bean instance tied to the
* map is ignored and the bean passed to this method is used instead.
* @param key must be a String
* @return the old value, if there was one, or null
*/
abstract public Object put(Object bean, Object key, Object value);
/**
* Set the property of a bean. This allows a <code>BeanMap</code>
* to be used statically for multiple beans--the bean instance tied to the
* map is ignored and the bean passed to this method is used instead.
* @param key must be a String
* @return the old value, if there was one, or null
*/
abstract public Object put(Object bean, Object key, Object value);
/**
* Change the underlying bean this map should use.
* @param bean the new JavaBean
* @see #getBean
*/
public void setBean(Object bean) {
this.bean = bean;
}
/**
* Change the underlying bean this map should use.
* @param bean the new JavaBean
* @see #getBean
*/
public void setBean(Object bean) {
this.bean = bean;
}
/**
* Return the bean currently in use by this map.
* @return the current JavaBean
* @see #setBean
*/
public Object getBean() {
return bean;
}
/**
* Return the bean currently in use by this map.
* @return the current JavaBean
* @see #setBean
*/
public Object getBean() {
return bean;
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
public void clear() {
throw new UnsupportedOperationException();
}
@Override
public boolean containsKey(Object key) {
return keySet().contains(key);
}
public boolean containsKey(Object key) {
return keySet().contains(key);
}
@Override
public boolean containsValue(Object value) {
for (Iterator it = keySet().iterator(); it.hasNext();) {
Object v = get(it.next());
if (((value == null) && (v == null)) || (value != null && value.equals(v))) {
return true;
}
}
return false;
}
public boolean containsValue(Object value) {
for (Iterator it = keySet().iterator(); it.hasNext();) {
Object v = get(it.next());
if (((value == null) && (v == null)) || (value != null && value.equals(v)))
return true;
}
return false;
}
@Override
public int size() {
return keySet().size();
}
public int size() {
return keySet().size();
}
@Override
public boolean isEmpty() {
return size() == 0;
}
public boolean isEmpty() {
return size() == 0;
}
@Override
public Object remove(Object key) {
throw new UnsupportedOperationException();
}
public Object remove(Object key) {
throw new UnsupportedOperationException();
}
@Override
public void putAll(Map t) {
for (Object key : t.keySet()) {
put(key, t.get(key));
}
}
public void putAll(Map t) {
for (Iterator it = t.keySet().iterator(); it.hasNext();) {
Object key = it.next();
put(key, t.get(key));
}
}
@Override
public boolean equals(Object o) {
if (o == null || !(o instanceof Map)) {
return false;
}
Map other = (Map)o;
if (size() != other.size()) {
return false;
}
for (Object key : keySet()) {
if (!other.containsKey(key)) {
return false;
}
Object v1 = get(key);
Object v2 = other.get(key);
if (!((v1 == null) ? v2 == null : v1.equals(v2))) {
return false;
}
}
return true;
}
public boolean equals(Object o) {
if (o == null || !(o instanceof Map)) {
return false;
}
Map other = (Map)o;
if (size() != other.size()) {
return false;
}
for (Iterator it = keySet().iterator(); it.hasNext();) {
Object key = it.next();
if (!other.containsKey(key)) {
return false;
}
Object v1 = get(key);
Object v2 = other.get(key);
if (!((v1 == null) ? v2 == null : v1.equals(v2))) {
return false;
}
}
return true;
}
@Override
public int hashCode() {
int code = 0;
for (Object key : keySet()) {
Object value = get(key);
code += ((key == null) ? 0 : key.hashCode()) ^
((value == null) ? 0 : value.hashCode());
}
return code;
}
public int hashCode() {
int code = 0;
for (Iterator it = keySet().iterator(); it.hasNext();) {
Object key = it.next();
Object value = get(key);
code += ((key == null) ? 0 : key.hashCode()) ^
((value == null) ? 0 : value.hashCode());
}
return code;
}
// TODO: optimize
@Override
public Set entrySet() {
HashMap copy = new HashMap();
for (Object key : keySet()) {
copy.put(key, get(key));
}
return Collections.unmodifiableMap(copy).entrySet();
}
// TODO: optimize
public Set entrySet() {
HashMap copy = new HashMap();
for (Iterator it = keySet().iterator(); it.hasNext();) {
Object key = it.next();
copy.put(key, get(key));
}
return Collections.unmodifiableMap(copy).entrySet();
}
@Override
public Collection values() {
Set keys = keySet();
List values = new ArrayList(keys.size());
for (Iterator it = keys.iterator(); it.hasNext();) {
values.add(get(it.next()));
}
return Collections.unmodifiableCollection(values);
}
public Collection values() {
Set keys = keySet();
List values = new ArrayList(keys.size());
for (Iterator it = keys.iterator(); it.hasNext();) {
values.add(get(it.next()));
}
return Collections.unmodifiableCollection(values);
}
/*
* @see java.util.AbstractMap#toString
*/
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append('{');
for (Iterator it = keySet().iterator(); it.hasNext();) {
Object key = it.next();
sb.append(key);
sb.append('=');
sb.append(get(key));
if (it.hasNext()) {
sb.append(", ");
}
}
sb.append('}');
return sb.toString();
}
/*
* @see java.util.AbstractMap#toString
*/
public String toString()
{
StringBuffer sb = new StringBuffer();
sb.append('{');
for (Iterator it = keySet().iterator(); it.hasNext();) {
Object key = it.next();
sb.append(key);
sb.append('=');
sb.append(get(key));
if (it.hasNext()) {
sb.append(", ");
}
}
sb.append('}');
return sb.toString();
}
}
@@ -15,23 +15,12 @@
*/
package org.springframework.cglib.beans;
import java.beans.PropertyDescriptor;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.beans.*;
import java.util.*;
import org.springframework.cglib.core.*;
import org.springframework.asm.ClassVisitor;
import org.springframework.asm.Label;
import org.springframework.asm.Type;
import org.springframework.cglib.core.ClassEmitter;
import org.springframework.cglib.core.CodeEmitter;
import org.springframework.cglib.core.Constants;
import org.springframework.cglib.core.EmitUtils;
import org.springframework.cglib.core.MethodInfo;
import org.springframework.cglib.core.ObjectSwitchCallback;
import org.springframework.cglib.core.ReflectUtils;
import org.springframework.cglib.core.Signature;
import org.springframework.cglib.core.TypeUtils;
@SuppressWarnings({"rawtypes", "unchecked"})
class BeanMapEmitter extends ClassEmitter {
@@ -61,7 +50,7 @@ class BeanMapEmitter extends ClassEmitter {
EmitUtils.null_constructor(this);
EmitUtils.factory_method(this, NEW_INSTANCE);
generateConstructor();
Map getters = makePropertyMap(ReflectUtils.getBeanGetters(type));
Map setters = makePropertyMap(ReflectUtils.getBeanSetters(type));
Map allProps = new HashMap();
@@ -90,8 +79,8 @@ class BeanMapEmitter extends ClassEmitter {
private Map makePropertyMap(PropertyDescriptor[] props) {
Map names = new HashMap();
for (PropertyDescriptor prop : props) {
names.put(prop.getName(), prop);
for (int i = 0; i < props.length; i++) {
names.put(props[i].getName(), props[i]);
}
return names;
}
@@ -108,7 +97,7 @@ class BeanMapEmitter extends ClassEmitter {
e.return_value();
e.end_method();
}
private void generateGet(Class type, final Map getters) {
final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, BEAN_MAP_GET, null);
e.load_arg(0);
@@ -116,16 +105,14 @@ class BeanMapEmitter extends ClassEmitter {
e.load_arg(1);
e.checkcast(Constants.TYPE_STRING);
EmitUtils.string_switch(e, getNames(getters), Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() {
@Override
public void processCase(Object key, Label end) {
public void processCase(Object key, Label end) {
PropertyDescriptor pd = (PropertyDescriptor)getters.get(key);
MethodInfo method = ReflectUtils.getMethodInfo(pd.getReadMethod());
e.invoke(method);
e.box(method.getSignature().getReturnType());
e.return_value();
}
@Override
public void processDefault() {
public void processDefault() {
e.aconst_null();
e.return_value();
}
@@ -140,8 +127,7 @@ class BeanMapEmitter extends ClassEmitter {
e.load_arg(1);
e.checkcast(Constants.TYPE_STRING);
EmitUtils.string_switch(e, getNames(setters), Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() {
@Override
public void processCase(Object key, Label end) {
public void processCase(Object key, Label end) {
PropertyDescriptor pd = (PropertyDescriptor)setters.get(key);
if (pd.getReadMethod() == null) {
e.aconst_null();
@@ -158,8 +144,7 @@ class BeanMapEmitter extends ClassEmitter {
e.invoke(write);
e.return_value();
}
@Override
public void processDefault() {
public void processDefault() {
// fall-through
}
});
@@ -167,7 +152,7 @@ class BeanMapEmitter extends ClassEmitter {
e.return_value();
e.end_method();
}
private void generateKeySet(String[] allNames) {
// static initializer
declare_field(Constants.ACC_STATIC | Constants.ACC_PRIVATE, "keys", FIXED_KEY_SET, null);
@@ -193,14 +178,12 @@ class BeanMapEmitter extends ClassEmitter {
final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, GET_PROPERTY_TYPE, null);
e.load_arg(0);
EmitUtils.string_switch(e, allNames, Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() {
@Override
public void processCase(Object key, Label end) {
public void processCase(Object key, Label end) {
PropertyDescriptor pd = (PropertyDescriptor)allProps.get(key);
EmitUtils.load_class(e, Type.getType(pd.getPropertyType()));
e.return_value();
}
@Override
public void processDefault() {
public void processDefault() {
e.aconst_null();
e.return_value();
}
@@ -16,11 +16,8 @@
package org.springframework.cglib.beans;
import java.security.ProtectionDomain;
import org.springframework.cglib.core.*;
import org.springframework.asm.ClassVisitor;
import org.springframework.cglib.core.AbstractClassGenerator;
import org.springframework.cglib.core.KeyFactory;
import org.springframework.cglib.core.ReflectUtils;
/**
* @author Juozas Baliuka
@@ -28,123 +25,118 @@ import org.springframework.cglib.core.ReflectUtils;
@SuppressWarnings({"rawtypes", "unchecked"})
abstract public class BulkBean
{
private static final BulkBeanKey KEY_FACTORY =
(BulkBeanKey)KeyFactory.create(BulkBeanKey.class);
private static final BulkBeanKey KEY_FACTORY =
(BulkBeanKey)KeyFactory.create(BulkBeanKey.class);
interface BulkBeanKey {
public Object newInstance(String target, String[] getters, String[] setters, String[] types);
}
protected Class target;
protected String[] getters, setters;
protected Class[] types;
protected BulkBean() { }
abstract public void getPropertyValues(Object bean, Object[] values);
abstract public void setPropertyValues(Object bean, Object[] values);
interface BulkBeanKey {
public Object newInstance(String target, String[] getters, String[] setters, String[] types);
}
public Object[] getPropertyValues(Object bean) {
Object[] values = new Object[getters.length];
getPropertyValues(bean, values);
return values;
}
public Class[] getPropertyTypes() {
return types.clone();
}
public String[] getGetters() {
return getters.clone();
}
public String[] getSetters() {
return setters.clone();
}
protected Class target;
protected String[] getters, setters;
protected Class[] types;
public static BulkBean create(Class target, String[] getters, String[] setters, Class[] types) {
Generator gen = new Generator();
gen.setTarget(target);
gen.setGetters(getters);
gen.setSetters(setters);
gen.setTypes(types);
return gen.create();
}
protected BulkBean() { }
public static class Generator extends AbstractClassGenerator {
private static final Source SOURCE = new Source(BulkBean.class.getName());
private Class target;
private String[] getters;
private String[] setters;
private Class[] types;
abstract public void getPropertyValues(Object bean, Object[] values);
abstract public void setPropertyValues(Object bean, Object[] values);
public Generator() {
super(SOURCE);
}
public Object[] getPropertyValues(Object bean) {
Object[] values = new Object[getters.length];
getPropertyValues(bean, values);
return values;
}
public Class[] getPropertyTypes() {
return types.clone();
}
public String[] getGetters() {
return getters.clone();
}
public String[] getSetters() {
return setters.clone();
}
public static BulkBean create(Class target, String[] getters, String[] setters, Class[] types) {
Generator gen = new Generator();
gen.setTarget(target);
gen.setGetters(getters);
gen.setSetters(setters);
gen.setTypes(types);
return gen.create();
}
public static class Generator extends AbstractClassGenerator {
private static final Source SOURCE = new Source(BulkBean.class.getName());
private Class target;
private String[] getters;
private String[] setters;
private Class[] types;
public Generator() {
super(SOURCE);
}
public void setTarget(Class target) {
this.target = target;
public void setTarget(Class target) {
this.target = target;
// SPRING PATCH BEGIN
setContextClass(target);
// SPRING PATCH END
}
}
public void setGetters(String[] getters) {
this.getters = getters;
}
public void setGetters(String[] getters) {
this.getters = getters;
}
public void setSetters(String[] setters) {
this.setters = setters;
}
public void setSetters(String[] setters) {
this.setters = setters;
}
public void setTypes(Class[] types) {
this.types = types;
}
public void setTypes(Class[] types) {
this.types = types;
}
@Override
protected ClassLoader getDefaultClassLoader() {
return target.getClassLoader();
}
protected ClassLoader getDefaultClassLoader() {
return target.getClassLoader();
}
@Override
protected ProtectionDomain getProtectionDomain() {
return ReflectUtils.getProtectionDomain(target);
}
protected ProtectionDomain getProtectionDomain() {
return ReflectUtils.getProtectionDomain(target);
}
public BulkBean create() {
setNamePrefix(target.getName());
String targetClassName = target.getName();
String[] typeClassNames = ReflectUtils.getNames(types);
Object key = KEY_FACTORY.newInstance(targetClassName, getters, setters, typeClassNames);
return (BulkBean)super.create(key);
}
public BulkBean create() {
setNamePrefix(target.getName());
String targetClassName = target.getName();
String[] typeClassNames = ReflectUtils.getNames(types);
Object key = KEY_FACTORY.newInstance(targetClassName, getters, setters, typeClassNames);
return (BulkBean)super.create(key);
}
@Override
public void generateClass(ClassVisitor v) throws Exception {
new BulkBeanEmitter(v, getClassName(), target, getters, setters, types);
}
public void generateClass(ClassVisitor v) throws Exception {
new BulkBeanEmitter(v, getClassName(), target, getters, setters, types);
}
@Override
protected Object firstInstance(Class type) {
BulkBean instance = (BulkBean)ReflectUtils.newInstance(type);
instance.target = target;
protected Object firstInstance(Class type) {
BulkBean instance = (BulkBean)ReflectUtils.newInstance(type);
instance.target = target;
int length = getters.length;
instance.getters = new String[length];
System.arraycopy(getters, 0, instance.getters, 0, length);
instance.setters = new String[length];
System.arraycopy(setters, 0, instance.setters, 0, length);
instance.types = new Class[types.length];
System.arraycopy(types, 0, instance.types, 0, types.length);
int length = getters.length;
instance.getters = new String[length];
System.arraycopy(getters, 0, instance.getters, 0, length);
return instance;
}
instance.setters = new String[length];
System.arraycopy(setters, 0, instance.setters, 0, length);
instance.types = new Class[types.length];
System.arraycopy(types, 0, instance.types, 0, types.length);
return instance;
}
@Override
protected Object nextInstance(Object instance) {
return instance;
}
}
protected Object nextInstance(Object instance) {
return instance;
}
}
}
@@ -17,19 +17,9 @@ package org.springframework.cglib.beans;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import org.springframework.cglib.core.*;
import org.springframework.asm.ClassVisitor;
import org.springframework.asm.Type;
import org.springframework.cglib.core.Block;
import org.springframework.cglib.core.ClassEmitter;
import org.springframework.cglib.core.CodeEmitter;
import org.springframework.cglib.core.Constants;
import org.springframework.cglib.core.EmitUtils;
import org.springframework.cglib.core.Local;
import org.springframework.cglib.core.MethodInfo;
import org.springframework.cglib.core.ReflectUtils;
import org.springframework.cglib.core.Signature;
import org.springframework.cglib.core.TypeUtils;
@SuppressWarnings({"rawtypes", "unchecked"})
class BulkBeanEmitter extends ClassEmitter {
@@ -43,7 +33,7 @@ class BulkBeanEmitter extends ClassEmitter {
TypeUtils.parseType("org.springframework.cglib.beans.BulkBean");
private static final Type BULK_BEAN_EXCEPTION =
TypeUtils.parseType("org.springframework.cglib.beans.BulkBeanException");
public BulkBeanEmitter(ClassVisitor v,
String className,
Class target,
@@ -126,7 +116,7 @@ class BulkBeanEmitter extends ClassEmitter {
}
e.end_method();
}
private static void validate(Class target,
String[] getters,
String[] setters,
@@ -20,7 +20,7 @@ public class BulkBeanException extends RuntimeException
{
private int index;
private Throwable cause;
public BulkBeanException(String message, int index) {
super(message);
this.index = index;
@@ -35,9 +35,8 @@ public class BulkBeanException extends RuntimeException
public int getIndex() {
return index;
}
@Override
public Throwable getCause() {
public Throwable getCause() {
return cause;
}
}
@@ -15,12 +15,7 @@
*/
package org.springframework.cglib.beans;
import java.util.AbstractSet;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.*;
@SuppressWarnings({"rawtypes", "unchecked"})
public /* need it for class loading */ class FixedKeySet extends AbstractSet {
@@ -32,13 +27,11 @@ public /* need it for class loading */ class FixedKeySet extends AbstractSet {
set = Collections.unmodifiableSet(new HashSet(Arrays.asList(keys)));
}
@Override
public Iterator iterator() {
public Iterator iterator() {
return set.iterator();
}
@Override
public int size() {
public int size() {
return size;
}
}
@@ -18,129 +18,115 @@ package org.springframework.cglib.beans;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.security.ProtectionDomain;
import org.springframework.cglib.core.*;
import org.springframework.asm.ClassVisitor;
import org.springframework.asm.Type;
import org.springframework.cglib.core.AbstractClassGenerator;
import org.springframework.cglib.core.ClassEmitter;
import org.springframework.cglib.core.CodeEmitter;
import org.springframework.cglib.core.Constants;
import org.springframework.cglib.core.EmitUtils;
import org.springframework.cglib.core.MethodInfo;
import org.springframework.cglib.core.ReflectUtils;
import org.springframework.cglib.core.Signature;
import org.springframework.cglib.core.TypeUtils;
/**
* @author Chris Nokleberg
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public class ImmutableBean
{
private static final Type ILLEGAL_STATE_EXCEPTION =
TypeUtils.parseType("IllegalStateException");
private static final Signature CSTRUCT_OBJECT =
TypeUtils.parseConstructor("Object");
private static final Class[] OBJECT_CLASSES = { Object.class };
private static final String FIELD_NAME = "CGLIB$RWBean";
private static final Type ILLEGAL_STATE_EXCEPTION =
TypeUtils.parseType("IllegalStateException");
private static final Signature CSTRUCT_OBJECT =
TypeUtils.parseConstructor("Object");
private static final Class[] OBJECT_CLASSES = { Object.class };
private static final String FIELD_NAME = "CGLIB$RWBean";
private ImmutableBean() {
}
private ImmutableBean() {
}
public static Object create(Object bean) {
Generator gen = new Generator();
gen.setBean(bean);
return gen.create();
}
public static Object create(Object bean) {
Generator gen = new Generator();
gen.setBean(bean);
return gen.create();
}
public static class Generator extends AbstractClassGenerator {
private static final Source SOURCE = new Source(ImmutableBean.class.getName());
private Object bean;
private Class target;
public static class Generator extends AbstractClassGenerator {
private static final Source SOURCE = new Source(ImmutableBean.class.getName());
private Object bean;
private Class target;
public Generator() {
super(SOURCE);
}
public Generator() {
super(SOURCE);
}
public void setBean(Object bean) {
this.bean = bean;
target = bean.getClass();
public void setBean(Object bean) {
this.bean = bean;
target = bean.getClass();
// SPRING PATCH BEGIN
setContextClass(target);
// SPRING PATCH END
}
}
@Override
protected ClassLoader getDefaultClassLoader() {
return target.getClassLoader();
}
protected ClassLoader getDefaultClassLoader() {
return target.getClassLoader();
}
@Override
protected ProtectionDomain getProtectionDomain() {
return ReflectUtils.getProtectionDomain(target);
}
protected ProtectionDomain getProtectionDomain() {
return ReflectUtils.getProtectionDomain(target);
}
public Object create() {
String name = target.getName();
setNamePrefix(name);
return super.create(name);
}
public Object create() {
String name = target.getName();
setNamePrefix(name);
return super.create(name);
}
@Override
public void generateClass(ClassVisitor v) {
Type targetType = Type.getType(target);
ClassEmitter ce = new ClassEmitter(v);
ce.begin_class(Constants.V1_8,
Constants.ACC_PUBLIC,
getClassName(),
targetType,
null,
Constants.SOURCE_FILE);
public void generateClass(ClassVisitor v) {
Type targetType = Type.getType(target);
ClassEmitter ce = new ClassEmitter(v);
ce.begin_class(Constants.V1_8,
Constants.ACC_PUBLIC,
getClassName(),
targetType,
null,
Constants.SOURCE_FILE);
ce.declare_field(Constants.ACC_FINAL | Constants.ACC_PRIVATE, FIELD_NAME, targetType, null);
ce.declare_field(Constants.ACC_FINAL | Constants.ACC_PRIVATE, FIELD_NAME, targetType, null);
CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, CSTRUCT_OBJECT, null);
e.load_this();
e.super_invoke_constructor();
e.load_this();
e.load_arg(0);
e.checkcast(targetType);
e.putfield(FIELD_NAME);
e.return_value();
e.end_method();
CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, CSTRUCT_OBJECT, null);
e.load_this();
e.super_invoke_constructor();
e.load_this();
e.load_arg(0);
e.checkcast(targetType);
e.putfield(FIELD_NAME);
e.return_value();
e.end_method();
PropertyDescriptor[] descriptors = ReflectUtils.getBeanProperties(target);
Method[] getters = ReflectUtils.getPropertyMethods(descriptors, true, false);
Method[] setters = ReflectUtils.getPropertyMethods(descriptors, false, true);
PropertyDescriptor[] descriptors = ReflectUtils.getBeanProperties(target);
Method[] getters = ReflectUtils.getPropertyMethods(descriptors, true, false);
Method[] setters = ReflectUtils.getPropertyMethods(descriptors, false, true);
for (Method getter2 : getters) {
MethodInfo getter = ReflectUtils.getMethodInfo(getter2);
e = EmitUtils.begin_method(ce, getter, Constants.ACC_PUBLIC);
e.load_this();
e.getfield(FIELD_NAME);
e.invoke(getter);
e.return_value();
e.end_method();
}
for (int i = 0; i < getters.length; i++) {
MethodInfo getter = ReflectUtils.getMethodInfo(getters[i]);
e = EmitUtils.begin_method(ce, getter, Constants.ACC_PUBLIC);
e.load_this();
e.getfield(FIELD_NAME);
e.invoke(getter);
e.return_value();
e.end_method();
}
for (Method setter2 : setters) {
MethodInfo setter = ReflectUtils.getMethodInfo(setter2);
e = EmitUtils.begin_method(ce, setter, Constants.ACC_PUBLIC);
e.throw_exception(ILLEGAL_STATE_EXCEPTION, "Bean is immutable");
e.end_method();
}
for (int i = 0; i < setters.length; i++) {
MethodInfo setter = ReflectUtils.getMethodInfo(setters[i]);
e = EmitUtils.begin_method(ce, setter, Constants.ACC_PUBLIC);
e.throw_exception(ILLEGAL_STATE_EXCEPTION, "Bean is immutable");
e.end_method();
}
ce.end_class();
}
ce.end_class();
}
@Override
protected Object firstInstance(Class type) {
return ReflectUtils.newInstance(type, OBJECT_CLASSES, new Object[]{ bean });
}
protected Object firstInstance(Class type) {
return ReflectUtils.newInstance(type, OBJECT_CLASSES, new Object[]{ bean });
}
// TODO: optimize
@Override
protected Object nextInstance(Object instance) {
return firstInstance(instance.getClass());
}
}
// TODO: optimize
protected Object nextInstance(Object instance) {
return firstInstance(instance.getClass());
}
}
}
@@ -138,7 +138,7 @@ public class ResolvableType implements Serializable {
/**
* Private constructor used to create a new {@code ResolvableType} for cache key purposes,
* Private constructor used to create a new {@link ResolvableType} for cache key purposes,
* with no upfront resolution.
*/
private ResolvableType(
@@ -153,7 +153,7 @@ public class ResolvableType implements Serializable {
}
/**
* Private constructor used to create a new {@code ResolvableType} for cache value purposes,
* Private constructor used to create a new {@link ResolvableType} for cache value purposes,
* with upfront resolution and a pre-calculated hash.
* @since 4.2
*/
@@ -169,7 +169,7 @@ public class ResolvableType implements Serializable {
}
/**
* Private constructor used to create a new {@code ResolvableType} for uncached purposes,
* Private constructor used to create a new {@link ResolvableType} for uncached purposes,
* with upfront resolution but lazily calculated hash.
*/
private ResolvableType(Type type, @Nullable TypeProvider typeProvider,
@@ -184,7 +184,7 @@ public class ResolvableType implements Serializable {
}
/**
* Private constructor used to create a new {@code ResolvableType} on a {@link Class} basis.
* Private constructor used to create a new {@link ResolvableType} on a {@link Class} basis.
* <p>Avoids all {@code instanceof} checks in order to create a straight {@link Class} wrapper.
* @since 4.2
*/
@@ -223,7 +223,7 @@ public class ResolvableType implements Serializable {
/**
* Return the underlying source of the resolvable type. Will return a {@link Field},
* {@link MethodParameter} or {@link Type} depending on how the {@code ResolvableType}
* {@link MethodParameter} or {@link Type} depending on how the {@link ResolvableType}
* was constructed. This method is primarily to provide access to additional type
* information or meta-data that alternative JVM languages may provide.
*/
@@ -341,14 +341,13 @@ public class ResolvableType implements Serializable {
}
}
if (ourResolved == null) {
ourResolved = toClass();
ourResolved = resolve(Object.class);
}
Class<?> otherResolved = other.toClass();
// We need an exact type match for generics
// List<CharSequence> is not assignable from List<String>
if (exactMatch ? !ourResolved.equals(otherResolved) :
!ClassUtils.isAssignable(ourResolved, otherResolved)) {
if (exactMatch ? !ourResolved.equals(otherResolved) : !ClassUtils.isAssignable(ourResolved, otherResolved)) {
return false;
}
@@ -359,15 +358,13 @@ public class ResolvableType implements Serializable {
if (ourGenerics.length != typeGenerics.length) {
return false;
}
if (ourGenerics.length > 0) {
if (matchedBefore == null) {
matchedBefore = new IdentityHashMap<>(1);
}
matchedBefore.put(this.type, other.type);
for (int i = 0; i < ourGenerics.length; i++) {
if (!ourGenerics[i].isAssignableFrom(typeGenerics[i], matchedBefore)) {
return false;
}
if (matchedBefore == null) {
matchedBefore = new IdentityHashMap<>(1);
}
matchedBefore.put(this.type, other.type);
for (int i = 0; i < ourGenerics.length; i++) {
if (!ourGenerics[i].isAssignableFrom(typeGenerics[i], matchedBefore)) {
return false;
}
}
}
@@ -432,12 +429,12 @@ public class ResolvableType implements Serializable {
}
/**
* Return this type as a {@code ResolvableType} of the specified class. Searches
* Return this type as a {@link ResolvableType} of the specified class. Searches
* {@link #getSuperType() supertype} and {@link #getInterfaces() interface}
* hierarchies to find a match, returning {@link #NONE} if this type does not
* implement or extend the specified class.
* @param type the required type (typically narrowed)
* @return a {@code ResolvableType} representing this object as the specified
* @return a {@link ResolvableType} representing this object as the specified
* type, or {@link #NONE} if not resolvable as that type
* @see #asCollection()
* @see #asMap()
@@ -462,9 +459,9 @@ public class ResolvableType implements Serializable {
}
/**
* Return a {@code ResolvableType} representing the direct supertype of this type.
* Return a {@link ResolvableType} representing the direct supertype of this type.
* <p>If no supertype is available this method returns {@link #NONE}.
* <p>Note: The resulting {@code ResolvableType} instance may not be {@link Serializable}.
* <p>Note: The resulting {@link ResolvableType} instance may not be {@link Serializable}.
* @see #getInterfaces()
*/
public ResolvableType getSuperType() {
@@ -491,10 +488,10 @@ public class ResolvableType implements Serializable {
}
/**
* Return a {@code ResolvableType} array representing the direct interfaces
* Return a {@link ResolvableType} array representing the direct interfaces
* implemented by this type. If this type does not implement any interfaces an
* empty array is returned.
* <p>Note: The resulting {@code ResolvableType} instances may not be {@link Serializable}.
* <p>Note: The resulting {@link ResolvableType} instances may not be {@link Serializable}.
* @see #getSuperType()
*/
public ResolvableType[] getInterfaces() {
@@ -624,17 +621,17 @@ public class ResolvableType implements Serializable {
}
/**
* Return a {@code ResolvableType} for the specified nesting level.
* Return a {@link ResolvableType} for the specified nesting level.
* <p>See {@link #getNested(int, Map)} for details.
* @param nestingLevel the nesting level
* @return the {@code ResolvableType} type, or {@code #NONE}
* @return the {@link ResolvableType} type, or {@code #NONE}
*/
public ResolvableType getNested(int nestingLevel) {
return getNested(nestingLevel, null);
}
/**
* Return a {@code ResolvableType} for the specified nesting level.
* Return a {@link ResolvableType} for the specified nesting level.
* <p>The nesting level refers to the specific generic parameter that should be returned.
* A nesting level of 1 indicates this type; 2 indicates the first nested generic;
* 3 the second; and so on. For example, given {@code List<Set<Integer>>} level 1 refers
@@ -651,7 +648,7 @@ public class ResolvableType implements Serializable {
* current type, 2 for the first nested generic, 3 for the second and so on
* @param typeIndexesPerLevel a map containing the generic index for a given
* nesting level (may be {@code null})
* @return a {@code ResolvableType} for the nested level, or {@link #NONE}
* @return a {@link ResolvableType} for the nested level, or {@link #NONE}
*/
public ResolvableType getNested(int nestingLevel, @Nullable Map<Integer, Integer> typeIndexesPerLevel) {
ResolvableType result = this;
@@ -673,7 +670,7 @@ public class ResolvableType implements Serializable {
}
/**
* Return a {@code ResolvableType} representing the generic parameter for the
* Return a {@link ResolvableType} representing the generic parameter for the
* given indexes. Indexes are zero based; for example given the type
* {@code Map<Integer, List<String>>}, {@code getGeneric(0)} will access the
* {@code Integer}. Nested generics can be accessed by specifying multiple indexes;
@@ -683,7 +680,7 @@ public class ResolvableType implements Serializable {
* <p>If no generic is available at the specified indexes {@link #NONE} is returned.
* @param indexes the indexes that refer to the generic parameter
* (may be omitted to return the first generic)
* @return a {@code ResolvableType} for the specified generic, or {@link #NONE}
* @return a {@link ResolvableType} for the specified generic, or {@link #NONE}
* @see #hasGenerics()
* @see #getGenerics()
* @see #resolveGeneric(int...)
@@ -706,12 +703,12 @@ public class ResolvableType implements Serializable {
}
/**
* Return an array of {@code ResolvableType ResolvableTypes} representing the generic parameters of
* Return an array of {@link ResolvableType ResolvableTypes} representing the generic parameters of
* this type. If no generics are available an empty array is returned. If you need to
* access a specific generic consider using the {@link #getGeneric(int...)} method as
* it allows access to nested generics and protects against
* {@code IndexOutOfBoundsExceptions}.
* @return an array of {@code ResolvableType ResolvableTypes} representing the generic parameters
* @return an array of {@link ResolvableType ResolvableTypes} representing the generic parameters
* (never {@code null})
* @see #hasGenerics()
* @see #getGeneric(int...)
@@ -783,7 +780,7 @@ public class ResolvableType implements Serializable {
/**
* Convenience method that will {@link #getGeneric(int...) get} and
* {@link #resolve() resolve} a specific generic parameter.
* {@link #resolve() resolve} a specific generic parameters.
* @param indexes the indexes that refer to the generic parameter
* (may be omitted to return the first generic)
* @return a resolved {@link Class} or {@code null}
@@ -845,7 +842,7 @@ public class ResolvableType implements Serializable {
/**
* Resolve this type by a single level, returning the resolved value or {@link #NONE}.
* <p>Note: The returned {@code ResolvableType} should only be used as an intermediary
* <p>Note: The returned {@link ResolvableType} should only be used as an intermediary
* as it cannot be serialized.
*/
ResolvableType resolveType() {
@@ -967,7 +964,7 @@ public class ResolvableType implements Serializable {
}
/**
* Adapts this {@code ResolvableType} to a {@link VariableResolver}.
* Adapts this {@link ResolvableType} to a {@link VariableResolver}.
*/
@Nullable
VariableResolver asVariableResolver() {
@@ -1014,12 +1011,12 @@ public class ResolvableType implements Serializable {
// Factory methods
/**
* Return a {@code ResolvableType} for the specified {@link Class},
* Return a {@link ResolvableType} for the specified {@link Class},
* using the full generic type information for assignability checks.
* <p>For example: {@code ResolvableType.forClass(MyArrayList.class)}.
* @param clazz the class to introspect ({@code null} is semantically
* equivalent to {@code Object.class} for typical use cases here)
* @return a {@code ResolvableType} for the specified class
* @return a {@link ResolvableType} for the specified class
* @see #forClass(Class, Class)
* @see #forClassWithGenerics(Class, Class...)
*/
@@ -1028,13 +1025,13 @@ public class ResolvableType implements Serializable {
}
/**
* Return a {@code ResolvableType} for the specified {@link Class},
* Return a {@link ResolvableType} for the specified {@link Class},
* doing assignability checks against the raw class only (analogous to
* {@link Class#isAssignableFrom}, which this serves as a wrapper for).
* <p>For example: {@code ResolvableType.forRawClass(List.class)}.
* @param clazz the class to introspect ({@code null} is semantically
* equivalent to {@code Object.class} for typical use cases here)
* @return a {@code ResolvableType} for the specified class
* @return a {@link ResolvableType} for the specified class
* @since 4.2
* @see #forClass(Class)
* @see #getRawClass()
@@ -1058,12 +1055,12 @@ public class ResolvableType implements Serializable {
}
/**
* Return a {@code ResolvableType} for the specified base type
* Return a {@link ResolvableType} for the specified base type
* (interface or base class) with a given implementation class.
* <p>For example: {@code ResolvableType.forClass(List.class, MyArrayList.class)}.
* @param baseType the base type (must not be {@code null})
* @param implementationClass the implementation class
* @return a {@code ResolvableType} for the specified base type backed by the
* @return a {@link ResolvableType} for the specified base type backed by the
* given implementation class
* @see #forClass(Class)
* @see #forClassWithGenerics(Class, Class...)
@@ -1075,10 +1072,10 @@ public class ResolvableType implements Serializable {
}
/**
* Return a {@code ResolvableType} for the specified {@link Class} with pre-declared generics.
* Return a {@link ResolvableType} for the specified {@link Class} with pre-declared generics.
* @param clazz the class (or interface) to introspect
* @param generics the generics of the class
* @return a {@code ResolvableType} for the specific class and generics
* @return a {@link ResolvableType} for the specific class and generics
* @see #forClassWithGenerics(Class, ResolvableType...)
*/
public static ResolvableType forClassWithGenerics(Class<?> clazz, Class<?>... generics) {
@@ -1092,10 +1089,10 @@ public class ResolvableType implements Serializable {
}
/**
* Return a {@code ResolvableType} for the specified {@link Class} with pre-declared generics.
* Return a {@link ResolvableType} for the specified {@link Class} with pre-declared generics.
* @param clazz the class (or interface) to introspect
* @param generics the generics of the class
* @return a {@code ResolvableType} for the specific class and generics
* @return a {@link ResolvableType} for the specific class and generics
* @see #forClassWithGenerics(Class, Class...)
*/
public static ResolvableType forClassWithGenerics(Class<?> clazz, ResolvableType... generics) {
@@ -1116,12 +1113,12 @@ public class ResolvableType implements Serializable {
}
/**
* Return a {@code ResolvableType} for the specified instance. The instance does not
* Return a {@link ResolvableType} for the specified instance. The instance does not
* convey generic information but if it implements {@link ResolvableTypeProvider} a
* more precise {@code ResolvableType} can be used than the simple one based on
* more precise {@link ResolvableType} can be used than the simple one based on
* the {@link #forClass(Class) Class instance}.
* @param instance the instance (possibly {@code null})
* @return a {@code ResolvableType} for the specified instance,
* @return a {@link ResolvableType} for the specified instance,
* or {@code NONE} for {@code null}
* @since 4.2
* @see ResolvableTypeProvider
@@ -1137,9 +1134,9 @@ public class ResolvableType implements Serializable {
}
/**
* Return a {@code ResolvableType} for the specified {@link Field}.
* Return a {@link ResolvableType} for the specified {@link Field}.
* @param field the source field
* @return a {@code ResolvableType} for the specified field
* @return a {@link ResolvableType} for the specified field
* @see #forField(Field, Class)
*/
public static ResolvableType forField(Field field) {
@@ -1148,13 +1145,13 @@ public class ResolvableType implements Serializable {
}
/**
* Return a {@code ResolvableType} for the specified {@link Field} with a given
* Return a {@link ResolvableType} for the specified {@link Field} with a given
* implementation.
* <p>Use this variant when the class that declares the field includes generic
* parameter variables that are satisfied by the implementation class.
* @param field the source field
* @param implementationClass the implementation class
* @return a {@code ResolvableType} for the specified field
* @return a {@link ResolvableType} for the specified field
* @see #forField(Field)
*/
public static ResolvableType forField(Field field, Class<?> implementationClass) {
@@ -1164,13 +1161,13 @@ public class ResolvableType implements Serializable {
}
/**
* Return a {@code ResolvableType} for the specified {@link Field} with a given
* Return a {@link ResolvableType} for the specified {@link Field} with a given
* implementation.
* <p>Use this variant when the class that declares the field includes generic
* parameter variables that are satisfied by the implementation type.
* @param field the source field
* @param implementationType the implementation type
* @return a {@code ResolvableType} for the specified field
* @return a {@link ResolvableType} for the specified field
* @see #forField(Field)
*/
public static ResolvableType forField(Field field, @Nullable ResolvableType implementationType) {
@@ -1181,7 +1178,7 @@ public class ResolvableType implements Serializable {
}
/**
* Return a {@code ResolvableType} for the specified {@link Field} with the
* Return a {@link ResolvableType} for the specified {@link Field} with the
* given nesting level.
* @param field the source field
* @param nestingLevel the nesting level (1 for the outer level; 2 for a nested
@@ -1194,7 +1191,7 @@ public class ResolvableType implements Serializable {
}
/**
* Return a {@code ResolvableType} for the specified {@link Field} with a given
* Return a {@link ResolvableType} for the specified {@link Field} with a given
* implementation and the given nesting level.
* <p>Use this variant when the class that declares the field includes generic
* parameter variables that are satisfied by the implementation class.
@@ -1202,7 +1199,7 @@ public class ResolvableType implements Serializable {
* @param nestingLevel the nesting level (1 for the outer level; 2 for a nested
* generic type; etc)
* @param implementationClass the implementation class
* @return a {@code ResolvableType} for the specified field
* @return a {@link ResolvableType} for the specified field
* @see #forField(Field)
*/
public static ResolvableType forField(Field field, int nestingLevel, @Nullable Class<?> implementationClass) {
@@ -1212,10 +1209,10 @@ public class ResolvableType implements Serializable {
}
/**
* Return a {@code ResolvableType} for the specified {@link Constructor} parameter.
* Return a {@link ResolvableType} for the specified {@link Constructor} parameter.
* @param constructor the source constructor (must not be {@code null})
* @param parameterIndex the parameter index
* @return a {@code ResolvableType} for the specified constructor parameter
* @return a {@link ResolvableType} for the specified constructor parameter
* @see #forConstructorParameter(Constructor, int, Class)
*/
public static ResolvableType forConstructorParameter(Constructor<?> constructor, int parameterIndex) {
@@ -1224,14 +1221,14 @@ public class ResolvableType implements Serializable {
}
/**
* Return a {@code ResolvableType} for the specified {@link Constructor} parameter
* Return a {@link ResolvableType} for the specified {@link Constructor} parameter
* with a given implementation. Use this variant when the class that declares the
* constructor includes generic parameter variables that are satisfied by the
* implementation class.
* @param constructor the source constructor (must not be {@code null})
* @param parameterIndex the parameter index
* @param implementationClass the implementation class
* @return a {@code ResolvableType} for the specified constructor parameter
* @return a {@link ResolvableType} for the specified constructor parameter
* @see #forConstructorParameter(Constructor, int)
*/
public static ResolvableType forConstructorParameter(Constructor<?> constructor, int parameterIndex,
@@ -1243,9 +1240,9 @@ public class ResolvableType implements Serializable {
}
/**
* Return a {@code ResolvableType} for the specified {@link Method} return type.
* Return a {@link ResolvableType} for the specified {@link Method} return type.
* @param method the source for the method return type
* @return a {@code ResolvableType} for the specified method return
* @return a {@link ResolvableType} for the specified method return
* @see #forMethodReturnType(Method, Class)
*/
public static ResolvableType forMethodReturnType(Method method) {
@@ -1254,12 +1251,12 @@ public class ResolvableType implements Serializable {
}
/**
* Return a {@code ResolvableType} for the specified {@link Method} return type.
* Return a {@link ResolvableType} for the specified {@link Method} return type.
* <p>Use this variant when the class that declares the method includes generic
* parameter variables that are satisfied by the implementation class.
* @param method the source for the method return type
* @param implementationClass the implementation class
* @return a {@code ResolvableType} for the specified method return
* @return a {@link ResolvableType} for the specified method return
* @see #forMethodReturnType(Method)
*/
public static ResolvableType forMethodReturnType(Method method, Class<?> implementationClass) {
@@ -1269,10 +1266,10 @@ public class ResolvableType implements Serializable {
}
/**
* Return a {@code ResolvableType} for the specified {@link Method} parameter.
* Return a {@link ResolvableType} for the specified {@link Method} parameter.
* @param method the source method (must not be {@code null})
* @param parameterIndex the parameter index
* @return a {@code ResolvableType} for the specified method parameter
* @return a {@link ResolvableType} for the specified method parameter
* @see #forMethodParameter(Method, int, Class)
* @see #forMethodParameter(MethodParameter)
*/
@@ -1282,13 +1279,13 @@ public class ResolvableType implements Serializable {
}
/**
* Return a {@code ResolvableType} for the specified {@link Method} parameter with a
* Return a {@link ResolvableType} for the specified {@link Method} parameter with a
* given implementation. Use this variant when the class that declares the method
* includes generic parameter variables that are satisfied by the implementation class.
* @param method the source method (must not be {@code null})
* @param parameterIndex the parameter index
* @param implementationClass the implementation class
* @return a {@code ResolvableType} for the specified method parameter
* @return a {@link ResolvableType} for the specified method parameter
* @see #forMethodParameter(Method, int, Class)
* @see #forMethodParameter(MethodParameter)
*/
@@ -1299,9 +1296,9 @@ public class ResolvableType implements Serializable {
}
/**
* Return a {@code ResolvableType} for the specified {@link MethodParameter}.
* Return a {@link ResolvableType} for the specified {@link MethodParameter}.
* @param methodParameter the source method parameter (must not be {@code null})
* @return a {@code ResolvableType} for the specified method parameter
* @return a {@link ResolvableType} for the specified method parameter
* @see #forMethodParameter(Method, int)
*/
public static ResolvableType forMethodParameter(MethodParameter methodParameter) {
@@ -1309,12 +1306,12 @@ public class ResolvableType implements Serializable {
}
/**
* Return a {@code ResolvableType} for the specified {@link MethodParameter} with a
* Return a {@link ResolvableType} for the specified {@link MethodParameter} with a
* given implementation type. Use this variant when the class that declares the method
* includes generic parameter variables that are satisfied by the implementation type.
* @param methodParameter the source method parameter (must not be {@code null})
* @param implementationType the implementation type
* @return a {@code ResolvableType} for the specified method parameter
* @return a {@link ResolvableType} for the specified method parameter
* @see #forMethodParameter(MethodParameter)
*/
public static ResolvableType forMethodParameter(MethodParameter methodParameter,
@@ -1329,11 +1326,11 @@ public class ResolvableType implements Serializable {
}
/**
* Return a {@code ResolvableType} for the specified {@link MethodParameter},
* Return a {@link ResolvableType} for the specified {@link MethodParameter},
* overriding the target type to resolve with a specific given type.
* @param methodParameter the source method parameter (must not be {@code null})
* @param targetType the type to resolve (a part of the method parameter's type)
* @return a {@code ResolvableType} for the specified method parameter
* @return a {@link ResolvableType} for the specified method parameter
* @see #forMethodParameter(Method, int)
*/
public static ResolvableType forMethodParameter(MethodParameter methodParameter, @Nullable Type targetType) {
@@ -1342,13 +1339,13 @@ public class ResolvableType implements Serializable {
}
/**
* Return a {@code ResolvableType} for the specified {@link MethodParameter} at
* Return a {@link ResolvableType} for the specified {@link MethodParameter} at
* a specific nesting level, overriding the target type to resolve with a specific
* given type.
* @param methodParameter the source method parameter (must not be {@code null})
* @param targetType the type to resolve (a part of the method parameter's type)
* @param nestingLevel the nesting level to use
* @return a {@code ResolvableType} for the specified method parameter
* @return a {@link ResolvableType} for the specified method parameter
* @since 5.2
* @see #forMethodParameter(Method, int)
*/
@@ -1361,9 +1358,9 @@ public class ResolvableType implements Serializable {
}
/**
* Return a {@code ResolvableType} as an array of the specified {@code componentType}.
* Return a {@link ResolvableType} as an array of the specified {@code componentType}.
* @param componentType the component type
* @return a {@code ResolvableType} as an array of the specified component type
* @return a {@link ResolvableType} as an array of the specified component type
*/
public static ResolvableType forArrayComponent(ResolvableType componentType) {
Assert.notNull(componentType, "Component type must not be null");
@@ -1372,10 +1369,10 @@ public class ResolvableType implements Serializable {
}
/**
* Return a {@code ResolvableType} for the specified {@link Type}.
* <p>Note: The resulting {@code ResolvableType} instance may not be {@link Serializable}.
* Return a {@link ResolvableType} for the specified {@link Type}.
* <p>Note: The resulting {@link ResolvableType} instance may not be {@link Serializable}.
* @param type the source type (potentially {@code null})
* @return a {@code ResolvableType} for the specified {@link Type}
* @return a {@link ResolvableType} for the specified {@link Type}
* @see #forType(Type, ResolvableType)
*/
public static ResolvableType forType(@Nullable Type type) {
@@ -1383,12 +1380,12 @@ public class ResolvableType implements Serializable {
}
/**
* Return a {@code ResolvableType} for the specified {@link Type} backed by the given
* Return a {@link ResolvableType} for the specified {@link Type} backed by the given
* owner type.
* <p>Note: The resulting {@code ResolvableType} instance may not be {@link Serializable}.
* <p>Note: The resulting {@link ResolvableType} instance may not be {@link Serializable}.
* @param type the source type or {@code null}
* @param owner the owner type used to resolve variables
* @return a {@code ResolvableType} for the specified {@link Type} and owner
* @return a {@link ResolvableType} for the specified {@link Type} and owner
* @see #forType(Type)
*/
public static ResolvableType forType(@Nullable Type type, @Nullable ResolvableType owner) {
@@ -1401,10 +1398,10 @@ public class ResolvableType implements Serializable {
/**
* Return a {@code ResolvableType} for the specified {@link ParameterizedTypeReference}.
* <p>Note: The resulting {@code ResolvableType} instance may not be {@link Serializable}.
* Return a {@link ResolvableType} for the specified {@link ParameterizedTypeReference}.
* <p>Note: The resulting {@link ResolvableType} instance may not be {@link Serializable}.
* @param typeReference the reference to obtain the source type from
* @return a {@code ResolvableType} for the specified {@link ParameterizedTypeReference}
* @return a {@link ResolvableType} for the specified {@link ParameterizedTypeReference}
* @since 4.3.12
* @see #forType(Type)
*/
@@ -1413,23 +1410,23 @@ public class ResolvableType implements Serializable {
}
/**
* Return a {@code ResolvableType} for the specified {@link Type} backed by a given
* Return a {@link ResolvableType} for the specified {@link Type} backed by a given
* {@link VariableResolver}.
* @param type the source type or {@code null}
* @param variableResolver the variable resolver or {@code null}
* @return a {@code ResolvableType} for the specified {@link Type} and {@link VariableResolver}
* @return a {@link ResolvableType} for the specified {@link Type} and {@link VariableResolver}
*/
static ResolvableType forType(@Nullable Type type, @Nullable VariableResolver variableResolver) {
return forType(type, null, variableResolver);
}
/**
* Return a {@code ResolvableType} for the specified {@link Type} backed by a given
* Return a {@link ResolvableType} for the specified {@link Type} backed by a given
* {@link VariableResolver}.
* @param type the source type or {@code null}
* @param typeProvider the type provider or {@code null}
* @param variableResolver the variable resolver or {@code null}
* @return a {@code ResolvableType} for the specified {@link Type} and {@link VariableResolver}
* @return a {@link ResolvableType} for the specified {@link Type} and {@link VariableResolver}
*/
static ResolvableType forType(
@Nullable Type type, @Nullable TypeProvider typeProvider, @Nullable VariableResolver variableResolver) {
@@ -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-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.
@@ -429,9 +429,8 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
String filePath = new File(path).getAbsolutePath();
int prefixIndex = filePath.indexOf(':');
if (prefixIndex == 1) {
// Possibly a drive prefix on Windows (for example, "c:"), so we prepend a slash
// and convert the drive letter to uppercase for consistent duplicate detection.
filePath = "/" + StringUtils.capitalize(filePath);
// Possibly "c:" drive prefix on Windows, to be upper-cased for proper duplicate detection
filePath = StringUtils.capitalize(filePath);
}
// # can appear in directories/filenames, java.net.URL should not treat it as a fragment
filePath = StringUtils.replace(filePath, "#", "%23");
@@ -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-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.
@@ -20,12 +20,12 @@ import org.springframework.util.Assert;
/**
* Implementation of {@link BackOff} that increases the back off period for each
* retry attempt. When the interval has reached the {@linkplain #setMaxInterval
* retry attempt. When the interval has reached the {@link #setMaxInterval(long)
* max interval}, it is no longer increased. Stops retrying once the
* {@linkplain #setMaxElapsedTime max elapsed time} has been reached.
* {@link #setMaxElapsedTime(long) max elapsed time} has been reached.
*
* <p>Example: The default interval is {@value #DEFAULT_INITIAL_INTERVAL} ms;
* the default multiplier is {@value #DEFAULT_MULTIPLIER}; and the default max
* <p>Example: The default interval is {@value #DEFAULT_INITIAL_INTERVAL} ms,
* the default multiplier is {@value #DEFAULT_MULTIPLIER}, and the default max
* interval is {@value #DEFAULT_MAX_INTERVAL}. For 10 attempts the sequence will be
* as follows:
*
@@ -44,9 +44,10 @@ import org.springframework.util.Assert;
* 10 30000
* </pre>
*
* <p>Note that the default max elapsed time is {@link Long#MAX_VALUE}.
* Use {@link #setMaxElapsedTime} to limit the maximum length of time that an
* instance should accumulate before returning {@link BackOffExecution#STOP}.
* <p>Note that the default max elapsed time is {@link Long#MAX_VALUE}. Use
* {@link #setMaxElapsedTime(long)} to limit the maximum length of time
* that an instance should accumulate before returning
* {@link BackOffExecution#STOP}.
*
* @author Stephane Nicoll
* @since 4.1
@@ -106,7 +107,7 @@ public class ExponentialBackOff implements BackOff {
/**
* Set the initial interval in milliseconds.
* The initial interval in milliseconds.
*/
public void setInitialInterval(long initialInterval) {
this.initialInterval = initialInterval;
@@ -120,7 +121,7 @@ public class ExponentialBackOff implements BackOff {
}
/**
* Set the value to multiply the current interval by for each retry attempt.
* The value to multiply the current interval by for each retry attempt.
*/
public void setMultiplier(double multiplier) {
checkMultiplier(multiplier);
@@ -135,21 +136,21 @@ public class ExponentialBackOff implements BackOff {
}
/**
* Set the maximum back off time in milliseconds.
* The maximum back off time.
*/
public void setMaxInterval(long maxInterval) {
this.maxInterval = maxInterval;
}
/**
* Return the maximum back off time in milliseconds.
* Return the maximum back off time.
*/
public long getMaxInterval() {
return this.maxInterval;
}
/**
* Set the maximum elapsed time in milliseconds after which a call to
* The maximum elapsed time in milliseconds after which a call to
* {@link BackOffExecution#nextBackOff()} returns {@link BackOffExecution#STOP}.
*/
public void setMaxElapsedTime(long maxElapsedTime) {
@@ -183,9 +184,10 @@ public class ExponentialBackOff implements BackOff {
@Override
public long nextBackOff() {
if (this.currentElapsedTime >= getMaxElapsedTime()) {
if (this.currentElapsedTime >= maxElapsedTime) {
return STOP;
}
long nextInterval = computeNextInterval();
this.currentElapsedTime += nextInterval;
return nextInterval;
@@ -212,6 +214,7 @@ public class ExponentialBackOff implements BackOff {
return Math.min(i, maxInterval);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("ExponentialBackOff{");
@@ -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.
@@ -200,8 +200,8 @@ class ResolvableTypeTests {
@Test
void forFieldMustNotBeNull() throws Exception {
assertThatIllegalArgumentException()
.isThrownBy(() -> ResolvableType.forField(null))
.withMessage("Field must not be null");
.isThrownBy(() -> ResolvableType.forField(null))
.withMessage("Field must not be null");
}
@Test
@@ -214,8 +214,8 @@ class ResolvableTypeTests {
@Test
void forConstructorParameterMustNotBeNull() throws Exception {
assertThatIllegalArgumentException()
.isThrownBy(() -> ResolvableType.forConstructorParameter(null, 0))
.withMessage("Constructor must not be null");
.isThrownBy(() -> ResolvableType.forConstructorParameter(null, 0))
.withMessage("Constructor must not be null");
}
@Test
@@ -228,8 +228,8 @@ class ResolvableTypeTests {
@Test
void forMethodParameterByIndexMustNotBeNull() throws Exception {
assertThatIllegalArgumentException()
.isThrownBy(() -> ResolvableType.forMethodParameter(null, 0))
.withMessage("Method must not be null");
.isThrownBy(() -> ResolvableType.forMethodParameter(null, 0))
.withMessage("Method must not be null");
}
@Test
@@ -268,8 +268,8 @@ class ResolvableTypeTests {
@Test
void forMethodParameterMustNotBeNull() throws Exception {
assertThatIllegalArgumentException()
.isThrownBy(() -> ResolvableType.forMethodParameter(null))
.withMessage("MethodParameter must not be null");
.isThrownBy(() -> ResolvableType.forMethodParameter(null))
.withMessage("MethodParameter must not be null");
}
@Test // SPR-16210
@@ -295,8 +295,8 @@ class ResolvableTypeTests {
@Test
void forMethodReturnMustNotBeNull() throws Exception {
assertThatIllegalArgumentException()
.isThrownBy(() -> ResolvableType.forMethodReturnType(null))
.withMessage("Method must not be null");
.isThrownBy(() -> ResolvableType.forMethodReturnType(null))
.withMessage("Method must not be null");
}
@Test
@@ -343,7 +343,7 @@ class ResolvableTypeTests {
ResolvableType type = ResolvableType.forField(field);
assertThat(type.isArray()).isTrue();
assertThat(type.getComponentType().getType())
.isEqualTo(((Class) field.getGenericType()).getComponentType());
.isEqualTo(((Class) field.getGenericType()).getComponentType());
}
@Test
@@ -535,7 +535,7 @@ class ResolvableTypeTests {
void getGenericsFromParameterizedType() throws Exception {
ResolvableType type = ResolvableType.forClass(List.class, ExtendsList.class);
ResolvableType[] generics = type.getGenerics();
assertThat(generics).hasSize(1);
assertThat(generics.length).isEqualTo(1);
assertThat(generics[0].resolve()).isEqualTo(CharSequence.class);
}
@@ -543,7 +543,7 @@ class ResolvableTypeTests {
void getGenericsFromClass() throws Exception {
ResolvableType type = ResolvableType.forClass(List.class);
ResolvableType[] generics = type.getGenerics();
assertThat(generics).hasSize(1);
assertThat(generics.length).isEqualTo(1);
assertThat(generics[0].getType().toString()).isEqualTo("E");
}
@@ -558,7 +558,7 @@ class ResolvableTypeTests {
void getResolvedGenerics() throws Exception {
ResolvableType type = ResolvableType.forClass(List.class, ExtendsList.class);
Class<?>[] generics = type.resolveGenerics();
assertThat(generics).hasSize(1);
assertThat(generics.length).isEqualTo(1);
assertThat(generics[0]).isEqualTo(CharSequence.class);
}
@@ -686,6 +686,7 @@ class ResolvableTypeTests {
assertThat(type.resolve()).isEqualTo(CharSequence.class);
}
@Test
void resolveBoundedTypeVariableWildcardResult() throws Exception {
ResolvableType type = ResolvableType.forMethodReturnType(Methods.class.getMethod("boundedTypeVariableWildcardResult"));
@@ -700,26 +701,30 @@ class ResolvableTypeTests {
@Test
void resolveTypeVariableFromSimpleInterfaceType() {
ResolvableType type = ResolvableType.forClass(MySimpleInterfaceType.class).as(MyInterfaceType.class);
ResolvableType type = ResolvableType.forClass(
MySimpleInterfaceType.class).as(MyInterfaceType.class);
assertThat(type.resolveGeneric()).isEqualTo(String.class);
}
@Test
void resolveTypeVariableFromSimpleCollectionInterfaceType() {
ResolvableType type = ResolvableType.forClass(MyCollectionInterfaceType.class).as(MyInterfaceType.class);
ResolvableType type = ResolvableType.forClass(
MyCollectionInterfaceType.class).as(MyInterfaceType.class);
assertThat(type.resolveGeneric()).isEqualTo(Collection.class);
assertThat(type.resolveGeneric(0, 0)).isEqualTo(String.class);
}
@Test
void resolveTypeVariableFromSimpleSuperclassType() {
ResolvableType type = ResolvableType.forClass(MySimpleSuperclassType.class).as(MySuperclassType.class);
ResolvableType type = ResolvableType.forClass(
MySimpleSuperclassType.class).as(MySuperclassType.class);
assertThat(type.resolveGeneric()).isEqualTo(String.class);
}
@Test
void resolveTypeVariableFromSimpleCollectionSuperclassType() {
ResolvableType type = ResolvableType.forClass(MyCollectionSuperclassType.class).as(MySuperclassType.class);
ResolvableType type = ResolvableType.forClass(
MyCollectionSuperclassType.class).as(MySuperclassType.class);
assertThat(type.resolveGeneric()).isEqualTo(Collection.class);
assertThat(type.resolveGeneric(0, 0)).isEqualTo(String.class);
}
@@ -746,7 +751,8 @@ class ResolvableTypeTests {
void resolveTypeVariableFromSuperType() throws Exception {
ResolvableType type = ResolvableType.forClass(ExtendsList.class);
assertThat(type.resolve()).isEqualTo(ExtendsList.class);
assertThat(type.asCollection().resolveGeneric()).isEqualTo(CharSequence.class);
assertThat(type.asCollection().resolveGeneric())
.isEqualTo(CharSequence.class);
}
@Test
@@ -958,8 +964,8 @@ class ResolvableTypeTests {
@Test
void isAssignableFromMustNotBeNull() throws Exception {
assertThatIllegalArgumentException()
.isThrownBy(() -> ResolvableType.forClass(Object.class).isAssignableFrom((ResolvableType) null))
.withMessage("ResolvableType must not be null");
.isThrownBy(() -> ResolvableType.forClass(Object.class).isAssignableFrom((ResolvableType) null))
.withMessage("ResolvableType must not be null");
}
@Test
@@ -998,7 +1004,6 @@ class ResolvableTypeTests {
void isAssignableFromCannotBeResolved() throws Exception {
ResolvableType objectType = ResolvableType.forClass(Object.class);
ResolvableType unresolvableVariable = ResolvableType.forField(AssignmentBase.class.getField("o"));
assertThat(unresolvableVariable.resolve()).isNull();
assertThatResolvableType(objectType).isAssignableFrom(unresolvableVariable);
assertThatResolvableType(unresolvableVariable).isAssignableFrom(objectType);
@@ -1215,9 +1220,9 @@ class ResolvableTypeTests {
@Test
void forClassWithMismatchedGenerics() throws Exception {
assertThatIllegalArgumentException()
.isThrownBy(() -> ResolvableType.forClassWithGenerics(Map.class, Integer.class))
.withMessageContaining("Mismatched number of generics specified for")
.withMessageContaining("java.util.Map<K,V>");
.isThrownBy(() -> ResolvableType.forClassWithGenerics(Map.class, Integer.class))
.withMessageContaining("Mismatched number of generics specified for")
.withMessageContaining("java.util.Map<K,V>");
}
@Test
@@ -1272,7 +1277,7 @@ class ResolvableTypeTests {
}
@Test
void hasUnresolvableGenericsWhenImplementingRawInterface() throws Exception {
void hasUnresolvableGenericsWhenImplementesRawInterface() throws Exception {
ResolvableType type = ResolvableType.forClass(MySimpleInterfaceTypeWithImplementsRaw.class);
for (ResolvableType generic : type.getGenerics()) {
assertThat(generic.resolve()).isNotNull();
@@ -1302,7 +1307,7 @@ class ResolvableTypeTests {
Type type = resolvableType.getType();
assertThat(type).isInstanceOf(ParameterizedType.class);
assertThat(((ParameterizedType) type).getRawType()).isEqualTo(Callable.class);
assertThat(((ParameterizedType) type).getActualTypeArguments()).hasSize(1);
assertThat(((ParameterizedType) type).getActualTypeArguments().length).isEqualTo(1);
assertThat(((ParameterizedType) type).getActualTypeArguments()[0]).isEqualTo(String.class);
}
@@ -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
@@ -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.
@@ -21,39 +21,13 @@ import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link ConcurrentLruCache}.
*
* @author Juergen Hoeller
* @author Sam Brannen
*/
class ConcurrentLruCacheTests {
private final ConcurrentLruCache<String, String> cache = new ConcurrentLruCache<>(2, key -> key + "value");
@Test
void zeroCapacity() {
ConcurrentLruCache<String, String> cache = new ConcurrentLruCache<>(0, key -> key + "value");
assertThat(cache.sizeLimit()).isZero();
assertThat(cache.size()).isZero();
assertThat(cache.get("k1")).isEqualTo("k1value");
assertThat(cache.size()).isZero();
assertThat(cache.contains("k1")).isFalse();
assertThat(cache.get("k2")).isEqualTo("k2value");
assertThat(cache.size()).isZero();
assertThat(cache.contains("k1")).isFalse();
assertThat(cache.contains("k2")).isFalse();
assertThat(cache.get("k3")).isEqualTo("k3value");
assertThat(cache.size()).isZero();
assertThat(cache.contains("k1")).isFalse();
assertThat(cache.contains("k2")).isFalse();
assertThat(cache.contains("k3")).isFalse();
}
@Test
void getAndSize() {
assertThat(this.cache.sizeLimit()).isEqualTo(2);
@@ -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) {
@@ -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.
@@ -154,12 +154,9 @@ final class LogAdapter {
private static final LoggerContext loggerContext =
LogManager.getContext(Log4jLog.class.getClassLoader(), false);
private final String name;
private final transient ExtendedLogger logger;
private final ExtendedLogger logger;
public Log4jLog(String name) {
this.name = name;
LoggerContext context = loggerContext;
if (context == null) {
// Circular call in early-init scenario -> static field not initialized yet
@@ -273,10 +270,6 @@ final class LogAdapter {
this.logger.logIfEnabled(FQCN, level, null, message, exception);
}
}
protected Object readResolve() {
return new Log4jLog(this.name);
}
}
@@ -406,11 +406,8 @@ public class BeanPropertyRowMapper<T> implements RowMapper<T> {
/**
* Retrieve a JDBC object value for the specified column.
* <p>The default implementation calls
* {@link JdbcUtils#getResultSetValue(java.sql.ResultSet, int, Class)}
* using the type of the specified {@link PropertyDescriptor}.
* <p>Subclasses may override this to check specific value types upfront,
* or to post-process values returned from {@code getResultSetValue}.
* <p>The default implementation delegates to
* {@link #getColumnValue(ResultSet, int, Class)}.
* @param rs is the ResultSet holding the data
* @param index is the column index
* @param pd the bean property that each result object is expected to match
@@ -427,8 +424,8 @@ public class BeanPropertyRowMapper<T> implements RowMapper<T> {
* Retrieve a JDBC object value for the specified column.
* <p>The default implementation calls
* {@link JdbcUtils#getResultSetValue(java.sql.ResultSet, int, Class)}.
* <p>Subclasses may override this to check specific value types upfront,
* or to post-process values returned from {@code getResultSetValue}.
* Subclasses may override this to check specific value types upfront,
* or to post-process values return from {@code getResultSetValue}.
* @param rs is the ResultSet holding the data
* @param index is the column index
* @param paramType the target parameter type
@@ -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-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
package org.springframework.jdbc.core.metadata;
import java.sql.DatabaseMetaData;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
@@ -114,28 +115,28 @@ public class CallMetaDataContext {
}
/**
* Specify a limited set of the {@code in} parameters to be used.
* Specify a limited set of in parameters to be used.
*/
public void setLimitedInParameterNames(Set<String> limitedInParameterNames) {
this.limitedInParameterNames = limitedInParameterNames;
}
/**
* Get the limited set of the {@code in} parameters to be used.
* Get a limited set of in parameters to be used.
*/
public Set<String> getLimitedInParameterNames() {
return this.limitedInParameterNames;
}
/**
* Specify the names of the {@code out} parameters.
* Specify the names of the out parameters.
*/
public void setOutParameterNames(List<String> outParameterNames) {
this.outParameterNames = outParameterNames;
}
/**
* Get the list of the {@code out} parameter names.
* Get a list of the out parameter names.
*/
public List<String> getOutParameterNames() {
return this.outParameterNames;
@@ -433,14 +434,14 @@ public class CallMetaDataContext {
if (paramNameToUse == null) {
paramNameToUse = "";
}
if (meta.isOutParameter()) {
if (meta.getParameterType() == DatabaseMetaData.procedureColumnOut) {
workParams.add(provider.createDefaultOutParameter(paramNameToUse, meta));
outParamNames.add(paramNameToUse);
if (logger.isDebugEnabled()) {
logger.debug("Added meta-data out parameter for '" + paramNameToUse + "'");
}
}
else if (meta.isInOutParameter()) {
else if (meta.getParameterType() == DatabaseMetaData.procedureColumnInOut) {
workParams.add(provider.createDefaultInOutParameter(paramNameToUse, meta));
outParamNames.add(paramNameToUse);
if (logger.isDebugEnabled()) {
@@ -553,7 +554,7 @@ public class CallMetaDataContext {
Map<String, String> callParameterNames = CollectionUtils.newHashMap(this.callParameters.size());
for (SqlParameter parameter : this.callParameters) {
if (parameter.isInputValueProvided()) {
String parameterName = parameter.getName();
String parameterName = parameter.getName();
String parameterNameToMatch = provider.parameterNameToUse(parameterName);
if (parameterNameToMatch != null) {
callParameterNames.put(parameterNameToMatch.toLowerCase(), parameterName);
@@ -605,7 +606,7 @@ public class CallMetaDataContext {
int i = 0;
for (SqlParameter parameter : this.callParameters) {
if (parameter.isInputValueProvided()) {
String parameterName = parameter.getName();
String parameterName = parameter.getName();
matchedParameters.put(parameterName, parameterValues[i++]);
}
}
@@ -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?
*/
@@ -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.
@@ -107,28 +107,6 @@ public class CallParameterMetaData {
this.parameterType == DatabaseMetaData.procedureColumnResult));
}
/**
* Determine whether the declared parameter qualifies as an 'out' parameter
* for our purposes: type {@link DatabaseMetaData#procedureColumnOut},
* or in case of a function, {@link DatabaseMetaData#functionColumnOut}.
* @since 5.3.31
*/
public boolean isOutParameter() {
return (this.function ? this.parameterType == DatabaseMetaData.functionColumnOut :
this.parameterType == DatabaseMetaData.procedureColumnOut);
}
/**
* Determine whether the declared parameter qualifies as an 'in-out' parameter
* for our purposes: type {@link DatabaseMetaData#procedureColumnInOut},
* or in case of a function, {@link DatabaseMetaData#functionColumnInOut}.
* @since 5.3.31
*/
public boolean isInOutParameter() {
return (this.function ? this.parameterType == DatabaseMetaData.functionColumnInOut :
this.parameterType == DatabaseMetaData.procedureColumnInOut);
}
/**
* Return the parameter SQL type.
*/
@@ -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.
@@ -35,8 +35,7 @@ import org.springframework.util.StringUtils;
/**
* A generic implementation of the {@link CallMetaDataProvider} interface.
*
* <p>This class can be extended to provide database specific behavior.
* This class can be extended to provide database specific behavior.
*
* @author Thomas Risberg
* @author Juergen Hoeller
@@ -114,7 +113,7 @@ public class GenericCallMetaDataProvider implements CallMetaDataProvider {
@Nullable String schemaName, @Nullable String procedureName) throws SQLException {
this.procedureColumnMetaDataUsed = true;
processProcedureColumns(databaseMetaData, catalogName, schemaName, procedureName);
processProcedureColumns(databaseMetaData, catalogName, schemaName, procedureName);
}
@Override
@@ -125,19 +124,52 @@ public class GenericCallMetaDataProvider implements CallMetaDataProvider {
@Override
@Nullable
public String procedureNameToUse(@Nullable String procedureName) {
return identifierNameToUse(procedureName);
if (procedureName == null) {
return null;
}
else if (isStoresUpperCaseIdentifiers()) {
return procedureName.toUpperCase();
}
else if (isStoresLowerCaseIdentifiers()) {
return procedureName.toLowerCase();
}
else {
return procedureName;
}
}
@Override
@Nullable
public String catalogNameToUse(@Nullable String catalogName) {
return identifierNameToUse(catalogName);
if (catalogName == null) {
return null;
}
else if (isStoresUpperCaseIdentifiers()) {
return catalogName.toUpperCase();
}
else if (isStoresLowerCaseIdentifiers()) {
return catalogName.toLowerCase();
}
else {
return catalogName;
}
}
@Override
@Nullable
public String schemaNameToUse(@Nullable String schemaName) {
return identifierNameToUse(schemaName);
if (schemaName == null) {
return null;
}
else if (isStoresUpperCaseIdentifiers()) {
return schemaName.toUpperCase();
}
else if (isStoresLowerCaseIdentifiers()) {
return schemaName.toLowerCase();
}
else {
return schemaName;
}
}
@Override
@@ -165,7 +197,23 @@ public class GenericCallMetaDataProvider implements CallMetaDataProvider {
@Override
@Nullable
public String parameterNameToUse(@Nullable String parameterName) {
return identifierNameToUse(parameterName);
if (parameterName == null) {
return null;
}
else if (isStoresUpperCaseIdentifiers()) {
return parameterName.toUpperCase();
}
else if (isStoresLowerCaseIdentifiers()) {
return parameterName.toLowerCase();
}
else {
return parameterName;
}
}
@Override
public boolean byPassReturnParameter(String parameterName) {
return false;
}
@Override
@@ -208,11 +256,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.
@@ -273,22 +316,6 @@ public class GenericCallMetaDataProvider implements CallMetaDataProvider {
}
@Nullable
private String identifierNameToUse(@Nullable String identifierName) {
if (identifierName == null) {
return null;
}
else if (isStoresUpperCaseIdentifiers()) {
return identifierName.toUpperCase();
}
else if (isStoresLowerCaseIdentifiers()) {
return identifierName.toLowerCase();
}
else {
return identifierName;
}
}
/**
* Process the procedure column meta-data.
*/
@@ -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.
@@ -526,6 +526,17 @@ public interface NamedParameterJdbcOperations {
int update(String sql, SqlParameterSource paramSource, KeyHolder generatedKeyHolder, String[] keyColumnNames)
throws DataAccessException;
/**
* Executes a batch using the supplied SQL statement with the batch of supplied arguments.
* @param sql the SQL statement to execute
* @param batchValues the array of Maps containing the batch of arguments for the query
* @return an array containing the numbers of rows affected by each update in the batch
* (may also contain special JDBC-defined negative values for affected rows such as
* {@link java.sql.Statement#SUCCESS_NO_INFO}/{@link java.sql.Statement#EXECUTE_FAILED})
* @throws DataAccessException if there is any problem issuing the update
*/
int[] batchUpdate(String sql, Map<String, ?>[] batchValues);
/**
* Execute a batch using the supplied SQL statement with the batch of supplied arguments.
* @param sql the SQL statement to execute
@@ -538,15 +549,4 @@ public interface NamedParameterJdbcOperations {
*/
int[] batchUpdate(String sql, SqlParameterSource[] batchArgs);
/**
* Executes a batch using the supplied SQL statement with the batch of supplied arguments.
* @param sql the SQL statement to execute
* @param batchValues the array of Maps containing the batch of arguments for the query
* @return an array containing the numbers of rows affected by each update in the batch
* (may also contain special JDBC-defined negative values for affected rows such as
* {@link java.sql.Statement#SUCCESS_NO_INFO}/{@link java.sql.Statement#EXECUTE_FAILED})
* @throws DataAccessException if there is any problem issuing the update
*/
int[] batchUpdate(String sql, Map<String, ?>[] batchValues);
}
@@ -359,6 +359,11 @@ public class NamedParameterJdbcTemplate implements NamedParameterJdbcOperations
return getJdbcOperations().update(psc, generatedKeyHolder);
}
@Override
public int[] batchUpdate(String sql, Map<String, ?>[] batchValues) {
return batchUpdate(sql, SqlParameterSourceUtils.createBatch(batchValues));
}
@Override
public int[] batchUpdate(String sql, SqlParameterSource[] batchArgs) {
if (batchArgs.length == 0) {
@@ -383,11 +388,6 @@ public class NamedParameterJdbcTemplate implements NamedParameterJdbcOperations
});
}
@Override
public int[] batchUpdate(String sql, Map<String, ?>[] batchValues) {
return batchUpdate(sql, SqlParameterSourceUtils.createBatch(batchValues));
}
/**
* Build a {@link PreparedStatementCreator} based on the given SQL and named parameters.
@@ -436,7 +436,6 @@ public class NamedParameterJdbcTemplate implements NamedParameterJdbcOperations
* @return a representation of the parsed SQL statement
*/
protected ParsedSql getParsedSql(String sql) {
Assert.notNull(sql, "SQL must not be null");
return this.parsedSqlCache.get(sql);
}
@@ -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
@@ -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.
@@ -304,8 +304,8 @@ public abstract class AbstractAdaptableMessageListener
* @see #setMessageConverter
*/
protected Message buildMessage(Session session, Object result) throws JMSException {
Object content = preProcessResponse(result instanceof JmsResponse ?
((JmsResponse<?>) result).getResponse() : result);
Object content = preProcessResponse(result instanceof JmsResponse
? ((JmsResponse<?>) result).getResponse() : result);
MessageConverter converter = getMessageConverter();
if (converter != null) {
@@ -553,29 +553,6 @@ public abstract class AbstractAdaptableMessageListener
}
return this.headers;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(getClass().getSimpleName());
if (this.payload == null) {
sb.append(" [rawMessage=").append(this.message);
}
else {
sb.append(" [payload=");
if (this.payload instanceof byte[]) {
sb.append("byte[").append(((byte[]) this.payload).length).append(']');
}
else {
sb.append(this.payload);
}
}
if (this.headers != null) {
sb.append(", headers=").append(this.headers);
}
sb.append(']');
return sb.toString();
}
}
}
@@ -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.
@@ -40,7 +40,6 @@ import org.springframework.jms.support.QosSettings;
import org.springframework.jms.support.converter.MappingJackson2MessageConverter;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.jms.support.converter.MessageType;
import org.springframework.jms.support.converter.MessagingMessageConverter;
import org.springframework.messaging.Message;
import org.springframework.messaging.converter.MessageConversionException;
import org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory;
@@ -341,45 +340,6 @@ public class MessagingMessageListenerAdapterTests {
return responseMessage;
}
@Test
void lazyResolutionMessageToStringProvidesBestEffortWithUnresolvedPayload() throws JMSException {
MessagingMessageListenerAdapter adapter = getSimpleInstance("echo", Message.class);
MessagingMessageConverter messagingMessageConverter = adapter.getMessagingMessageConverter();
assertThat(messagingMessageConverter).isNotNull();
TextMessage message = new StubTextMessage();
assertThat(messagingMessageConverter.fromMessage(message)).isInstanceOfSatisfying(Message.class, msg ->
assertThat(msg.toString()).contains("rawMessage=").contains(message.toString())
.doesNotContain("payload=").doesNotContain("headers="));
}
@Test
void lazyResolutionMessageToStringWithResolvedPayload() throws JMSException {
MessagingMessageListenerAdapter adapter = getSimpleInstance("echo", Message.class);
MessagingMessageConverter messagingMessageConverter = adapter.getMessagingMessageConverter();
assertThat(messagingMessageConverter).isNotNull();
TextMessage message = new StubTextMessage("Hello");
assertThat(messagingMessageConverter.fromMessage(message)).isInstanceOfSatisfying(Message.class, msg -> {
msg.getPayload(); // force resolution
assertThat(msg.toString()).contains("payload=Hello")
.doesNotContain("rawMessage=").doesNotContain("headers=");
});
}
@Test
void lazyResolutionMessageToStringWithResolvedPayloadAndHeaders() throws JMSException {
MessagingMessageListenerAdapter adapter = getSimpleInstance("echo", Message.class);
MessagingMessageConverter messagingMessageConverter = adapter.getMessagingMessageConverter();
assertThat(messagingMessageConverter).isNotNull();
TextMessage message = new StubTextMessage("Hello");
message.setJMSPriority(7);
assertThat(messagingMessageConverter.fromMessage(message)).isInstanceOfSatisfying(Message.class, msg -> {
msg.getPayload();
msg.getHeaders(); // force resolution
assertThat(msg.toString()).contains("payload=Hello").contains("headers=").contains("jms_priority=7")
.doesNotContain("rawMessage=");
});
}
protected MessagingMessageListenerAdapter getSimpleInstance(String methodName, Class<?>... parameterTypes) {
Method m = ReflectionUtils.findMethod(SampleBean.class, methodName, parameterTypes);
@@ -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;
@@ -200,7 +200,7 @@ public final class MessageBuilder<T> {
* @since 4.1
*/
@SuppressWarnings("unchecked")
public static <T> Message<T> createMessage(T payload, MessageHeaders messageHeaders) {
public static <T> Message<T> createMessage(@Nullable T payload, MessageHeaders messageHeaders) {
Assert.notNull(payload, "Payload must not be null");
Assert.notNull(messageHeaders, "MessageHeaders must not be null");
if (payload instanceof Throwable) {
@@ -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.
@@ -96,7 +96,7 @@ public class HibernateExceptionTranslator implements PersistenceExceptionTransla
DataAccessException dae = this.jdbcExceptionTranslator.translate(
"Hibernate operation: " + jdbcEx.getMessage(), jdbcEx.getSQL(), jdbcEx.getSQLException());
if (dae != null) {
return dae;
throw dae;
}
}
return SessionFactoryUtils.convertHibernateAccessException(ex);
@@ -777,6 +777,8 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
/**
* Convert the given HibernateException to an appropriate exception
* from the {@code org.springframework.dao} hierarchy.
* <p>Will automatically apply a specified SQLExceptionTranslator to a
* Hibernate JDBCException, else rely on Hibernate's default translation.
* @param ex the HibernateException that occurred
* @return a corresponding DataAccessException
* @see SessionFactoryUtils#convertHibernateAccessException
@@ -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()
@@ -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.
@@ -124,8 +124,8 @@ public class HibernateJpaDialect extends DefaultJpaDialect {
* @since 5.1
* @see java.sql.SQLException
* @see org.hibernate.JDBCException
* @see org.springframework.jdbc.support.SQLExceptionSubclassTranslator
* @see org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator
* @see org.springframework.jdbc.support.SQLStateSQLExceptionTranslator
*/
public void setJdbcExceptionTranslator(SQLExceptionTranslator jdbcExceptionTranslator) {
this.jdbcExceptionTranslator = jdbcExceptionTranslator;
@@ -247,7 +247,7 @@ public class HibernateJpaDialect extends DefaultJpaDialect {
DataAccessException dae = this.jdbcExceptionTranslator.translate(
"Hibernate operation: " + jdbcEx.getMessage(), jdbcEx.getSQL(), jdbcEx.getSQLException());
if (dae != null) {
return dae;
throw dae;
}
}

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