diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/support/AnnotationAdapter.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/support/AnnotationAdapter.java index a1846e2b9c..9fdbb32662 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/annotation/support/AnnotationAdapter.java +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/support/AnnotationAdapter.java @@ -37,7 +37,7 @@ class AnnotationAdapter implements AnnotationVisitor { * delegate. * * @param delegate In most cases, the delegate will simply be - * {@link AsmUtils#EMPTY_VISITOR} + * {@link AsmUtils#ASM_EMPTY_VISITOR} */ public AnnotationAdapter(AnnotationVisitor delegate) { this.delegate = delegate; diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/support/AsmUtils.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/support/AsmUtils.java index 5c7c4b42bc..76da289a0e 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/annotation/support/AsmUtils.java +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/support/AsmUtils.java @@ -16,46 +16,42 @@ package org.springframework.context.annotation.support; +import static java.lang.String.*; +import static org.springframework.util.ClassUtils.*; + +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.Proxy; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.asm.ClassReader; import org.springframework.asm.commons.EmptyVisitor; - +import org.springframework.util.ClassUtils; /** - * Various utility methods commonly used when interacting with ASM. + * Various utility methods commonly used when interacting with ASM, classloading + * and creating {@link MutableAnnotation} instances. + * + * @author Chris Beams */ class AsmUtils { - public static final EmptyVisitor EMPTY_VISITOR = new EmptyVisitor(); + public static final EmptyVisitor ASM_EMPTY_VISITOR = new EmptyVisitor(); private static final Log log = LogFactory.getLog(AsmUtils.class); - /** - * @param className a standard, dot-delimeted, fully-qualified Java class name - * @return internal version of className, as per ASM guide section 2.1.2 - * "Internal Names" - */ - public static String convertClassNameToInternalName(String className) { - return className.replace('.', '/'); - } - /** * Convert a type descriptor to a classname suitable for classloading with * Class.forName(). * * @param typeDescriptor see ASM guide section 2.1.3 */ - public static String convertTypeDescriptorToClassName(String typeDescriptor) { + public static String convertAsmTypeDescriptorToClassName(String typeDescriptor) { final String internalName; // See ASM guide section 2.1.2 - // TODO: SJC-242 should catch all possible cases. use case statement and switch on - // char - // TODO: SJC-242 converting from primitive to object here won't be intuitive to - // users if ("V".equals(typeDescriptor)) return Void.class.getName(); if ("I".equals(typeDescriptor)) @@ -84,9 +80,9 @@ class AsmUtils { /** * @param methodDescriptor see ASM guide section 2.1.4 */ - public static String getReturnTypeFromMethodDescriptor(String methodDescriptor) { + public static String getReturnTypeFromAsmMethodDescriptor(String methodDescriptor) { String returnTypeDescriptor = methodDescriptor.substring(methodDescriptor.indexOf(')') + 1); - return convertTypeDescriptorToClassName(returnTypeDescriptor); + return convertAsmTypeDescriptorToClassName(returnTypeDescriptor); } /** @@ -97,22 +93,9 @@ class AsmUtils { * classpath * @throws RuntimeException if an IOException occurs when creating the new ClassReader */ - public static ClassReader newClassReader(String pathToClass, ClassLoader classLoader) { - InputStream is = Util.getClassAsStream(pathToClass, classLoader); - return newClassReader(is); - } - - /** - * Convenience method that simply returns a new ASM {@link ClassReader} instance based - * on the supplied bytes byte array. This method is exactly equivalent to - * calling new ClassReader(byte[]), and is mainly provided for symmetry with usage of - * {@link #newClassReader(InputStream)}. - * - * @param bytes byte array that will be provided as input to the new ClassReader - * instance. - */ - public static ClassReader newClassReader(byte[] bytes) { - return new ClassReader(bytes); + public static ClassReader newAsmClassReader(String pathToClass, ClassLoader classLoader) { + InputStream is = getClassAsStream(pathToClass, classLoader); + return newAsmClassReader(is); } /** @@ -124,7 +107,7 @@ class AsmUtils { * * @param is InputStream that will be provided to the new ClassReader instance. */ - public static ClassReader newClassReader(InputStream is) { + public static ClassReader newAsmClassReader(InputStream is) { try { return new ClassReader(is); } catch (IOException ex) { @@ -138,4 +121,108 @@ class AsmUtils { } } + /** + * Uses the default ClassLoader to load pathToClass. Appends '.class' to + * pathToClass before attempting to load. + * + * @param pathToClass resource path to class, not including .class suffix. e.g.: + * com/acme/MyClass + * + * @return inputStream for pathToClass + * + * @throws RuntimeException if pathToClass does not exist + */ + public static InputStream getClassAsStream(String pathToClass, ClassLoader classLoader) { + String classFileName = pathToClass + ClassUtils.CLASS_FILE_SUFFIX; + + InputStream is = classLoader.getResourceAsStream(classFileName); + + if (is == null) + throw new RuntimeException( + new FileNotFoundException("Class file [" + classFileName + "] not found")); + + return is; + } + + /** + * Loads the specified class using the default class loader, rethrowing any + * {@link ClassNotFoundException} as an unchecked exception. + * + * @param type of class to be returned + * @param fqClassName fully-qualified class name + * + * @return newly loaded class instance + * + * @throws IllegalArgumentException if configClassName cannot be loaded. + * + * @see #loadClass(String) + * @see #loadToolingSafeClass(String) + * @see ClassUtils#getDefaultClassLoader() + */ + @SuppressWarnings("unchecked") + public static Class loadRequiredClass(String fqClassName) { + try { + return (Class) getDefaultClassLoader().loadClass(fqClassName); + } catch (ClassNotFoundException ex) { + throw new IllegalArgumentException(format( + "Class [%s] could not be loaded, check your CLASSPATH.", fqClassName), ex); + } + } + + /** + * Loads the specified class using the default class loader, gracefully handling any + * {@link ClassNotFoundException} that may be thrown by issuing a WARN level logging + * statement and return null. This functionality is specifically implemented to + * accomodate tooling (Spring IDE) concerns, where user-defined types will not be + * available to the tooling. + *

+ * Because {@link ClassNotFoundException} is compensated for by returning null, callers + * must take care to handle the null case appropriately. + *

+ * In cases where the WARN logging statement is not desired, use the + * {@link #loadClass(String)} method, which returns null but issues no logging + * statements. + *

+ * This method should only ever return null in the case of a user-defined type be + * processed at tooling time. Therefore, tooling may not be able to represent any custom + * annotation semantics, but JavaConfig itself will not have any problem loading and + * respecting them at actual runtime. + * + * @param type of class to be returned + * @param fqClassName fully-qualified class name + * + * @return newly loaded class, null if class could not be found. + * + * @see #loadClass(String) + * @see #loadRequiredClass(String) + * @see ClassUtils#getDefaultClassLoader() + */ + @SuppressWarnings("unchecked") + public static Class loadToolingSafeClass(String fqClassName, ClassLoader classLoader) { + try { + return (Class) classLoader.loadClass(fqClassName); + } catch (ClassNotFoundException ex) { + log.warn(format("Unable to load class [%s], likely due to tooling-specific restrictions." + + "Attempting to continue, but unexpected errors may occur", fqClassName), ex); + return null; + } + } + + /** + * Creates a {@link MutableAnnotation} for {@code annoType}. JDK dynamic proxies are + * used, and the returned proxy implements both {@link MutableAnnotation} and annotation + * type {@code A} + * + * @param annotation type that must be supplied and returned + * @param annoType type of annotation to create + */ + public static A createMutableAnnotation(Class annoType, ClassLoader classLoader) { + MutableAnnotationInvocationHandler handler = new MutableAnnotationInvocationHandler(annoType); + Class[] interfaces = new Class[] { annoType, MutableAnnotation.class }; + + @SuppressWarnings("unchecked") + A mutableAnno = (A) Proxy.newProxyInstance(classLoader, interfaces, handler); + return mutableAnno; + } + } diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/support/ConfigurationClassMethodVisitor.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/support/ConfigurationClassMethodVisitor.java index 6a0a3c73c4..f7d9081f95 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/annotation/support/ConfigurationClassMethodVisitor.java +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/support/ConfigurationClassMethodVisitor.java @@ -17,8 +17,6 @@ package org.springframework.context.annotation.support; import static org.springframework.context.annotation.support.AsmUtils.*; -import static org.springframework.context.annotation.support.MutableAnnotationUtils.*; -import static org.springframework.context.annotation.support.Util.*; import static org.springframework.util.ClassUtils.*; import java.lang.annotation.Annotation; @@ -62,7 +60,7 @@ class ConfigurationClassMethodVisitor extends MethodAdapter { */ public ConfigurationClassMethodVisitor(ConfigurationClass configClass, String methodName, String methodDescriptor, int modifiers, ClassLoader classLoader) { - super(AsmUtils.EMPTY_VISITOR); + super(ASM_EMPTY_VISITOR); this.configClass = configClass; this.methodName = methodName; @@ -77,14 +75,14 @@ class ConfigurationClassMethodVisitor extends MethodAdapter { */ @Override public AnnotationVisitor visitAnnotation(String annoTypeDesc, boolean visible) { - String annoClassName = AsmUtils.convertTypeDescriptorToClassName(annoTypeDesc); + String annoClassName = convertAsmTypeDescriptorToClassName(annoTypeDesc); Class annoClass = loadToolingSafeClass(annoClassName, classLoader); if (annoClass == null) return super.visitAnnotation(annoTypeDesc, visible); - Annotation annotation = createMutableAnnotation(annoClass); + Annotation annotation = createMutableAnnotation(annoClass, classLoader); annotations.add(annotation); @@ -126,11 +124,11 @@ class ConfigurationClassMethodVisitor extends MethodAdapter { * that type is an interface. */ private ModelClass initReturnTypeFromMethodDescriptor(String methodDescriptor) { - final ModelClass returnType = new ModelClass(getReturnTypeFromMethodDescriptor(methodDescriptor)); + final ModelClass returnType = new ModelClass(getReturnTypeFromAsmMethodDescriptor(methodDescriptor)); // detect whether the return type is an interface - newClassReader(convertClassNameToResourcePath(returnType.getName()), classLoader).accept( - new ClassAdapter(AsmUtils.EMPTY_VISITOR) { + newAsmClassReader(convertClassNameToResourcePath(returnType.getName()), classLoader).accept( + new ClassAdapter(ASM_EMPTY_VISITOR) { @Override public void visit(int arg0, int arg1, String arg2, String arg3, String arg4, String[] arg5) { returnType.setInterface((arg1 & Opcodes.ACC_INTERFACE) == Opcodes.ACC_INTERFACE); diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/support/ConfigurationClassVisitor.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/support/ConfigurationClassVisitor.java index 78fdef59d5..34f791aaef 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/annotation/support/ConfigurationClassVisitor.java +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/support/ConfigurationClassVisitor.java @@ -17,7 +17,7 @@ package org.springframework.context.annotation.support; import static java.lang.String.*; -import static org.springframework.context.annotation.support.MutableAnnotationUtils.*; +import static org.springframework.context.annotation.support.AsmUtils.*; import static org.springframework.util.ClassUtils.*; import java.util.HashMap; @@ -60,7 +60,7 @@ class ConfigurationClassVisitor extends ClassAdapter { public ConfigurationClassVisitor(ConfigurationClass configClass, ConfigurationModel model, ProblemReporter problemReporter, ClassLoader classLoader) { - super(AsmUtils.EMPTY_VISITOR); + super(ASM_EMPTY_VISITOR); this.configClass = configClass; this.model = model; this.problemReporter = problemReporter; @@ -101,7 +101,7 @@ class ConfigurationClassVisitor extends ClassAdapter { ConfigurationClassVisitor visitor = new ConfigurationClassVisitor(configClass, model, problemReporter, classLoader); - ClassReader reader = AsmUtils.newClassReader(superTypeDesc, classLoader); + ClassReader reader = newAsmClassReader(superTypeDesc, classLoader); reader.accept(visitor, false); } @@ -118,10 +118,10 @@ class ConfigurationClassVisitor extends ClassAdapter { */ @Override public AnnotationVisitor visitAnnotation(String annoTypeDesc, boolean visible) { - String annoTypeName = AsmUtils.convertTypeDescriptorToClassName(annoTypeDesc); + String annoTypeName = convertAsmTypeDescriptorToClassName(annoTypeDesc); if (Configuration.class.getName().equals(annoTypeName)) { - Configuration mutableConfiguration = createMutableAnnotation(Configuration.class); + Configuration mutableConfiguration = createMutableAnnotation(Configuration.class, classLoader); configClass.setConfigurationAnnotation(mutableConfiguration); return new MutableAnnotationVisitor(mutableConfiguration, classLoader); } @@ -182,7 +182,7 @@ class ConfigurationClassVisitor extends ClassAdapter { new ConfigurationClassVisitor(innerConfigClass, new ConfigurationModel(), problemReporter, classLoader); ccVisitor.setProcessInnerClasses(false); - ClassReader reader = AsmUtils.newClassReader(name, classLoader); + ClassReader reader = newAsmClassReader(name, classLoader); reader.accept(ccVisitor, false); if (innerClasses.containsKey(outerName)) diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/support/ConfigurationEnhancer.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/support/ConfigurationEnhancer.java index 78518b6813..d6179af61c 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/annotation/support/ConfigurationEnhancer.java +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/support/ConfigurationEnhancer.java @@ -17,7 +17,7 @@ package org.springframework.context.annotation.support; import static java.lang.String.*; -import static org.springframework.context.annotation.support.Util.*; +import static org.springframework.context.annotation.support.AsmUtils.*; import java.lang.annotation.Annotation; import java.lang.reflect.Method; diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/support/ConfigurationParser.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/support/ConfigurationParser.java index 7e9954c7a4..533f6cbba8 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/annotation/support/ConfigurationParser.java +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/support/ConfigurationParser.java @@ -16,6 +16,8 @@ package org.springframework.context.annotation.support; +import static org.springframework.context.annotation.support.AsmUtils.*; + import org.springframework.asm.ClassReader; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.parsing.ProblemReporter; @@ -71,7 +73,7 @@ public class ConfigurationParser { String resourcePath = ClassUtils.convertClassNameToResourcePath(className); - ClassReader configClassReader = AsmUtils.newClassReader(Util.getClassAsStream(resourcePath, classLoader)); + ClassReader configClassReader = newAsmClassReader(getClassAsStream(resourcePath, classLoader)); ConfigurationClass configClass = new ConfigurationClass(); configClass.setBeanName(configurationId); diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/support/ImportAnnotationVisitor.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/support/ImportAnnotationVisitor.java index 6253a27eff..d6527b2fd9 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/annotation/support/ImportAnnotationVisitor.java +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/support/ImportAnnotationVisitor.java @@ -49,7 +49,7 @@ class ImportAnnotationVisitor extends AnnotationAdapter { private final ClassLoader classLoader; public ImportAnnotationVisitor(ConfigurationModel model, ProblemReporter problemReporter, ClassLoader classLoader) { - super(AsmUtils.EMPTY_VISITOR); + super(ASM_EMPTY_VISITOR); this.model = model; this.problemReporter = problemReporter; this.classLoader = classLoader; @@ -60,7 +60,7 @@ class ImportAnnotationVisitor extends AnnotationAdapter { Assert.isTrue("value".equals(attribName), format("expected 'value' attribute, got unknown '%s' attribute", attribName)); - return new AnnotationAdapter(AsmUtils.EMPTY_VISITOR) { + return new AnnotationAdapter(ASM_EMPTY_VISITOR) { @Override public void visit(String na, Object type) { Assert.isInstanceOf(Type.class, type); @@ -80,7 +80,7 @@ class ImportAnnotationVisitor extends AnnotationAdapter { private void processClassToImport(String classToImport) { ConfigurationClass configClass = new ConfigurationClass(); - ClassReader reader = newClassReader(convertClassNameToResourcePath(classToImport), classLoader); + ClassReader reader = newAsmClassReader(convertClassNameToResourcePath(classToImport), classLoader); reader.accept(new ConfigurationClassVisitor(configClass, model, problemReporter, classLoader), false); diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/support/MutableAnnotation.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/support/MutableAnnotation.java index c4bf3ec99c..7796031257 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/annotation/support/MutableAnnotation.java +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/support/MutableAnnotation.java @@ -31,7 +31,7 @@ import org.springframework.context.annotation.Configuration; * *

Note: the visibility of this interface would be reduced to package-private save for an * obscure restriction of JDK dynamic proxies. - * {@link MutableAnnotationUtils#createMutableAnnotation(Class)} creates a proxy based on + * {@link AsmUtils#createMutableAnnotation} creates a proxy based on * two interfaces: this one, and whatever annotation is currently being parsed. The * restriction is that both interfaces may not be package-private if they are in separate * packages. In order to avoid unnecessarily restricting the visibility options for @@ -39,9 +39,10 @@ import org.springframework.context.annotation.Configuration; * not to use this annotation outside this package. * * @author Chris Beams - * @see MutableAnnotationUtils + * @since 3.0 * @see MutableAnnotationVisitor * @see MutableAnnotationInvocationHandler + * @see AsmUtils#createMutableAnnotation */ public interface MutableAnnotation { diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/support/MutableAnnotationArrayVisitor.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/support/MutableAnnotationArrayVisitor.java index 556e12a8c0..a693f36fe7 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/annotation/support/MutableAnnotationArrayVisitor.java +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/support/MutableAnnotationArrayVisitor.java @@ -16,8 +16,7 @@ package org.springframework.context.annotation.support; -import static org.springframework.context.annotation.support.MutableAnnotationUtils.*; -import static org.springframework.context.annotation.support.Util.*; +import static org.springframework.context.annotation.support.AsmUtils.*; import java.lang.annotation.Annotation; import java.lang.reflect.Array; @@ -32,7 +31,7 @@ import org.springframework.asm.AnnotationVisitor; * * @author Chris Beams * @see MutableAnnotation - * @see MutableAnnotationUtils + * @see AsmUtils#createMutableAnnotation */ class MutableAnnotationArrayVisitor extends AnnotationAdapter { @@ -43,7 +42,7 @@ class MutableAnnotationArrayVisitor extends AnnotationAdapter { private final ClassLoader classLoader; public MutableAnnotationArrayVisitor(MutableAnnotation mutableAnno, String attribName, ClassLoader classLoader) { - super(AsmUtils.EMPTY_VISITOR); + super(AsmUtils.ASM_EMPTY_VISITOR); this.mutableAnno = mutableAnno; this.attribName = attribName; @@ -57,13 +56,13 @@ class MutableAnnotationArrayVisitor extends AnnotationAdapter { @Override public AnnotationVisitor visitAnnotation(String na, String annoTypeDesc) { - String annoTypeName = AsmUtils.convertTypeDescriptorToClassName(annoTypeDesc); + String annoTypeName = convertAsmTypeDescriptorToClassName(annoTypeDesc); Class annoType = loadToolingSafeClass(annoTypeName, classLoader); if (annoType == null) return super.visitAnnotation(na, annoTypeDesc); - Annotation anno = createMutableAnnotation(annoType); + Annotation anno = createMutableAnnotation(annoType, classLoader); values.add(anno); return new MutableAnnotationVisitor(anno, classLoader); } diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/support/MutableAnnotationInvocationHandler.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/support/MutableAnnotationInvocationHandler.java index 1e7eccd5a3..bd78d875b5 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/annotation/support/MutableAnnotationInvocationHandler.java +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/support/MutableAnnotationInvocationHandler.java @@ -37,7 +37,7 @@ import org.springframework.util.StringUtils; * * @author Chris Beams * @see MutableAnnotation - * @see MutableAnnotationUtils + * @see AsmUtils#createMutableAnnotation */ final class MutableAnnotationInvocationHandler implements InvocationHandler { diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/support/MutableAnnotationUtils.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/support/MutableAnnotationUtils.java deleted file mode 100644 index 87422cf2f5..0000000000 --- a/org.springframework.context/src/main/java/org/springframework/context/annotation/support/MutableAnnotationUtils.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2002-2009 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.context.annotation.support; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Proxy; - - -class MutableAnnotationUtils { - - /** - * Creates a {@link MutableAnnotation} for {@code annoType}. JDK dynamic proxies are - * used, and the returned proxy implements both {@link MutableAnnotation} and annotation - * type {@code A} - * - * @param annotation type that must be supplied and returned - * @param annoType type of annotation to create - */ - public static A createMutableAnnotation(Class annoType) { - MutableAnnotationInvocationHandler handler = new MutableAnnotationInvocationHandler(annoType); - ClassLoader classLoader = MutableAnnotationUtils.class.getClassLoader(); - Class[] interfaces = new Class[] { annoType, MutableAnnotation.class }; - - @SuppressWarnings("unchecked") - A mutableAnno = (A) Proxy.newProxyInstance(classLoader, interfaces, handler); - return mutableAnno; - } - -} diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/support/MutableAnnotationVisitor.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/support/MutableAnnotationVisitor.java index d71776334b..a8f1d41c96 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/annotation/support/MutableAnnotationVisitor.java +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/support/MutableAnnotationVisitor.java @@ -16,8 +16,7 @@ package org.springframework.context.annotation.support; -import static org.springframework.context.annotation.support.MutableAnnotationUtils.*; -import static org.springframework.context.annotation.support.Util.*; +import static org.springframework.context.annotation.support.AsmUtils.*; import java.lang.annotation.Annotation; import java.lang.reflect.Field; @@ -34,7 +33,7 @@ import org.springframework.util.Assert; * @author Chris Beams * @see MutableAnnotation * @see MutableAnnotationInvocationHandler - * @see MutableAnnotationUtils + * @see AsmUtils#createMutableAnnotation */ class MutableAnnotationVisitor implements AnnotationVisitor { @@ -52,7 +51,7 @@ class MutableAnnotationVisitor implements AnnotationVisitor { * @throws IllegalArgumentException if mutableAnno is not of type * {@link MutableAnnotation} * - * @see MutableAnnotationUtils#createMutableAnnotation(Class) + * @see AsmUtils#createMutableAnnotation */ public MutableAnnotationVisitor(Annotation mutableAnno, ClassLoader classLoader) { Assert.isInstanceOf(MutableAnnotation.class, mutableAnno, "annotation must be mutable"); @@ -86,7 +85,7 @@ class MutableAnnotationVisitor implements AnnotationVisitor { @SuppressWarnings("unchecked") public void visitEnum(String attribName, String enumTypeDescriptor, String strEnumValue) { - String enumClassName = AsmUtils.convertTypeDescriptorToClassName(enumTypeDescriptor); + String enumClassName = convertAsmTypeDescriptorToClassName(enumTypeDescriptor); Class enumClass = loadToolingSafeClass(enumClassName, classLoader); @@ -98,13 +97,13 @@ class MutableAnnotationVisitor implements AnnotationVisitor { } public AnnotationVisitor visitAnnotation(String attribName, String attribAnnoTypeDesc) { - String annoTypeName = AsmUtils.convertTypeDescriptorToClassName(attribAnnoTypeDesc); + String annoTypeName = convertAsmTypeDescriptorToClassName(attribAnnoTypeDesc); Class annoType = loadToolingSafeClass(annoTypeName, classLoader); if (annoType == null) - return AsmUtils.EMPTY_VISITOR.visitAnnotation(attribName, attribAnnoTypeDesc); + return ASM_EMPTY_VISITOR.visitAnnotation(attribName, attribAnnoTypeDesc); - Annotation anno = createMutableAnnotation(annoType); + Annotation anno = createMutableAnnotation(annoType, classLoader); try { Field attribute = mutableAnno.getClass().getField(attribName); diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/support/Util.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/support/Util.java deleted file mode 100644 index ef11761d91..0000000000 --- a/org.springframework.context/src/main/java/org/springframework/context/annotation/support/Util.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright 2002-2009 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.context.annotation.support; - -import static java.lang.String.*; -import static org.springframework.util.ClassUtils.*; - -import java.io.FileNotFoundException; -import java.io.InputStream; -import java.lang.reflect.Constructor; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.util.ClassUtils; -import org.springframework.util.ReflectionUtils; - -import sun.security.x509.Extension; - - -/** - * Misc utils - * - * @author Chris Beams - */ -class Util { - - private static final Log log = LogFactory.getLog(Util.class); - - private Util() { - } - - /** - * Returns instance of type T by invoking its default or no-arg constructor. - *

- * Any reflection-related issues are re-thrown as unchecked. - */ - public static T getInstance(Class clazz) { - try { - Constructor noArgCtor = clazz.getDeclaredConstructor(); - ReflectionUtils.makeAccessible(noArgCtor); - return noArgCtor.newInstance(); - } catch (Exception ex) { - ReflectionUtils.handleReflectionException(ex); - throw new IllegalStateException(format("Unexpected reflection exception - %s: %s", ex.getClass() - .getName(), ex.getMessage())); - } - } - - /** - * Loads the specified class using the default class loader, gracefully handling any - * {@link ClassNotFoundException} that may be thrown. This functionality is specifically - * implemented to accomodate tooling (Spring IDE) concerns, where user-defined types - * will not be - * - * @param type of class to be returned - * @param fqClassName fully-qualified class name - * - * @return newly loaded class instance, null if class could not be found - * - * @see #loadRequiredClass(String) - * @see #loadToolingSafeClass(String) - * @see ClassUtils#getDefaultClassLoader() - */ - @SuppressWarnings("unchecked") - public static Class loadClass(String fqClassName) { - try { - return (Class) ClassUtils.getDefaultClassLoader().loadClass(fqClassName); - } catch (ClassNotFoundException ex) { - return null; - } - } - - /** - * Loads the specified class using the default class loader, rethrowing any - * {@link ClassNotFoundException} as an unchecked exception. - * - * @param type of class to be returned - * @param fqClassName fully-qualified class name - * - * @return newly loaded class instance - * - * @throws IllegalArgumentException if configClassName cannot be loaded. - * - * @see #loadClass(String) - * @see #loadToolingSafeClass(String) - * @see ClassUtils#getDefaultClassLoader() - */ - @SuppressWarnings("unchecked") - public static Class loadRequiredClass(String fqClassName) { - try { - return (Class) getDefaultClassLoader().loadClass(fqClassName); - } catch (ClassNotFoundException ex) { - throw new IllegalArgumentException(format( - "Class [%s] could not be loaded, check your CLASSPATH.", fqClassName), ex); - } - } - - /** - * Loads the specified class using the default class loader, gracefully handling any - * {@link ClassNotFoundException} that may be thrown by issuing a WARN level logging - * statement and return null. This functionality is specifically implemented to - * accomodate tooling (Spring IDE) concerns, where user-defined types will not be - * available to the tooling. - *

- * ASM class reading is used throughout JavaConfig, but there are certain cases where - * classloading cannot be avoided - specifically in cases where users define their own - * {@link Extension} annotations. This method should therefore be - * used sparingly but consistently where required. - *

- * Because {@link ClassNotFoundException} is compensated for by returning null, callers - * must take care to handle the null case appropriately. - *

- * In cases where the WARN logging statement is not desired, use the - * {@link #loadClass(String)} method, which returns null but issues no logging - * statements. - *

- * This method should only ever return null in the case of a user-defined type be - * processed at tooling time. Therefore, tooling may not be able to represent any custom - * annotation semantics, but JavaConfig itself will not have any problem loading and - * respecting them at actual runtime. - * - * @param type of class to be returned - * @param fqClassName fully-qualified class name - * - * @return newly loaded class, null if class could not be found. - * - * @see #loadClass(String) - * @see #loadRequiredClass(String) - * @see ClassUtils#getDefaultClassLoader() - */ - @SuppressWarnings("unchecked") - public static Class loadToolingSafeClass(String fqClassName, ClassLoader classLoader) { - try { - return (Class) classLoader.loadClass(fqClassName); - } catch (ClassNotFoundException ex) { - log.warn(format("Unable to load class [%s], likely due to tooling-specific restrictions." - + "Attempting to continue, but unexpected errors may occur", fqClassName), ex); - return null; - } - } - - /** - * Uses the default ClassLoader to load pathToClass. Appends '.class' to - * pathToClass before attempting to load. - * - * @param pathToClass resource path to class, not including .class suffix. e.g.: - * com/acme/MyClass - * - * @return inputStream for pathToClass - * - * @throws RuntimeException if pathToClass does not exist - */ - public static InputStream getClassAsStream(String pathToClass, ClassLoader classLoader) { - String classFileName = pathToClass + ClassUtils.CLASS_FILE_SUFFIX; - - InputStream is = classLoader.getResourceAsStream(classFileName); - - if (is == null) - throw new RuntimeException( - new FileNotFoundException("Class file [" + classFileName + "] not found")); - - return is; - } - -} diff --git a/org.springframework.context/src/test/java/org/springframework/context/annotation/support/BeanMethodTests.java b/org.springframework.context/src/test/java/org/springframework/context/annotation/support/BeanMethodTests.java index fe4e82e7a8..c7e26aba51 100644 --- a/org.springframework.context/src/test/java/org/springframework/context/annotation/support/BeanMethodTests.java +++ b/org.springframework.context/src/test/java/org/springframework/context/annotation/support/BeanMethodTests.java @@ -19,7 +19,7 @@ import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; import static org.springframework.context.annotation.ScopedProxyMode.*; import static org.springframework.context.annotation.StandardScopes.*; -import static org.springframework.context.annotation.support.MutableAnnotationUtils.*; +import static org.springframework.context.annotation.support.AsmUtils.*; import java.lang.reflect.Modifier; @@ -30,10 +30,8 @@ import org.springframework.beans.factory.parsing.Location; import org.springframework.beans.factory.parsing.ProblemReporter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Scope; -import org.springframework.context.annotation.support.BeanMethod; -import org.springframework.context.annotation.support.ConfigurationClass; -import org.springframework.context.annotation.support.ModelClass; import org.springframework.core.io.ClassPathResource; +import org.springframework.util.ClassUtils; /** @@ -45,7 +43,7 @@ public class BeanMethodTests { private ProblemReporter problemReporter = new FailFastProblemReporter(); private String beanName = "foo"; - private Bean beanAnno = createMutableAnnotation(Bean.class); + private Bean beanAnno = createMutableAnnotation(Bean.class, ClassUtils.getDefaultClassLoader()); private ModelClass returnType = new ModelClass("FooType"); private ConfigurationClass declaringClass = new ConfigurationClass(); { declaringClass.setName("test.Config"); }