diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/Configuration.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/Configuration.java index 832ba30e9d2..84d01b7adf4 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/Configuration.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/Configuration.java @@ -29,70 +29,74 @@ import org.springframework.stereotype.Component; /** - * Annotation indicating that a class is a "Java Configuration" class, meaning that it exposes one - * or more {@link Bean} methods. Holds similar information to that held in the default values of a - * bean factory; can generally be thought of as the JavaConfig equivalent of XML's 'beans' root - * element. - * - *

Note however that the information here is not used to populate the defaults of the owning bean - * factory, which would affect other configurations. In the style of the Java configuration - * mechanism generally, each Java configuration class is kept isolated.

- * - *

Configuration-annotated classes are also candidates for component scanning thanks to the fact - * that this annotation is meta-annotated with {@link Component @Component}.

- * - * @author Rod Johnson - * @author Chris Beams + * Annotation indicating that a class is a "Java Configuration" class, meaning that it + * exposes one or more {@link Bean} methods. Holds similar information to that held in the + * default values of a bean factory; can generally be thought of as the JavaConfig + * equivalent of XML's 'beans' root element. + * + *

+ * Note however that the information here is not used to populate the defaults of the owning + * bean factory, which would affect other configurations. In the style of the Java + * configuration mechanism generally, each Java configuration class is kept isolated. + *

+ * + *

+ * Configuration-annotated classes are also candidates for component scanning thanks to the + * fact that this annotation is meta-annotated with {@link Component @Component}. + *

+ * + * @author Rod Johnson + * @author Chris Beams */ @Component -@Target({ ElementType.TYPE }) +@Target( { ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface Configuration { - /** - * Configuration name. Allow different variants, such as test, production - * etc. Default will always match. - * @return - */ - String[] names() default ""; + /** + * Configuration name. Allow different variants, such as test, production etc. Default + * will always match. + * + * @return + */ + String[] names() default ""; - /** - * Specifies the default autowiring strategy. - * - * @see Autowire - * @return - */ - Autowire defaultAutowire() default Autowire.INHERITED; + /** + * Specifies the default autowiring strategy. + * + * @see Autowire + * @return + */ + Autowire defaultAutowire() default Autowire.INHERITED; -// /** -// * Dependency check strategy. By default, the dependency check is -// * unspecified, that is the default Spring option will apply. In most cases, -// * it means no dependency check will be done. -// * -// * @see DependencyCheck -// * @return -// */ -// DependencyCheck defaultDependencyCheck() default DependencyCheck.UNSPECIFIED; -// -// /** -// * Bean instantiation strategy. By default, it is unspecified. -// * -// * @see Lazy -// * @return -// */ -// Lazy defaultLazy() default Lazy.UNSPECIFIED; + // /** + // * Dependency check strategy. By default, the dependency check is + // * unspecified, that is the default Spring option will apply. In most cases, + // * it means no dependency check will be done. + // * + // * @see DependencyCheck + // * @return + // */ + // DependencyCheck defaultDependencyCheck() default DependencyCheck.UNSPECIFIED; + // + // /** + // * Bean instantiation strategy. By default, it is unspecified. + // * + // * @see Lazy + // * @return + // */ + // Lazy defaultLazy() default Lazy.UNSPECIFIED; - /** - * Do we autowire with aspects from the enclosing factory scope? - */ - boolean useFactoryAspects() default false; + /** + * Do we autowire with aspects from the enclosing factory scope? + */ + boolean useFactoryAspects() default false; - /** - * Do we check {@link Required @Required} methods to make sure they've been - * called? - */ - boolean checkRequired() default false; + /** + * Do we check {@link Required @Required} methods to make sure they've been called? + */ + boolean checkRequired() default false; } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/ConfigurationClass.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/ConfigurationClass.java index 732e481969f..cb6fabddf9a 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/ConfigurationClass.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/ConfigurationClass.java @@ -30,308 +30,312 @@ import sun.security.x509.Extension; /** - * Abstract representation of a user-defined {@link Configuration @Configuration} class. Includes a - * set of Bean methods, AutoBean methods, ExternalBean methods, ExternalValue methods, etc. Includes - * all such methods defined in the ancestry of the class, in a 'flattened-out' manner. Note that - * each BeanMethod representation does still contain source information about where it was - * originally detected (for the purpose of tooling with Spring IDE). - * - *

Like the rest of the {@link org.springframework.config.java.model model} package, - * this class follows the fluent interface / builder pattern such that a model can be built up - * easily by method chaining.

- * - * @author Chris Beams + * Abstract representation of a user-defined {@link Configuration @Configuration} class. + * Includes a set of Bean methods, AutoBean methods, ExternalBean methods, ExternalValue + * methods, etc. Includes all such methods defined in the ancestry of the class, in a + * 'flattened-out' manner. Note that each BeanMethod representation does still contain + * source information about where it was originally detected (for the purpose of tooling + * with Spring IDE). + * + *

+ * Like the rest of the {@link org.springframework.config.java.model model} package, this + * class follows the fluent interface / builder pattern such that a model can be built up + * easily by method chaining. + *

+ * + * @author Chris Beams */ // TODO: SJC-242 update documentation in light of generalization changes // consider removing all refs to Bean, ExternalBean, etc. public final class ConfigurationClass extends ModelClass implements Validatable { - private String beanName; - - private int modifiers; - - private Configuration metadata; - - private HashSet methods = new HashSet(); - - private HashSet pluginAnnotations = new HashSet(); - - private ConfigurationClass declaringClass; - - public ConfigurationClass() { } - - // TODO: get rid of constructors used only for testing. put in testing util. - /** - * Creates a new ConfigurationClass named className. - * - * @param name fully-qualified Configuration class being represented - * - * @see #setClassName(String) - */ - ConfigurationClass(String name) { - this(name, null, defaultAnnotation(), 0); - } - - ConfigurationClass(String name, Configuration metadata) { - this(name, null, metadata, 0); - } - - ConfigurationClass(String name, int modifiers) { - this(name, null, defaultAnnotation(), modifiers); - } - - private static Configuration defaultAnnotation() { - @Configuration class Prototype { } - return Prototype.class.getAnnotation(Configuration.class); - } - - /** - * Creates a new ConfigurationClass object. - * - * @param name Fully qualified name of the class being represented - * @param id Bean name/id (if any) of this configuration class. used only in the case - * of XML integration where {@link Configuration} beans may have a - * user-specified id. - * @param metadata Configuration annotation resident on this class. May be null indicating - * that the user specified this class to be processed but failed to properly - * annotate it. - * @param modifiers Per {@link java.lang.reflect.Modifier} - */ - public ConfigurationClass(String name, String id, Configuration metadata, int modifiers) { - super(name); - Assert.hasText(name, "Configuration class name must have text"); - - setBeanName(id); - setMetadata(metadata); - setModifiers(modifiers); - } - - public ConfigurationClass addMethod(ModelMethod method) { - method.setDeclaringClass(this); - methods.add(method); - return this; - } - - public String getBeanName() { - return beanName == null ? getName() : beanName; - } - - public ConfigurationClass setBeanName(String id) { - this.beanName = id; - return this; - } - - public Set getMethods() { - return methods; - } - - public Annotation[] getPluginAnnotations() { - return pluginAnnotations.toArray(new Annotation[pluginAnnotations.size()]); - } - - /** - * Add a {@link Extension @Plugin}-annotated annotation to this configuration class. - * - * @param pluginAnno type-level Plugin annotation - */ - public ConfigurationClass addPluginAnnotation(Annotation pluginAnno) { - pluginAnnotations.add(pluginAnno); - return this; - } - - - public ConfigurationClass setDeclaringClass(ConfigurationClass configurationClass) { - this.declaringClass = configurationClass; - return this; - } - - public ConfigurationClass getDeclaringClass() { - return declaringClass; - } - - public int getModifiers() { - return modifiers; - } - - public ConfigurationClass setModifiers(int modifiers) { - Assert.isTrue(modifiers >= 0, "modifiers must be non-negative"); - this.modifiers = modifiers; - return this; - } - - public Configuration getMetadata() { - return this.metadata; - } - - public ConfigurationClass setMetadata(Configuration configAnno) { - this.metadata = configAnno; - return this; - } - - public void validate(List errors) { - - // configuration classes must be annotated with @Configuration - if (metadata == null) - errors.add(new NonAnnotatedConfigurationError()); - - // a configuration class may not be final (CGLIB limitation) - if (Modifier.isFinal(modifiers)) - errors.add(new FinalConfigurationError()); - - for(ModelMethod method : methods) - method.validate(errors); - } - - - @Override - public String toString() { - return format("%s; modifiers=%d; methods=%s", - super.toString(), modifiers, methods); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ((declaringClass == null) ? 0 : declaringClass.hashCode()); - result = prime * result + ((beanName == null) ? 0 : beanName.hashCode()); - result = prime * result + ((metadata == null) ? 0 : metadata.hashCode()); - result = prime * result + ((methods == null) ? 0 : methods.hashCode()); - result = prime * result + modifiers; - result = prime * result + ((pluginAnnotations == null) ? 0 : pluginAnnotations.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (!super.equals(obj)) - return false; - if (getClass() != obj.getClass()) - return false; - ConfigurationClass other = (ConfigurationClass) obj; - if (declaringClass == null) { - if (other.declaringClass != null) - return false; - } else if (!declaringClass.equals(other.declaringClass)) - return false; - if (beanName == null) { - if (other.beanName != null) - return false; - } else if (!beanName.equals(other.beanName)) - return false; - if (metadata == null) { - if (other.metadata != null) - return false; - } else if (!metadata.equals(other.metadata)) - return false; - if (methods == null) { - if (other.methods != null) - return false; - } else if (!methods.equals(other.methods)) - return false; - if (modifiers != other.modifiers) - return false; - if (pluginAnnotations == null) { - if (other.pluginAnnotations != null) - return false; - } else if (!pluginAnnotations.equals(other.pluginAnnotations)) - return false; - return true; - } - - - /** Configuration classes must be annotated with {@link Configuration @Configuration}. */ - public class NonAnnotatedConfigurationError extends UsageError { - public NonAnnotatedConfigurationError() { - super(ConfigurationClass.this, -1); - } - - @Override - public String getDescription() { - return format("%s was provided as a Java Configuration class but was not annotated with @%s. " - + "Update the class definition to continue.", - getSimpleName(), Configuration.class.getSimpleName()); - } - } - - /** Configuration classes must be non-final to accommodate CGLIB subclassing. */ - public class FinalConfigurationError extends UsageError { - public FinalConfigurationError() { - super(ConfigurationClass.this, -1); - } - - @Override - public String getDescription() { - return format("@%s class may not be final. Remove the final modifier to continue.", - Configuration.class.getSimpleName()); - } - } - - - public class InvalidPluginException extends UsageError { - - private final Annotation invalidPluginAnnotation; - - public InvalidPluginException(Annotation invalidPluginAnnotation) { - super(ConfigurationClass.this, -1); - this.invalidPluginAnnotation = invalidPluginAnnotation; - } - - @Override - public String getDescription() { - return format("Annotation [%s] was not annotated with @Plugin", invalidPluginAnnotation); - } - - } - - /** - * Error raised when a Bean marked as 'allowOverriding=false' is attempted to be overridden by - * another bean definition. - * - * @see Bean#allowOverriding() - */ - public class IllegalBeanOverrideError extends UsageError { - private final ConfigurationClass authoritativeClass; - private final ModelMethod finalMethodInQuestion; - - /** - * Creates a new IllegalBeanOverrideError object. - * - * @param violatingClass class attempting an illegal override. null value signifies - * that the violating class is unknown or that there is no - * class to speak of (in the case of an XML bean definition - * doing the illegal overriding) - * @param finalMethodInQuestion the method that has been marked 'allowOverriding=false' - */ - public IllegalBeanOverrideError(ConfigurationClass violatingClass, - ModelMethod finalMethodInQuestion) { - super(violatingClass, -1); - this.authoritativeClass = ConfigurationClass.this; - this.finalMethodInQuestion = finalMethodInQuestion; - } - - @Override - public String getDescription() { - return format("Illegal attempt by '%s' to override bean definition originally " - + "specified by %s.%s. Consider removing 'allowOverride=false' from original method.", - finalMethodInQuestion.getName(), authoritativeClass.getSimpleName(), - finalMethodInQuestion.getName()); - } - } - - public boolean hasMethod(String methodName) { - return getMethod(methodName) != null; - } - - public ModelMethod getMethod(String methodName) { - - for(ModelMethod method : methods) - if(methodName.equals(method.getName())) - return method; - - return null; - } + private String beanName; + + private int modifiers; + + private Configuration metadata; + + private HashSet methods = new HashSet(); + + private HashSet pluginAnnotations = new HashSet(); + + private ConfigurationClass declaringClass; + + public ConfigurationClass() { + } + + // TODO: get rid of constructors used only for testing. put in testing util. + /** + * Creates a new ConfigurationClass named className. + * + * @param name fully-qualified Configuration class being represented + * + * @see #setClassName(String) + */ + ConfigurationClass(String name) { + this(name, null, defaultAnnotation(), 0); + } + + ConfigurationClass(String name, Configuration metadata) { + this(name, null, metadata, 0); + } + + ConfigurationClass(String name, int modifiers) { + this(name, null, defaultAnnotation(), modifiers); + } + + private static Configuration defaultAnnotation() { + @Configuration + class Prototype { + } + return Prototype.class.getAnnotation(Configuration.class); + } + + /** + * Creates a new ConfigurationClass object. + * + * @param name Fully qualified name of the class being represented + * @param id Bean name/id (if any) of this configuration class. used only in the case of + * XML integration where {@link Configuration} beans may have a user-specified + * id. + * @param metadata Configuration annotation resident on this class. May be null + * indicating that the user specified this class to be processed but failed to + * properly annotate it. + * @param modifiers Per {@link java.lang.reflect.Modifier} + */ + public ConfigurationClass(String name, String id, Configuration metadata, int modifiers) { + super(name); + Assert.hasText(name, "Configuration class name must have text"); + + setBeanName(id); + setMetadata(metadata); + setModifiers(modifiers); + } + + public ConfigurationClass addMethod(ModelMethod method) { + method.setDeclaringClass(this); + methods.add(method); + return this; + } + + public String getBeanName() { + return beanName == null ? getName() : beanName; + } + + public ConfigurationClass setBeanName(String id) { + this.beanName = id; + return this; + } + + public Set getMethods() { + return methods; + } + + public Annotation[] getPluginAnnotations() { + return pluginAnnotations.toArray(new Annotation[pluginAnnotations.size()]); + } + + /** + * Add a {@link Extension @Plugin}-annotated annotation to this configuration class. + * + * @param pluginAnno type-level Plugin annotation + */ + public ConfigurationClass addPluginAnnotation(Annotation pluginAnno) { + pluginAnnotations.add(pluginAnno); + return this; + } + + + public ConfigurationClass setDeclaringClass(ConfigurationClass configurationClass) { + this.declaringClass = configurationClass; + return this; + } + + public ConfigurationClass getDeclaringClass() { + return declaringClass; + } + + public int getModifiers() { + return modifiers; + } + + public ConfigurationClass setModifiers(int modifiers) { + Assert.isTrue(modifiers >= 0, "modifiers must be non-negative"); + this.modifiers = modifiers; + return this; + } + + public Configuration getMetadata() { + return this.metadata; + } + + public ConfigurationClass setMetadata(Configuration configAnno) { + this.metadata = configAnno; + return this; + } + + public void validate(List errors) { + + // configuration classes must be annotated with @Configuration + if (metadata == null) + errors.add(new NonAnnotatedConfigurationError()); + + // a configuration class may not be final (CGLIB limitation) + if (Modifier.isFinal(modifiers)) + errors.add(new FinalConfigurationError()); + + for (ModelMethod method : methods) + method.validate(errors); + } + + + @Override + public String toString() { + return format("%s; modifiers=%d; methods=%s", super.toString(), modifiers, methods); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + ((declaringClass == null) ? 0 : declaringClass.hashCode()); + result = prime * result + ((beanName == null) ? 0 : beanName.hashCode()); + result = prime * result + ((metadata == null) ? 0 : metadata.hashCode()); + result = prime * result + ((methods == null) ? 0 : methods.hashCode()); + result = prime * result + modifiers; + result = prime * result + ((pluginAnnotations == null) ? 0 : pluginAnnotations.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + ConfigurationClass other = (ConfigurationClass) obj; + if (declaringClass == null) { + if (other.declaringClass != null) + return false; + } else if (!declaringClass.equals(other.declaringClass)) + return false; + if (beanName == null) { + if (other.beanName != null) + return false; + } else if (!beanName.equals(other.beanName)) + return false; + if (metadata == null) { + if (other.metadata != null) + return false; + } else if (!metadata.equals(other.metadata)) + return false; + if (methods == null) { + if (other.methods != null) + return false; + } else if (!methods.equals(other.methods)) + return false; + if (modifiers != other.modifiers) + return false; + if (pluginAnnotations == null) { + if (other.pluginAnnotations != null) + return false; + } else if (!pluginAnnotations.equals(other.pluginAnnotations)) + return false; + return true; + } + + + /** Configuration classes must be annotated with {@link Configuration @Configuration}. */ + public class NonAnnotatedConfigurationError extends UsageError { + public NonAnnotatedConfigurationError() { + super(ConfigurationClass.this, -1); + } + + @Override + public String getDescription() { + return format("%s was provided as a Java Configuration class but was not annotated with @%s. " + + "Update the class definition to continue.", getSimpleName(), Configuration.class + .getSimpleName()); + } + } + + /** Configuration classes must be non-final to accommodate CGLIB subclassing. */ + public class FinalConfigurationError extends UsageError { + public FinalConfigurationError() { + super(ConfigurationClass.this, -1); + } + + @Override + public String getDescription() { + return format("@%s class may not be final. Remove the final modifier to continue.", + Configuration.class.getSimpleName()); + } + } + + + public class InvalidPluginException extends UsageError { + + private final Annotation invalidPluginAnnotation; + + public InvalidPluginException(Annotation invalidPluginAnnotation) { + super(ConfigurationClass.this, -1); + this.invalidPluginAnnotation = invalidPluginAnnotation; + } + + @Override + public String getDescription() { + return format("Annotation [%s] was not annotated with @Plugin", invalidPluginAnnotation); + } + + } + + /** + * Error raised when a Bean marked as 'allowOverriding=false' is attempted to be + * overridden by another bean definition. + * + * @see Bean#allowOverriding() + */ + public class IllegalBeanOverrideError extends UsageError { + private final ConfigurationClass authoritativeClass; + private final ModelMethod finalMethodInQuestion; + + /** + * Creates a new IllegalBeanOverrideError object. + * + * @param violatingClass class attempting an illegal override. null value signifies + * that the violating class is unknown or that there is no class to speak of + * (in the case of an XML bean definition doing the illegal overriding) + * @param finalMethodInQuestion the method that has been marked + * 'allowOverriding=false' + */ + public IllegalBeanOverrideError(ConfigurationClass violatingClass, ModelMethod finalMethodInQuestion) { + super(violatingClass, -1); + this.authoritativeClass = ConfigurationClass.this; + this.finalMethodInQuestion = finalMethodInQuestion; + } + + @Override + public String getDescription() { + return format("Illegal attempt by '%s' to override bean definition originally " + + "specified by %s.%s. Consider removing 'allowOverride=false' from original method.", + finalMethodInQuestion.getName(), authoritativeClass.getSimpleName(), + finalMethodInQuestion.getName()); + } + } + + public boolean hasMethod(String methodName) { + return getMethod(methodName) != null; + } + + public ModelMethod getMethod(String methodName) { + + for (ModelMethod method : methods) + if (methodName.equals(method.getName())) + return method; + + return null; + } } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/ConfigurationModel.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/ConfigurationModel.java index 5c813c8a787..337966f7dc0 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/ConfigurationModel.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/ConfigurationModel.java @@ -21,154 +21,157 @@ import java.util.ArrayList; import java.util.List; - /** - * An abstract representation of a set of user-provided "Configuration classes", usually but not - * necessarily annotated with {@link Configuration @Configuration}. The model is populated with a - * {@link org.springframework.config.java.internal.parsing.ConfigurationParser} implementation which - * may be reflection-based or ASM-based. Once a model has been populated, it can then be rendered - * out to a set of BeanDefinitions. The model provides an important layer of indirection between the - * complexity of parsing a set of classes and the complexity of representing the contents of those - * classes as BeanDefinitions. - * - *

Interface follows the builder pattern for method chaining.

- * - * @author Chris Beams - * @see org.springframework.config.java.internal.parsing.ConfigurationParser + * An abstract representation of a set of user-provided "Configuration classes", usually but + * not necessarily annotated with {@link Configuration @Configuration}. The model is + * populated with a + * {@link org.springframework.config.java.internal.parsing.ConfigurationParser} + * implementation which may be reflection-based or ASM-based. Once a model has been + * populated, it can then be rendered out to a set of BeanDefinitions. The model provides an + * important layer of indirection between the complexity of parsing a set of classes and the + * complexity of representing the contents of those classes as BeanDefinitions. + * + *

+ * Interface follows the builder pattern for method chaining. + *

+ * + * @author Chris Beams + * @see org.springframework.config.java.internal.parsing.ConfigurationParser */ public final class ConfigurationModel implements Validatable { - /* list is used because order and collection equality matters. */ - private final ArrayList configurationClasses = new ArrayList(); - private final ArrayList validators = new ArrayList(); - - /** - * Add a {@link Configuration @Configuration} class to the model. Classes may be added at will - * and without any particular validation. Malformed classes will be caught and errors processed - * during {@link #validate() validation} - * - * @param configurationClass user-supplied Configuration class - */ - public ConfigurationModel add(ConfigurationClass configurationClass) { - configurationClasses.add(configurationClass); - return this; - } - - public void registerValidator(Validator validator) { - validators.add(validator); - } - - /** - * Return configuration classes that have been directly added to this model. - * - * @see #getAllConfigurationClasses() - */ - public ConfigurationClass[] getConfigurationClasses() { - return configurationClasses.toArray(new ConfigurationClass[] { }); - } - -// /** -// * Return all configuration classes, including all imported configuration classes. This method -// * should be generally preferred over {@link #getConfigurationClasses()} -// * -// * @see #getConfigurationClasses() -// */ -// public ConfigurationClass[] getAllConfigurationClasses() { -// ArrayList allConfigClasses = new ArrayList(); -// -// for (ConfigurationClass configClass : configurationClasses) -// allConfigClasses.addAll(configClass.getSelfAndAllImports()); -// -// return allConfigClasses.toArray(new ConfigurationClass[allConfigClasses.size()]); -// } - - public ConfigurationClass[] getAllConfigurationClasses() { - return configurationClasses.toArray(new ConfigurationClass[configurationClasses.size()]); - } - - /** - * Recurses through the model validating each object along the way and aggregating any errors. - * - * @see ConfigurationClass#validate(java.util.List) - * @see ModelMethod#validate(java.util.List) - * @see Validator - * @see UsageError - */ - public void validate(List errors) { - // user must specify at least one configuration - if (configurationClasses.isEmpty()) - errors.add(new EmptyModelError()); - - // cascade through model and allow handlers to register validators - // depending on where they are registered (with the model, the class, or the method) - // they will be called directly or indirectly below - for (ConfigurationClass configClass : getAllConfigurationClasses()) { - for(ModelMethod method : configClass.getMethods()) { - for(Validator validator : method.getValidators()) { - if(validator.supports(method)) - method.registerValidator(validator); - // TODO: support class-level validation - // if(validator.supports(configClass)) - // configClass.registerValidator(validator); - if(validator.supports(this)) - this.registerValidator(validator); - } - } - } - - // process any validators registered directly with this model object - for(Validator validator : validators) - validator.validate(this, errors); - - // each individual configuration class must be well-formed - // note that each configClass detects usage errors on its imports recursively - // note that each configClass will recursively process its respective methods - for (ConfigurationClass configClass : configurationClasses) - configClass.validate(errors); - } - - @Override - public String toString() { - return format("%s: configurationClasses=%s", - getClass().getSimpleName(), configurationClasses); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((configurationClasses == null) ? 0 : configurationClasses.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - ConfigurationModel other = (ConfigurationModel) obj; - if (configurationClasses == null) { - if (other.configurationClasses != null) - return false; - } else if (!configurationClasses.equals(other.configurationClasses)) - return false; - return true; - } - - - public class EmptyModelError extends UsageError { - public EmptyModelError() { - super(null, 0); - } - - @Override - public String getDescription() { - return format("Configuration model was empty. Make sure at least one " - + "@%s class has been specified.", Configuration.class.getSimpleName()); - } - } + /* list is used because order and collection equality matters. */ + private final ArrayList configurationClasses = new ArrayList(); + private final ArrayList validators = new ArrayList(); + + /** + * Add a {@link Configuration @Configuration} class to the model. Classes may be added + * at will and without any particular validation. Malformed classes will be caught and + * errors processed during {@link #validate() validation} + * + * @param configurationClass user-supplied Configuration class + */ + public ConfigurationModel add(ConfigurationClass configurationClass) { + configurationClasses.add(configurationClass); + return this; + } + + public void registerValidator(Validator validator) { + validators.add(validator); + } + + /** + * Return configuration classes that have been directly added to this model. + * + * @see #getAllConfigurationClasses() + */ + public ConfigurationClass[] getConfigurationClasses() { + return configurationClasses.toArray(new ConfigurationClass[] {}); + } + + // /** + // * Return all configuration classes, including all imported configuration classes. + // This method + // * should be generally preferred over {@link #getConfigurationClasses()} + // * + // * @see #getConfigurationClasses() + // */ + // public ConfigurationClass[] getAllConfigurationClasses() { + // ArrayList allConfigClasses = new ArrayList(); + // + // for (ConfigurationClass configClass : configurationClasses) + // allConfigClasses.addAll(configClass.getSelfAndAllImports()); + // + // return allConfigClasses.toArray(new ConfigurationClass[allConfigClasses.size()]); + // } + + public ConfigurationClass[] getAllConfigurationClasses() { + return configurationClasses.toArray(new ConfigurationClass[configurationClasses.size()]); + } + + /** + * Recurses through the model validating each object along the way and aggregating any + * errors. + * + * @see ConfigurationClass#validate(java.util.List) + * @see ModelMethod#validate(java.util.List) + * @see Validator + * @see UsageError + */ + public void validate(List errors) { + // user must specify at least one configuration + if (configurationClasses.isEmpty()) + errors.add(new EmptyModelError()); + + // cascade through model and allow handlers to register validators + // depending on where they are registered (with the model, the class, or the method) + // they will be called directly or indirectly below + for (ConfigurationClass configClass : getAllConfigurationClasses()) { + for (ModelMethod method : configClass.getMethods()) { + for (Validator validator : method.getValidators()) { + if (validator.supports(method)) + method.registerValidator(validator); + // TODO: support class-level validation + // if(validator.supports(configClass)) + // configClass.registerValidator(validator); + if (validator.supports(this)) + this.registerValidator(validator); + } + } + } + + // process any validators registered directly with this model object + for (Validator validator : validators) + validator.validate(this, errors); + + // each individual configuration class must be well-formed + // note that each configClass detects usage errors on its imports recursively + // note that each configClass will recursively process its respective methods + for (ConfigurationClass configClass : configurationClasses) + configClass.validate(errors); + } + + @Override + public String toString() { + return format("%s: configurationClasses=%s", getClass().getSimpleName(), configurationClasses); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((configurationClasses == null) ? 0 : configurationClasses.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ConfigurationModel other = (ConfigurationModel) obj; + if (configurationClasses == null) { + if (other.configurationClasses != null) + return false; + } else if (!configurationClasses.equals(other.configurationClasses)) + return false; + return true; + } + + + public class EmptyModelError extends UsageError { + public EmptyModelError() { + super(null, 0); + } + + @Override + public String getDescription() { + return format("Configuration model was empty. Make sure at least one " + + "@%s class has been specified.", Configuration.class.getSimpleName()); + } + } } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/Factory.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/Factory.java index bea48c6d2ec..6b0bd567708 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/Factory.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/Factory.java @@ -24,9 +24,10 @@ import java.lang.annotation.Target; import net.sf.cglib.proxy.Callback; import net.sf.cglib.proxy.NoOp; + /** * Meta-annotation used to identify annotations as producers of beans and/or values. - * + * * @author Chris Beams */ @Retention(RetentionPolicy.RUNTIME) @@ -34,22 +35,21 @@ import net.sf.cglib.proxy.NoOp; @Documented public @interface Factory { - /** - * Specifies which registrar (if any) should be used to register - * bean definitions for this {@link Factory} method. - */ - Class registrarType(); + /** + * Specifies which registrar (if any) should be used to register bean definitions for + * this {@link Factory} method. + */ + Class registrarType(); - /** - * Specifies what (if any) callback should be used when processing this {@link Factory} method. - * Defaults to CGLIB's {@link NoOp}, which does nothing. - * TODO: rename (interceptorType)? to keep with the -or|-ar nomenclature - */ - Class callbackType() default NoOp.class; + /** + * Specifies what (if any) callback should be used when processing this {@link Factory} + * method. Defaults to CGLIB's {@link NoOp}, which does nothing. TODO: rename + * (interceptorType)? to keep with the -or|-ar nomenclature + */ + Class callbackType() default NoOp.class; - /** - * TODO: document - * TODO: rename - */ - Class[] validatorTypes() default {}; + /** + * TODO: document TODO: rename + */ + Class[] validatorTypes() default {}; } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/MalformedConfigurationException.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/MalformedConfigurationException.java index ba3e2a2c939..4fda73f97b8 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/MalformedConfigurationException.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/MalformedConfigurationException.java @@ -22,54 +22,54 @@ import java.util.List; /** * TODO: rename to UsageException / move outside .internal? - * - * @author Chris Beams + * + * @author Chris Beams */ @SuppressWarnings("serial") public class MalformedConfigurationException extends RuntimeException { - private final List errors; + private final List errors; - public MalformedConfigurationException(String message) { - super(message); - this.errors = new ArrayList(); - } + public MalformedConfigurationException(String message) { + super(message); + this.errors = new ArrayList(); + } - public MalformedConfigurationException(UsageError... errors) { - super(toString(errors)); - this.errors = Arrays.asList(errors); - } + public MalformedConfigurationException(UsageError... errors) { + super(toString(errors)); + this.errors = Arrays.asList(errors); + } - public boolean containsError(Class errorType) { - for (UsageError error : errors) - if (error.getClass().isAssignableFrom(errorType)) - return true; + public boolean containsError(Class errorType) { + for (UsageError error : errors) + if (error.getClass().isAssignableFrom(errorType)) + return true; - return false; - } + return false; + } - /** - * Render a list of syntax errors as output suitable for diagnosis via System.err. - */ - private static String toString(UsageError... errors) { - StringBuilder sb = new StringBuilder(); + /** + * Render a list of syntax errors as output suitable for diagnosis via System.err. + */ + private static String toString(UsageError... errors) { + StringBuilder sb = new StringBuilder(); - sb.append("\n"); + sb.append("\n"); - if (errors.length == 1) - sb.append("A usage error has "); - else - sb.append(errors.length + " usage errors have "); + if (errors.length == 1) + sb.append("A usage error has "); + else + sb.append(errors.length + " usage errors have "); - sb.append("been detected:\n"); + sb.append("been detected:\n"); - for (int i = 0; i < errors.length; i++) { - sb.append(errors[i].toString()); - if ((i + 1) < errors.length) - sb.append('\n'); - } + for (int i = 0; i < errors.length; i++) { + sb.append(errors[i].toString()); + if ((i + 1) < errors.length) + sb.append('\n'); + } - return sb.toString(); - } + return sb.toString(); + } } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/ModelClass.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/ModelClass.java index 5f7fcc657b8..589014f6640 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/ModelClass.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/ModelClass.java @@ -21,139 +21,141 @@ import org.springframework.util.ClassUtils; /** - * Abstract representation of a class, free from java reflection. - * Base class used within the internal JavaConfig metamodel for - * representing {@link Configuration} classes. - * + * Abstract representation of a class, free from java reflection. Base class used within the + * internal JavaConfig metamodel for representing {@link Configuration} classes. + * * @author Chris Beams */ // TODO: Consider eliminating in favor of just ConfigurationClass public class ModelClass implements BeanMetadataElement { - private String name; - private boolean isInterface; - private String source; - - /** - * Creates a new and empty ModelClass instance. - */ - public ModelClass() { } - - /** - * Creates a new ModelClass instance - * - * @param name fully-qualified name of the class being represented - */ - public ModelClass(String name) { - this(name, false); - } - - /** - * Creates a new ModelClass instance - * - * @param name fully-qualified name of the class being represented - * @param isInterface whether the represented type is an interface - */ - public ModelClass(String name, boolean isInterface) { - this.name = name; - this.isInterface = isInterface; - } - - /** - * Returns the fully-qualified name of this class. - */ - public String getName() { - return name; - } - - /** - * Sets the fully-qualified name of this class. - */ - public void setName(String className) { - this.name = className; - } - - /** - * Returns the non-qualified name of this class. Given com.acme.Foo, returns 'Foo'. - */ - public String getSimpleName() { - return name == null ? null : ClassUtils.getShortName(name); - } - - /** - * Returns whether the class represented by this ModelClass instance is an interface. - */ - public boolean isInterface() { - return isInterface; - } - - /** - * Signifies that this class is (true) or is not (false) an interface. - */ - public void setInterface(boolean isInterface) { - this.isInterface = isInterface; - } - - /** - * Returns a resource path-formatted representation of the .java - * file that declares this class - */ - public String getSource() { - return source; - } - - /** - * Set the source location for this class. Must be a resource-path formatted string. - * - * @param source resource path to the .java file that declares this class. - */ - public void setSource(Object source) { - Assert.isInstanceOf(String.class, source); - this.source = (String) source; - } - - /** - * Given a ModelClass instance representing a class com.acme.Foo, this method will return - *
-     * ModelClass: name=Foo
-     * 
- */ - @Override - public String toString() { - return String.format("%s: name=%s", getClass().getSimpleName(), getSimpleName()); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = (prime * result) + (isInterface ? 1231 : 1237); - result = (prime * result) + ((name == null) ? 0 : name.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - - if (obj == null) - return false; - - if (getClass() != obj.getClass()) - return false; - - ModelClass other = (ModelClass) obj; - if (isInterface != other.isInterface) - return false; - - if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; - - return true; - } + private String name; + private boolean isInterface; + private String source; + + /** + * Creates a new and empty ModelClass instance. + */ + public ModelClass() { + } + + /** + * Creates a new ModelClass instance + * + * @param name fully-qualified name of the class being represented + */ + public ModelClass(String name) { + this(name, false); + } + + /** + * Creates a new ModelClass instance + * + * @param name fully-qualified name of the class being represented + * @param isInterface whether the represented type is an interface + */ + public ModelClass(String name, boolean isInterface) { + this.name = name; + this.isInterface = isInterface; + } + + /** + * Returns the fully-qualified name of this class. + */ + public String getName() { + return name; + } + + /** + * Sets the fully-qualified name of this class. + */ + public void setName(String className) { + this.name = className; + } + + /** + * Returns the non-qualified name of this class. Given com.acme.Foo, returns 'Foo'. + */ + public String getSimpleName() { + return name == null ? null : ClassUtils.getShortName(name); + } + + /** + * Returns whether the class represented by this ModelClass instance is an interface. + */ + public boolean isInterface() { + return isInterface; + } + + /** + * Signifies that this class is (true) or is not (false) an interface. + */ + public void setInterface(boolean isInterface) { + this.isInterface = isInterface; + } + + /** + * Returns a resource path-formatted representation of the .java file that declares this + * class + */ + public String getSource() { + return source; + } + + /** + * Set the source location for this class. Must be a resource-path formatted string. + * + * @param source resource path to the .java file that declares this class. + */ + public void setSource(Object source) { + Assert.isInstanceOf(String.class, source); + this.source = (String) source; + } + + /** + * Given a ModelClass instance representing a class com.acme.Foo, this method will + * return + * + *
+	 * ModelClass: name=Foo
+	 * 
+ */ + @Override + public String toString() { + return String.format("%s: name=%s", getClass().getSimpleName(), getSimpleName()); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = (prime * result) + (isInterface ? 1231 : 1237); + result = (prime * result) + ((name == null) ? 0 : name.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + + if (obj == null) + return false; + + if (getClass() != obj.getClass()) + return false; + + ModelClass other = (ModelClass) obj; + if (isInterface != other.isInterface) + return false; + + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + + return true; + } } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/ModelMethod.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/ModelMethod.java index d5b667cc83a..2f53c73ac68 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/ModelMethod.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/ModelMethod.java @@ -34,207 +34,206 @@ import org.springframework.util.Assert; /** TODO: JAVADOC */ public final class ModelMethod implements Validatable { - private final String name; - private final int modifiers; - private final ModelClass returnType; - private final List annotations = new ArrayList(); - private transient ConfigurationClass declaringClass; - private transient int lineNumber; - private transient Factory factoryAnno; - private transient final List validators = new ArrayList(); - - public ModelMethod(String name, int modifiers, ModelClass returnType, Annotation... annotations) { - Assert.hasText(name); - this.name = name; - - Assert.notNull(annotations); - for(Annotation annotation : annotations) { - this.annotations.add(annotation); - if(factoryAnno == null) - factoryAnno = annotation.annotationType().getAnnotation(Factory.class); - } - - Assert.isTrue(modifiers >= 0, "modifiers must be non-negative: " + modifiers); - this.modifiers = modifiers; - - Assert.notNull(returnType); - this.returnType = returnType; - } - - public String getName() { - return name; - } - - public ModelClass getReturnType() { - return returnType; - } - - /** - * @see java.lang.reflect.Modifier - */ - public int getModifiers() { - return modifiers; - } - - /** - * Returns the annotation on this method matching annoType or null - * IllegalStateException} if not present. - * - * @see #getRequiredAnnotation(Class) - */ - @SuppressWarnings("unchecked") - public T getAnnotation(Class annoType) { - for(Annotation anno : annotations) - if(anno.annotationType().equals(annoType)) - return (T) anno; - - return null; - } - - /** - * Returns the annotation on this method matching annoType or throws - * {@link IllegalStateException} if not present. - * - * @see #getAnnotation(Class) - */ - public T getRequiredAnnotation(Class annoType) { - T anno = getAnnotation(annoType); - - if(anno == null) - throw new IllegalStateException( - format("annotation %s not found on %s", annoType.getSimpleName(), this)); - - return anno; - } - - /** - * Sets up bi-directional relationship between this method and its declaring class. - * - * @see ConfigurationClass#addMethod(ModelMethod) - */ - public void setDeclaringClass(ConfigurationClass declaringClass) { - this.declaringClass = declaringClass; - } - - public ConfigurationClass getDeclaringClass() { - return declaringClass; - } - - public void setLineNumber(int lineNumber) { - this.lineNumber = lineNumber; - } - - public int getLineNumber() { - return lineNumber; - } - - public void registerValidator(Validator validator) { - validators.add(validator); - } - - public void validate(List errors) { - for(Validator validator : validators) - validator.validate(this, errors); - - if (Modifier.isPrivate(getModifiers())) - errors.add(new PrivateMethodError()); - - if (Modifier.isFinal(getModifiers())) - errors.add(new FinalMethodError()); - } - - public BeanDefinitionRegistrar getRegistrar() { - return getInstance(factoryAnno.registrarType()); - } - - public Set getValidators() { - HashSet validator = new HashSet(); - - for(Class validatorType : factoryAnno.validatorTypes()) - validator.add(getInstance(validatorType)); - - return validator; - } - - public Callback getCallback() { - Class callbackType = factoryAnno.callbackType(); - - if(callbackType.equals(NoOp.class)) - return NoOp.INSTANCE; - - return getInstance(callbackType); - } - - @Override - public String toString() { - String returnTypeName = returnType == null ? "" : returnType.getSimpleName(); - return String.format("%s: name=%s; returnType=%s; modifiers=%d", - getClass().getSimpleName(), name, returnTypeName, modifiers); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((annotations == null) ? 0 : annotations.hashCode()); - result = prime * result + modifiers; - result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + ((returnType == null) ? 0 : returnType.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - ModelMethod other = (ModelMethod) obj; - if (annotations == null) { - if (other.annotations != null) - return false; - } else if (!annotations.equals(other.annotations)) - return false; - if (modifiers != other.modifiers) - return false; - if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; - if (returnType == null) { - if (other.returnType != null) - return false; - } else if (!returnType.equals(other.returnType)) - return false; - return true; - } - - /** JavaConfigMethods must be visible (non-private) in order to accommodate CGLIB. */ - public class PrivateMethodError extends UsageError { - public PrivateMethodError() { - super(getDeclaringClass(), getLineNumber()); - } - - @Override - public String getDescription() { - return format("method '%s' may not be private", getName()); - } - } - - /** JavaConfigMethods must be extensible (non-final) in order to accommodate CGLIB. */ - public class FinalMethodError extends UsageError { - public FinalMethodError() { - super(getDeclaringClass(), getLineNumber()); - } - - @Override - public String getDescription() { - return format("method '%s' may not be final - remove the final modifier to continue", - getName()); - } - } - + private final String name; + private final int modifiers; + private final ModelClass returnType; + private final List annotations = new ArrayList(); + private transient ConfigurationClass declaringClass; + private transient int lineNumber; + private transient Factory factoryAnno; + private transient final List validators = new ArrayList(); + + public ModelMethod(String name, int modifiers, ModelClass returnType, Annotation... annotations) { + Assert.hasText(name); + this.name = name; + + Assert.notNull(annotations); + for (Annotation annotation : annotations) { + this.annotations.add(annotation); + if (factoryAnno == null) + factoryAnno = annotation.annotationType().getAnnotation(Factory.class); + } + + Assert.isTrue(modifiers >= 0, "modifiers must be non-negative: " + modifiers); + this.modifiers = modifiers; + + Assert.notNull(returnType); + this.returnType = returnType; + } + + public String getName() { + return name; + } + + public ModelClass getReturnType() { + return returnType; + } + + /** + * @see java.lang.reflect.Modifier + */ + public int getModifiers() { + return modifiers; + } + + /** + * Returns the annotation on this method matching annoType or null + * IllegalStateException} if not present. + * + * @see #getRequiredAnnotation(Class) + */ + @SuppressWarnings("unchecked") + public T getAnnotation(Class annoType) { + for (Annotation anno : annotations) + if (anno.annotationType().equals(annoType)) + return (T) anno; + + return null; + } + + /** + * Returns the annotation on this method matching annoType or throws + * {@link IllegalStateException} if not present. + * + * @see #getAnnotation(Class) + */ + public T getRequiredAnnotation(Class annoType) { + T anno = getAnnotation(annoType); + + if (anno == null) + throw new IllegalStateException(format("annotation %s not found on %s", annoType.getSimpleName(), + this)); + + return anno; + } + + /** + * Sets up bi-directional relationship between this method and its declaring class. + * + * @see ConfigurationClass#addMethod(ModelMethod) + */ + public void setDeclaringClass(ConfigurationClass declaringClass) { + this.declaringClass = declaringClass; + } + + public ConfigurationClass getDeclaringClass() { + return declaringClass; + } + + public void setLineNumber(int lineNumber) { + this.lineNumber = lineNumber; + } + + public int getLineNumber() { + return lineNumber; + } + + public void registerValidator(Validator validator) { + validators.add(validator); + } + + public void validate(List errors) { + for (Validator validator : validators) + validator.validate(this, errors); + + if (Modifier.isPrivate(getModifiers())) + errors.add(new PrivateMethodError()); + + if (Modifier.isFinal(getModifiers())) + errors.add(new FinalMethodError()); + } + + public BeanDefinitionRegistrar getRegistrar() { + return getInstance(factoryAnno.registrarType()); + } + + public Set getValidators() { + HashSet validator = new HashSet(); + + for (Class validatorType : factoryAnno.validatorTypes()) + validator.add(getInstance(validatorType)); + + return validator; + } + + public Callback getCallback() { + Class callbackType = factoryAnno.callbackType(); + + if (callbackType.equals(NoOp.class)) + return NoOp.INSTANCE; + + return getInstance(callbackType); + } + + @Override + public String toString() { + String returnTypeName = returnType == null ? "" : returnType.getSimpleName(); + return String.format("%s: name=%s; returnType=%s; modifiers=%d", getClass().getSimpleName(), name, + returnTypeName, modifiers); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((annotations == null) ? 0 : annotations.hashCode()); + result = prime * result + modifiers; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((returnType == null) ? 0 : returnType.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ModelMethod other = (ModelMethod) obj; + if (annotations == null) { + if (other.annotations != null) + return false; + } else if (!annotations.equals(other.annotations)) + return false; + if (modifiers != other.modifiers) + return false; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + if (returnType == null) { + if (other.returnType != null) + return false; + } else if (!returnType.equals(other.returnType)) + return false; + return true; + } + + /** JavaConfigMethods must be visible (non-private) in order to accommodate CGLIB. */ + public class PrivateMethodError extends UsageError { + public PrivateMethodError() { + super(getDeclaringClass(), getLineNumber()); + } + + @Override + public String getDescription() { + return format("method '%s' may not be private", getName()); + } + } + + /** JavaConfigMethods must be extensible (non-final) in order to accommodate CGLIB. */ + public class FinalMethodError extends UsageError { + public FinalMethodError() { + super(getDeclaringClass(), getLineNumber()); + } + + @Override + public String getDescription() { + return format("method '%s' may not be final - remove the final modifier to continue", getName()); + } + } + } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/Scopes.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/Scopes.java index 1b0636c9b1b..18782484f41 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/Scopes.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/Scopes.java @@ -19,23 +19,24 @@ package org.springframework.config.java; /** * Enumerates the names of the scopes supported out of the box in Spring. *

- * Not modeled as an actual java enum because annotations that accept a scope - * attribute must allow for user-defined scope names. Given that java - * enums are not extensible, these must remain simple string constants. - * - * @author Chris Beams + * Not modeled as an actual java enum because annotations that accept a scope attribute must + * allow for user-defined scope names. Given that java enums are not extensible, these must + * remain simple string constants. + * + * @author Chris Beams * @since 3.0 */ public class Scopes { - - private Scopes() { } - public static final String SINGLETON = "singleton"; // see BeanDefinition.SCOPE_SINGLETON; + private Scopes() { + } + + public static final String SINGLETON = "singleton"; - public static final String PROTOTYPE = "prototype"; // see BeanDefinition.SCOPE_PROTOTYPE; + public static final String PROTOTYPE = "prototype"; - public static final String REQUEST = "request"; // see WebApplicationContext.SCOPE_REQUEST; + public static final String REQUEST = "request"; - public static final String SESSION = "session"; // see WebApplicationContext.SCOPE_SESSION; + public static final String SESSION = "session"; } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/UsageError.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/UsageError.java index 43b0f78ec90..d7d9122d1a0 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/UsageError.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/UsageError.java @@ -16,57 +16,58 @@ package org.springframework.config.java; - /** - * Represents an invalid usage of JavaConfig constructs, e.g. a {@link Configuration} that declares - * no {@link Bean @Bean} methods, or declaring both {@link Bean @Bean} and - * {@link ExternalBean @ExternalBean} on a single method. Explore the type hierarchy to discover all - * possible usage errors. - * - * @author Chris Beams - * @see MalformedConfigurationException + * Represents an invalid usage of JavaConfig constructs, e.g. a {@link Configuration} that + * declares no {@link Bean @Bean} methods, or declaring both {@link Bean @Bean} and + * {@link ExternalBean @ExternalBean} on a single method. Explore the type hierarchy to + * discover all possible usage errors. + * + * @author Chris Beams + * @see MalformedConfigurationException */ public abstract class UsageError { - private final ModelClass clazz; - private final int lineNumber; + private final ModelClass clazz; + private final int lineNumber; - /** - * Create a new usage error, providing information about where the error was detected. - * - * @param modelClass class in which this error was detected. Null value indicates that the - * error was not local to a single class. - * @param lineNumber line number on which this error was detected (useful for tooling integration) - * - * @see ModelClass#getSource() - */ - public UsageError(ModelClass modelClass, int lineNumber) { - this.clazz = modelClass; - this.lineNumber = lineNumber; - } + /** + * Create a new usage error, providing information about where the error was detected. + * + * @param modelClass class in which this error was detected. Null value indicates that + * the error was not local to a single class. + * @param lineNumber line number on which this error was detected (useful for tooling + * integration) + * + * @see ModelClass#getSource() + */ + public UsageError(ModelClass modelClass, int lineNumber) { + this.clazz = modelClass; + this.lineNumber = lineNumber; + } - /** - * Human-readable description of this error suitable for console output or IDE tooling. - */ - public abstract String getDescription(); + /** + * Human-readable description of this error suitable for console output or IDE tooling. + */ + public abstract String getDescription(); - /** - * Same as {@link #getDescription()} but attributed with class and line number information. If - * modelClass constructor parameter was null, class and line number information will be omitted. - */ - public final String getAttributedDescription() { - if (clazz == null) - return getDescription(); + /** + * Same as {@link #getDescription()} but attributed with class and line number + * information. If modelClass constructor parameter was null, class and line number + * information will be omitted. + */ + public final String getAttributedDescription() { + if (clazz == null) + return getDescription(); - return String.format("%s:%d: %s", clazz.getSource(), lineNumber, getDescription()); - } + return String.format("%s:%d: %s", clazz.getSource(), lineNumber, getDescription()); + } - /** - * Delegates directly to {@link #getAttributedDescription()}. - */ - @Override - public String toString() { - return getAttributedDescription(); - } + /** + * Delegates directly to {@link #getAttributedDescription()}. + */ + @Override + public String toString() { + return getAttributedDescription(); + } } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/Util.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/Util.java index 499f318aa98..d974a920089 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/Util.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/Util.java @@ -14,9 +14,10 @@ import org.springframework.util.ReflectionUtils; import sun.security.x509.Extension; + /** * Misc utils - * + * * @author Chris Beams */ // TODO: SJC-242 general - check cycles with s101 @@ -25,140 +26,142 @@ import sun.security.x509.Extension; // TODO: SJC-242 rename, repackage, document public 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} or {@link Factory} 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) { - try { - return (Class) ClassUtils.getDefaultClassLoader().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) { - String classFileName = pathToClass + ClassUtils.CLASS_FILE_SUFFIX; - - InputStream is = ClassUtils.getDefaultClassLoader().getResourceAsStream(classFileName); - - if (is == null) - throw new RuntimeException( - new FileNotFoundException("Class file [" + classFileName + "] not found")); - - return is; - } + 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} or {@link Factory} 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) { + try { + return (Class) ClassUtils.getDefaultClassLoader().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) { + String classFileName = pathToClass + ClassUtils.CLASS_FILE_SUFFIX; + + InputStream is = ClassUtils.getDefaultClassLoader().getResourceAsStream(classFileName); + + if (is == null) + throw new RuntimeException(new FileNotFoundException("Class file [" + classFileName + + "] not found")); + + return is; + } } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/Validatable.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/Validatable.java index a5ce87e3187..a6546774f32 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/Validatable.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/Validatable.java @@ -2,18 +2,19 @@ package org.springframework.config.java; import java.util.List; + /** * Indicates a type is able to be validated for errors. * * @see Validator - * + * * @author Chris Beams */ public interface Validatable { - /** - * Validates this object, adding any errors to the supplied list of errors. - */ - public void validate(List errors); + /** + * Validates this object, adding any errors to the supplied list of errors. + */ + public void validate(List errors); } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/Validator.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/Validator.java index 7246ee0ee7d..09b577056d7 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/Validator.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/Validator.java @@ -4,10 +4,10 @@ import java.util.List; /** Marker interface */ -//TODO: SJC-242 document -//TODO: SJC-242 rename +// TODO: SJC-242 document +// TODO: SJC-242 rename public interface Validator { - boolean supports(Object object); - - void validate(Object object, List errors); + boolean supports(Object object); + + void validate(Object object, List errors); } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/AbstractMethodInterceptor.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/AbstractMethodInterceptor.java index d03684a7481..698c5b85457 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/AbstractMethodInterceptor.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/AbstractMethodInterceptor.java @@ -30,19 +30,19 @@ import org.springframework.util.Assert; /** * Base class for all {@link MethodInterceptor} implementations. - * + * * @author Chris Beams */ public abstract class AbstractMethodInterceptor implements BeanFactoryAware, MethodInterceptor { - protected final Log log = LogFactory.getLog(this.getClass()); - protected DefaultListableBeanFactory beanFactory; + protected final Log log = LogFactory.getLog(this.getClass()); + protected DefaultListableBeanFactory beanFactory; - public void setBeanFactory(BeanFactory beanFactory) throws BeansException { - Assert.isInstanceOf(DefaultListableBeanFactory.class, beanFactory); - this.beanFactory = (DefaultListableBeanFactory) beanFactory; - } + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + Assert.isInstanceOf(DefaultListableBeanFactory.class, beanFactory); + this.beanFactory = (DefaultListableBeanFactory) beanFactory; + } - protected String getBeanName(Method method) { - return method.getName(); - } + protected String getBeanName(Method method) { + return method.getName(); + } } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/Bean.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/Bean.java index 5c2e74ef3cb..a7eb53ac26b 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/Bean.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/Bean.java @@ -37,181 +37,184 @@ import org.springframework.config.java.Validator; /** - * Annotation to be applied to methods that create beans in a Spring context. The name of the bean - * is the method name. (It is also possible to specify aliases using the aliases array on this - * annotation.) + * Annotation to be applied to methods that create beans in a Spring context. The name of + * the bean is the method name. (It is also possible to specify aliases using the aliases + * array on this annotation.) * - *

Contains information similar to that held in Spring's internal BeanDefinition metadata.

+ *

+ * Contains information similar to that held in Spring's internal BeanDefinition metadata. + *

+ * + *

+ * Bean creation methods must be non-private (default, public or protected). Bean creation + * methods may throw any exception, which will be caught and handled by the Spring container + * on processing of the configuration class.
+ * Bean creation methods must return an object type. The decision to return a class or an + * interface will be significant in the event of proxying. Bean methods that return + * interfaces will be proxied using dynamic proxies; those that return a class will require + * CGLIB or other subclass-based proxying. It is recommended to return an interface where + * possible, as this is also consistent with best practice around loose coupling. + *

+ * + *

+ * Bean creation methods may reference other bean creation methods by calling them directly, + * as follows. This ensures that references between beans are strongly typed: + *

* - *

Bean creation methods must be non-private (default, public or protected). Bean creation - * methods may throw any exception, which will be caught and handled by the Spring container on - * processing of the configuration class.
- * Bean creation methods must return an object type. The decision to return a class or an interface - * will be significant in the event of proxying. Bean methods that return interfaces will be proxied - * using dynamic proxies; those that return a class will require CGLIB or other subclass-based - * proxying. It is recommended to return an interface where possible, as this is also consistent - * with best practice around loose coupling.

- * - *

Bean creation methods may reference other bean creation methods by calling them directly, as - * follows. This ensures that references between beans are strongly typed:

- * * @see Configuration * @see BeanNamingStrategy - * - * @author Rod Johnson - * @author Costin Leau - * @author Chris Beams + * + * @author Rod Johnson + * @author Costin Leau + * @author Chris Beams */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented -@Factory(registrarType=BeanRegistrar.class, - callbackType=BeanMethodInterceptor.class, - validatorTypes={BeanValidator.class, IllegalBeanOverrideValidator.class}) +@Factory(registrarType = BeanRegistrar.class, callbackType = BeanMethodInterceptor.class, validatorTypes = { + BeanValidator.class, IllegalBeanOverrideValidator.class }) public @interface Bean { - /** - * Role this bean plays in the overall application configuration. - * - * @see BeanDefinition#ROLE_APPLICATION - * @see BeanDefinition#ROLE_INFRASTRUCTURE - * @see BeanDefinition#ROLE_SUPPORT - * - * @see AbstractBeanDefinition the 'role' field is assigned by default to ROLE_APPLICATION - */ - int role() default BeanDefinition.ROLE_APPLICATION; - - /** - * Bean aliases. - */ - String[] aliases() default { }; - - /** - * Scope: whether the bean is a singleton, prototype or custom scope. - * Default is singleton. - */ - String scope() default Scopes.SINGLETON; - - /** - * Bean autowire strategy. - */ - Autowire autowire() default Autowire.INHERITED; - -// /** -// * Bean lazy strategy. -// */ -// Lazy lazy() default Lazy.UNSPECIFIED; -// -// /** -// * A bean may be marked as primary, useful for disambiguation when looking -// * up beans by type. -// * -// * @see org.springframework.config.java.context.JavaConfigApplicationContext#getBean(Class); -// */ -// Primary primary() default Primary.UNSPECIFIED; - - /** - * Bean init method name. Normally this is not needed, as the initialization - * (with parameterization) can be done directly through java code. - */ - String initMethodName() default ""; - - /** - * Bean destroy method name. - */ - String destroyMethodName() default ""; - -// /** -// * Bean dependency check strategy. -// */ -// DependencyCheck dependencyCheck() default DependencyCheck.UNSPECIFIED; - - /** - * Beans on which the current bean depends on. - */ - String[] dependsOn() default { }; - -// /** -// * Metadata for the current bean. -// */ -// Meta[] meta() default { }; - - /** - * Allow the bean to be overridden in another JavaConfig, XML or other - * non-Java configuration. This is consistent with - * DefaultListableBeanFactory's allowBeanDefinitionOverriding property, - * which defaults to true. - * - * @return whether overriding of this bean is allowed - */ - boolean allowOverriding() default true; + /** + * Role this bean plays in the overall application configuration. + * + * @see BeanDefinition#ROLE_APPLICATION + * @see BeanDefinition#ROLE_INFRASTRUCTURE + * @see BeanDefinition#ROLE_SUPPORT + * + * @see AbstractBeanDefinition the 'role' field is assigned by default to + * ROLE_APPLICATION + */ + int role() default BeanDefinition.ROLE_APPLICATION; + + /** + * Bean aliases. + */ + String[] aliases() default {}; + + /** + * Scope: whether the bean is a singleton, prototype or custom scope. Default is + * singleton. + */ + String scope() default Scopes.SINGLETON; + + /** + * Bean autowire strategy. + */ + Autowire autowire() default Autowire.INHERITED; + + // /** + // * Bean lazy strategy. + // */ + // Lazy lazy() default Lazy.UNSPECIFIED; + // + // /** + // * A bean may be marked as primary, useful for disambiguation when looking + // * up beans by type. + // * + // * @see + // org.springframework.config.java.context.JavaConfigApplicationContext#getBean(Class); + // */ + // Primary primary() default Primary.UNSPECIFIED; + + /** + * Bean init method name. Normally this is not needed, as the initialization (with + * parameterization) can be done directly through java code. + */ + String initMethodName() default ""; + + /** + * Bean destroy method name. + */ + String destroyMethodName() default ""; + + // /** + // * Bean dependency check strategy. + // */ + // DependencyCheck dependencyCheck() default DependencyCheck.UNSPECIFIED; + + /** + * Beans on which the current bean depends on. + */ + String[] dependsOn() default {}; + + // /** + // * Metadata for the current bean. + // */ + // Meta[] meta() default { }; + + /** + * Allow the bean to be overridden in another JavaConfig, XML or other non-Java + * configuration. This is consistent with DefaultListableBeanFactory's + * allowBeanDefinitionOverriding property, which defaults to true. + * + * @return whether overriding of this bean is allowed + */ + boolean allowOverriding() default true; } /** * Detects any user errors when declaring {@link Bean}-annotated methods. - * + * * @author Chris Beams */ class BeanValidator implements Validator { - public boolean supports(Object object) { - return object instanceof ModelMethod; - } - - public void validate(Object object, List errors) { - ModelMethod method = (ModelMethod) object; - - // TODO: re-enable for @ScopedProxy support -// if (method.getAnnotation(ScopedProxy.class) == null) -// return; -// -// Bean bean = method.getRequiredAnnotation(Bean.class); -// -// if (bean.scope().equals(DefaultScopes.SINGLETON) -// || bean.scope().equals(DefaultScopes.PROTOTYPE)) -// errors.add(new InvalidScopedProxyDeclarationError(method)); - } - + public boolean supports(Object object) { + return object instanceof ModelMethod; + } + + public void validate(Object object, List errors) { + ModelMethod method = (ModelMethod) object; + + // TODO: re-enable for @ScopedProxy support + // if (method.getAnnotation(ScopedProxy.class) == null) + // return; + // + // Bean bean = method.getRequiredAnnotation(Bean.class); + // + // if (bean.scope().equals(DefaultScopes.SINGLETON) + // || bean.scope().equals(DefaultScopes.PROTOTYPE)) + // errors.add(new InvalidScopedProxyDeclarationError(method)); + } + } - + /** * Detects any illegally-overridden {@link Bean} definitions within a particular * {@link ConfigurationModel} * * @see Bean#allowOverriding() - * + * * @author Chris Beams */ class IllegalBeanOverrideValidator implements Validator { - - public boolean supports(Object object) { - return object instanceof ConfigurationModel; - } - - public void validate(Object object, List errors) { - ConfigurationModel model = (ConfigurationModel) object; - - ConfigurationClass[] allClasses = model.getAllConfigurationClasses(); - - for (int i = 0; i < allClasses.length; i++) { - for (ModelMethod method : allClasses[i].getMethods()) { - Bean bean = method.getAnnotation(Bean.class); - - if (bean == null || bean.allowOverriding()) - continue; - - for (int j = i + 1; j < allClasses.length; j++) - if (allClasses[j].hasMethod(method.getName())) - errors.add(allClasses[i].new IllegalBeanOverrideError(allClasses[j], method)); - } - } - } + + public boolean supports(Object object) { + return object instanceof ConfigurationModel; + } + + public void validate(Object object, List errors) { + ConfigurationModel model = (ConfigurationModel) object; + + ConfigurationClass[] allClasses = model.getAllConfigurationClasses(); + + for (int i = 0; i < allClasses.length; i++) { + for (ModelMethod method : allClasses[i].getMethods()) { + Bean bean = method.getAnnotation(Bean.class); + + if (bean == null || bean.allowOverriding()) + continue; + + for (int j = i + 1; j < allClasses.length; j++) + if (allClasses[j].hasMethod(method.getName())) + errors.add(allClasses[i].new IllegalBeanOverrideError(allClasses[j], method)); + } + } + } } - - - diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/BeanMethodInterceptor.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/BeanMethodInterceptor.java index 8904e14ac70..996dc4e9b41 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/BeanMethodInterceptor.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/BeanMethodInterceptor.java @@ -23,6 +23,7 @@ import net.sf.cglib.proxy.MethodProxy; import org.springframework.beans.factory.config.ConfigurableBeanFactory; + /** * Intercepts the invocation of any {@link Bean}-annotated methods in order to ensure proper * handling of bean semantics such as scoping and AOP proxying. @@ -34,51 +35,54 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory; */ class BeanMethodInterceptor extends AbstractMethodInterceptor { - /** - * Enhances a {@link Bean @Bean} method to check the supplied BeanFactory for the existence - * of this bean object. - */ - public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { - String beanName = getBeanName(method); - - // TODO: re-enable for @ScopedProxy support -// boolean isScopedProxy = (AnnotationUtils.findAnnotation(method, ScopedProxy.class) != null); -// -// String scopedBeanName = ScopedProxy.Util.resolveHiddenScopedProxyBeanName(beanName); -// if (isScopedProxy && beanFactory.isCurrentlyInCreation(scopedBeanName)) -// beanName = scopedBeanName; + /** + * Enhances a {@link Bean @Bean} method to check the supplied BeanFactory for the + * existence of this bean object. + */ + public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { + String beanName = getBeanName(method); + + // TODO: re-enable for @ScopedProxy support + // boolean isScopedProxy = (AnnotationUtils.findAnnotation(method, + // ScopedProxy.class) != null); + // + // String scopedBeanName = + // ScopedProxy.Util.resolveHiddenScopedProxyBeanName(beanName); + // if (isScopedProxy && beanFactory.isCurrentlyInCreation(scopedBeanName)) + // beanName = scopedBeanName; - if (factoryContainsBean(beanName)) { - // we have an already existing cached instance of this bean -> retrieve it - Object cachedBean = beanFactory.getBean(beanName); - if (log.isInfoEnabled()) - log.info(format("Returning cached singleton object [%s] for @Bean method %s.%s", - cachedBean, method.getDeclaringClass().getSimpleName(), beanName)); + if (factoryContainsBean(beanName)) { + // we have an already existing cached instance of this bean -> retrieve it + Object cachedBean = beanFactory.getBean(beanName); + if (log.isInfoEnabled()) + log.info(format("Returning cached singleton object [%s] for @Bean method %s.%s", cachedBean, + method.getDeclaringClass().getSimpleName(), beanName)); - return cachedBean; - } + return cachedBean; + } - return proxy.invokeSuper(obj, args); - } + return proxy.invokeSuper(obj, args); + } - /** - * Check the beanFactory to see whether the bean named beanName already exists. - * Accounts for the fact that the requested bean may be "in creation", i.e.: we're in the - * middle of servicing the initial request for this bean. From JavaConfig's perspective, - * this means that the bean does not actually yet exist, and that it is now our job to - * create it for the first time by executing the logic in the corresponding Bean method. - *

- * Said another way, this check repurposes {@link ConfigurableBeanFactory#isCurrentlyInCreation(String)} - * to determine whether the container is calling this method or the user is calling this method. - * - * @param beanName name of bean to check for - * - * @return true if beanName already exists in beanFactory - */ - private boolean factoryContainsBean(String beanName) { - return beanFactory.containsBean(beanName) - && !beanFactory.isCurrentlyInCreation(beanName); - } + /** + * Check the beanFactory to see whether the bean named beanName already + * exists. Accounts for the fact that the requested bean may be "in creation", i.e.: + * we're in the middle of servicing the initial request for this bean. From JavaConfig's + * perspective, this means that the bean does not actually yet exist, and that it is now + * our job to create it for the first time by executing the logic in the corresponding + * Bean method. + *

+ * Said another way, this check repurposes + * {@link ConfigurableBeanFactory#isCurrentlyInCreation(String)} to determine whether + * the container is calling this method or the user is calling this method. + * + * @param beanName name of bean to check for + * + * @return true if beanName already exists in beanFactory + */ + private boolean factoryContainsBean(String beanName) { + return beanFactory.containsBean(beanName) && !beanFactory.isCurrentlyInCreation(beanName); + } } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/BeanRegistrar.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/BeanRegistrar.java index b62eca4a9fb..5151b1c19f3 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/BeanRegistrar.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/ext/BeanRegistrar.java @@ -26,178 +26,183 @@ import org.springframework.util.Assert; // TODO: SJC-242 document BeanHandler // TODO: SJC-242 make package-private class BeanRegistrar implements BeanDefinitionRegistrar { - - private static final Log logger = LogFactory.getLog(BeanRegistrar.class); - - /** - * Ensures that member is a method and is annotated (directly or indirectly) - * with {@link Bean @Bean}. - */ - public boolean accepts(Method method) { - return AnnotationUtils.findAnnotation(method, Bean.class) != null; - } - - // TODO: SJC-242 method too long - public void register(ModelMethod method, BeanDefinitionRegistry registry) { - RootBeanDefinition beanDef = new JavaConfigBeanDefinition(); - - ConfigurationClass configClass = method.getDeclaringClass(); - - beanDef.setFactoryBeanName(configClass.getBeanName()); - beanDef.setFactoryMethodName(method.getName()); - - Bean bean = method.getRequiredAnnotation(Bean.class); - - Configuration defaults = configClass.getMetadata(); - - // consider scoping - beanDef.setScope(bean.scope()); - - // consider autowiring - if (bean.autowire() != AnnotationUtils.getDefaultValue(Bean.class, "autowire")) - beanDef.setAutowireMode(bean.autowire().value()); - else if (defaults.defaultAutowire() != AnnotationUtils.getDefaultValue(Configuration.class, "defaultAutowire")) - beanDef.setAutowireMode(defaults.defaultAutowire().value()); - - String beanName = method.getName(); - - // has this already been overriden (i.e.: via XML)? - if (containsBeanDefinitionIncludingAncestry(beanName, registry)) { - BeanDefinition existingBeanDef = getBeanDefinitionIncludingAncestry(beanName, registry); - - // is the existing bean definition one that was created by JavaConfig? - if (!(existingBeanDef instanceof JavaConfigBeanDefinition)) { - // no -> then it's an external override, probably XML - - // ensure that overriding is ok - if (bean.allowOverriding() == false) { - UsageError error = configClass.new IllegalBeanOverrideError(null, method); - throw new MalformedConfigurationException(error); - } - - // overriding is legal, return immediately - logger.info(format("Skipping loading bean definition for %s: a definition for bean '%s' already exists. " - + "This is likely due to an override in XML.", - method, beanName)); - return; - } - } - - // propagate this bean's 'role' attribute - beanDef.setRole(bean.role()); - - // consider aliases - for (String alias : bean.aliases()) - registry.registerAlias(beanName, alias); - - // TODO: re-enable for Lazy support -// // is this bean marked as primary for disambiguation? -// if (bean.primary() == Primary.TRUE) -// beanDef.setPrimary(true); -// -// // is this bean lazily instantiated? -// if ((bean.lazy() == Lazy.TRUE) -// || ((bean.lazy() == Lazy.UNSPECIFIED) && (defaults.defaultLazy() == Lazy.TRUE))) -// beanDef.setLazyInit(true); - - // does this bean have a custom init-method specified? - String initMethodName = bean.initMethodName(); - if (hasText(initMethodName)) - beanDef.setInitMethodName(initMethodName); - - // does this bean have a custom destroy-method specified? - String destroyMethodName = bean.destroyMethodName(); - if (hasText(destroyMethodName)) - beanDef.setDestroyMethodName(destroyMethodName); - - // TODO: re-enable for @ScopedProxy support - // is this method annotated with @ScopedProxy? -// ScopedProxy scopedProxy = method.getAnnotation(ScopedProxy.class); -// if (scopedProxy != null) { -// RootBeanDefinition targetDef = beanDef; -// -// // Create a scoped proxy definition for the original bean name, -// // "hiding" the target bean in an internal target definition. -// String targetBeanName = ScopedProxy.Util.resolveHiddenScopedProxyBeanName(beanName); -// RootBeanDefinition scopedProxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class); -// scopedProxyDefinition.getPropertyValues().addPropertyValue("targetBeanName", targetBeanName); -// -// if (scopedProxy.proxyTargetClass()) -// targetDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE); -// // ScopedFactoryBean's "proxyTargetClass" default is TRUE, so we -// // don't need to set it explicitly here. -// else -// scopedProxyDefinition.getPropertyValues().addPropertyValue("proxyTargetClass", Boolean.FALSE); -// -// // The target bean should be ignored in favor of the scoped proxy. -// targetDef.setAutowireCandidate(false); -// -// // Register the target bean as separate bean in the factory -// registry.registerBeanDefinition(targetBeanName, targetDef); -// -// // replace the original bean definition with the target one -// beanDef = scopedProxyDefinition; -// } - - // TODO: re-enable for @Meta support - // does this bean method have any @Meta annotations? -// for (Meta meta : bean.meta()) -// beanDef.addMetadataAttribute(new BeanMetadataAttribute(meta.key(), meta.value())); - - if(bean.dependsOn().length > 0) - beanDef.setDependsOn(bean.dependsOn()); - - logger.info(format("Registering bean definition for @Bean method %s.%s()", - configClass.getName(), beanName)); - - registry.registerBeanDefinition(beanName, beanDef); - - } - - private boolean containsBeanDefinitionIncludingAncestry(String beanName, BeanDefinitionRegistry registry) { - try { - getBeanDefinitionIncludingAncestry(beanName, registry); - return true; - } catch (NoSuchBeanDefinitionException ex) { - return false; - } - } - - private BeanDefinition getBeanDefinitionIncludingAncestry(String beanName, BeanDefinitionRegistry registry) { - Assert.isInstanceOf(ConfigurableListableBeanFactory.class, registry); - ConfigurableListableBeanFactory clbf = (ConfigurableListableBeanFactory)registry; - - do { - if (clbf.containsBeanDefinition(beanName)) - return registry.getBeanDefinition(beanName); - - BeanFactory parent = clbf.getParentBeanFactory(); - if (parent == null) { - clbf = null; - } else if (parent instanceof ConfigurableListableBeanFactory) { - clbf = (ConfigurableListableBeanFactory) parent; - // TODO: re-enable -// } else if (parent instanceof AbstractApplicationContext) { -// clbf = ((AbstractApplicationContext) parent).getBeanFactory(); - } else { - throw new IllegalStateException("unknown parent type: " + parent.getClass().getName()); - } - } while (clbf != null); - - throw new NoSuchBeanDefinitionException( - format("No bean definition matching name '%s' " - + "could be found in %s or its ancestry", beanName, registry)); - } + + private static final Log logger = LogFactory.getLog(BeanRegistrar.class); + + /** + * Ensures that member is a method and is annotated (directly or indirectly) + * with {@link Bean @Bean}. + */ + public boolean accepts(Method method) { + return AnnotationUtils.findAnnotation(method, Bean.class) != null; + } + + // TODO: SJC-242 method too long + public void register(ModelMethod method, BeanDefinitionRegistry registry) { + RootBeanDefinition beanDef = new ConfigurationClassBeanDefinition(); + + ConfigurationClass configClass = method.getDeclaringClass(); + + beanDef.setFactoryBeanName(configClass.getBeanName()); + beanDef.setFactoryMethodName(method.getName()); + + Bean bean = method.getRequiredAnnotation(Bean.class); + + Configuration defaults = configClass.getMetadata(); + + // consider scoping + beanDef.setScope(bean.scope()); + + // consider autowiring + if (bean.autowire() != AnnotationUtils.getDefaultValue(Bean.class, "autowire")) + beanDef.setAutowireMode(bean.autowire().value()); + else if (defaults.defaultAutowire() != AnnotationUtils.getDefaultValue(Configuration.class, + "defaultAutowire")) + beanDef.setAutowireMode(defaults.defaultAutowire().value()); + + String beanName = method.getName(); + + // has this already been overriden (i.e.: via XML)? + if (containsBeanDefinitionIncludingAncestry(beanName, registry)) { + BeanDefinition existingBeanDef = getBeanDefinitionIncludingAncestry(beanName, registry); + + // is the existing bean definition one that was created by JavaConfig? + if (!(existingBeanDef instanceof ConfigurationClassBeanDefinition)) { + // no -> then it's an external override, probably XML + + // ensure that overriding is ok + if (bean.allowOverriding() == false) { + UsageError error = configClass.new IllegalBeanOverrideError(null, method); + throw new MalformedConfigurationException(error); + } + + // overriding is legal, return immediately + logger.info(format( + "Skipping loading bean definition for %s: a definition for bean '%s' already exists. " + + "This is likely due to an override in XML.", method, beanName)); + return; + } + } + + // propagate this bean's 'role' attribute + beanDef.setRole(bean.role()); + + // consider aliases + for (String alias : bean.aliases()) + registry.registerAlias(beanName, alias); + + // TODO: re-enable for Lazy support + // // is this bean marked as primary for disambiguation? + // if (bean.primary() == Primary.TRUE) + // beanDef.setPrimary(true); + // + // // is this bean lazily instantiated? + // if ((bean.lazy() == Lazy.TRUE) + // || ((bean.lazy() == Lazy.UNSPECIFIED) && (defaults.defaultLazy() == Lazy.TRUE))) + // beanDef.setLazyInit(true); + + // does this bean have a custom init-method specified? + String initMethodName = bean.initMethodName(); + if (hasText(initMethodName)) + beanDef.setInitMethodName(initMethodName); + + // does this bean have a custom destroy-method specified? + String destroyMethodName = bean.destroyMethodName(); + if (hasText(destroyMethodName)) + beanDef.setDestroyMethodName(destroyMethodName); + + // TODO: re-enable for @ScopedProxy support + // is this method annotated with @ScopedProxy? + // ScopedProxy scopedProxy = method.getAnnotation(ScopedProxy.class); + // if (scopedProxy != null) { + // RootBeanDefinition targetDef = beanDef; + // + // // Create a scoped proxy definition for the original bean name, + // // "hiding" the target bean in an internal target definition. + // String targetBeanName = + // ScopedProxy.Util.resolveHiddenScopedProxyBeanName(beanName); + // RootBeanDefinition scopedProxyDefinition = new + // RootBeanDefinition(ScopedProxyFactoryBean.class); + // scopedProxyDefinition.getPropertyValues().addPropertyValue("targetBeanName", + // targetBeanName); + // + // if (scopedProxy.proxyTargetClass()) + // targetDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, + // Boolean.TRUE); + // // ScopedFactoryBean's "proxyTargetClass" default is TRUE, so we + // // don't need to set it explicitly here. + // else + // scopedProxyDefinition.getPropertyValues().addPropertyValue("proxyTargetClass", + // Boolean.FALSE); + // + // // The target bean should be ignored in favor of the scoped proxy. + // targetDef.setAutowireCandidate(false); + // + // // Register the target bean as separate bean in the factory + // registry.registerBeanDefinition(targetBeanName, targetDef); + // + // // replace the original bean definition with the target one + // beanDef = scopedProxyDefinition; + // } + + // TODO: re-enable for @Meta support + // does this bean method have any @Meta annotations? + // for (Meta meta : bean.meta()) + // beanDef.addMetadataAttribute(new BeanMetadataAttribute(meta.key(), + // meta.value())); + + if (bean.dependsOn().length > 0) + beanDef.setDependsOn(bean.dependsOn()); + + logger.info(format("Registering bean definition for @Bean method %s.%s()", configClass.getName(), + beanName)); + + registry.registerBeanDefinition(beanName, beanDef); + + } + + private boolean containsBeanDefinitionIncludingAncestry(String beanName, BeanDefinitionRegistry registry) { + try { + getBeanDefinitionIncludingAncestry(beanName, registry); + return true; + } catch (NoSuchBeanDefinitionException ex) { + return false; + } + } + + private BeanDefinition getBeanDefinitionIncludingAncestry(String beanName, BeanDefinitionRegistry registry) { + Assert.isInstanceOf(ConfigurableListableBeanFactory.class, registry); + ConfigurableListableBeanFactory clbf = (ConfigurableListableBeanFactory) registry; + + do { + if (clbf.containsBeanDefinition(beanName)) + return registry.getBeanDefinition(beanName); + + BeanFactory parent = clbf.getParentBeanFactory(); + if (parent == null) { + clbf = null; + } else if (parent instanceof ConfigurableListableBeanFactory) { + clbf = (ConfigurableListableBeanFactory) parent; + // TODO: re-enable + // } else if (parent instanceof AbstractApplicationContext) { + // clbf = ((AbstractApplicationContext) parent).getBeanFactory(); + } else { + throw new IllegalStateException("unknown parent type: " + parent.getClass().getName()); + } + } while (clbf != null); + + throw new NoSuchBeanDefinitionException(format("No bean definition matching name '%s' " + + "could be found in %s or its ancestry", beanName, registry)); + } } /** - * {@link RootBeanDefinition} marker subclass used to signify that a bean definition - * created by JavaConfig as opposed to any other configuration source. Used in bean - * overriding cases where it's necessary to determine whether the bean definition was created - * externally (e.g. via XML). + * {@link RootBeanDefinition} marker subclass used to signify that a bean definition created + * by JavaConfig as opposed to any other configuration source. Used in bean overriding cases + * where it's necessary to determine whether the bean definition was created externally + * (e.g. via XML). */ @SuppressWarnings("serial") -// TODO: SJC-242 what to do about JavaConfigBeanDefinition? -class JavaConfigBeanDefinition extends RootBeanDefinition { +class ConfigurationClassBeanDefinition extends RootBeanDefinition { } \ No newline at end of file diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/enhancement/AddAnnotationAdapter.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/enhancement/AddAnnotationAdapter.java index 688ae7333d7..bbd8834b6f7 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/enhancement/AddAnnotationAdapter.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/enhancement/AddAnnotationAdapter.java @@ -24,93 +24,92 @@ import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; - /** - * Transforms a class by adding bytecode for a class-level annotation. - * Checks to ensure that the desired annotation is not already present - * before adding. Used by {@link ConfigurationEnhancer} to dynamically add - * an {@link org.aspectj.lang.Aspect} annotation to an enhanced Configuration - * subclass. + * Transforms a class by adding bytecode for a class-level annotation. Checks to ensure that + * the desired annotation is not already present before adding. Used by + * {@link ConfigurationEnhancer} to dynamically add an {@link org.aspectj.lang.Aspect} + * annotation to an enhanced Configuration subclass. *

* This class was originally adapted from examples the ASM 3.0 documentation. - * + * * @author Chris Beams */ class AddAnnotationAdapter extends ClassAdapter { - private String annotationDesc; - private boolean isAnnotationPresent; + private String annotationDesc; + private boolean isAnnotationPresent; - /** - * Creates a new AddAnnotationAdapter instance. - * - * @param cv the ClassVisitor delegate - * @param annotationDesc name of the annotation to be added - * (in type descriptor format) - */ - public AddAnnotationAdapter(ClassVisitor cv, String annotationDesc) { - super(cv); - this.annotationDesc = annotationDesc; - } + /** + * Creates a new AddAnnotationAdapter instance. + * + * @param cv the ClassVisitor delegate + * @param annotationDesc name of the annotation to be added (in type descriptor format) + */ + public AddAnnotationAdapter(ClassVisitor cv, String annotationDesc) { + super(cv); + this.annotationDesc = annotationDesc; + } - /** - * Ensures that the version of the resulting class is Java 5 or better. - */ - @Override - public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { - int v = (version & 0xFF) < Constants.V1_5 ? Constants.V1_5 : version; - cv.visit(v, access, name, signature, superName, interfaces); - } + /** + * Ensures that the version of the resulting class is Java 5 or better. + */ + @Override + public void visit(int version, int access, String name, String signature, String superName, + String[] interfaces) { + int v = (version & 0xFF) < Constants.V1_5 ? Constants.V1_5 : version; + cv.visit(v, access, name, signature, superName, interfaces); + } - /** - * Checks to ensure that the desired annotation is not already present. - */ - @Override - public AnnotationVisitor visitAnnotation(String desc, boolean visible) { - if (visible && desc.equals(annotationDesc)) { - isAnnotationPresent = true; - } - return cv.visitAnnotation(desc, visible); - } + /** + * Checks to ensure that the desired annotation is not already present. + */ + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + if (visible && desc.equals(annotationDesc)) { + isAnnotationPresent = true; + } + return cv.visitAnnotation(desc, visible); + } - @Override - public void visitInnerClass(String name, String outerName, String innerName, int access) { - addAnnotation(); - cv.visitInnerClass(name, outerName, innerName, access); - } + @Override + public void visitInnerClass(String name, String outerName, String innerName, int access) { + addAnnotation(); + cv.visitInnerClass(name, outerName, innerName, access); + } - @Override - public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { - addAnnotation(); - return cv.visitField(access, name, desc, signature, value); - } + @Override + public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { + addAnnotation(); + return cv.visitField(access, name, desc, signature, value); + } - @Override - public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { - addAnnotation(); - return cv.visitMethod(access, name, desc, signature, exceptions); - } + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, + String[] exceptions) { + addAnnotation(); + return cv.visitMethod(access, name, desc, signature, exceptions); + } - /** - * Kicks off the process of actually adding the desired annotation. - * - * @see #addAnnotation() - */ - @Override - public void visitEnd() { - addAnnotation(); - cv.visitEnd(); - } + /** + * Kicks off the process of actually adding the desired annotation. + * + * @see #addAnnotation() + */ + @Override + public void visitEnd() { + addAnnotation(); + cv.visitEnd(); + } - /** - * Actually adds the desired annotation. - */ - private void addAnnotation() { - if (!isAnnotationPresent) { - AnnotationVisitor av = cv.visitAnnotation(annotationDesc, true); - if (av != null) { - av.visitEnd(); - } - isAnnotationPresent = true; - } - } + /** + * Actually adds the desired annotation. + */ + private void addAnnotation() { + if (!isAnnotationPresent) { + AnnotationVisitor av = cv.visitAnnotation(annotationDesc, true); + if (av != null) { + av.visitEnd(); + } + isAnnotationPresent = true; + } + } } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/enhancement/ConfigurationEnhancer.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/enhancement/ConfigurationEnhancer.java index 7838d6cb49a..399035aa3d3 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/enhancement/ConfigurationEnhancer.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/enhancement/ConfigurationEnhancer.java @@ -46,7 +46,6 @@ import org.springframework.config.java.ConfigurationModel; import org.springframework.config.java.ModelMethod; - /** * Enhances {@link Configuration} classes by generating a CGLIB subclass capable of * interacting with the Spring container to respect bean semantics. @@ -57,176 +56,173 @@ import org.springframework.config.java.ModelMethod; */ public class ConfigurationEnhancer { - private static final Log log = LogFactory.getLog(ConfigurationEnhancer.class); - - private final ArrayList> callbackTypes = - new ArrayList>(); - - private final LinkedHashSet registrars = - new LinkedHashSet(); - - private final ArrayList callbackInstances = - new ArrayList(); - - private final CallbackFilter callbackFilter = - new CallbackFilter() { - public int accept(Method candidateMethod) { - Iterator iter = registrars.iterator(); - for(int i=0; iter.hasNext(); i++) - if(iter.next().accepts(candidateMethod)) - return i; - - throw new IllegalStateException(format("No registrar is capable of " + - "handling method [%s]. Perhaps you forgot to add a catch-all registrar?", - candidateMethod.getName())); - } - }; - - - /** - * Creates a new {@link ConfigurationEnhancer} instance. - */ - public ConfigurationEnhancer(DefaultListableBeanFactory beanFactory, ConfigurationModel model) { - notNull(beanFactory, "beanFactory must be non-null"); - notNull(model, "model must be non-null"); - - populateRegistrarsAndCallbacks(beanFactory, model); - } - - - /** - * Reads the contents of {@code model} in order to populate {@link #registrars}, - * {@link #callbackInstances} and {@link #callbackTypes} appropriately. - * - * @see #callbackFilter - */ - private void populateRegistrarsAndCallbacks(DefaultListableBeanFactory beanFactory, ConfigurationModel model) { - - for (ConfigurationClass configClass : model.getAllConfigurationClasses()) { - for (ModelMethod method : configClass.getMethods()) { - registrars.add(method.getRegistrar()); - - Callback callback = method.getCallback(); - - if(callback instanceof BeanFactoryAware) - ((BeanFactoryAware)callback).setBeanFactory(beanFactory); - - callbackInstances.add(callback); - } - } - - // register a 'catch-all' registrar - registrars.add(new BeanDefinitionRegistrar() { - - public boolean accepts(Method method) { - return true; - } - - public void register(ModelMethod method, BeanDefinitionRegistry registry) { - // no-op - } - }); - callbackInstances.add(NoOp.INSTANCE); - - for(Callback callback : callbackInstances) - callbackTypes.add(callback.getClass()); - } - - - /** - * Loads the specified class and generates a CGLIB subclass of it equipped with container-aware - * callbacks capable of respecting scoping and other bean semantics. - * - * @return fully-qualified name of the enhanced subclass - */ - public String enhance(String configClassName) { - if (log.isInfoEnabled()) - log.info("Enhancing " + configClassName); - - Class superclass = loadRequiredClass(configClassName); - - Class subclass = createClass(newEnhancer(superclass), superclass); - - subclass = nestOneClassDeeperIfAspect(superclass, subclass); - - if (log.isInfoEnabled()) - log.info(format("Successfully enhanced %s; enhanced class name is: %s", - configClassName, subclass.getName())); - - return subclass.getName(); - } - - /** - * Creates a new CGLIB {@link Enhancer} instance. - */ - private Enhancer newEnhancer(Class superclass) { - Enhancer enhancer = new Enhancer(); - - // because callbackFilter and callbackTypes are dynamically populated - // there's no opportunity for caching. This does not appear to be causing - // any performance problem. - enhancer.setUseCache(false); - - enhancer.setSuperclass(superclass); - enhancer.setUseFactory(false); - enhancer.setCallbackFilter(callbackFilter); - enhancer.setCallbackTypes(callbackTypes.toArray(new Class[]{})); - - return enhancer; - } - - /** - * Uses enhancer to generate a subclass of superclass, ensuring that - * {@link #callbackInstances} are registered for the new subclass. - */ - private Class createClass(Enhancer enhancer, Class superclass) { - Class subclass = enhancer.createClass(); - - Enhancer.registerCallbacks(subclass, callbackInstances.toArray(new Callback[] {})); - - return subclass; - } - - /** - * Works around a constraint imposed by the AspectJ 5 annotation-style programming model. See - * comments inline for detail. - * - * @return original subclass instance unless superclass is annnotated with @Aspect, in which - * case a subclass of the subclass is returned - */ - private Class nestOneClassDeeperIfAspect(Class superclass, Class origSubclass) { - boolean superclassIsAnAspect = false; - - // check for @Aspect by name rather than by class literal to avoid - // requiring AspectJ as a runtime dependency. - for(Annotation anno : superclass.getAnnotations()) - if(anno.annotationType().getName().equals("org.aspectj.lang.annotation.Aspect")) - superclassIsAnAspect = true; - - if(!superclassIsAnAspect) - return origSubclass; - - // the superclass is annotated with AspectJ's @Aspect. - // this means that we must create a subclass of the subclass - // in order to avoid some guard logic in Spring core that disallows - // extending a concrete aspect class. - Enhancer enhancer = newEnhancer(origSubclass); - enhancer.setStrategy(new DefaultGeneratorStrategy() { - @Override - protected byte[] transform(byte[] b) throws Exception { - ClassWriter writer = new ClassWriter(false); - ClassAdapter adapter = - new AddAnnotationAdapter(writer, "Lorg/aspectj/lang/annotation/Aspect;"); - ClassReader reader = new ClassReader(b); - reader.accept(adapter, false); - return writer.toByteArray(); - } - }); - - // create a subclass of the original subclass - Class newSubclass = createClass(enhancer, origSubclass); - - return newSubclass; - } - + private static final Log log = LogFactory.getLog(ConfigurationEnhancer.class); + + private final ArrayList> callbackTypes = new ArrayList>(); + + private final LinkedHashSet registrars = new LinkedHashSet(); + + private final ArrayList callbackInstances = new ArrayList(); + + private final CallbackFilter callbackFilter = new CallbackFilter() { + public int accept(Method candidateMethod) { + Iterator iter = registrars.iterator(); + for (int i = 0; iter.hasNext(); i++) + if (iter.next().accepts(candidateMethod)) + return i; + + throw new IllegalStateException(format("No registrar is capable of " + + "handling method [%s]. Perhaps you forgot to add a catch-all registrar?", + candidateMethod.getName())); + } + }; + + + /** + * Creates a new {@link ConfigurationEnhancer} instance. + */ + public ConfigurationEnhancer(DefaultListableBeanFactory beanFactory, ConfigurationModel model) { + notNull(beanFactory, "beanFactory must be non-null"); + notNull(model, "model must be non-null"); + + populateRegistrarsAndCallbacks(beanFactory, model); + } + + + /** + * Reads the contents of {@code model} in order to populate {@link #registrars}, + * {@link #callbackInstances} and {@link #callbackTypes} appropriately. + * + * @see #callbackFilter + */ + private void populateRegistrarsAndCallbacks(DefaultListableBeanFactory beanFactory, + ConfigurationModel model) { + + for (ConfigurationClass configClass : model.getAllConfigurationClasses()) { + for (ModelMethod method : configClass.getMethods()) { + registrars.add(method.getRegistrar()); + + Callback callback = method.getCallback(); + + if (callback instanceof BeanFactoryAware) + ((BeanFactoryAware) callback).setBeanFactory(beanFactory); + + callbackInstances.add(callback); + } + } + + // register a 'catch-all' registrar + registrars.add(new BeanDefinitionRegistrar() { + + public boolean accepts(Method method) { + return true; + } + + public void register(ModelMethod method, BeanDefinitionRegistry registry) { + // no-op + } + }); + callbackInstances.add(NoOp.INSTANCE); + + for (Callback callback : callbackInstances) + callbackTypes.add(callback.getClass()); + } + + + /** + * Loads the specified class and generates a CGLIB subclass of it equipped with + * container-aware callbacks capable of respecting scoping and other bean semantics. + * + * @return fully-qualified name of the enhanced subclass + */ + public String enhance(String configClassName) { + if (log.isInfoEnabled()) + log.info("Enhancing " + configClassName); + + Class superclass = loadRequiredClass(configClassName); + + Class subclass = createClass(newEnhancer(superclass), superclass); + + subclass = nestOneClassDeeperIfAspect(superclass, subclass); + + if (log.isInfoEnabled()) + log.info(format("Successfully enhanced %s; enhanced class name is: %s", configClassName, subclass + .getName())); + + return subclass.getName(); + } + + /** + * Creates a new CGLIB {@link Enhancer} instance. + */ + private Enhancer newEnhancer(Class superclass) { + Enhancer enhancer = new Enhancer(); + + // because callbackFilter and callbackTypes are dynamically populated + // there's no opportunity for caching. This does not appear to be causing + // any performance problem. + enhancer.setUseCache(false); + + enhancer.setSuperclass(superclass); + enhancer.setUseFactory(false); + enhancer.setCallbackFilter(callbackFilter); + enhancer.setCallbackTypes(callbackTypes.toArray(new Class[] {})); + + return enhancer; + } + + /** + * Uses enhancer to generate a subclass of superclass, ensuring that + * {@link #callbackInstances} are registered for the new subclass. + */ + private Class createClass(Enhancer enhancer, Class superclass) { + Class subclass = enhancer.createClass(); + + Enhancer.registerCallbacks(subclass, callbackInstances.toArray(new Callback[] {})); + + return subclass; + } + + /** + * Works around a constraint imposed by the AspectJ 5 annotation-style programming + * model. See comments inline for detail. + * + * @return original subclass instance unless superclass is annnotated with @Aspect, in + * which case a subclass of the subclass is returned + */ + private Class nestOneClassDeeperIfAspect(Class superclass, Class origSubclass) { + boolean superclassIsAnAspect = false; + + // check for @Aspect by name rather than by class literal to avoid + // requiring AspectJ as a runtime dependency. + for (Annotation anno : superclass.getAnnotations()) + if (anno.annotationType().getName().equals("org.aspectj.lang.annotation.Aspect")) + superclassIsAnAspect = true; + + if (!superclassIsAnAspect) + return origSubclass; + + // the superclass is annotated with AspectJ's @Aspect. + // this means that we must create a subclass of the subclass + // in order to avoid some guard logic in Spring core that disallows + // extending a concrete aspect class. + Enhancer enhancer = newEnhancer(origSubclass); + enhancer.setStrategy(new DefaultGeneratorStrategy() { + @Override + protected byte[] transform(byte[] b) throws Exception { + ClassWriter writer = new ClassWriter(false); + ClassAdapter adapter = new AddAnnotationAdapter(writer, + "Lorg/aspectj/lang/annotation/Aspect;"); + ClassReader reader = new ClassReader(b); + reader.accept(adapter, false); + return writer.toByteArray(); + } + }); + + // create a subclass of the original subclass + Class newSubclass = createClass(enhancer, origSubclass); + + return newSubclass; + } + } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/AnnotationAdapter.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/AnnotationAdapter.java index 759a8f3b387..e7266ac0fe2 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/AnnotationAdapter.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/AnnotationAdapter.java @@ -19,48 +19,47 @@ import org.objectweb.asm.AnnotationVisitor; /** - * An empty AnnotationVisitor that delegates to another AnnotationVisitor. - * This class can be used as a super class to quickly implement - * useful annotation adapter classes, just by overriding the necessary - * methods. Note that for some reason, ASM doesn't provide this class - * (it does provide MethodAdapter and ClassAdapter), thus we're following - * the general pattern and adding our own here. - * + * An empty AnnotationVisitor that delegates to another AnnotationVisitor. This class can be + * used as a super class to quickly implement useful annotation adapter classes, just by + * overriding the necessary methods. Note that for some reason, ASM doesn't provide this + * class (it does provide MethodAdapter and ClassAdapter), thus we're following the general + * pattern and adding our own here. + * * @author Chris Beams */ class AnnotationAdapter implements AnnotationVisitor { - private AnnotationVisitor delegate; + private AnnotationVisitor delegate; - /** - * Creates a new AnnotationAdapter instance that will delegate - * all its calls to delegate. - * - * @param delegate In most cases, the delegate will simply be - * {@link AsmUtils#EMPTY_VISITOR} - */ - public AnnotationAdapter(AnnotationVisitor delegate) { - this.delegate = delegate; - } + /** + * Creates a new AnnotationAdapter instance that will delegate all its calls to + * delegate. + * + * @param delegate In most cases, the delegate will simply be + * {@link AsmUtils#EMPTY_VISITOR} + */ + public AnnotationAdapter(AnnotationVisitor delegate) { + this.delegate = delegate; + } - public void visit(String arg0, Object arg1) { - delegate.visit(arg0, arg1); - } + public void visit(String arg0, Object arg1) { + delegate.visit(arg0, arg1); + } - public AnnotationVisitor visitAnnotation(String arg0, String arg1) { - return delegate.visitAnnotation(arg0, arg1); - } + public AnnotationVisitor visitAnnotation(String arg0, String arg1) { + return delegate.visitAnnotation(arg0, arg1); + } - public AnnotationVisitor visitArray(String arg0) { - return delegate.visitArray(arg0); - } + public AnnotationVisitor visitArray(String arg0) { + return delegate.visitArray(arg0); + } - public void visitEnum(String arg0, String arg1, String arg2) { - delegate.visitEnum(arg0, arg1, arg2); - } + public void visitEnum(String arg0, String arg1, String arg2) { + delegate.visitEnum(arg0, arg1, arg2); + } - public void visitEnd() { - delegate.visitEnd(); - } + public void visitEnd() { + delegate.visitEnd(); + } } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/AsmUtils.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/AsmUtils.java index 47929253b13..06764003195 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/AsmUtils.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/AsmUtils.java @@ -30,108 +30,115 @@ import org.springframework.config.java.Util; */ class AsmUtils { - public static final EmptyVisitor 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) { - 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)) - return Integer.class.getName(); - if("Z".equals(typeDescriptor)) - return Boolean.class.getName(); - - // strip the leading array/object/primitive identifier - if(typeDescriptor.startsWith("[[")) - internalName = typeDescriptor.substring(3); - else if(typeDescriptor.startsWith("[")) - internalName = typeDescriptor.substring(2); - else - internalName = typeDescriptor.substring(1); - - // convert slashes to dots - String className = internalName.replace('/', '.'); - - // and strip trailing semicolon (if present) - if(className.endsWith(";")) - className = className.substring(0, internalName.length()-1); - - return className; - } - - /** - * @param methodDescriptor see ASM guide section 2.1.4 - */ - public static String getReturnTypeFromMethodDescriptor(String methodDescriptor) { - String returnTypeDescriptor = methodDescriptor.substring(methodDescriptor.indexOf(')')+1); - return convertTypeDescriptorToClassName(returnTypeDescriptor); - } - - /** - * Creates a new ASM {@link ClassReader} for pathToClass. Appends '.class' - * to pathToClass before attempting to load. - * - * @throws RuntimeException if pathToClass+.class cannot be found on the classpath - * @throws RuntimeException if an IOException occurs when creating the new ClassReader - */ - public static ClassReader newClassReader(String pathToClass) { - InputStream is = Util.getClassAsStream(pathToClass); - 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. - * - * @return - */ - public static ClassReader newClassReader(byte[] bytes) { - return new ClassReader(bytes); - } - - /** - * Convenience method that creates and returns a new ASM {@link ClassReader} for the given - * InputStream is, closing the InputStream after creating the ClassReader and rethrowing - * any IOException thrown during ClassReader instantiation as an unchecked exception. Logs and ignores - * any IOException thrown when closing the InputStream. - * - * @param is InputStream that will be provided to the new ClassReader instance. - */ - public static ClassReader newClassReader(InputStream is) { - try { - return new ClassReader(is); - } catch (IOException ex) { - throw new RuntimeException("An unexpected exception occurred while creating ASM ClassReader: " + ex); - } finally { - try { - is.close(); - } catch (IOException ex) { - log.error("Ignoring exception thrown while closing InputStream", ex); - } - } - } + public static final EmptyVisitor 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) { + 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)) + return Integer.class.getName(); + if ("Z".equals(typeDescriptor)) + return Boolean.class.getName(); + + // strip the leading array/object/primitive identifier + if (typeDescriptor.startsWith("[[")) + internalName = typeDescriptor.substring(3); + else if (typeDescriptor.startsWith("[")) + internalName = typeDescriptor.substring(2); + else + internalName = typeDescriptor.substring(1); + + // convert slashes to dots + String className = internalName.replace('/', '.'); + + // and strip trailing semicolon (if present) + if (className.endsWith(";")) + className = className.substring(0, internalName.length() - 1); + + return className; + } + + /** + * @param methodDescriptor see ASM guide section 2.1.4 + */ + public static String getReturnTypeFromMethodDescriptor(String methodDescriptor) { + String returnTypeDescriptor = methodDescriptor.substring(methodDescriptor.indexOf(')') + 1); + return convertTypeDescriptorToClassName(returnTypeDescriptor); + } + + /** + * Creates a new ASM {@link ClassReader} for pathToClass. Appends '.class' to + * pathToClass before attempting to load. + * + * @throws RuntimeException if pathToClass+.class cannot be found on the + * classpath + * @throws RuntimeException if an IOException occurs when creating the new ClassReader + */ + public static ClassReader newClassReader(String pathToClass) { + InputStream is = Util.getClassAsStream(pathToClass); + 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. + * + * @return + */ + public static ClassReader newClassReader(byte[] bytes) { + return new ClassReader(bytes); + } + + /** + * Convenience method that creates and returns a new ASM {@link ClassReader} for the + * given InputStream is, closing the InputStream after creating the + * ClassReader and rethrowing any IOException thrown during ClassReader instantiation as + * an unchecked exception. Logs and ignores any IOException thrown when closing the + * InputStream. + * + * @param is InputStream that will be provided to the new ClassReader instance. + */ + public static ClassReader newClassReader(InputStream is) { + try { + return new ClassReader(is); + } catch (IOException ex) { + throw new RuntimeException("An unexpected exception occurred while creating ASM ClassReader: " + + ex); + } finally { + try { + is.close(); + } catch (IOException ex) { + log.error("Ignoring exception thrown while closing InputStream", ex); + } + } + } } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/ConfigurationClassMethodVisitor.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/ConfigurationClassMethodVisitor.java index 2fdda4f1478..d4d81861ae6 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/ConfigurationClassMethodVisitor.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/ConfigurationClassMethodVisitor.java @@ -34,113 +34,114 @@ import org.springframework.config.java.Factory; import org.springframework.config.java.ModelClass; import org.springframework.config.java.ModelMethod; + /** - * Visits a single method declared in a given {@link Configuration} class. Determines whether the - * method is a {@link Factory} method and if so, adds it to the {@link ConfigurationClass}. + * Visits a single method declared in a given {@link Configuration} class. Determines + * whether the method is a {@link Factory} method and if so, adds it to the + * {@link ConfigurationClass}. * * @author Chris Beams */ class ConfigurationClassMethodVisitor extends MethodAdapter { - private final ConfigurationClass configClass; - private final String methodName; - private final int modifiers; - private final ModelClass returnType; - private final ArrayList annotations = new ArrayList(); - - private boolean isModelMethod = false; - private int lineNumber; - - /** - * Creates a new {@link ConfigurationClassMethodVisitor} instance. - * - * @param configClass model object to which this method will be added - * @param methodName name of the method declared in the {@link Configuration} class - * @param methodDescriptor ASM representation of the method signature - * @param modifiers modifiers for this method - */ - public ConfigurationClassMethodVisitor(ConfigurationClass configClass, String methodName, - String methodDescriptor, int modifiers) { - super(AsmUtils.EMPTY_VISITOR); - - this.configClass = configClass; - this.methodName = methodName; - this.returnType = initReturnTypeFromMethodDescriptor(methodDescriptor); - this.modifiers = modifiers; - } - - /** - * Visits a single annotation on this method. Will be called once for each - * annotation present (regardless of its RetentionPolicy). - */ - @Override - public AnnotationVisitor visitAnnotation(String annoTypeDesc, boolean visible) { - String annoClassName = AsmUtils.convertTypeDescriptorToClassName(annoTypeDesc); - - Class annoClass = loadToolingSafeClass(annoClassName); - - if(annoClass == null) - return super.visitAnnotation(annoTypeDesc, visible); - - Annotation annotation = createMutableAnnotation(annoClass); - - annotations.add(annotation); - - return new MutableAnnotationVisitor(annotation); - } - - /** - * Provides the line number of this method within its declaring class. In reality, - * this number is always inaccurate - lineNo represents the line number - * of the first instruction in this method. Method declaration line numbers are - * not in any way tracked in the bytecode. Any tooling or output that reads this - * value will have to compensate and estimate where the actual method declaration - * is. - */ - @Override - public void visitLineNumber(int lineNo, Label start) { - this.lineNumber = lineNo; - } - - /** - * Parses through all {@link #annotations} on this method in order to determine whether - * it is a {@link Factory} method or not and if so adds it to the - * enclosing {@link #configClass}. - */ - @Override - public void visitEnd() { - for(Annotation anno : annotations) { - if(anno.annotationType().getAnnotation(Factory.class) != null) { - isModelMethod = true; - break; - } - } - - if(!isModelMethod) - return; - - Annotation[] annoArray = annotations.toArray(new Annotation[] { }); - ModelMethod method = new ModelMethod(methodName, modifiers, returnType, annoArray); - method.setLineNumber(lineNumber); - configClass.addMethod(method); - } - - /** - * Determines return type from ASM methodDescriptor and determines whether - * that type is an interface. - */ - private static ModelClass initReturnTypeFromMethodDescriptor(String methodDescriptor) { - final ModelClass returnType = new ModelClass(getReturnTypeFromMethodDescriptor(methodDescriptor)); - - // detect whether the return type is an interface - newClassReader(convertClassNameToResourcePath(returnType.getName())).accept( - new ClassAdapter(AsmUtils.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); - } - }, false); - - return returnType; - } + private final ConfigurationClass configClass; + private final String methodName; + private final int modifiers; + private final ModelClass returnType; + private final ArrayList annotations = new ArrayList(); + + private boolean isModelMethod = false; + private int lineNumber; + + /** + * Creates a new {@link ConfigurationClassMethodVisitor} instance. + * + * @param configClass model object to which this method will be added + * @param methodName name of the method declared in the {@link Configuration} class + * @param methodDescriptor ASM representation of the method signature + * @param modifiers modifiers for this method + */ + public ConfigurationClassMethodVisitor(ConfigurationClass configClass, String methodName, + String methodDescriptor, int modifiers) { + super(AsmUtils.EMPTY_VISITOR); + + this.configClass = configClass; + this.methodName = methodName; + this.returnType = initReturnTypeFromMethodDescriptor(methodDescriptor); + this.modifiers = modifiers; + } + + /** + * Visits a single annotation on this method. Will be called once for each annotation + * present (regardless of its RetentionPolicy). + */ + @Override + public AnnotationVisitor visitAnnotation(String annoTypeDesc, boolean visible) { + String annoClassName = AsmUtils.convertTypeDescriptorToClassName(annoTypeDesc); + + Class annoClass = loadToolingSafeClass(annoClassName); + + if (annoClass == null) + return super.visitAnnotation(annoTypeDesc, visible); + + Annotation annotation = createMutableAnnotation(annoClass); + + annotations.add(annotation); + + return new MutableAnnotationVisitor(annotation); + } + + /** + * Provides the line number of this method within its declaring class. In reality, this + * number is always inaccurate - lineNo represents the line number of the + * first instruction in this method. Method declaration line numbers are not in any way + * tracked in the bytecode. Any tooling or output that reads this value will have to + * compensate and estimate where the actual method declaration is. + */ + @Override + public void visitLineNumber(int lineNo, Label start) { + this.lineNumber = lineNo; + } + + /** + * Parses through all {@link #annotations} on this method in order to determine whether + * it is a {@link Factory} method or not and if so adds it to the enclosing + * {@link #configClass}. + */ + @Override + public void visitEnd() { + for (Annotation anno : annotations) { + if (anno.annotationType().getAnnotation(Factory.class) != null) { + isModelMethod = true; + break; + } + } + + if (!isModelMethod) + return; + + Annotation[] annoArray = annotations.toArray(new Annotation[] {}); + ModelMethod method = new ModelMethod(methodName, modifiers, returnType, annoArray); + method.setLineNumber(lineNumber); + configClass.addMethod(method); + } + + /** + * Determines return type from ASM methodDescriptor and determines whether + * that type is an interface. + */ + private static ModelClass initReturnTypeFromMethodDescriptor(String methodDescriptor) { + final ModelClass returnType = new ModelClass(getReturnTypeFromMethodDescriptor(methodDescriptor)); + + // detect whether the return type is an interface + newClassReader(convertClassNameToResourcePath(returnType.getName())).accept( + new ClassAdapter(AsmUtils.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); + } + }, false); + + return returnType; + } } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/ConfigurationClassVisitor.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/ConfigurationClassVisitor.java index 016effa90ee..d4f2bf65f1f 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/ConfigurationClassVisitor.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/ConfigurationClassVisitor.java @@ -37,198 +37,196 @@ import org.springframework.util.ClassUtils; /** - * Visits a {@link Configuration} class, populating a {@link ConfigurationClass} instance with - * information gleaned along the way. + * Visits a {@link Configuration} class, populating a {@link ConfigurationClass} instance + * with information gleaned along the way. * * @author Chris Beams */ class ConfigurationClassVisitor extends ClassAdapter { - private static final Log log = LogFactory.getLog(ConfigurationClassVisitor.class); - private static final String OBJECT_DESC = convertClassNameToResourcePath(Object.class.getName()); - - private final ConfigurationClass configClass; - private final ConfigurationModel model; - private final HashMap innerClasses = new HashMap(); - - private boolean processInnerClasses = true; - - public ConfigurationClassVisitor(ConfigurationClass configClass, ConfigurationModel model) { - super(AsmUtils.EMPTY_VISITOR); - this.configClass = configClass; - this.model = model; - } - - public void setProcessInnerClasses(boolean processInnerClasses) { - this.processInnerClasses = processInnerClasses; - } - - @Override - public void visitSource(String sourceFile, String debug) { - String resourcePath = - convertClassNameToResourcePath(configClass.getName()) - .substring(0, configClass.getName().lastIndexOf('.')+1) - .concat(sourceFile); - - configClass.setSource(resourcePath); - } - - @Override - public void visit(int classVersion, int modifiers, String classTypeDesc, String arg3, - String superTypeDesc, String[] arg5) { - visitSuperType(superTypeDesc); - - configClass.setName(convertResourcePathToClassName(classTypeDesc)); - - // ASM always adds ACC_SUPER to the opcodes/modifiers for class definitions. - // Unknown as to why (JavaDoc is silent on the matter), but it should be - // eliminated in order to comply with java.lang.reflect.Modifier values. - configClass.setModifiers(modifiers - Opcodes.ACC_SUPER); - } - - private void visitSuperType(String superTypeDesc) { - // traverse up the type hierarchy unless the next ancestor is java.lang.Object - if(OBJECT_DESC.equals(superTypeDesc)) - return; - - ConfigurationClassVisitor visitor = new ConfigurationClassVisitor(configClass, model); - - ClassReader reader = AsmUtils.newClassReader(superTypeDesc); - reader.accept(visitor, false); - } - - /** - * Visits a class level annotation on a {@link Configuration @Configuration} class. - * Accounts for all possible class-level annotations that are respected by JavaConfig - * including AspectJ's {@code @Aspect} annotation. - *

- * Upon encountering such an annotation, update the {@link #configClass} model object - * appropriately, and then return an {@link AnnotationVisitor} implementation that can - * populate the annotation appropriately with data. - * - * @see MutableAnnotation - */ - @Override - public AnnotationVisitor visitAnnotation(String annoTypeDesc, boolean visible) { - String annoTypeName = AsmUtils.convertTypeDescriptorToClassName(annoTypeDesc); - - if (Configuration.class.getName().equals(annoTypeName)) { - Configuration mutableConfiguration = createMutableAnnotation(Configuration.class); - configClass.setMetadata(mutableConfiguration); - return new MutableAnnotationVisitor(mutableConfiguration); - } - - // TODO: re-enable for @Import support -// if (Import.class.getName().equals(annoTypeName)) { -// ImportStack importStack = ImportStackHolder.getImportStack(); -// -// if(importStack.contains(configClass)) -// throw new CircularImportException(configClass, importStack); -// -// importStack.push(configClass); -// -// return new ImportAnnotationVisitor(model); -// } - - // ------------------------------------- - // Detect @Plugin annotations - // ------------------------------------- - PluginAnnotationDetectingClassVisitor classVisitor = new PluginAnnotationDetectingClassVisitor(); - - String className = AsmUtils.convertTypeDescriptorToClassName(annoTypeDesc); - String resourcePath = ClassUtils.convertClassNameToResourcePath(className); - ClassReader reader = AsmUtils.newClassReader(resourcePath); - reader.accept(classVisitor, false); - - if(!classVisitor.hasPluginAnnotation()) - return super.visitAnnotation(annoTypeDesc, visible); - - Class annoType = loadToolingSafeClass(annoTypeName); - - if(annoType == null) - return super.visitAnnotation(annoTypeDesc, visible); - - Annotation pluginAnno = createMutableAnnotation(annoType); - configClass.addPluginAnnotation(pluginAnno); - return new MutableAnnotationVisitor(pluginAnno); - } - - private static class PluginAnnotationDetectingClassVisitor extends ClassAdapter { - private boolean hasPluginAnnotation = false; - private final Extension pluginAnnotation = createMutableAnnotation(Extension.class); - - public PluginAnnotationDetectingClassVisitor() { - super(AsmUtils.EMPTY_VISITOR); - } - - @Override - public AnnotationVisitor visitAnnotation(String typeDesc, boolean arg1) { - if(Extension.class.getName().equals(AsmUtils.convertTypeDescriptorToClassName(typeDesc))) { - hasPluginAnnotation = true; - return new MutableAnnotationVisitor(pluginAnnotation); - } - return super.visitAnnotation(typeDesc, arg1); - } - - public boolean hasPluginAnnotation() { - return hasPluginAnnotation; - } - - public Extension getPluginAnnotation() { - return pluginAnnotation; - } - } - - /** - * Delegates all {@link Configuration @Configuration} class method parsing to - * {@link ConfigurationClassMethodVisitor}. - */ - @Override - public MethodVisitor visitMethod(int modifiers, String methodName, String methodDescriptor, - String arg3, String[] arg4) { - - return new ConfigurationClassMethodVisitor(configClass, methodName, methodDescriptor, modifiers); - } - - /** - * Implementation deals with inner classes here even though it would have - * been more intuitive to deal with outer classes. Due to limitations in ASM - * (resulting from limitations in the VM spec) we cannot directly look for outer classes - * in all cases, so instead build up a model of {@link #innerClasses} and process - * declaring class logic in a kind of inverted manner. - */ - @Override - public void visitInnerClass(String name, String outerName, String innerName, int access) { - if(processInnerClasses == false) - return; - - String innerClassName = convertResourcePathToClassName(name); - String configClassName = configClass.getName(); - - // if the innerClassName is equal to configClassName, we just - // ran into the outermost inner class look up the outer class - // associated with this - if(innerClassName.equals(configClassName)) { - if(innerClasses.containsKey(outerName)) { - configClass.setDeclaringClass(innerClasses.get(outerName)); - } - return; - } - - ConfigurationClass innerConfigClass = new ConfigurationClass(); - - ConfigurationClassVisitor ccVisitor = - new ConfigurationClassVisitor(innerConfigClass, new ConfigurationModel()); - ccVisitor.setProcessInnerClasses(false); - - ClassReader reader = AsmUtils.newClassReader(name); - reader.accept(ccVisitor, false); - - if(innerClasses.containsKey(outerName)) - innerConfigClass.setDeclaringClass(innerClasses.get(outerName)); - - // is the inner class a @Configuration class? If so, add it to the list - if(innerConfigClass.getMetadata() != null) - innerClasses.put(name, innerConfigClass); - } + private static final Log log = LogFactory.getLog(ConfigurationClassVisitor.class); + private static final String OBJECT_DESC = convertClassNameToResourcePath(Object.class.getName()); + + private final ConfigurationClass configClass; + private final ConfigurationModel model; + private final HashMap innerClasses = new HashMap(); + + private boolean processInnerClasses = true; + + public ConfigurationClassVisitor(ConfigurationClass configClass, ConfigurationModel model) { + super(AsmUtils.EMPTY_VISITOR); + this.configClass = configClass; + this.model = model; + } + + public void setProcessInnerClasses(boolean processInnerClasses) { + this.processInnerClasses = processInnerClasses; + } + + @Override + public void visitSource(String sourceFile, String debug) { + String resourcePath = convertClassNameToResourcePath(configClass.getName()).substring(0, + configClass.getName().lastIndexOf('.') + 1).concat(sourceFile); + + configClass.setSource(resourcePath); + } + + @Override + public void visit(int classVersion, int modifiers, String classTypeDesc, String arg3, + String superTypeDesc, String[] arg5) { + visitSuperType(superTypeDesc); + + configClass.setName(convertResourcePathToClassName(classTypeDesc)); + + // ASM always adds ACC_SUPER to the opcodes/modifiers for class definitions. + // Unknown as to why (JavaDoc is silent on the matter), but it should be + // eliminated in order to comply with java.lang.reflect.Modifier values. + configClass.setModifiers(modifiers - Opcodes.ACC_SUPER); + } + + private void visitSuperType(String superTypeDesc) { + // traverse up the type hierarchy unless the next ancestor is java.lang.Object + if (OBJECT_DESC.equals(superTypeDesc)) + return; + + ConfigurationClassVisitor visitor = new ConfigurationClassVisitor(configClass, model); + + ClassReader reader = AsmUtils.newClassReader(superTypeDesc); + reader.accept(visitor, false); + } + + /** + * Visits a class level annotation on a {@link Configuration @Configuration} class. + * Accounts for all possible class-level annotations that are respected by JavaConfig + * including AspectJ's {@code @Aspect} annotation. + *

+ * Upon encountering such an annotation, update the {@link #configClass} model object + * appropriately, and then return an {@link AnnotationVisitor} implementation that can + * populate the annotation appropriately with data. + * + * @see MutableAnnotation + */ + @Override + public AnnotationVisitor visitAnnotation(String annoTypeDesc, boolean visible) { + String annoTypeName = AsmUtils.convertTypeDescriptorToClassName(annoTypeDesc); + + if (Configuration.class.getName().equals(annoTypeName)) { + Configuration mutableConfiguration = createMutableAnnotation(Configuration.class); + configClass.setMetadata(mutableConfiguration); + return new MutableAnnotationVisitor(mutableConfiguration); + } + + // TODO: re-enable for @Import support + // if (Import.class.getName().equals(annoTypeName)) { + // ImportStack importStack = ImportStackHolder.getImportStack(); + // + // if(importStack.contains(configClass)) + // throw new CircularImportException(configClass, importStack); + // + // importStack.push(configClass); + // + // return new ImportAnnotationVisitor(model); + // } + + // ------------------------------------- + // Detect @Plugin annotations + // ------------------------------------- + PluginAnnotationDetectingClassVisitor classVisitor = new PluginAnnotationDetectingClassVisitor(); + + String className = AsmUtils.convertTypeDescriptorToClassName(annoTypeDesc); + String resourcePath = ClassUtils.convertClassNameToResourcePath(className); + ClassReader reader = AsmUtils.newClassReader(resourcePath); + reader.accept(classVisitor, false); + + if (!classVisitor.hasPluginAnnotation()) + return super.visitAnnotation(annoTypeDesc, visible); + + Class annoType = loadToolingSafeClass(annoTypeName); + + if (annoType == null) + return super.visitAnnotation(annoTypeDesc, visible); + + Annotation pluginAnno = createMutableAnnotation(annoType); + configClass.addPluginAnnotation(pluginAnno); + return new MutableAnnotationVisitor(pluginAnno); + } + + private static class PluginAnnotationDetectingClassVisitor extends ClassAdapter { + private boolean hasPluginAnnotation = false; + private final Extension pluginAnnotation = createMutableAnnotation(Extension.class); + + public PluginAnnotationDetectingClassVisitor() { + super(AsmUtils.EMPTY_VISITOR); + } + + @Override + public AnnotationVisitor visitAnnotation(String typeDesc, boolean arg1) { + if (Extension.class.getName().equals(AsmUtils.convertTypeDescriptorToClassName(typeDesc))) { + hasPluginAnnotation = true; + return new MutableAnnotationVisitor(pluginAnnotation); + } + return super.visitAnnotation(typeDesc, arg1); + } + + public boolean hasPluginAnnotation() { + return hasPluginAnnotation; + } + + public Extension getPluginAnnotation() { + return pluginAnnotation; + } + } + + /** + * Delegates all {@link Configuration @Configuration} class method parsing to + * {@link ConfigurationClassMethodVisitor}. + */ + @Override + public MethodVisitor visitMethod(int modifiers, String methodName, String methodDescriptor, String arg3, + String[] arg4) { + + return new ConfigurationClassMethodVisitor(configClass, methodName, methodDescriptor, modifiers); + } + + /** + * Implementation deals with inner classes here even though it would have been more + * intuitive to deal with outer classes. Due to limitations in ASM (resulting from + * limitations in the VM spec) we cannot directly look for outer classes in all cases, + * so instead build up a model of {@link #innerClasses} and process declaring class + * logic in a kind of inverted manner. + */ + @Override + public void visitInnerClass(String name, String outerName, String innerName, int access) { + if (processInnerClasses == false) + return; + + String innerClassName = convertResourcePathToClassName(name); + String configClassName = configClass.getName(); + + // if the innerClassName is equal to configClassName, we just + // ran into the outermost inner class look up the outer class + // associated with this + if (innerClassName.equals(configClassName)) { + if (innerClasses.containsKey(outerName)) { + configClass.setDeclaringClass(innerClasses.get(outerName)); + } + return; + } + + ConfigurationClass innerConfigClass = new ConfigurationClass(); + + ConfigurationClassVisitor ccVisitor = new ConfigurationClassVisitor(innerConfigClass, + new ConfigurationModel()); + ccVisitor.setProcessInnerClasses(false); + + ClassReader reader = AsmUtils.newClassReader(name); + reader.accept(ccVisitor, false); + + if (innerClasses.containsKey(outerName)) + innerConfigClass.setDeclaringClass(innerClasses.get(outerName)); + + // is the inner class a @Configuration class? If so, add it to the list + if (innerConfigClass.getMetadata() != null) + innerClasses.put(name, innerConfigClass); + } } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/MutableAnnotation.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/MutableAnnotation.java index 65d68385f21..6fc66f390ef 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/MutableAnnotation.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/MutableAnnotation.java @@ -16,16 +16,15 @@ package org.springframework.config.java.internal.parsing; /** - * 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 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 user-defined annotations, this interface becomes - * public. Because it is in the internal.* package, it won't pollute the public - * API, but developers should take caution not to use this annotation outside - * the internal.parsing package. + * 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 + * 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 + * user-defined annotations, this interface becomes public. Because it is in the internal.* + * package, it won't pollute the public API, but developers should take caution not to use + * this annotation outside the internal.parsing package. * * @author Chris Beams */ diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/MutableAnnotationArrayVisitor.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/MutableAnnotationArrayVisitor.java index b67f034e4cc..e5cba8fb165 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/MutableAnnotationArrayVisitor.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/MutableAnnotationArrayVisitor.java @@ -29,43 +29,43 @@ import org.objectweb.asm.AnnotationVisitor; /** TODO: JAVADOC */ class MutableAnnotationArrayVisitor extends AnnotationAdapter { - - private static final Log log = LogFactory.getLog(MutableAnnotationArrayVisitor.class); - - private final ArrayList values = new ArrayList(); - private final MutableAnnotation mutableAnno; - private final String attribName; - public MutableAnnotationArrayVisitor(MutableAnnotation mutableAnno, String attribName) { - super(AsmUtils.EMPTY_VISITOR); + private static final Log log = LogFactory.getLog(MutableAnnotationArrayVisitor.class); - this.mutableAnno = mutableAnno; - this.attribName = attribName; - } + private final ArrayList values = new ArrayList(); + private final MutableAnnotation mutableAnno; + private final String attribName; - @Override - public void visit(String na, Object value) { - values.add(value); - } + public MutableAnnotationArrayVisitor(MutableAnnotation mutableAnno, String attribName) { + super(AsmUtils.EMPTY_VISITOR); - @Override - public AnnotationVisitor visitAnnotation(String na, String annoTypeDesc) { - String annoTypeName = AsmUtils.convertTypeDescriptorToClassName(annoTypeDesc); - Class annoType = loadToolingSafeClass(annoTypeName); - - if(annoType == null) - return super.visitAnnotation(na, annoTypeDesc); - - Annotation anno = createMutableAnnotation(annoType); - values.add(anno); - return new MutableAnnotationVisitor(anno); - } + this.mutableAnno = mutableAnno; + this.attribName = attribName; + } - @Override - public void visitEnd() { - Class arrayType = mutableAnno.getAttributeType(attribName); - Object[] array = (Object[])Array.newInstance(arrayType.getComponentType(), 0); - mutableAnno.setAttributeValue(attribName, values.toArray(array)); - } + @Override + public void visit(String na, Object value) { + values.add(value); + } + + @Override + public AnnotationVisitor visitAnnotation(String na, String annoTypeDesc) { + String annoTypeName = AsmUtils.convertTypeDescriptorToClassName(annoTypeDesc); + Class annoType = loadToolingSafeClass(annoTypeName); + + if (annoType == null) + return super.visitAnnotation(na, annoTypeDesc); + + Annotation anno = createMutableAnnotation(annoType); + values.add(anno); + return new MutableAnnotationVisitor(anno); + } + + @Override + public void visitEnd() { + Class arrayType = mutableAnno.getAttributeType(attribName); + Object[] array = (Object[]) Array.newInstance(arrayType.getComponentType(), 0); + mutableAnno.setAttributeValue(attribName, values.toArray(array)); + } } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/MutableAnnotationInvocationHandler.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/MutableAnnotationInvocationHandler.java index f33ba0e8954..a89f3215f22 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/MutableAnnotationInvocationHandler.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/MutableAnnotationInvocationHandler.java @@ -33,170 +33,173 @@ import org.springframework.util.StringUtils; /** TODO: JAVADOC */ final class MutableAnnotationInvocationHandler implements InvocationHandler { - private final Class annoType; - private final HashMap attributes = new HashMap(); - private final HashMap> attributeTypes = new HashMap>(); - - public MutableAnnotationInvocationHandler(Class annoType) { - // pre-populate the attributes hash will all the names - // and default values of the attributes defined in 'annoType' - Method[] attribs = annoType.getDeclaredMethods(); - for(Method attrib : attribs) { - this.attributes.put(attrib.getName(), getDefaultValue(annoType, attrib.getName())); - this.attributeTypes.put(attrib.getName(), getAttributeType(annoType, attrib.getName())); - } - - this.annoType = annoType; - } - - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - Assert.isInstanceOf(Annotation.class, proxy); - - String methodName = method.getName(); - - // first -> check to see if this method is an attribute on our annotation - if(attributes.containsKey(methodName)) - return attributes.get(methodName); - - - // second -> is it a method from java.lang.annotation.Annotation? - if(methodName.equals("annotationType")) - return annoType; - - - // third -> is it a method from java.lang.Object? - if(methodName.equals("toString")) - return format("@%s(%s)", annoType.getName(), getAttribs()); - - if(methodName.equals("equals")) - return isEqualTo(proxy, args[0]); - - if(methodName.equals("hashCode")) - return calculateHashCode(proxy); - - - // finally -> is it a method specified by MutableAnno? - if(methodName.equals("setAttributeValue")) { - attributes.put((String)args[0], args[1]); - return null; // setAttributeValue has a 'void' return type - } - - if(methodName.equals("getAttributeType")) - return attributeTypes.get(args[0]); - - throw new UnsupportedOperationException("this proxy does not support method: " + methodName); - } - - /** - * Conforms to the hashCode() specification for Annotation. - * - * @see Annotation#hashCode() - */ - private Object calculateHashCode(Object proxy) { - int sum = 0; - - for (String attribName : attributes.keySet()) { - Object attribValue = attributes.get(attribName); - - final int attribNameHashCode = attribName.hashCode(); - final int attribValueHashCode; - - if (attribValue == null) - // memberValue may be null when a mutable annotation is being added to a collection - // and before it has actually been visited (and populated) by MutableAnnotationVisitor - attribValueHashCode = 0; - else if (attribValue.getClass().isArray()) - attribValueHashCode = Arrays.hashCode((Object[]) attribValue); - else - attribValueHashCode = attribValue.hashCode(); - - sum += (127 * attribNameHashCode) ^ attribValueHashCode; - } - - return sum; - } - - /** - * Compares proxy object and other object by comparing the return values - * of the methods specified by their common {@link Annotation} ancestry. - *

- * other must be the same type as or a subtype of proxy. - * Will return false otherwise. - *

- * Eagerly returns true if {@code proxy} == other

- *

- * Conforms strictly to the equals() specification for Annotation

- * - * @see Annotation#equals(Object) - */ - private Object isEqualTo(Object proxy, Object other) { - if (proxy == other) - return true; - - if (other == null) - return false; - - if(!annoType.isAssignableFrom(other.getClass())) - return false; - - for (String attribName : attributes.keySet()) { - Object thisVal; - Object thatVal; - - try { - thisVal = attributes.get(attribName); - thatVal = other.getClass().getDeclaredMethod(attribName).invoke(other); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - - if ((thisVal == null) && (thatVal != null)) - return false; - - if ((thatVal == null) && (thisVal != null)) - return false; - - if (thatVal.getClass().isArray()) { - if (!Arrays.equals((Object[]) thatVal, (Object[]) thisVal)) { - return false; - } - } else if (thisVal instanceof Double) { - if (!Double.valueOf((Double) thisVal).equals(Double.valueOf((Double) thatVal))) - return false; - } else if (thisVal instanceof Float) { - if (!Float.valueOf((Float) thisVal).equals(Float.valueOf((Float) thatVal))) - return false; - } else if (!thisVal.equals(thatVal)) { - return false; - } - } - - return true; - } - - private String getAttribs() { - ArrayList attribs = new ArrayList(); - - for (String attribName : attributes.keySet()) - attribs.add(format("%s=%s", attribName, attributes.get(attribName))); - - return StringUtils.collectionToDelimitedString(attribs, ", "); - } - - /** - * Retrieve the type of the given annotation attribute. - */ - private static Class getAttributeType(Class annotationType, String attributeName) { - Method method = null; - - try { - method = annotationType.getDeclaredMethod(attributeName); - } - catch (Exception ex) { - ReflectionUtils.handleReflectionException(ex); - } - - return method.getReturnType(); - } + private final Class annoType; + private final HashMap attributes = new HashMap(); + private final HashMap> attributeTypes = new HashMap>(); + + public MutableAnnotationInvocationHandler(Class annoType) { + // pre-populate the attributes hash will all the names + // and default values of the attributes defined in 'annoType' + Method[] attribs = annoType.getDeclaredMethods(); + for (Method attrib : attribs) { + this.attributes.put(attrib.getName(), getDefaultValue(annoType, attrib.getName())); + this.attributeTypes.put(attrib.getName(), getAttributeType(annoType, attrib.getName())); + } + + this.annoType = annoType; + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + Assert.isInstanceOf(Annotation.class, proxy); + + String methodName = method.getName(); + + // first -> check to see if this method is an attribute on our annotation + if (attributes.containsKey(methodName)) + return attributes.get(methodName); + + + // second -> is it a method from java.lang.annotation.Annotation? + if (methodName.equals("annotationType")) + return annoType; + + + // third -> is it a method from java.lang.Object? + if (methodName.equals("toString")) + return format("@%s(%s)", annoType.getName(), getAttribs()); + + if (methodName.equals("equals")) + return isEqualTo(proxy, args[0]); + + if (methodName.equals("hashCode")) + return calculateHashCode(proxy); + + + // finally -> is it a method specified by MutableAnno? + if (methodName.equals("setAttributeValue")) { + attributes.put((String) args[0], args[1]); + return null; // setAttributeValue has a 'void' return type + } + + if (methodName.equals("getAttributeType")) + return attributeTypes.get(args[0]); + + throw new UnsupportedOperationException("this proxy does not support method: " + methodName); + } + + /** + * Conforms to the hashCode() specification for Annotation. + * + * @see Annotation#hashCode() + */ + private Object calculateHashCode(Object proxy) { + int sum = 0; + + for (String attribName : attributes.keySet()) { + Object attribValue = attributes.get(attribName); + + final int attribNameHashCode = attribName.hashCode(); + final int attribValueHashCode; + + if (attribValue == null) + // memberValue may be null when a mutable annotation is being added to a + // collection + // and before it has actually been visited (and populated) by + // MutableAnnotationVisitor + attribValueHashCode = 0; + else if (attribValue.getClass().isArray()) + attribValueHashCode = Arrays.hashCode((Object[]) attribValue); + else + attribValueHashCode = attribValue.hashCode(); + + sum += (127 * attribNameHashCode) ^ attribValueHashCode; + } + + return sum; + } + + /** + * Compares proxy object and other object by comparing the return + * values of the methods specified by their common {@link Annotation} ancestry. + *

+ * other must be the same type as or a subtype of proxy. Will + * return false otherwise. + *

+ * Eagerly returns true if {@code proxy} == other + *

+ *

+ * Conforms strictly to the equals() specification for Annotation + *

+ * + * @see Annotation#equals(Object) + */ + private Object isEqualTo(Object proxy, Object other) { + if (proxy == other) + return true; + + if (other == null) + return false; + + if (!annoType.isAssignableFrom(other.getClass())) + return false; + + for (String attribName : attributes.keySet()) { + Object thisVal; + Object thatVal; + + try { + thisVal = attributes.get(attribName); + thatVal = other.getClass().getDeclaredMethod(attribName).invoke(other); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + + if ((thisVal == null) && (thatVal != null)) + return false; + + if ((thatVal == null) && (thisVal != null)) + return false; + + if (thatVal.getClass().isArray()) { + if (!Arrays.equals((Object[]) thatVal, (Object[]) thisVal)) { + return false; + } + } else if (thisVal instanceof Double) { + if (!Double.valueOf((Double) thisVal).equals(Double.valueOf((Double) thatVal))) + return false; + } else if (thisVal instanceof Float) { + if (!Float.valueOf((Float) thisVal).equals(Float.valueOf((Float) thatVal))) + return false; + } else if (!thisVal.equals(thatVal)) { + return false; + } + } + + return true; + } + + private String getAttribs() { + ArrayList attribs = new ArrayList(); + + for (String attribName : attributes.keySet()) + attribs.add(format("%s=%s", attribName, attributes.get(attribName))); + + return StringUtils.collectionToDelimitedString(attribs, ", "); + } + + /** + * Retrieve the type of the given annotation attribute. + */ + private static Class getAttributeType(Class annotationType, String attributeName) { + Method method = null; + + try { + method = annotationType.getDeclaredMethod(attributeName); + } catch (Exception ex) { + ReflectionUtils.handleReflectionException(ex); + } + + return method.getReturnType(); + } } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/MutableAnnotationUtils.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/MutableAnnotationUtils.java index cd50f4f683a..da16ea54271 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/MutableAnnotationUtils.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/MutableAnnotationUtils.java @@ -22,22 +22,22 @@ import java.lang.reflect.Proxy; /** TODO: JAVADOC */ 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}; + /** + * 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; - } + @SuppressWarnings("unchecked") + A mutableAnno = (A) Proxy.newProxyInstance(classLoader, interfaces, handler); + return mutableAnno; + } } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/MutableAnnotationVisitor.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/MutableAnnotationVisitor.java index 2d92ac1a955..26558da9e99 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/MutableAnnotationVisitor.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/parsing/MutableAnnotationVisitor.java @@ -32,84 +32,85 @@ import org.springframework.util.Assert; * Populates a given {@link MutableAnnotation} instance with its attributes. */ class MutableAnnotationVisitor implements AnnotationVisitor { - - private static final Log log = LogFactory.getLog(MutableAnnotationVisitor.class); - - protected final MutableAnnotation mutableAnno; - - /** - * Creates a new {@link MutableAnnotationVisitor} instance that will populate - * the the attributes of the given mutableAnno. Accepts {@link Annotation} - * instead of {@link MutableAnnotation} to avoid the need for callers to typecast. - * - * @param mutableAnno {@link MutableAnnotation} instance to visit and populate - * - * @throws IllegalArgumentException if mutableAnno is not of type - * {@link MutableAnnotation} - * - * @see MutableAnnotationUtils#createMutableAnnotation(Class) - */ - public MutableAnnotationVisitor(Annotation mutableAnno) { - Assert.isInstanceOf(MutableAnnotation.class, mutableAnno, "annotation must be mutable"); - this.mutableAnno = (MutableAnnotation)mutableAnno; - } - - public AnnotationVisitor visitArray(final String attribName) { - return new MutableAnnotationArrayVisitor(mutableAnno, attribName); - } - - public void visit(String attribName, Object attribValue) { - Class attribReturnType = mutableAnno.getAttributeType(attribName); - - if (attribReturnType.equals(Class.class)) { - // the attribute type is Class -> load it and set it. - String fqClassName = ((Type) attribValue).getClassName(); - - Class classVal = loadToolingSafeClass(fqClassName); - - if(classVal == null) - return; - - mutableAnno.setAttributeValue(attribName, classVal); - return; - } - - // otherwise, assume the value can be set literally - mutableAnno.setAttributeValue(attribName, attribValue); - } - - @SuppressWarnings("unchecked") - public void visitEnum(String attribName, String enumTypeDescriptor, String strEnumValue) { - String enumClassName = AsmUtils.convertTypeDescriptorToClassName(enumTypeDescriptor); - - Class enumClass = loadToolingSafeClass(enumClassName); - - if(enumClass == null) - return; - - Enum enumValue = Enum.valueOf(enumClass, strEnumValue); - mutableAnno.setAttributeValue(attribName, enumValue); - } - - public AnnotationVisitor visitAnnotation(String attribName, String attribAnnoTypeDesc) { - String annoTypeName = AsmUtils.convertTypeDescriptorToClassName(attribAnnoTypeDesc); - Class annoType = loadToolingSafeClass(annoTypeName); - - if(annoType == null) - return AsmUtils.EMPTY_VISITOR.visitAnnotation(attribName, attribAnnoTypeDesc); - - Annotation anno = createMutableAnnotation(annoType); - - try { - Field attribute = mutableAnno.getClass().getField(attribName); - attribute.set(mutableAnno, anno); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - - return new MutableAnnotationVisitor(anno); - } - - public void visitEnd() { } + + private static final Log log = LogFactory.getLog(MutableAnnotationVisitor.class); + + protected final MutableAnnotation mutableAnno; + + /** + * Creates a new {@link MutableAnnotationVisitor} instance that will populate the the + * attributes of the given mutableAnno. Accepts {@link Annotation} instead of + * {@link MutableAnnotation} to avoid the need for callers to typecast. + * + * @param mutableAnno {@link MutableAnnotation} instance to visit and populate + * + * @throws IllegalArgumentException if mutableAnno is not of type + * {@link MutableAnnotation} + * + * @see MutableAnnotationUtils#createMutableAnnotation(Class) + */ + public MutableAnnotationVisitor(Annotation mutableAnno) { + Assert.isInstanceOf(MutableAnnotation.class, mutableAnno, "annotation must be mutable"); + this.mutableAnno = (MutableAnnotation) mutableAnno; + } + + public AnnotationVisitor visitArray(final String attribName) { + return new MutableAnnotationArrayVisitor(mutableAnno, attribName); + } + + public void visit(String attribName, Object attribValue) { + Class attribReturnType = mutableAnno.getAttributeType(attribName); + + if (attribReturnType.equals(Class.class)) { + // the attribute type is Class -> load it and set it. + String fqClassName = ((Type) attribValue).getClassName(); + + Class classVal = loadToolingSafeClass(fqClassName); + + if (classVal == null) + return; + + mutableAnno.setAttributeValue(attribName, classVal); + return; + } + + // otherwise, assume the value can be set literally + mutableAnno.setAttributeValue(attribName, attribValue); + } + + @SuppressWarnings("unchecked") + public void visitEnum(String attribName, String enumTypeDescriptor, String strEnumValue) { + String enumClassName = AsmUtils.convertTypeDescriptorToClassName(enumTypeDescriptor); + + Class enumClass = loadToolingSafeClass(enumClassName); + + if (enumClass == null) + return; + + Enum enumValue = Enum.valueOf(enumClass, strEnumValue); + mutableAnno.setAttributeValue(attribName, enumValue); + } + + public AnnotationVisitor visitAnnotation(String attribName, String attribAnnoTypeDesc) { + String annoTypeName = AsmUtils.convertTypeDescriptorToClassName(attribAnnoTypeDesc); + Class annoType = loadToolingSafeClass(annoTypeName); + + if (annoType == null) + return AsmUtils.EMPTY_VISITOR.visitAnnotation(attribName, attribAnnoTypeDesc); + + Annotation anno = createMutableAnnotation(annoType); + + try { + Field attribute = mutableAnno.getClass().getField(attribName); + attribute.set(mutableAnno, anno); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + + return new MutableAnnotationVisitor(anno); + } + + public void visitEnd() { + } } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/plugin/Extension.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/plugin/Extension.java index 928c9037a46..6bb68f2ee70 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/plugin/Extension.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/plugin/Extension.java @@ -31,18 +31,18 @@ import org.springframework.core.Ordered; @Target(ElementType.ANNOTATION_TYPE) @Inherited public @interface Extension { - /** - * The class that handles this plugin. - */ - // TODO: SJC-242 rename to handlerType / handlerClass - Class> handler(); - - /** - * The order in which this plugin will be processed - * relative to others. Per the semantics of {@link Ordered}, - * lower integer values will be treated as higher priority. - * - * @see Ordered - */ - int order() default 0; + /** + * The class that handles this plugin. + */ + // TODO: SJC-242 rename to handlerType / handlerClass + Class> handler(); + + /** + * The order in which this plugin will be processed relative to others. Per the + * semantics of {@link Ordered}, lower integer values will be treated as higher + * priority. + * + * @see Ordered + */ + int order() default 0; } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/plugin/ExtensionAnnotationBeanDefinitionRegistrar.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/plugin/ExtensionAnnotationBeanDefinitionRegistrar.java index 22cd1a44168..ab7f8553b72 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/plugin/ExtensionAnnotationBeanDefinitionRegistrar.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/plugin/ExtensionAnnotationBeanDefinitionRegistrar.java @@ -24,9 +24,9 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry; * Registers bean definitions based on {@link Extension} metadata */ public interface ExtensionAnnotationBeanDefinitionRegistrar { - - /** + + /** * */ - void handle(A annotation, BeanDefinitionRegistry registry); + void handle(A annotation, BeanDefinitionRegistry registry); } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationModelBeanDefinitionReader.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationModelBeanDefinitionReader.java index e59b9d4b4f7..a30a59f5d72 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationModelBeanDefinitionReader.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationModelBeanDefinitionReader.java @@ -15,7 +15,6 @@ */ package org.springframework.config.java.support; - import static java.lang.String.*; import static org.springframework.config.java.Util.*; @@ -44,145 +43,146 @@ import org.springframework.core.io.Resource; import org.springframework.util.Assert; - /** - * Reads a given fully-populated {@link ConfigurationModel}, registering bean definitions with - * the given {@link BeanDefinitionRegistry} based on its contents. + * Reads a given fully-populated {@link ConfigurationModel}, registering bean definitions + * with the given {@link BeanDefinitionRegistry} based on its contents. *

* This class was modeled after the {@link BeanDefinitionReader} hierarchy, but does not * implement/extend any of its artifacts as {@link ConfigurationModel} is not a * {@link Resource}. - * - * @author Chris Beams + * + * @author Chris Beams */ class ConfigurationModelBeanDefinitionReader { - private static final Log log = LogFactory.getLog(ConfigurationModelBeanDefinitionReader.class); - - private final DefaultListableBeanFactory beanFactory; - - - /** - * Creates a new {@link ConfigurationModelBeanDefinitionReader} instance. - */ - public ConfigurationModelBeanDefinitionReader(DefaultListableBeanFactory beanFactory) { - this.beanFactory = beanFactory; - } - - /** - * Reads {@code model}, registering bean definitions with {@link #beanFactory} - * based on its contents. - * - * @return number of bean definitions generated - */ - public int loadBeanDefinitions(ConfigurationModel model) { - int initialBeanDefCount = beanFactory.getBeanDefinitionCount(); - - for (ConfigurationClass configClass : model.getAllConfigurationClasses()) - loadBeanDefinitionsForConfigurationClass(configClass); - - return beanFactory.getBeanDefinitionCount() - initialBeanDefCount; - } - - /** - * Reads a particular {@link ConfigurationClass}, registering bean definitions - * for the class itself, all its {@link Factory} methods and all its {@link Extension} - * annotations. - */ - private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass) { - doLoadBeanDefinitionForConfigurationClass(configClass); - - for (ModelMethod method : configClass.getMethods()) - loadBeanDefinitionsForModelMethod(method); - - Annotation[] pluginAnnotations = configClass.getPluginAnnotations(); - Arrays.sort(pluginAnnotations, new PluginComparator()); - for (Annotation annotation : pluginAnnotations) - loadBeanDefinitionsForExtensionAnnotation(annotation); - } - - /** - * Registers the {@link Configuration} class itself as a bean definition. - */ - private void doLoadBeanDefinitionForConfigurationClass(ConfigurationClass configClass) { - Configuration metadata = configClass.getMetadata(); - - if (metadata.checkRequired() == true) { - RootBeanDefinition requiredAnnotationPostProcessor = new RootBeanDefinition(); - Class beanClass = RequiredAnnotationBeanPostProcessor.class; - String beanName = beanClass.getName() + "#0"; - requiredAnnotationPostProcessor.setBeanClass(beanClass); - requiredAnnotationPostProcessor.setResourceDescription("ensures @Required methods have been invoked"); - beanFactory.registerBeanDefinition(beanName, requiredAnnotationPostProcessor); - } - - GenericBeanDefinition configBeanDef = new GenericBeanDefinition(); - configBeanDef.setBeanClassName(configClass.getName()); - - String configBeanName = configClass.getBeanName(); - - // consider the case where it's already been defined (probably in XML) - // and potentially has PropertyValues and ConstructorArgs) - if (beanFactory.containsBeanDefinition(configBeanName)) { - if (log.isInfoEnabled()) - log.info(format("Copying property and constructor arg values from existing bean definition for " - + "@Configuration class %s to new bean definition", configBeanName)); - AbstractBeanDefinition existing = (AbstractBeanDefinition)beanFactory.getBeanDefinition(configBeanName); - configBeanDef.setPropertyValues(existing.getPropertyValues()); - configBeanDef.setConstructorArgumentValues(existing.getConstructorArgumentValues()); - configBeanDef.setResource(existing.getResource()); - } - - if (log.isInfoEnabled()) - log.info(format("Registering bean definition for @Configuration class %s", configBeanName)); - - beanFactory.registerBeanDefinition(configBeanName, configBeanDef); - } - - - /** - * Reads a particular {@link ModelMethod}, registering bean definitions - * with {@link #beanFactory} based on its contents. - * - * @see Factory - */ - private void loadBeanDefinitionsForModelMethod(ModelMethod method) { - method.getRegistrar().register(method, beanFactory); - } - - @SuppressWarnings("unchecked") - private void loadBeanDefinitionsForExtensionAnnotation(Annotation anno) { - //ExtensionAnnotationUtils.getRegistrarFor(anno).registerBeanDefinitionsWith(beanFactory); - // there is a fixed assumption that in order for this annotation to have - // been registered in the first place, it must be meta-annotated with @Plugin - // assert this as an invariant now - Class annoClass = anno.getClass(); - Extension extensionAnno = AnnotationUtils.findAnnotation(annoClass, Extension.class); - Assert.isTrue(extensionAnno != null, - format("%s annotation is not annotated as a @%s", - annoClass, Extension.class.getSimpleName())); - - Class extHandlerClass = extensionAnno.handler(); - - ExtensionAnnotationBeanDefinitionRegistrar extHandler = getInstance(extHandlerClass); - extHandler.handle(anno, beanFactory); - } - - private static class PluginComparator implements Comparator { - public int compare(Annotation a1, Annotation a2) { - Integer i1 = getOrder(a1); - Integer i2 = getOrder(a2); - return i1.compareTo(i2); - } - - private Integer getOrder(Annotation a) { - Extension plugin = a.annotationType().getAnnotation(Extension.class); - if(plugin == null) - throw new IllegalArgumentException( - "annotation was not annotated with @Plugin: " + a.annotationType()); - return plugin.order(); - } - } - - + private static final Log log = LogFactory.getLog(ConfigurationModelBeanDefinitionReader.class); + + private final DefaultListableBeanFactory beanFactory; + + + /** + * Creates a new {@link ConfigurationModelBeanDefinitionReader} instance. + */ + public ConfigurationModelBeanDefinitionReader(DefaultListableBeanFactory beanFactory) { + this.beanFactory = beanFactory; + } + + /** + * Reads {@code model}, registering bean definitions with {@link #beanFactory} based on + * its contents. + * + * @return number of bean definitions generated + */ + public int loadBeanDefinitions(ConfigurationModel model) { + int initialBeanDefCount = beanFactory.getBeanDefinitionCount(); + + for (ConfigurationClass configClass : model.getAllConfigurationClasses()) + loadBeanDefinitionsForConfigurationClass(configClass); + + return beanFactory.getBeanDefinitionCount() - initialBeanDefCount; + } + + /** + * Reads a particular {@link ConfigurationClass}, registering bean definitions for the + * class itself, all its {@link Factory} methods and all its {@link Extension} + * annotations. + */ + private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass) { + doLoadBeanDefinitionForConfigurationClass(configClass); + + for (ModelMethod method : configClass.getMethods()) + loadBeanDefinitionsForModelMethod(method); + + Annotation[] pluginAnnotations = configClass.getPluginAnnotations(); + Arrays.sort(pluginAnnotations, new PluginComparator()); + for (Annotation annotation : pluginAnnotations) + loadBeanDefinitionsForExtensionAnnotation(annotation); + } + + /** + * Registers the {@link Configuration} class itself as a bean definition. + */ + private void doLoadBeanDefinitionForConfigurationClass(ConfigurationClass configClass) { + Configuration metadata = configClass.getMetadata(); + + if (metadata.checkRequired() == true) { + RootBeanDefinition requiredAnnotationPostProcessor = new RootBeanDefinition(); + Class beanClass = RequiredAnnotationBeanPostProcessor.class; + String beanName = beanClass.getName() + "#0"; + requiredAnnotationPostProcessor.setBeanClass(beanClass); + requiredAnnotationPostProcessor + .setResourceDescription("ensures @Required methods have been invoked"); + beanFactory.registerBeanDefinition(beanName, requiredAnnotationPostProcessor); + } + + GenericBeanDefinition configBeanDef = new GenericBeanDefinition(); + configBeanDef.setBeanClassName(configClass.getName()); + + String configBeanName = configClass.getBeanName(); + + // consider the case where it's already been defined (probably in XML) + // and potentially has PropertyValues and ConstructorArgs) + if (beanFactory.containsBeanDefinition(configBeanName)) { + if (log.isInfoEnabled()) + log.info(format( + "Copying property and constructor arg values from existing bean definition for " + + "@Configuration class %s to new bean definition", configBeanName)); + AbstractBeanDefinition existing = (AbstractBeanDefinition) beanFactory + .getBeanDefinition(configBeanName); + configBeanDef.setPropertyValues(existing.getPropertyValues()); + configBeanDef.setConstructorArgumentValues(existing.getConstructorArgumentValues()); + configBeanDef.setResource(existing.getResource()); + } + + if (log.isInfoEnabled()) + log.info(format("Registering bean definition for @Configuration class %s", configBeanName)); + + beanFactory.registerBeanDefinition(configBeanName, configBeanDef); + } + + + /** + * Reads a particular {@link ModelMethod}, registering bean definitions with + * {@link #beanFactory} based on its contents. + * + * @see Factory + */ + private void loadBeanDefinitionsForModelMethod(ModelMethod method) { + method.getRegistrar().register(method, beanFactory); + } + + @SuppressWarnings("unchecked") + private void loadBeanDefinitionsForExtensionAnnotation(Annotation anno) { + // ExtensionAnnotationUtils.getRegistrarFor(anno).registerBeanDefinitionsWith(beanFactory); + // there is a fixed assumption that in order for this annotation to have + // been registered in the first place, it must be meta-annotated with @Plugin + // assert this as an invariant now + Class annoClass = anno.getClass(); + Extension extensionAnno = AnnotationUtils.findAnnotation(annoClass, Extension.class); + Assert.isTrue(extensionAnno != null, format("%s annotation is not annotated as a @%s", annoClass, + Extension.class.getSimpleName())); + + Class extHandlerClass = extensionAnno.handler(); + + ExtensionAnnotationBeanDefinitionRegistrar extHandler = getInstance(extHandlerClass); + extHandler.handle(anno, beanFactory); + } + + private static class PluginComparator implements Comparator { + public int compare(Annotation a1, Annotation a2) { + Integer i1 = getOrder(a1); + Integer i2 = getOrder(a2); + return i1.compareTo(i2); + } + + private Integer getOrder(Annotation a) { + Extension plugin = a.annotationType().getAnnotation(Extension.class); + if (plugin == null) + throw new IllegalArgumentException("annotation was not annotated with @Plugin: " + + a.annotationType()); + return plugin.order(); + } + } + + } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationPostProcessor.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationPostProcessor.java index 99c337fa0d2..cd763cddd77 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationPostProcessor.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationPostProcessor.java @@ -40,128 +40,133 @@ import org.springframework.util.ClassUtils; /** - * {@link BeanFactoryPostProcessor} used for bootstrapping {@link Configuration @Configuration} - * beans from Spring XML files. + * {@link BeanFactoryPostProcessor} used for bootstrapping {@link Configuration + * @Configuration} beans from Spring XML files. */ public class ConfigurationPostProcessor implements Ordered, BeanFactoryPostProcessor { - private static final Log logger = LogFactory.getLog(ConfigurationPostProcessor.class); - - - /** - * Returns the order in which this {@link BeanPostProcessor} will be executed. - * Returns {@link Ordered#HIGHEST_PRECEDENCE}. - */ - public int getOrder() { - return Ordered.HIGHEST_PRECEDENCE; - } - - - /** - * Searches beanFactory for any {@link Configuration} classes in order - * to parse and enhance them. Also registers any {@link BeanPostProcessor} objects - * necessary to fulfill JavaConfig requirements. - */ - public void postProcessBeanFactory(ConfigurableListableBeanFactory clBeanFactory) throws BeansException { - if(!(clBeanFactory instanceof DefaultListableBeanFactory)) - throw new IllegalStateException("beanFactory must be of type " - + DefaultListableBeanFactory.class.getSimpleName()); - - DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) clBeanFactory; - - ConfigurationModel model = new ConfigurationModel(); - - parseAnyConfigurationClasses(beanFactory, model); - - enhanceAnyConfigurationClasses(beanFactory, model); - } - - private void parseAnyConfigurationClasses(DefaultListableBeanFactory beanFactory, ConfigurationModel model) { - - // linked map is important for maintaining predictable ordering of configuration classes. - // this is important in bean / value override situations. - LinkedHashMap configClassResources = new LinkedHashMap(); - - for (String beanName : beanFactory.getBeanDefinitionNames()) { - BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName); - if (beanDef.isAbstract()) - continue; - - if (isConfigClass(beanDef)) { - String path = ClassUtils.convertClassNameToResourcePath(beanDef.getBeanClassName()); - configClassResources.put(beanName, new ClassPathResource(path)); - } - } - - ConfigurationModelBeanDefinitionReader modelBeanDefinitionReader = new ConfigurationModelBeanDefinitionReader(beanFactory); - ConfigurationParser parser = new ConfigurationParser(model); - - for (String id : configClassResources.keySet()) - parser.parse(configClassResources.get(id), id); - - ArrayList errors = new ArrayList(); - model.validate(errors); - if (errors.size() > 0) - throw new MalformedConfigurationException(errors.toArray(new UsageError[] { })); - - modelBeanDefinitionReader.loadBeanDefinitions(model); - } - - /** - * Post-processes a BeanFactory in search of Configuration class BeanDefinitions; any candidates - * are then enhanced by a {@link ConfigurationEnhancer}. Candidate status is determined by - * BeanDefinition attribute metadata. - * - * @author Chris Beams - * @see ConfigurationEnhancer - * @see BeanFactoryPostProcessor - */ - private void enhanceAnyConfigurationClasses(DefaultListableBeanFactory beanFactory, ConfigurationModel model) { - - ConfigurationEnhancer enhancer = new ConfigurationEnhancer(beanFactory, model); - - int configClassesEnhanced = 0; - - for (String beanName : beanFactory.getBeanDefinitionNames()) { - BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName); - - if (!isConfigClass(beanDef)) - continue; - - String configClassName = beanDef.getBeanClassName(); - - String enhancedClassName = enhancer.enhance(configClassName); - - if (logger.isDebugEnabled()) - logger.debug(String.format("Replacing bean definition '%s' existing class name '%s' with enhanced class name '%s'", - beanName, configClassName, enhancedClassName)); - - beanDef.setBeanClassName(enhancedClassName); - - configClassesEnhanced++; - } - - if (configClassesEnhanced == 0) - logger.warn("Found no @Configuration class BeanDefinitions within " + beanFactory); - } - - /** - * Determines whether the class for beanDef is a {@link Configuration}-annotated - * class. Returns false if beanDef has no class specified. - *

- * Note: the classloading used within should not be problematic or interfere with tooling in any - * way. BeanFactoryPostProcessing happens only during actual runtime processing via - * {@link JavaConfigApplicationContext} or via XML using {@link ConfigurationPostProcessor}. In - * any case, tooling (Spring IDE) will hook in at a lower level than this class and - * thus never encounter this classloading. Should this become problematic, it would not be - * too difficult to replace the following with ASM logic that traverses the class hierarchy in - * order to find whether the class is directly or indirectly annotated with - * {@link Configuration}. - */ + private static final Log logger = LogFactory.getLog(ConfigurationPostProcessor.class); + + + /** + * Returns the order in which this {@link BeanPostProcessor} will be executed. Returns + * {@link Ordered#HIGHEST_PRECEDENCE}. + */ + public int getOrder() { + return Ordered.HIGHEST_PRECEDENCE; + } + + + /** + * Searches beanFactory for any {@link Configuration} classes in order to + * parse and enhance them. Also registers any {@link BeanPostProcessor} objects + * necessary to fulfill JavaConfig requirements. + */ + public void postProcessBeanFactory(ConfigurableListableBeanFactory clBeanFactory) throws BeansException { + if (!(clBeanFactory instanceof DefaultListableBeanFactory)) + throw new IllegalStateException("beanFactory must be of type " + + DefaultListableBeanFactory.class.getSimpleName()); + + DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) clBeanFactory; + + ConfigurationModel model = new ConfigurationModel(); + + parseAnyConfigurationClasses(beanFactory, model); + + enhanceAnyConfigurationClasses(beanFactory, model); + } + + private void parseAnyConfigurationClasses(DefaultListableBeanFactory beanFactory, ConfigurationModel model) { + + // linked map is important for maintaining predictable ordering of configuration + // classes. + // this is important in bean / value override situations. + LinkedHashMap configClassResources = new LinkedHashMap(); + + for (String beanName : beanFactory.getBeanDefinitionNames()) { + BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName); + if (beanDef.isAbstract()) + continue; + + if (isConfigClass(beanDef)) { + String path = ClassUtils.convertClassNameToResourcePath(beanDef.getBeanClassName()); + configClassResources.put(beanName, new ClassPathResource(path)); + } + } + + ConfigurationModelBeanDefinitionReader modelBeanDefinitionReader = new ConfigurationModelBeanDefinitionReader( + beanFactory); + ConfigurationParser parser = new ConfigurationParser(model); + + for (String id : configClassResources.keySet()) + parser.parse(configClassResources.get(id), id); + + ArrayList errors = new ArrayList(); + model.validate(errors); + if (errors.size() > 0) + throw new MalformedConfigurationException(errors.toArray(new UsageError[] {})); + + modelBeanDefinitionReader.loadBeanDefinitions(model); + } + + /** + * Post-processes a BeanFactory in search of Configuration class BeanDefinitions; any + * candidates are then enhanced by a {@link ConfigurationEnhancer}. Candidate status is + * determined by BeanDefinition attribute metadata. + * + * @author Chris Beams + * @see ConfigurationEnhancer + * @see BeanFactoryPostProcessor + */ + private void enhanceAnyConfigurationClasses(DefaultListableBeanFactory beanFactory, + ConfigurationModel model) { + + ConfigurationEnhancer enhancer = new ConfigurationEnhancer(beanFactory, model); + + int configClassesEnhanced = 0; + + for (String beanName : beanFactory.getBeanDefinitionNames()) { + BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName); + + if (!isConfigClass(beanDef)) + continue; + + String configClassName = beanDef.getBeanClassName(); + + String enhancedClassName = enhancer.enhance(configClassName); + + if (logger.isDebugEnabled()) + logger + .debug(String + .format( + "Replacing bean definition '%s' existing class name '%s' with enhanced class name '%s'", + beanName, configClassName, enhancedClassName)); + + beanDef.setBeanClassName(enhancedClassName); + + configClassesEnhanced++; + } + + if (configClassesEnhanced == 0) + logger.warn("Found no @Configuration class BeanDefinitions within " + beanFactory); + } + + /** + * Determines whether the class for beanDef is a {@link Configuration} + * -annotated class. Returns false if beanDef has no class specified. + *

+ * Note: the classloading used within should not be problematic or interfere with + * tooling in any way. BeanFactoryPostProcessing happens only during actual runtime + * processing via {@link JavaConfigApplicationContext} or via XML using + * {@link ConfigurationPostProcessor}. In any case, tooling (Spring IDE) will hook in at + * a lower level than this class and thus never encounter this classloading. Should this + * become problematic, it would not be too difficult to replace the following with ASM + * logic that traverses the class hierarchy in order to find whether the class is + * directly or indirectly annotated with {@link Configuration}. + */ private static boolean isConfigClass(BeanDefinition beanDef) { - String className = beanDef.getBeanClassName(); - return className != null - && loadRequiredClass(className).isAnnotationPresent(Configuration.class); + String className = beanDef.getBeanClassName(); + return className != null && loadRequiredClass(className).isAnnotationPresent(Configuration.class); } } diff --git a/org.springframework.config.java/src/test/java/test/basic/AbstractJavaConfigTests.java b/org.springframework.config.java/src/test/java/test/basic/AbstractJavaConfigTests.java index a26ab49ba1d..f86769d2587 100644 --- a/org.springframework.config.java/src/test/java/test/basic/AbstractJavaConfigTests.java +++ b/org.springframework.config.java/src/test/java/test/basic/AbstractJavaConfigTests.java @@ -2,13 +2,14 @@ package test.basic; import org.junit.Before; + public abstract class AbstractJavaConfigTests { @Before public void setUp() { - + } - - //protected - + + // protected + } diff --git a/org.springframework.config.java/src/test/java/test/basic/AutowiredConfigurationTests.java b/org.springframework.config.java/src/test/java/test/basic/AutowiredConfigurationTests.java index 41c08c91f9f..c608c2b361c 100644 --- a/org.springframework.config.java/src/test/java/test/basic/AutowiredConfigurationTests.java +++ b/org.springframework.config.java/src/test/java/test/basic/AutowiredConfigurationTests.java @@ -13,49 +13,56 @@ import org.springframework.context.support.ClassPathXmlApplicationContext; import test.beans.Colour; import test.beans.TestBean; + public class AutowiredConfigurationTests { - public @Test void test() { + public @Test + void test() { ClassPathXmlApplicationContext factory = new ClassPathXmlApplicationContext( - AutowiredConfigurationTests.class.getSimpleName() + ".xml", - AutowiredConfigurationTests.class); - + AutowiredConfigurationTests.class.getSimpleName() + ".xml", AutowiredConfigurationTests.class); + assertThat(factory.getBean("colour", Colour.class), equalTo(Colour.RED)); assertThat(factory.getBean("testBean", TestBean.class).getName(), equalTo(Colour.RED.toString())); } - + @Configuration static class AutowiredConfig { - private @Autowired Colour colour; - - public @Bean TestBean testBean() { + private @Autowired + Colour colour; + + public @Bean + TestBean testBean() { return new TestBean(colour.toString()); } } - + @Configuration static class ColorConfig { - public @Bean Colour colour() { return Colour.RED; } + public @Bean + Colour colour() { + return Colour.RED; + } } - - - - public @Test void testValueInjection() { + + + public @Test + void testValueInjection() { System.setProperty("myProp", "foo"); - + ClassPathXmlApplicationContext factory = new ClassPathXmlApplicationContext( - "ValueInjectionTests.xml", AutowiredConfigurationTests.class); - + "ValueInjectionTests.xml", AutowiredConfigurationTests.class); + TestBean testBean = factory.getBean("testBean", TestBean.class); assertThat(testBean.getName(), equalTo("foo")); } - + @Configuration static class ValueConfig { - - @Value("#{systemProperties.myProp}") + + @Value("#{systemProperties.myProp}") private String name = "default"; - - public @Bean TestBean testBean() { + + public @Bean + TestBean testBean() { return new TestBean(name); } } diff --git a/org.springframework.config.java/src/test/java/test/basic/BasicTests.java b/org.springframework.config.java/src/test/java/test/basic/BasicTests.java index cf950082877..116d18c89b6 100644 --- a/org.springframework.config.java/src/test/java/test/basic/BasicTests.java +++ b/org.springframework.config.java/src/test/java/test/basic/BasicTests.java @@ -17,82 +17,87 @@ import org.springframework.config.java.support.ConfigurationPostProcessor; import test.beans.ITestBean; import test.beans.TestBean; + public class BasicTests { - + /** * Creates a new {@link BeanFactory}, populates it with a {@link BeanDefinition} for - * each of the given {@link Configuration} configClasses, and then post-processes - * the factory using JavaConfig's {@link ConfigurationPostProcessor}. When complete, - * the factory is ready to service requests for any {@link Bean} methods declared by - * configClasses. + * each of the given {@link Configuration} configClasses, and then + * post-processes the factory using JavaConfig's {@link ConfigurationPostProcessor}. + * When complete, the factory is ready to service requests for any {@link Bean} methods + * declared by configClasses. * - * @param configClasses the {@link Configuration} classes under test. may be an empty list. + * @param configClasses the {@link Configuration} classes under test. may be an empty + * list. * * @return fully initialized and post-processed {@link BeanFactory} */ private static BeanFactory initBeanFactory(Class... configClasses) { DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); - - for(Class configClass : configClasses) { + + for (Class configClass : configClasses) { String configBeanName = configClass.getName(); - factory.registerBeanDefinition(configBeanName, rootBeanDefinition(configClass).getBeanDefinition()); + factory.registerBeanDefinition(configBeanName, rootBeanDefinition(configClass) + .getBeanDefinition()); } - + new ConfigurationPostProcessor().postProcessBeanFactory(factory); - + factory.addBeanPostProcessor(new AutowiredAnnotationBeanPostProcessor()); - + return factory; } - - + + @Test public void simplestPossibleConfiguration() { BeanFactory factory = initBeanFactory(SimplestPossibleConfig.class); - + String stringBean = factory.getBean("stringBean", String.class); - + assertThat(stringBean, equalTo("foo")); } - + @Configuration static class SimplestPossibleConfig { - public @Bean String stringBean() { + public @Bean + String stringBean() { return "foo"; } } - - + + @Test public void configurationWithPrototypeScopedBeans() { BeanFactory factory = initBeanFactory(ConfigWithPrototypeBean.class); - + TestBean foo = factory.getBean("foo", TestBean.class); ITestBean bar = factory.getBean("bar", ITestBean.class); ITestBean baz = factory.getBean("baz", ITestBean.class); - + assertThat(foo.getSpouse(), sameInstance(bar)); assertThat(bar.getSpouse(), not(sameInstance(baz))); } - + @Configuration static class ConfigWithPrototypeBean { - public @Bean TestBean foo() { + public @Bean + TestBean foo() { TestBean foo = new TestBean("foo"); foo.setSpouse(bar()); return foo; } - - public @Bean TestBean bar() { + + public @Bean + TestBean bar() { TestBean bar = new TestBean("bar"); bar.setSpouse(baz()); return bar; } - - @Bean(scope=Scopes.PROTOTYPE) + + @Bean(scope = Scopes.PROTOTYPE) public TestBean baz() { return new TestBean("bar"); } } } - diff --git a/org.springframework.config.java/src/test/java/test/beans/Colour.java b/org.springframework.config.java/src/test/java/test/beans/Colour.java index 225e1d7d16a..140a4dee314 100644 --- a/org.springframework.config.java/src/test/java/test/beans/Colour.java +++ b/org.springframework.config.java/src/test/java/test/beans/Colour.java @@ -20,19 +20,21 @@ import org.springframework.core.enums.ShortCodedLabeledEnum; /** * TODO: JAVADOC - * - * @author Rob Harrop + * + * @author Rob Harrop */ @SuppressWarnings("serial") public class Colour extends ShortCodedLabeledEnum { - public static final Colour RED = new Colour(0, "RED"); + public static final Colour RED = new Colour(0, "RED"); - public static final Colour BLUE = new Colour(1, "BLUE"); + public static final Colour BLUE = new Colour(1, "BLUE"); - public static final Colour GREEN = new Colour(2, "GREEN"); + public static final Colour GREEN = new Colour(2, "GREEN"); - public static final Colour PURPLE = new Colour(3, "PURPLE"); + public static final Colour PURPLE = new Colour(3, "PURPLE"); - private Colour(int code, String label) { super(code, label); } + private Colour(int code, String label) { + super(code, label); + } } diff --git a/org.springframework.config.java/src/test/java/test/beans/DependsOnTestBean.java b/org.springframework.config.java/src/test/java/test/beans/DependsOnTestBean.java index f743db5e9c0..9ef108f0175 100644 --- a/org.springframework.config.java/src/test/java/test/beans/DependsOnTestBean.java +++ b/org.springframework.config.java/src/test/java/test/beans/DependsOnTestBean.java @@ -18,14 +18,20 @@ package test.beans; /** TODO: JAVADOC */ public class DependsOnTestBean { - public TestBean tb; + public TestBean tb; - private int state; + private int state; - public void setTestBean(TestBean tb) { this.tb = tb; } + public void setTestBean(TestBean tb) { + this.tb = tb; + } - public int getState() { return state; } + public int getState() { + return state; + } - public TestBean getTestBean() { return tb; } + public TestBean getTestBean() { + return tb; + } } diff --git a/org.springframework.config.java/src/test/java/test/beans/INestedTestBean.java b/org.springframework.config.java/src/test/java/test/beans/INestedTestBean.java index e87242df139..0d47fb8fe8e 100644 --- a/org.springframework.config.java/src/test/java/test/beans/INestedTestBean.java +++ b/org.springframework.config.java/src/test/java/test/beans/INestedTestBean.java @@ -19,6 +19,6 @@ package test.beans; /** TODO: JAVADOC */ public interface INestedTestBean { - String getCompany(); + String getCompany(); } diff --git a/org.springframework.config.java/src/test/java/test/beans/IOther.java b/org.springframework.config.java/src/test/java/test/beans/IOther.java index 4a3da3dc89d..b3cc9fff15d 100644 --- a/org.springframework.config.java/src/test/java/test/beans/IOther.java +++ b/org.springframework.config.java/src/test/java/test/beans/IOther.java @@ -19,6 +19,6 @@ package test.beans; /** TODO: JAVADOC */ public interface IOther { - void absquatulate(); + void absquatulate(); } diff --git a/org.springframework.config.java/src/test/java/test/beans/ITestBean.java b/org.springframework.config.java/src/test/java/test/beans/ITestBean.java index 2e716120a60..76b0ce68f83 100644 --- a/org.springframework.config.java/src/test/java/test/beans/ITestBean.java +++ b/org.springframework.config.java/src/test/java/test/beans/ITestBean.java @@ -19,44 +19,44 @@ import java.io.IOException; /** - * Interface used for test beans. Two methods are the same as on Person, but if - * this extends person it breaks quite a few tests - * - * @author Rod Johnson + * Interface used for test beans. Two methods are the same as on Person, but if this extends + * person it breaks quite a few tests + * + * @author Rod Johnson */ public interface ITestBean { - int getAge(); + int getAge(); - void setAge(int age); + void setAge(int age); - String getName(); + String getName(); - void setName(String name); + void setName(String name); - ITestBean getSpouse(); + ITestBean getSpouse(); - void setSpouse(ITestBean spouse); + void setSpouse(ITestBean spouse); - /** - * t null no error. - */ - void exceptional(Throwable t) throws Throwable; + /** + * t null no error. + */ + void exceptional(Throwable t) throws Throwable; - Object returnsThis(); + Object returnsThis(); - INestedTestBean getDoctor(); + INestedTestBean getDoctor(); - INestedTestBean getLawyer(); + INestedTestBean getLawyer(); - IndexedTestBean getNestedIndexedBean(); + IndexedTestBean getNestedIndexedBean(); - /** - * Increment the age by one. - * - * @return the previous age - */ - int haveBirthday(); + /** + * Increment the age by one. + * + * @return the previous age + */ + int haveBirthday(); - void unreliableFileOperation() throws IOException; + void unreliableFileOperation() throws IOException; } diff --git a/org.springframework.config.java/src/test/java/test/beans/IndexedTestBean.java b/org.springframework.config.java/src/test/java/test/beans/IndexedTestBean.java index e3bb777f4fa..95ea56d5191 100644 --- a/org.springframework.config.java/src/test/java/test/beans/IndexedTestBean.java +++ b/org.springframework.config.java/src/test/java/test/beans/IndexedTestBean.java @@ -27,89 +27,119 @@ import java.util.TreeSet; /** - * TODO: JAVADOC - * - * @author Juergen Hoeller - * @since 11.11.2003 + * TODO: JAVADOC + * + * @author Juergen Hoeller + * @since 11.11.2003 */ public class IndexedTestBean { - private TestBean[] array; - - private Collection collection; - - private List list; - - private Set set; - - private SortedSet sortedSet; - - private Map map; - - private SortedMap sortedMap; - - public IndexedTestBean() { this(true); } - - public IndexedTestBean(boolean populate) { - if (populate) { - populate(); - } - } - - public void populate() { - TestBean tb0 = new TestBean("name0", 0); - TestBean tb1 = new TestBean("name1", 0); - TestBean tb2 = new TestBean("name2", 0); - TestBean tb3 = new TestBean("name3", 0); - TestBean tb4 = new TestBean("name4", 0); - TestBean tb5 = new TestBean("name5", 0); - TestBean tb6 = new TestBean("name6", 0); - TestBean tb7 = new TestBean("name7", 0); - TestBean tbX = new TestBean("nameX", 0); - TestBean tbY = new TestBean("nameY", 0); - this.array = new TestBean[] { tb0, tb1 }; - this.list = new ArrayList(); - this.list.add(tb2); - this.list.add(tb3); - this.set = new TreeSet(); - this.set.add(tb6); - this.set.add(tb7); - this.map = new HashMap(); - this.map.put("key1", tb4); - this.map.put("key2", tb5); - this.map.put("key.3", tb5); - List list = new ArrayList(); - list.add(tbX); - list.add(tbY); - this.map.put("key4", list); - } - - public TestBean[] getArray() { return array; } - - public void setArray(TestBean[] array) { this.array = array; } - - public Collection getCollection() { return collection; } - - public void setCollection(Collection collection) { this.collection = collection; } - - public List getList() { return list; } - - public void setList(List list) { this.list = list; } - - public Set getSet() { return set; } - - public void setSet(Set set) { this.set = set; } - - public SortedSet getSortedSet() { return sortedSet; } - - public void setSortedSet(SortedSet sortedSet) { this.sortedSet = sortedSet; } - - public Map getMap() { return map; } - - public void setMap(Map map) { this.map = map; } - - public SortedMap getSortedMap() { return sortedMap; } - - public void setSortedMap(SortedMap sortedMap) { this.sortedMap = sortedMap; } + private TestBean[] array; + + private Collection collection; + + private List list; + + private Set set; + + private SortedSet sortedSet; + + private Map map; + + private SortedMap sortedMap; + + public IndexedTestBean() { + this(true); + } + + public IndexedTestBean(boolean populate) { + if (populate) { + populate(); + } + } + + public void populate() { + TestBean tb0 = new TestBean("name0", 0); + TestBean tb1 = new TestBean("name1", 0); + TestBean tb2 = new TestBean("name2", 0); + TestBean tb3 = new TestBean("name3", 0); + TestBean tb4 = new TestBean("name4", 0); + TestBean tb5 = new TestBean("name5", 0); + TestBean tb6 = new TestBean("name6", 0); + TestBean tb7 = new TestBean("name7", 0); + TestBean tbX = new TestBean("nameX", 0); + TestBean tbY = new TestBean("nameY", 0); + this.array = new TestBean[] { tb0, tb1 }; + this.list = new ArrayList(); + this.list.add(tb2); + this.list.add(tb3); + this.set = new TreeSet(); + this.set.add(tb6); + this.set.add(tb7); + this.map = new HashMap(); + this.map.put("key1", tb4); + this.map.put("key2", tb5); + this.map.put("key.3", tb5); + List list = new ArrayList(); + list.add(tbX); + list.add(tbY); + this.map.put("key4", list); + } + + public TestBean[] getArray() { + return array; + } + + public void setArray(TestBean[] array) { + this.array = array; + } + + public Collection getCollection() { + return collection; + } + + public void setCollection(Collection collection) { + this.collection = collection; + } + + public List getList() { + return list; + } + + public void setList(List list) { + this.list = list; + } + + public Set getSet() { + return set; + } + + public void setSet(Set set) { + this.set = set; + } + + public SortedSet getSortedSet() { + return sortedSet; + } + + public void setSortedSet(SortedSet sortedSet) { + this.sortedSet = sortedSet; + } + + public Map getMap() { + return map; + } + + public void setMap(Map map) { + this.map = map; + } + + public SortedMap getSortedMap() { + return sortedMap; + } + + public void setSortedMap(SortedMap sortedMap) { + this.sortedMap = sortedMap; + } } diff --git a/org.springframework.config.java/src/test/java/test/beans/NestedTestBean.java b/org.springframework.config.java/src/test/java/test/beans/NestedTestBean.java index a9142f25d16..e87c2c46ccc 100644 --- a/org.springframework.config.java/src/test/java/test/beans/NestedTestBean.java +++ b/org.springframework.config.java/src/test/java/test/beans/NestedTestBean.java @@ -17,46 +17,47 @@ package test.beans; /** * Simple nested test bean used for testing bean factories, AOP framework etc. - * - * @author Trevor D. Cook - * @since 30.09.2003 + * + * @author Trevor D. Cook + * @since 30.09.2003 */ public class NestedTestBean implements INestedTestBean { - private String company = ""; + private String company = ""; - public NestedTestBean() { } + public NestedTestBean() { + } - public NestedTestBean(String company) { - setCompany(company); - } + public NestedTestBean(String company) { + setCompany(company); + } - public void setCompany(String company) { - this.company = ((company != null) ? company : ""); - } + public void setCompany(String company) { + this.company = ((company != null) ? company : ""); + } - public String getCompany() { - return company; - } + public String getCompany() { + return company; + } - @Override - public boolean equals(Object obj) { - if (!(obj instanceof NestedTestBean)) { - return false; - } + @Override + public boolean equals(Object obj) { + if (!(obj instanceof NestedTestBean)) { + return false; + } - NestedTestBean ntb = (NestedTestBean) obj; - return this.company.equals(ntb.company); - } + NestedTestBean ntb = (NestedTestBean) obj; + return this.company.equals(ntb.company); + } - @Override - public int hashCode() { - return this.company.hashCode(); - } + @Override + public int hashCode() { + return this.company.hashCode(); + } - @Override - public String toString() { - return "NestedTestBean: " + this.company; - } + @Override + public String toString() { + return "NestedTestBean: " + this.company; + } } diff --git a/org.springframework.config.java/src/test/java/test/beans/TestBean.java b/org.springframework.config.java/src/test/java/test/beans/TestBean.java index 027e18364eb..0911566a3f8 100644 --- a/org.springframework.config.java/src/test/java/test/beans/TestBean.java +++ b/org.springframework.config.java/src/test/java/test/beans/TestBean.java @@ -37,259 +37,383 @@ import java.util.Set; /** * Simple test bean used for testing bean factories, AOP framework etc. - * - * @author Rod Johnson - * @since 15 April 2001 + * + * @author Rod Johnson + * @since 15 April 2001 */ public class TestBean implements BeanNameAware, BeanFactoryAware, ITestBean, IOther, Comparable { - private String beanName; - - private String country; - - private BeanFactory beanFactory; - - private boolean postProcessed; - - private String name; - - private String sex; - - private int age; - - private boolean jedi; - - private ITestBean spouse; - - private String touchy; - - private String[] stringArray; - - private Integer[] someIntegerArray; - - private Date date = new Date(); - - private Float myFloat = new Float(0.0); - - private Collection friends = new LinkedList(); - - private Set someSet = new HashSet(); - - private Map someMap = new HashMap(); - - private List someList = new ArrayList(); - - private Properties someProperties = new Properties(); - - private INestedTestBean doctor = new NestedTestBean(); - - private INestedTestBean lawyer = new NestedTestBean(); - - private IndexedTestBean nestedIndexedBean; - - private boolean destroyed; - - private Number someNumber; - - private Colour favouriteColour; - - private Boolean someBoolean; - - private List otherColours; - - private List pets; - - public TestBean() { } - - public TestBean(String name) { this.name = name; } - - public TestBean(ITestBean spouse) { this.spouse = spouse; } - - public TestBean(String name, int age) { - this.name = name; - this.age = age; - } - - public TestBean(ITestBean spouse, Properties someProperties) { - this.spouse = spouse; - this.someProperties = someProperties; - } - - public void setBeanName(String beanName) { this.beanName = beanName; } - - public String getBeanName() { return beanName; } - - public void setBeanFactory(BeanFactory beanFactory) { this.beanFactory = beanFactory; } - - public BeanFactory getBeanFactory() { return beanFactory; } - - public void setPostProcessed(boolean postProcessed) { this.postProcessed = postProcessed; } + private String beanName; - public boolean isPostProcessed() { return postProcessed; } + private String country; - public String getName() { return name; } + private BeanFactory beanFactory; - public void setName(String name) { this.name = name; } + private boolean postProcessed; - public String getSex() { return sex; } + private String name; - public void setSex(String sex) { this.sex = sex; } + private String sex; - public int getAge() { return age; } + private int age; - public void setAge(int age) { this.age = age; } + private boolean jedi; - public boolean isJedi() { return jedi; } + private ITestBean spouse; - public void setJedi(boolean jedi) { this.jedi = jedi; } + private String touchy; - public ITestBean getSpouse() { return spouse; } + private String[] stringArray; - public void setSpouse(ITestBean spouse) { this.spouse = spouse; } + private Integer[] someIntegerArray; - public String getTouchy() { return touchy; } + private Date date = new Date(); - public void setTouchy(String touchy) throws Exception { - if (touchy.indexOf('.') != -1) { - throw new Exception("Can't contain a ."); - } + private Float myFloat = new Float(0.0); - if (touchy.indexOf(',') != -1) { - throw new NumberFormatException("Number format exception: contains a ,"); - } + private Collection friends = new LinkedList(); - this.touchy = touchy; - } + private Set someSet = new HashSet(); - public String getCountry() { return country; } + private Map someMap = new HashMap(); - public void setCountry(String country) { this.country = country; } + private List someList = new ArrayList(); - public String[] getStringArray() { return stringArray; } + private Properties someProperties = new Properties(); - public void setStringArray(String[] stringArray) { this.stringArray = stringArray; } + private INestedTestBean doctor = new NestedTestBean(); - public Integer[] getSomeIntegerArray() { return someIntegerArray; } + private INestedTestBean lawyer = new NestedTestBean(); - public void setSomeIntegerArray(Integer[] someIntegerArray) { this.someIntegerArray = someIntegerArray; } + private IndexedTestBean nestedIndexedBean; - public Date getDate() { return date; } + private boolean destroyed; - public void setDate(Date date) { this.date = date; } + private Number someNumber; - public Float getMyFloat() { return myFloat; } + private Colour favouriteColour; - public void setMyFloat(Float myFloat) { this.myFloat = myFloat; } + private Boolean someBoolean; - public Collection getFriends() { return friends; } + private List otherColours; - public void setFriends(Collection friends) { this.friends = friends; } + private List pets; - public Set getSomeSet() { return someSet; } + public TestBean() { + } - public void setSomeSet(Set someSet) { this.someSet = someSet; } + public TestBean(String name) { + this.name = name; + } - public Map getSomeMap() { return someMap; } + public TestBean(ITestBean spouse) { + this.spouse = spouse; + } - public void setSomeMap(Map someMap) { this.someMap = someMap; } + public TestBean(String name, int age) { + this.name = name; + this.age = age; + } - public List getSomeList() { return someList; } + public TestBean(ITestBean spouse, Properties someProperties) { + this.spouse = spouse; + this.someProperties = someProperties; + } - public void setSomeList(List someList) { this.someList = someList; } + public void setBeanName(String beanName) { + this.beanName = beanName; + } - public Properties getSomeProperties() { return someProperties; } + public String getBeanName() { + return beanName; + } - public void setSomeProperties(Properties someProperties) { this.someProperties = someProperties; } + public void setBeanFactory(BeanFactory beanFactory) { + this.beanFactory = beanFactory; + } - public INestedTestBean getDoctor() { return doctor; } + public BeanFactory getBeanFactory() { + return beanFactory; + } - public INestedTestBean getLawyer() { return lawyer; } + public void setPostProcessed(boolean postProcessed) { + this.postProcessed = postProcessed; + } - public void setDoctor(INestedTestBean bean) { doctor = bean; } + public boolean isPostProcessed() { + return postProcessed; + } - public void setLawyer(INestedTestBean bean) { lawyer = bean; } + public String getName() { + return name; + } - public Number getSomeNumber() { return someNumber; } + public void setName(String name) { + this.name = name; + } - public void setSomeNumber(Number someNumber) { this.someNumber = someNumber; } + public String getSex() { + return sex; + } - public Colour getFavouriteColour() { return favouriteColour; } + public void setSex(String sex) { + this.sex = sex; + } - public void setFavouriteColour(Colour favouriteColour) { this.favouriteColour = favouriteColour; } + public int getAge() { + return age; + } - public Boolean getSomeBoolean() { return someBoolean; } + public void setAge(int age) { + this.age = age; + } - public void setSomeBoolean(Boolean someBoolean) { this.someBoolean = someBoolean; } + public boolean isJedi() { + return jedi; + } - public IndexedTestBean getNestedIndexedBean() { return nestedIndexedBean; } + public void setJedi(boolean jedi) { + this.jedi = jedi; + } - public void setNestedIndexedBean(IndexedTestBean nestedIndexedBean) { this.nestedIndexedBean = nestedIndexedBean; } + public ITestBean getSpouse() { + return spouse; + } - public List getOtherColours() { return otherColours; } + public void setSpouse(ITestBean spouse) { + this.spouse = spouse; + } - public void setOtherColours(List otherColours) { this.otherColours = otherColours; } + public String getTouchy() { + return touchy; + } - public List getPets() { return pets; } + public void setTouchy(String touchy) throws Exception { + if (touchy.indexOf('.') != -1) { + throw new Exception("Can't contain a ."); + } - public void setPets(List pets) { this.pets = pets; } + if (touchy.indexOf(',') != -1) { + throw new NumberFormatException("Number format exception: contains a ,"); + } - /** - * @see ITestBean#exceptional(Throwable) - */ - public void exceptional(Throwable t) throws Throwable { - if (t != null) { - throw t; - } - } + this.touchy = touchy; + } - public void unreliableFileOperation() throws IOException { throw new IOException(); } + public String getCountry() { + return country; + } - /** - * @see ITestBean#returnsThis() - */ - public Object returnsThis() { return this; } + public void setCountry(String country) { + this.country = country; + } - /** - * @see IOther#absquatulate() - */ - public void absquatulate() { } + public String[] getStringArray() { + return stringArray; + } - public int haveBirthday() { return age++; } + public void setStringArray(String[] stringArray) { + this.stringArray = stringArray; + } - public void destroy() { this.destroyed = true; } + public Integer[] getSomeIntegerArray() { + return someIntegerArray; + } - public boolean wasDestroyed() { return destroyed; } + public void setSomeIntegerArray(Integer[] someIntegerArray) { + this.someIntegerArray = someIntegerArray; + } - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } + public Date getDate() { + return date; + } - if ((other == null) || !(other instanceof TestBean)) { - return false; - } + public void setDate(Date date) { + this.date = date; + } - TestBean tb2 = (TestBean) other; - return (ObjectUtils.nullSafeEquals(this.name, tb2.name) && (this.age == tb2.age)); - } + public Float getMyFloat() { + return myFloat; + } - @Override - public int hashCode() { return this.age; } + public void setMyFloat(Float myFloat) { + this.myFloat = myFloat; + } - public int compareTo(Object other) { - if ((this.name != null) && (other instanceof TestBean)) { - return this.name.compareTo(((TestBean) other).getName()); - } + public Collection getFriends() { + return friends; + } - return 1; - } + public void setFriends(Collection friends) { + this.friends = friends; + } - @Override - public String toString() { - String s = "name=" + name + "; age=" + age + "; touchy=" + touchy; - s += "; spouse={" + ((spouse != null) ? spouse.getName() : null) + "}"; - return s; - } + public Set getSomeSet() { + return someSet; + } + + public void setSomeSet(Set someSet) { + this.someSet = someSet; + } + + public Map getSomeMap() { + return someMap; + } + + public void setSomeMap(Map someMap) { + this.someMap = someMap; + } + + public List getSomeList() { + return someList; + } + + public void setSomeList(List someList) { + this.someList = someList; + } + + public Properties getSomeProperties() { + return someProperties; + } + + public void setSomeProperties(Properties someProperties) { + this.someProperties = someProperties; + } + + public INestedTestBean getDoctor() { + return doctor; + } + + public INestedTestBean getLawyer() { + return lawyer; + } + + public void setDoctor(INestedTestBean bean) { + doctor = bean; + } + + public void setLawyer(INestedTestBean bean) { + lawyer = bean; + } + + public Number getSomeNumber() { + return someNumber; + } + + public void setSomeNumber(Number someNumber) { + this.someNumber = someNumber; + } + + public Colour getFavouriteColour() { + return favouriteColour; + } + + public void setFavouriteColour(Colour favouriteColour) { + this.favouriteColour = favouriteColour; + } + + public Boolean getSomeBoolean() { + return someBoolean; + } + + public void setSomeBoolean(Boolean someBoolean) { + this.someBoolean = someBoolean; + } + + public IndexedTestBean getNestedIndexedBean() { + return nestedIndexedBean; + } + + public void setNestedIndexedBean(IndexedTestBean nestedIndexedBean) { + this.nestedIndexedBean = nestedIndexedBean; + } + + public List getOtherColours() { + return otherColours; + } + + public void setOtherColours(List otherColours) { + this.otherColours = otherColours; + } + + public List getPets() { + return pets; + } + + public void setPets(List pets) { + this.pets = pets; + } + + /** + * @see ITestBean#exceptional(Throwable) + */ + public void exceptional(Throwable t) throws Throwable { + if (t != null) { + throw t; + } + } + + public void unreliableFileOperation() throws IOException { + throw new IOException(); + } + + /** + * @see ITestBean#returnsThis() + */ + public Object returnsThis() { + return this; + } + + /** + * @see IOther#absquatulate() + */ + public void absquatulate() { + } + + public int haveBirthday() { + return age++; + } + + public void destroy() { + this.destroyed = true; + } + + public boolean wasDestroyed() { + return destroyed; + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + + if ((other == null) || !(other instanceof TestBean)) { + return false; + } + + TestBean tb2 = (TestBean) other; + return (ObjectUtils.nullSafeEquals(this.name, tb2.name) && (this.age == tb2.age)); + } + + @Override + public int hashCode() { + return this.age; + } + + public int compareTo(Object other) { + if ((this.name != null) && (other instanceof TestBean)) { + return this.name.compareTo(((TestBean) other).getName()); + } + + return 1; + } + + @Override + public String toString() { + String s = "name=" + name + "; age=" + age + "; touchy=" + touchy; + s += "; spouse={" + ((spouse != null) ? spouse.getName() : null) + "}"; + return s; + } }