From eed1a58b817e6c12f19bdaacf016194ff03ed42e Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 30 Apr 2013 21:30:27 +0200 Subject: [PATCH 001/708] Fixed annotation attribute processing for enum arrays Based on https://github.com/SpringSource/spring-framework/pull/263 (cherry picked from commit 06fdfb0) --- .../AnnotationAttributesReadingVisitor.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationAttributesReadingVisitor.java b/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationAttributesReadingVisitor.java index 77a8fef3a0..0b89eeec78 100644 --- a/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationAttributesReadingVisitor.java +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationAttributesReadingVisitor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,6 +28,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.asm.AnnotationVisitor; import org.springframework.asm.SpringAsmInfo; import org.springframework.asm.Type; @@ -50,14 +51,12 @@ abstract class AbstractRecursiveAnnotationVisitor extends AnnotationVisitor { protected final ClassLoader classLoader; - public AbstractRecursiveAnnotationVisitor(ClassLoader classLoader, AnnotationAttributes attributes) { super(SpringAsmInfo.ASM_VERSION); this.classLoader = classLoader; this.attributes = attributes; } - public void visit(String attributeName, Object attributeValue) { this.attributes.put(attributeName, attributeValue); } @@ -74,6 +73,11 @@ public AnnotationVisitor visitArray(String attributeName) { } public void visitEnum(String attributeName, String asmTypeDescriptor, String attributeValue) { + Object newValue = getEnumValue(asmTypeDescriptor, attributeValue); + visit(attributeName, newValue); + } + + protected Object getEnumValue(String asmTypeDescriptor, String attributeValue) { Object valueToUse = attributeValue; try { Class enumType = this.classLoader.loadClass(Type.getType(asmTypeDescriptor).getClassName()); @@ -88,7 +92,7 @@ public void visitEnum(String attributeName, String asmTypeDescriptor, String att catch (IllegalAccessException ex) { this.logger.warn("Could not access enum value while reading annotation metadata", ex); } - this.attributes.put(attributeName, valueToUse); + return valueToUse; } } @@ -104,7 +108,6 @@ final class RecursiveAnnotationArrayVisitor extends AbstractRecursiveAnnotationV private final List allNestedAttributes = new ArrayList(); - public RecursiveAnnotationArrayVisitor( String attributeName, AnnotationAttributes attributes, ClassLoader classLoader) { super(classLoader, attributes); @@ -152,14 +155,12 @@ class RecursiveAnnotationAttributesVisitor extends AbstractRecursiveAnnotationVi private final String annotationType; - public RecursiveAnnotationAttributesVisitor( String annotationType, AnnotationAttributes attributes, ClassLoader classLoader) { super(classLoader, attributes); this.annotationType = annotationType; } - public final void visitEnd() { try { Class annotationClass = this.classLoader.loadClass(this.annotationType); @@ -223,7 +224,6 @@ final class AnnotationAttributesReadingVisitor extends RecursiveAnnotationAttrib private final Map> metaAnnotationMap; - public AnnotationAttributesReadingVisitor( String annotationType, Map attributesMap, Map> metaAnnotationMap, ClassLoader classLoader) { @@ -258,4 +258,5 @@ private void registerMetaAnnotations(Class annotationClass) { this.metaAnnotationMap.put(annotationClass.getName(), metaAnnotationTypeNames); } } + } From fa44ab633636eb5125ada69e24fabe4e574ad6ca Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 30 Apr 2013 22:09:17 +0200 Subject: [PATCH 002/708] Removed unnecessary CGLIB 2.2 dependency from spring-test-mvc module (cherry picked from commit 657bd80) --- build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/build.gradle b/build.gradle index 3f6d91ff22..455638e263 100644 --- a/build.gradle +++ b/build.gradle @@ -695,7 +695,6 @@ project("spring-test-mvc") { testCompile(project(":spring-context-support")) testCompile(project(":spring-oxm")) testCompile("com.thoughtworks.xstream:xstream:1.3.1") - testCompile("cglib:cglib-nodep:2.2") testCompile("rome:rome:1.0") testCompile("javax.activation:activation:1.1") testCompile("javax.mail:mail:1.4") From 5ff2dfbe134675bfee636eb4f5801e73f16c1099 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 30 Apr 2013 22:09:50 +0200 Subject: [PATCH 003/708] Removed references to deprecated queryForInt method from documentation Issue: SPR-10257 (cherry picked from commit 6be954e) --- src/reference/docbook/jdbc.xml | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/reference/docbook/jdbc.xml b/src/reference/docbook/jdbc.xml index f4af0e3b23..39ff7f3b75 100644 --- a/src/reference/docbook/jdbc.xml +++ b/src/reference/docbook/jdbc.xml @@ -304,12 +304,12 @@ Here is a simple query for getting the number of rows in a relation: - int rowCount = this.jdbcTemplate.queryForInt("select count(*) from t_actor"); + int rowCount = this.jdbcTemplate.queryForObject("select count(*) from t_actor", int.class); A simple query using a bind variable: - int countOfActorsNamedJoe = this.jdbcTemplate.queryForInt( - "select count(*) from t_actor where first_name = ?", "Joe"); + int countOfActorsNamedJoe = this.jdbcTemplate.queryForObject( + "select count(*) from t_actor where first_name = ?", int.class, "Joe"); Querying for a String: @@ -567,7 +567,7 @@ public int countOfActorsByFirstName(String firstName) { SqlParameterSource namedParameters = new MapSqlParameterSource("first_name", firstName); - return namedParameterJdbcTemplate.queryForInt(sql, namedParameters); + return this.namedParameterJdbcTemplate.queryForObject(sql, int.class, namedParameters); } Notice the use of the named parameter notation in the value @@ -598,9 +598,9 @@ public int countOfActorsByFirstName(String firstName) { String sql = "select count(*) from T_ACTOR where first_name = :first_name"; - Map namedParameters = Collections.singletonMap("first_name", firstName); + Map<String, String> namedParameters = Collections.singletonMap("first_name", firstName); - return this.namedParameterJdbcTemplate.queryForInt(sql, namedParameters); + return this.namedParameterJdbcTemplate.queryForObject(sql, int.class, namedParameters); } One nice feature related to the @@ -664,7 +664,7 @@ public int countOfActors(Actor exampleActor) { SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(exampleActor); - return this.namedParameterJdbcTemplate.queryForInt(sql, namedParameters); + return this.namedParameterJdbcTemplate.queryForObject(sql, int.class, namedParameters); } Remember that the @@ -838,10 +838,8 @@ public class ExecuteAStatement {
Running queries - Some query methods return a single value. To retrieve a count or a - specific value from one row, use - queryForInt(..), - queryForLong(..) or + Some query methods return a single value. To retrieve a count or + a specific value from one row, use queryForObject(..). The latter converts the returned JDBC Type to the Java class that is passed in as an argument. If the type conversion is invalid, then an @@ -862,11 +860,11 @@ public class RunAQuery { } public int getCount() { - return this.jdbcTemplate.queryForInt("select count(*) from mytable"); + return this.jdbcTemplate.queryForObject("select count(*) from mytable", int.class); } public String getName() { - return (String) this.jdbcTemplate.queryForObject("select name from mytable", String.class); + return this.jdbcTemplate.queryForObject("select name from mytable", String.class); } public void setDataSource(DataSource dataSource) { From 03033f6c06a37d0899aa6f035f634d9d2d22c571 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 30 Apr 2013 22:10:26 +0200 Subject: [PATCH 004/708] Fixed typo in code example Issue: SPR-10394 (cherry picked from commit f374b7a) --- src/reference/docbook/scheduling.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reference/docbook/scheduling.xml b/src/reference/docbook/scheduling.xml index a30a0f7d99..6e3ab22cdb 100644 --- a/src/reference/docbook/scheduling.xml +++ b/src/reference/docbook/scheduling.xml @@ -380,7 +380,7 @@ public class TaskExecutorExample { @Configuration @EnableAsync -@EnableSCheduling +@EnableScheduling public class AppConfig { } From 886cf825cb53eeae908e07e7f48afcfe6622a4dc Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 30 Apr 2013 22:37:34 +0200 Subject: [PATCH 005/708] Added "getName()" accessor to MethodReference Issue: SPR-10422 (cherry picked from commit 5ff2653) --- .../expression/spel/ast/MethodReference.java | 159 +++++++++--------- 1 file changed, 84 insertions(+), 75 deletions(-) diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java index 9c74b9c564..e109278149 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/MethodReference.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,70 +47,14 @@ public class MethodReference extends SpelNodeImpl { public MethodReference(boolean nullSafe, String methodName, int pos, SpelNodeImpl... arguments) { - super(pos,arguments); + super(pos, arguments); this.name = methodName; this.nullSafe = nullSafe; } - class MethodValueRef implements ValueRef { - - private ExpressionState state; - private EvaluationContext evaluationContext; - private Object target; - private Object[] arguments; - - MethodValueRef(ExpressionState state, EvaluationContext evaluationContext, Object object, Object[] arguments) { - this.state = state; - this.evaluationContext = evaluationContext; - this.target = object; - this.arguments = arguments; - } - - public TypedValue getValue() { - MethodExecutor executorToUse = cachedExecutor; - if (executorToUse != null) { - try { - return executorToUse.execute(evaluationContext, target, arguments); - } - catch (AccessException ae) { - // Two reasons this can occur: - // 1. the method invoked actually threw a real exception - // 2. the method invoked was not passed the arguments it expected and has become 'stale' - - // In the first case we should not retry, in the second case we should see if there is a - // better suited method. - - // To determine which situation it is, the AccessException will contain a cause. - // If the cause is an InvocationTargetException, a user exception was thrown inside the method. - // Otherwise the method could not be invoked. - throwSimpleExceptionIfPossible(state, ae); - - // at this point we know it wasn't a user problem so worth a retry if a better candidate can be found - cachedExecutor = null; - } - } - - // either there was no accessor or it no longer existed - executorToUse = findAccessorForMethod(name, getTypes(arguments), target, evaluationContext); - cachedExecutor = executorToUse; - try { - return executorToUse.execute(evaluationContext, target, arguments); - } catch (AccessException ae) { - // Same unwrapping exception handling as above in above catch block - throwSimpleExceptionIfPossible(state, ae); - throw new SpelEvaluationException( getStartPosition(), ae, SpelMessage.EXCEPTION_DURING_METHOD_INVOCATION, - name, state.getActiveContextObject().getValue().getClass().getName(), ae.getMessage()); - } - } - - public void setValue(Object newValue) { - throw new IllegalAccessError(); - } - - public boolean isWritable() { - return false; - } + public final String getName() { + return this.name; } @Override @@ -122,22 +66,21 @@ protected ValueRef getValueRef(ExpressionState state) throws EvaluationException // expressions try { state.pushActiveContextObject(state.getRootContextObject()); - arguments[i] = children[i].getValueInternal(state).getValue(); + arguments[i] = this.children[i].getValueInternal(state).getValue(); } finally { state.popActiveContextObject(); } } if (currentContext.getValue() == null) { - if (nullSafe) { + if (this.nullSafe) { return ValueRef.NullValueRef.instance; } else { throw new SpelEvaluationException(getStartPosition(), SpelMessage.METHOD_CALL_ON_NULL_OBJECT_NOT_ALLOWED, - FormatHelper.formatMethodForMessage(name, getTypes(arguments))); + FormatHelper.formatMethodForMessage(this.name, getTypes(arguments))); } } - return new MethodValueRef(state,state.getEvaluationContext(),state.getActiveContextObject().getValue(),arguments); } @@ -150,7 +93,7 @@ public TypedValue getValueInternal(ExpressionState state) throws EvaluationExcep // expressions try { state.pushActiveContextObject(state.getRootContextObject()); - arguments[i] = children[i].getValueInternal(state).getValue(); + arguments[i] = this.children[i].getValueInternal(state).getValue(); } finally { state.popActiveContextObject(); @@ -162,7 +105,7 @@ public TypedValue getValueInternal(ExpressionState state) throws EvaluationExcep } else { throw new SpelEvaluationException(getStartPosition(), SpelMessage.METHOD_CALL_ON_NULL_OBJECT_NOT_ALLOWED, - FormatHelper.formatMethodForMessage(name, getTypes(arguments))); + FormatHelper.formatMethodForMessage(this.name, getTypes(arguments))); } } @@ -196,7 +139,8 @@ public TypedValue getValueInternal(ExpressionState state) throws EvaluationExcep try { return executorToUse.execute( state.getEvaluationContext(), state.getActiveContextObject().getValue(), arguments); - } catch (AccessException ae) { + } + catch (AccessException ae) { // Same unwrapping exception handling as above in above catch block throwSimpleExceptionIfPossible(state, ae); throw new SpelEvaluationException( getStartPosition(), ae, SpelMessage.EXCEPTION_DURING_METHOD_INVOCATION, @@ -204,7 +148,6 @@ public TypedValue getValueInternal(ExpressionState state) throws EvaluationExcep } } - /** * Decode the AccessException, throwing a lightweight evaluation exception or, if the cause was a RuntimeException, * throw the RuntimeException directly. @@ -235,7 +178,7 @@ private List getTypes(Object... arguments) { @Override public String toStringAST() { StringBuilder sb = new StringBuilder(); - sb.append(name).append("("); + sb.append(this.name).append("("); for (int i = 0; i < getChildCount(); i++) { if (i > 0) sb.append(","); @@ -245,9 +188,9 @@ public String toStringAST() { return sb.toString(); } - private MethodExecutor findAccessorForMethod(String name, - List argumentTypes, ExpressionState state) + private MethodExecutor findAccessorForMethod(String name, List argumentTypes, ExpressionState state) throws SpelEvaluationException { + return findAccessorForMethod(name,argumentTypes,state.getActiveContextObject().getValue(),state.getEvaluationContext()); } @@ -259,19 +202,85 @@ private MethodExecutor findAccessorForMethod(String name, if (mResolvers != null) { for (MethodResolver methodResolver : mResolvers) { try { - MethodExecutor cEx = methodResolver.resolve( - eContext, contextObject, name, argumentTypes); + MethodExecutor cEx = methodResolver.resolve(eContext, contextObject, name, argumentTypes); if (cEx != null) { return cEx; } } catch (AccessException ex) { - throw new SpelEvaluationException(getStartPosition(),ex, SpelMessage.PROBLEM_LOCATING_METHOD, name, contextObject.getClass()); + throw new SpelEvaluationException(getStartPosition(),ex, SpelMessage.PROBLEM_LOCATING_METHOD, + name, contextObject.getClass()); } } } - throw new SpelEvaluationException(getStartPosition(),SpelMessage.METHOD_NOT_FOUND, FormatHelper.formatMethodForMessage(name, argumentTypes), + throw new SpelEvaluationException(getStartPosition(),SpelMessage.METHOD_NOT_FOUND, + FormatHelper.formatMethodForMessage(name, argumentTypes), FormatHelper.formatClassNameForMessage(contextObject instanceof Class ? ((Class) contextObject) : contextObject.getClass())); } + + private class MethodValueRef implements ValueRef { + + private final ExpressionState state; + + private final EvaluationContext evaluationContext; + + private final Object target; + + private final Object[] arguments; + + MethodValueRef(ExpressionState state, EvaluationContext evaluationContext, Object object, Object[] arguments) { + this.state = state; + this.evaluationContext = evaluationContext; + this.target = object; + this.arguments = arguments; + } + + public TypedValue getValue() { + MethodExecutor executorToUse = cachedExecutor; + if (executorToUse != null) { + try { + return executorToUse.execute(this.evaluationContext, this.target, this.arguments); + } + catch (AccessException ae) { + // Two reasons this can occur: + // 1. the method invoked actually threw a real exception + // 2. the method invoked was not passed the arguments it expected and has become 'stale' + + // In the first case we should not retry, in the second case we should see if there is a + // better suited method. + + // To determine which situation it is, the AccessException will contain a cause. + // If the cause is an InvocationTargetException, a user exception was thrown inside the method. + // Otherwise the method could not be invoked. + throwSimpleExceptionIfPossible(this.state, ae); + + // at this point we know it wasn't a user problem so worth a retry if a better candidate can be found + cachedExecutor = null; + } + } + + // either there was no accessor or it no longer existed + executorToUse = findAccessorForMethod(name, getTypes(this.arguments), this.target, this.evaluationContext); + cachedExecutor = executorToUse; + try { + return executorToUse.execute(this.evaluationContext, this.target, this.arguments); + } + catch (AccessException ae) { + // Same unwrapping exception handling as above in above catch block + throwSimpleExceptionIfPossible(this.state, ae); + throw new SpelEvaluationException( getStartPosition(), ae, SpelMessage.EXCEPTION_DURING_METHOD_INVOCATION, + name, this.state.getActiveContextObject().getValue().getClass().getName(), ae.getMessage()); + } + } + + public void setValue(Object newValue) { + throw new IllegalAccessError(); + } + + public boolean isWritable() { + return false; + } + } + } From 265c0c1505dafdbbd7391367081f1d6eddd5c47e Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 23 Apr 2013 13:49:25 +0200 Subject: [PATCH 006/708] Minimized ASM usage In particular, avoid accidental usage of ASM for core JDK types - which will fail in case of a new bytecode version in the JDK, even if the application itself has been compiled with an earlier bytecode target. Issue: SPR-10292 (cherry picked from commit d3a4068) --- .../AnnotatedGenericBeanDefinition.java | 14 +- .../annotation/ConfigurationClass.java | 7 +- ...onfigurationClassBeanDefinitionReader.java | 4 +- .../annotation/ConfigurationClassParser.java | 229 +++++++++++------- ...tedConfigurationClassEnhancementTests.java | 4 +- ...lVariableTableParameterNameDiscoverer.java | 11 +- .../core/type/StandardAnnotationMetadata.java | 4 +- .../AnnotationMetadataReadingVisitor.java | 19 +- .../classreading/SimpleMetadataReader.java | 2 +- .../EnableTransactionManagementTests.java | 14 +- .../EnableCachingIntegrationTests.java | 15 +- ...TransactionManagementIntegrationTests.java | 5 +- 12 files changed, 198 insertions(+), 130 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AnnotatedGenericBeanDefinition.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AnnotatedGenericBeanDefinition.java index f0fd4c5cac..53c1011b8c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AnnotatedGenericBeanDefinition.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AnnotatedGenericBeanDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -58,15 +58,19 @@ public AnnotatedGenericBeanDefinition(Class beanClass) { * allowing for ASM-based processing and avoidance of early loading of the bean class. * Note that this constructor is functionally equivalent to * {@link org.springframework.context.annotation.ScannedGenericBeanDefinition - * ScannedGenericBeanDefinition}, however the semantics of the latter indicate that - * a bean was discovered specifically via component-scanning as opposed to other - * means. + * ScannedGenericBeanDefinition}, however the semantics of the latter indicate that a + * bean was discovered specifically via component-scanning as opposed to other means. * @param metadata the annotation metadata for the bean class in question * @since 3.1.1 */ public AnnotatedGenericBeanDefinition(AnnotationMetadata metadata) { Assert.notNull(metadata, "AnnotationMetadata must not be null"); - setBeanClassName(metadata.getClassName()); + if (metadata instanceof StandardAnnotationMetadata) { + setBeanClass(((StandardAnnotationMetadata) metadata).getIntrospectedClass()); + } + else { + setBeanClassName(metadata.getClassName()); + } this.metadata = metadata; } diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClass.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClass.java index a25fdcf4ce..0a5a646092 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClass.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClass.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -81,7 +81,7 @@ public ConfigurationClass(MetadataReader metadataReader, String beanName) { * using the {@link Import} annotation or automatically processed as a nested * configuration class (if imported is {@code true}). * @param metadataReader reader used to parse the underlying {@link Class} - * @param beanName name of the {@code @Configuration} class bean + * @param imported whether the given configuration class is being imported * @since 3.1.1 */ public ConfigurationClass(MetadataReader metadataReader, boolean imported) { @@ -110,7 +110,7 @@ public ConfigurationClass(Class clazz, String beanName) { * using the {@link Import} annotation or automatically processed as a nested * configuration class (if imported is {@code true}). * @param clazz the underlying {@link Class} to represent - * @param beanName name of the {@code @Configuration} class bean + * @param imported whether the given configuration class is being imported * @since 3.1.1 */ public ConfigurationClass(Class clazz, boolean imported) { @@ -119,6 +119,7 @@ public ConfigurationClass(Class clazz, boolean imported) { this.imported = imported; } + public AnnotationMetadata getMetadata() { return this.metadata; } diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java index e1676c6eb2..27ae171955 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassBeanDefinitionReader.java @@ -134,8 +134,6 @@ private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configC private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) { AnnotationMetadata metadata = configClass.getMetadata(); BeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata); - String className = metadata.getClassName(); - configBeanDef.setBeanClassName(className); if (ConfigurationClassUtils.checkConfigurationClassCandidate(configBeanDef, this.metadataReaderFactory)) { String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry); this.registry.registerBeanDefinition(configBeanName, configBeanDef); @@ -146,7 +144,7 @@ private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationCl } else { this.problemReporter.error( - new InvalidConfigurationImportProblem(className, configClass.getResource(), metadata)); + new InvalidConfigurationImportProblem(metadata.getClassName(), configClass.getResource(), metadata)); } } diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java index c03aca61dd..c5a47b891a 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java @@ -17,11 +17,12 @@ package org.springframework.context.annotation; import java.io.IOException; +import java.lang.annotation.Annotation; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Map; @@ -42,6 +43,7 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.context.ResourceLoaderAware; +import org.springframework.core.NestedIOException; import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.env.CompositePropertySource; import org.springframework.core.env.Environment; @@ -54,7 +56,7 @@ import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.core.type.filter.AssignableTypeFilter; -import org.springframework.util.CollectionUtils; +import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import static org.springframework.context.annotation.MetadataUtils.*; @@ -171,45 +173,12 @@ protected AnnotationMetadata doProcessConfigurationClass( ConfigurationClass configClass, AnnotationMetadata metadata) throws IOException { // recursively process any member (nested) classes first - for (String memberClassName : metadata.getMemberClassNames()) { - MetadataReader reader = this.metadataReaderFactory.getMetadataReader(memberClassName); - AnnotationMetadata memberClassMetadata = reader.getAnnotationMetadata(); - if (ConfigurationClassUtils.isConfigurationCandidate(memberClassMetadata)) { - processConfigurationClass(new ConfigurationClass(reader, true)); - } - } + processMemberClasses(metadata); // process any @PropertySource annotations - AnnotationAttributes propertySource = - attributesFor(metadata, org.springframework.context.annotation.PropertySource.class); + AnnotationAttributes propertySource = attributesFor(metadata, org.springframework.context.annotation.PropertySource.class); if (propertySource != null) { - String name = propertySource.getString("name"); - String[] locations = propertySource.getStringArray("value"); - int nLocations = locations.length; - if (nLocations == 0) { - throw new IllegalArgumentException("At least one @PropertySource(value) location is required"); - } - for (int i = 0; i < nLocations; i++) { - locations[i] = this.environment.resolveRequiredPlaceholders(locations[i]); - } - ClassLoader classLoader = this.resourceLoader.getClassLoader(); - if (!StringUtils.hasText(name)) { - for (String location : locations) { - this.propertySources.push(new ResourcePropertySource(location, classLoader)); - } - } - else { - if (nLocations == 1) { - this.propertySources.push(new ResourcePropertySource(name, locations[0], classLoader)); - } - else { - CompositePropertySource ps = new CompositePropertySource(name); - for (String location : locations) { - ps.addPropertySource(new ResourcePropertySource(location, classLoader)); - } - this.propertySources.push(ps); - } - } + processPropertySource(propertySource); } // process any @ComponentScan annotions @@ -228,9 +197,12 @@ protected AnnotationMetadata doProcessConfigurationClass( } // process any @Import annotations - Set imports = getImports(metadata.getClassName(), null, new HashSet()); - if (!CollectionUtils.isEmpty(imports)) { - processImport(configClass, imports.toArray(new String[imports.size()]), true); + + Set imports = new LinkedHashSet(); + Set visited = new LinkedHashSet(); + collectImports(metadata, imports, visited); + if (!imports.isEmpty()) { + processImport(configClass, imports, true); } // process any @ImportResource annotations @@ -279,6 +251,65 @@ else if (superclass.startsWith("java")) { return null; } + /** + * Register member (nested) classes that happen to be configuration classes themselves. + * @param metadata the metadata representation of the containing class + * @throws IOException if there is any problem reading metadata from a member class + */ + private void processMemberClasses(AnnotationMetadata metadata) throws IOException { + if (metadata instanceof StandardAnnotationMetadata) { + for (Class memberClass : ((StandardAnnotationMetadata) metadata).getIntrospectedClass().getDeclaredClasses()) { + if (ConfigurationClassUtils.isConfigurationCandidate(new StandardAnnotationMetadata(memberClass))) { + processConfigurationClass(new ConfigurationClass(memberClass, true)); + } + } + } + else { + for (String memberClassName : metadata.getMemberClassNames()) { + MetadataReader reader = this.metadataReaderFactory.getMetadataReader(memberClassName); + AnnotationMetadata memberClassMetadata = reader.getAnnotationMetadata(); + if (ConfigurationClassUtils.isConfigurationCandidate(memberClassMetadata)) { + processConfigurationClass(new ConfigurationClass(reader, true)); + } + } + } + } + + /** + * Process the given @PropertySource annotation metadata. + * @param propertySource metadata for the @PropertySource annotation found + * @throws IOException if loading a property source failed + */ + private void processPropertySource(AnnotationAttributes propertySource) throws IOException { + String name = propertySource.getString("name"); + String[] locations = propertySource.getStringArray("value"); + int nLocations = locations.length; + if (nLocations == 0) { + throw new IllegalArgumentException("At least one @PropertySource(value) location is required"); + } + for (int i = 0; i < nLocations; i++) { + locations[i] = this.environment.resolveRequiredPlaceholders(locations[i]); + } + ClassLoader classLoader = this.resourceLoader.getClassLoader(); + if (!StringUtils.hasText(name)) { + for (String location : locations) { + this.propertySources.push(new ResourcePropertySource(location, classLoader)); + } + } + else { + if (nLocations == 1) { + this.propertySources.push(new ResourcePropertySource(name, locations[0], classLoader)); + } + else { + CompositePropertySource ps = new CompositePropertySource(name); + for (String location : locations) { + ps.addPropertySource(new ResourcePropertySource(location, classLoader)); + } + this.propertySources.push(ps); + } + } + } + /** * Recursively collect all declared {@code @Import} values. Unlike most * meta-annotations it is valid to have several {@code @Import}s declared with @@ -287,69 +318,99 @@ else if (superclass.startsWith("java")) { *

For example, it is common for a {@code @Configuration} class to declare direct * {@code @Import}s in addition to meta-imports originating from an {@code @Enable} * annotation. - * @param className the class name to search - * @param imports the imports collected so far or {@code null} - * @param visited used to track visited classes to prevent infinite recursion (must not be null) - * @return a set of all {@link Import#value() import values} or {@code null} + * @param metadata the metadata representation of the class to search + * @param imports the imports collected so far + * @param visited used to track visited classes to prevent infinite recursion * @throws IOException if there is any problem reading metadata from the named class */ - private Set getImports(String className, Set imports, Set visited) throws IOException { - if (visited.add(className) && !className.startsWith("java")) { - AnnotationMetadata metadata = metadataReaderFactory.getMetadataReader(className).getAnnotationMetadata(); - for (String annotationType : metadata.getAnnotationTypes()) { - imports = getImports(annotationType, imports, visited); + private void collectImports(AnnotationMetadata metadata, Set imports, Set visited) throws IOException { + String className = metadata.getClassName(); + if (visited.add(className)) { + if (metadata instanceof StandardAnnotationMetadata) { + StandardAnnotationMetadata stdMetadata = (StandardAnnotationMetadata) metadata; + for (Annotation ann : stdMetadata.getIntrospectedClass().getAnnotations()) { + if (!ann.annotationType().getName().startsWith("java") && !(ann instanceof Import)) { + collectImports(new StandardAnnotationMetadata(ann.annotationType()), imports, visited); + } + } + Map attributes = stdMetadata.getAnnotationAttributes(Import.class.getName(), false); + if (attributes != null) { + Class[] value = (Class[]) attributes.get("value"); + if (!ObjectUtils.isEmpty(value)) { + imports.addAll(Arrays.asList(value)); + } + } } - Map attributes = metadata.getAnnotationAttributes(Import.class.getName(), true); - if (attributes != null) { - String[] value = (String[]) attributes.get("value"); - if (value != null && value.length > 0) { - imports = (imports == null ? new LinkedHashSet() : imports); - imports.addAll(Arrays.asList(value)); + else { + for (String annotationType : metadata.getAnnotationTypes()) { + if (!className.startsWith("java") && !className.equals(Import.class.getName())) { + try { + collectImports( + new StandardAnnotationMetadata(this.resourceLoader.getClassLoader().loadClass(annotationType)), + imports, visited); + } + catch (ClassNotFoundException ex) { + // + } + } + } + Map attributes = metadata.getAnnotationAttributes(Import.class.getName(), true); + if (attributes != null) { + String[] value = (String[]) attributes.get("value"); + if (!ObjectUtils.isEmpty(value)) { + imports.addAll(Arrays.asList(value)); + } } } } - return imports; } - private void processImport(ConfigurationClass configClass, String[] classesToImport, boolean checkForCircularImports) throws IOException { + private void processImport(ConfigurationClass configClass, Collection classesToImport, boolean checkForCircularImports) throws IOException { if (checkForCircularImports && this.importStack.contains(configClass)) { this.problemReporter.error(new CircularImportProblem(configClass, this.importStack, configClass.getMetadata())); } else { this.importStack.push(configClass); AnnotationMetadata importingClassMetadata = configClass.getMetadata(); - for (String candidate : classesToImport) { - MetadataReader reader = this.metadataReaderFactory.getMetadataReader(candidate); - if (new AssignableTypeFilter(ImportSelector.class).match(reader, this.metadataReaderFactory)) { - // the candidate class is an ImportSelector -> delegate to it to determine imports - try { - ImportSelector selector = BeanUtils.instantiateClass( - this.resourceLoader.getClassLoader().loadClass(candidate), ImportSelector.class); - processImport(configClass, selector.selectImports(importingClassMetadata), false); + try { + for (Object candidate : classesToImport) { + Object candidateToCheck = (candidate instanceof Class ? (Class) candidate : this.metadataReaderFactory.getMetadataReader((String) candidate)); + if (checkAssignability(ImportSelector.class, candidateToCheck)) { + // the candidate class is an ImportSelector -> delegate to it to determine imports + Class candidateClass = (candidate instanceof Class ? (Class) candidate : this.resourceLoader.getClassLoader().loadClass((String) candidate)); + ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class); + processImport(configClass, Arrays.asList(selector.selectImports(importingClassMetadata)), false); } - catch (ClassNotFoundException ex) { - throw new IllegalStateException(ex); - } - } - else if (new AssignableTypeFilter(ImportBeanDefinitionRegistrar.class).match(reader, this.metadataReaderFactory)) { - // the candidate class is an ImportBeanDefinitionRegistrar -> delegate to it to register additional bean definitions - try { - ImportBeanDefinitionRegistrar registrar = BeanUtils.instantiateClass( - this.resourceLoader.getClassLoader().loadClass(candidate), ImportBeanDefinitionRegistrar.class); + else if (checkAssignability(ImportBeanDefinitionRegistrar.class, candidateToCheck)) { + // the candidate class is an ImportBeanDefinitionRegistrar -> delegate to it to register additional bean definitions + Class candidateClass = (candidate instanceof Class ? (Class) candidate : this.resourceLoader.getClassLoader().loadClass((String) candidate)); + ImportBeanDefinitionRegistrar registrar = BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class); invokeAwareMethods(registrar); - registrar.registerBeanDefinitions(importingClassMetadata, registry); + registrar.registerBeanDefinitions(importingClassMetadata, this.registry); } - catch (ClassNotFoundException ex) { - throw new IllegalStateException(ex); + else { + // candidate class not an ImportSelector or ImportBeanDefinitionRegistrar -> process it as a @Configuration class + this.importStack.registerImport(importingClassMetadata.getClassName(), (candidate instanceof Class ? ((Class) candidate).getName() : (String) candidate)); + processConfigurationClass(candidateToCheck instanceof Class ? new ConfigurationClass((Class) candidateToCheck, true) : + new ConfigurationClass((MetadataReader) candidateToCheck, true)); } } - else { - // the candidate class not an ImportSelector or ImportBeanDefinitionRegistrar -> process it as a @Configuration class - this.importStack.registerImport(importingClassMetadata.getClassName(), candidate); - processConfigurationClass(new ConfigurationClass(reader, true)); - } } - this.importStack.pop(); + catch (ClassNotFoundException ex) { + throw new NestedIOException("Failed to load import candidate class", ex); + } + finally { + this.importStack.pop(); + } + } + } + + private boolean checkAssignability(Class clazz, Object candidate) throws IOException { + if (candidate instanceof Class) { + return clazz.isAssignableFrom((Class) candidate); + } + else { + return new AssignableTypeFilter(clazz).match((MetadataReader) candidate, this.metadataReaderFactory); } } diff --git a/spring-context/src/test/java/org/springframework/context/annotation/configuration/ImportedConfigurationClassEnhancementTests.java b/spring-context/src/test/java/org/springframework/context/annotation/configuration/ImportedConfigurationClassEnhancementTests.java index e895a207c3..2bc75f1754 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/configuration/ImportedConfigurationClassEnhancementTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/configuration/ImportedConfigurationClassEnhancementTests.java @@ -79,7 +79,7 @@ private void autowiredConfigClassBeanMethodsRespectScoping(Class... configCla @Test(expected=BeanDefinitionParsingException.class) - public void importingAnNonConfigurationClassCausesIllegalArgumentException() { + public void importingNonConfigurationClassCausesBeanDefinitionParsingException() { new AnnotationConfigApplicationContext(ConfigThatImportsNonConfigClass.class); } } @@ -103,5 +103,5 @@ class ConfigThatDoesImport extends Config { } class ConfigThatDoesNotImport extends Config { } @Configuration -@Import(Void.class) +@Import(TestBean.class) class ConfigThatImportsNonConfigClass { } \ No newline at end of file diff --git a/spring-core/src/main/java/org/springframework/core/LocalVariableTableParameterNameDiscoverer.java b/spring-core/src/main/java/org/springframework/core/LocalVariableTableParameterNameDiscoverer.java index 3cab216bf0..d846d4fdae 100644 --- a/spring-core/src/main/java/org/springframework/core/LocalVariableTableParameterNameDiscoverer.java +++ b/spring-core/src/main/java/org/springframework/core/LocalVariableTableParameterNameDiscoverer.java @@ -114,8 +114,15 @@ private Map inspectClass(Class clazz) { } catch (IOException ex) { if (logger.isDebugEnabled()) { - logger.debug("Exception thrown while reading '.class' file for class [" + clazz - + "] - unable to determine constructors/methods parameter names", ex); + logger.debug("Exception thrown while reading '.class' file for class [" + clazz + + "] - unable to determine constructors/methods parameter names", ex); + } + } + catch (IllegalArgumentException ex) { + if (logger.isDebugEnabled()) { + logger.debug("ASM ClassReader failed to parse class file [" + clazz + + "], probably due to a new Java class file version that isn't supported yet " + + "- unable to determine constructors/methods parameter names", ex); } } finally { diff --git a/spring-core/src/main/java/org/springframework/core/type/StandardAnnotationMetadata.java b/spring-core/src/main/java/org/springframework/core/type/StandardAnnotationMetadata.java index 1e5d00a9ca..37473c5a77 100644 --- a/spring-core/src/main/java/org/springframework/core/type/StandardAnnotationMetadata.java +++ b/spring-core/src/main/java/org/springframework/core/type/StandardAnnotationMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -145,6 +145,8 @@ public Map getAnnotationAttributes(String annotationType, boolea return AnnotationUtils.getAnnotationAttributes( ann, classValuesAsString, this.nestedAnnotationsAsMap); } + } + for (Annotation ann : anns) { for (Annotation metaAnn : ann.annotationType().getAnnotations()) { if (metaAnn.annotationType().getName().equals(annotationType)) { return AnnotationUtils.getAnnotationAttributes( diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationMetadataReadingVisitor.java b/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationMetadataReadingVisitor.java index 97a4897f81..982534a379 100644 --- a/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationMetadataReadingVisitor.java +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/AnnotationMetadataReadingVisitor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -105,19 +105,11 @@ public AnnotationAttributes getAnnotationAttributes(String annotationType) { } public AnnotationAttributes getAnnotationAttributes(String annotationType, boolean classValuesAsString) { - return getAnnotationAttributes(annotationType, classValuesAsString, false); - } - - public AnnotationAttributes getAnnotationAttributes( - String annotationType, boolean classValuesAsString, boolean nestedAttributesAsMap) { - AnnotationAttributes raw = this.attributeMap.get(annotationType); - return convertClassValues(raw, classValuesAsString, nestedAttributesAsMap); + return convertClassValues(raw, classValuesAsString); } - private AnnotationAttributes convertClassValues( - AnnotationAttributes original, boolean classValuesAsString, boolean nestedAttributesAsMap) { - + private AnnotationAttributes convertClassValues(AnnotationAttributes original, boolean classValuesAsString) { if (original == null) { return null; } @@ -126,12 +118,12 @@ private AnnotationAttributes convertClassValues( try { Object value = entry.getValue(); if (value instanceof AnnotationAttributes) { - value = convertClassValues((AnnotationAttributes)value, classValuesAsString, nestedAttributesAsMap); + value = convertClassValues((AnnotationAttributes) value, classValuesAsString); } else if (value instanceof AnnotationAttributes[]) { AnnotationAttributes[] values = (AnnotationAttributes[])value; for (int i = 0; i < values.length; i++) { - values[i] = convertClassValues(values[i], classValuesAsString, nestedAttributesAsMap); + values[i] = convertClassValues(values[i], classValuesAsString); } } else if (value instanceof Type) { @@ -182,4 +174,5 @@ public Set getAnnotatedMethods(String annotationType) { annotatedMethods.addAll(list); return annotatedMethods; } + } diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/SimpleMetadataReader.java b/spring-core/src/main/java/org/springframework/core/type/classreading/SimpleMetadataReader.java index af2d4c62d8..36c495e542 100644 --- a/spring-core/src/main/java/org/springframework/core/type/classreading/SimpleMetadataReader.java +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/SimpleMetadataReader.java @@ -64,7 +64,7 @@ final class SimpleMetadataReader implements MetadataReader { classReader.accept(visitor, ClassReader.SKIP_DEBUG); this.annotationMetadata = visitor; - // (since AnnotationMetadataReader extends ClassMetadataReadingVisitor) + // (since AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor) this.classMetadata = visitor; this.resource = resource; } diff --git a/spring-tx/src/test/java/org/springframework/transaction/annotation/EnableTransactionManagementTests.java b/spring-tx/src/test/java/org/springframework/transaction/annotation/EnableTransactionManagementTests.java index 5c11e26d5d..896170e048 100644 --- a/spring-tx/src/test/java/org/springframework/transaction/annotation/EnableTransactionManagementTests.java +++ b/spring-tx/src/test/java/org/springframework/transaction/annotation/EnableTransactionManagementTests.java @@ -16,13 +16,10 @@ package org.springframework.transaction.annotation; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.fail; - import java.util.Map; import org.junit.Test; + import org.springframework.aop.support.AopUtils; import org.springframework.context.annotation.AdviceMode; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -33,6 +30,9 @@ import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.AnnotationTransactionNamespaceHandlerTests.TransactionalTestBean; +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + /** * Tests demonstrating use of @EnableTransactionManagement @Configuration classes. * @@ -84,9 +84,9 @@ public void proxyTypeAspectJCausesRegistrationOfAnnotationTransactionAspect() { new AnnotationConfigApplicationContext(EnableAspectJTxConfig.class, TxManagerConfig.class); fail("should have thrown CNFE when trying to load AnnotationTransactionAspect. " + "Do you actually have org.springframework.aspects on the classpath?"); - } catch (Exception ex) { - System.out.println(ex); - assertThat(ex.getMessage().endsWith("AspectJTransactionManagementConfiguration.class] cannot be opened because it does not exist"), is(true)); + } + catch (Exception ex) { + assertThat(ex.getMessage().contains("AspectJTransactionManagementConfiguration"), is(true)); } } diff --git a/src/test/java/org/springframework/cache/annotation/EnableCachingIntegrationTests.java b/src/test/java/org/springframework/cache/annotation/EnableCachingIntegrationTests.java index 0f0fc64ac8..39d221b78e 100644 --- a/src/test/java/org/springframework/cache/annotation/EnableCachingIntegrationTests.java +++ b/src/test/java/org/springframework/cache/annotation/EnableCachingIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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,14 +16,11 @@ package org.springframework.cache.annotation; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; - import java.util.Collections; import java.util.List; import org.junit.Test; + import org.springframework.aop.Advisor; import org.springframework.aop.framework.Advised; import org.springframework.aop.support.AopUtils; @@ -36,6 +33,9 @@ import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Repository; +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + /** * Integration tests for the @EnableCaching annotation. * @@ -60,11 +60,12 @@ public void repositoryUsesAspectJAdviceMode() { ctx.register(Config.class, AspectJCacheConfig.class); try { ctx.refresh(); - } catch (Exception ex) { + } + catch (Exception ex) { // this test is a bit fragile, but gets the job done, proving that an // attempt was made to look up the AJ aspect. It's due to classpath issues // in .integration-tests that it's not found. - assertTrue(ex.getMessage().endsWith("AspectJCachingConfiguration.class] cannot be opened because it does not exist")); + assertTrue(ex.getMessage().contains("AspectJCachingConfiguration")); } } diff --git a/src/test/java/org/springframework/transaction/annotation/EnableTransactionManagementIntegrationTests.java b/src/test/java/org/springframework/transaction/annotation/EnableTransactionManagementIntegrationTests.java index 54511e7e7b..b5eb2f8e1b 100644 --- a/src/test/java/org/springframework/transaction/annotation/EnableTransactionManagementIntegrationTests.java +++ b/src/test/java/org/springframework/transaction/annotation/EnableTransactionManagementIntegrationTests.java @@ -115,11 +115,12 @@ public void repositoryUsesAspectJAdviceMode() { ctx.register(Config.class, AspectJTxConfig.class); try { ctx.refresh(); - } catch (Exception ex) { + } + catch (Exception ex) { // this test is a bit fragile, but gets the job done, proving that an // attempt was made to look up the AJ aspect. It's due to classpath issues // in .integration-tests that it's not found. - assertTrue(ex.getMessage().endsWith("AspectJTransactionManagementConfiguration.class] cannot be opened because it does not exist")); + assertTrue(ex.getMessage().contains("AspectJTransactionManagementConfiguration")); } } From d0513f7521bab5049f3ee08d86386b39d46362c3 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 23 Apr 2013 13:50:25 +0200 Subject: [PATCH 007/708] Revised Joda-Time support (as a side effect of JSR-310 support in Spring 4.0) Issue: SPR-9641 (cherry picked from commit b5d44e1) --- .../format/annotation/DateTimeFormat.java | 52 ++++--- .../format/datetime/DateFormatter.java | 24 ++-- .../datetime/DateFormatterRegistrar.java | 34 +++-- ...eTimeFormatAnnotationFormatterFactory.java | 5 +- .../joda/DateTimeFormatterFactory.java | 130 +++++++++--------- .../joda/DateTimeFormatterFactoryBean.java | 12 +- ...eTimeFormatAnnotationFormatterFactory.java | 24 ++-- .../format/datetime/joda/JodaTimeContext.java | 22 +-- .../datetime/joda/JodaTimeContextHolder.java | 5 +- .../datetime/joda/JodaTimeConverters.java | 88 +++++------- .../joda/JodaTimeFormatterRegistrar.java | 65 +++++---- .../format/datetime/joda/package-info.java | 2 +- .../DateTimeFormatterFactoryBeanTests.java | 3 +- .../joda/DateTimeFormatterFactoryTests.java | 3 +- .../joda/JodaTimeFormattingTests.java | 2 +- 15 files changed, 242 insertions(+), 229 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/format/annotation/DateTimeFormat.java b/spring-context/src/main/java/org/springframework/format/annotation/DateTimeFormat.java index 8d22560e0f..d2184e735f 100644 --- a/spring-context/src/main/java/org/springframework/format/annotation/DateTimeFormat.java +++ b/spring-context/src/main/java/org/springframework/format/annotation/DateTimeFormat.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -23,22 +23,26 @@ /** * Declares that a field should be formatted as a date time. - * Supports formatting by style pattern, ISO date time pattern, or custom format pattern string. - * Can be applied to {@code java.util.Date}, {@code java.util.Calendar}, {@code java.long.Long}, or Joda Time fields. - *

- * For style-based formatting, set the {@link #style()} attribute to be the style pattern code. + * + *

Supports formatting by style pattern, ISO date time pattern, or custom format pattern string. + * Can be applied to {@code java.util.Date}, {@code java.util.Calendar}, {@code java.long.Long}, + * Joda-Time value types; and as of Spring 4 and JDK 8, to JSR-310 java.time types too. + * + *

For style-based formatting, set the {@link #style()} attribute to be the style pattern code. * The first character of the code is the date style, and the second character is the time style. * Specify a character of 'S' for short style, 'M' for medium, 'L' for long, and 'F' for full. * A date or time may be omitted by specifying the style character '-'. - *

- * For ISO-based formatting, set the {@link #iso()} attribute to be the desired {@link ISO} format, such as {@link ISO#DATE}. -

- * For custom formatting, set the {@link #pattern()} attribute to be the DateTime pattern, such as {@code yyyy/MM/dd hh:mm:ss a}. - *

- * Each attribute is mutually exclusive, so only set one attribute per annotation instance (the one most convenient one for your formatting needs). + * + *

For ISO-based formatting, set the {@link #iso()} attribute to be the desired {@link ISO} format, + * such as {@link ISO#DATE}. For custom formatting, set the {@link #pattern()} attribute to be the + * DateTime pattern, such as {@code yyyy/MM/dd hh:mm:ss a}. + * + *

Each attribute is mutually exclusive, so only set one attribute per annotation instance + * (the one most convenient one for your formatting needs). * When the pattern attribute is specified, it takes precedence over both the style and ISO attribute. * When the iso attribute is specified, if takes precedence over the style attribute. - * When no annotation attributes are specified, the default format applied is style-based with a style code of 'SS' (short date, short time). + * When no annotation attributes are specified, the default format applied is style-based + * with a style code of 'SS' (short date, short time). * * @author Keith Donald * @author Juergen Hoeller @@ -51,23 +55,24 @@ /** * The style pattern to use to format the field. - * Defaults to 'SS' for short date time. - * Set this attribute when you wish to format your field in accordance with a common style other than the default style. + *

Defaults to 'SS' for short date time. Set this attribute when you wish to format + * your field in accordance with a common style other than the default style. */ String style() default "SS"; /** * The ISO pattern to use to format the field. * The possible ISO patterns are defined in the {@link ISO} enum. - * Defaults to ISO.NONE, indicating this attribute should be ignored. - * Set this attribute when you wish to format your field in accordance with an ISO date time format. + *

Defaults to {@link ISO#NONE}, indicating this attribute should be ignored. + * Set this attribute when you wish to format your field in accordance with an ISO format. */ ISO iso() default ISO.NONE; /** * The custom pattern to use to format the field. - * Defaults to empty String, indicating no custom pattern String has been specified. - * Set this attribute when you wish to format your field in accordance with a custom date time pattern not represented by a style or ISO format. + *

Defaults to empty String, indicating no custom pattern String has been specified. + * Set this attribute when you wish to format your field in accordance with a custom + * date time pattern not represented by a style or ISO format. */ String pattern() default ""; @@ -78,18 +83,21 @@ public enum ISO { /** - * The most common ISO Date Format {@code yyyy-MM-dd} e.g. 2000-10-31. + * The most common ISO Date Format {@code yyyy-MM-dd}, + * e.g. 2000-10-31. */ DATE, /** - * The most common ISO Time Format {@code HH:mm:ss.SSSZ} e.g. 01:30:00.000-05:00. + * The most common ISO Time Format {@code HH:mm:ss.SSSZ}, + * e.g. 01:30:00.000-05:00. */ TIME, /** - * The most common ISO DateTime Format {@code yyyy-MM-dd'T'HH:mm:ss.SSSZ} e.g. 2000-10-31 01:30:00.000-05:00. - * The default if no annotation value is specified. + * The most common ISO DateTime Format {@code yyyy-MM-dd'T'HH:mm:ss.SSSZ}, + * e.g. 2000-10-31 01:30:00.000-05:00. + *

This is the default if no annotation value is specified. */ DATE_TIME, diff --git a/spring-context/src/main/java/org/springframework/format/datetime/DateFormatter.java b/spring-context/src/main/java/org/springframework/format/datetime/DateFormatter.java index 365800aba8..b5c660f023 100644 --- a/spring-context/src/main/java/org/springframework/format/datetime/DateFormatter.java +++ b/spring-context/src/main/java/org/springframework/format/datetime/DateFormatter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -46,7 +46,7 @@ public class DateFormatter implements Formatter { private static final Map ISO_PATTERNS; static { - Map formats = new HashMap(); + Map formats = new HashMap(4); formats.put(ISO.DATE, "yyyy-MM-dd"); formats.put(ISO.TIME, "HH:mm:ss.SSSZ"); formats.put(ISO.DATE_TIME, "yyyy-MM-dd'T'HH:mm:ss.SSSZ"); @@ -89,6 +89,15 @@ public void setPattern(String pattern) { this.pattern = pattern; } + /** + * Set the ISO format used for this date. + * @param iso the {@link ISO} format + * @since 3.2 + */ + public void setIso(ISO iso) { + this.iso = iso; + } + /** * Set the style to use to format date values. *

If not specified, DateFormat's default style will be used. @@ -112,7 +121,7 @@ public void setStyle(int style) { *

  • 'F' = Full
  • *
  • '-' = Omitted
  • *