+ Source attribution is now consistent across all registered Problems

+ Various pruning of dead code and polish
This commit is contained in:
Chris Beams 2009-03-22 01:43:31 +00:00
parent 8b4ad4575c
commit 72fae2ea19
10 changed files with 122 additions and 307 deletions

View File

@ -23,24 +23,31 @@ import java.lang.reflect.Modifier;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.parsing.Location; import org.springframework.beans.factory.parsing.Location;
import org.springframework.beans.factory.parsing.Problem; import org.springframework.beans.factory.parsing.Problem;
import org.springframework.beans.factory.parsing.ProblemReporter; import org.springframework.beans.factory.parsing.ProblemReporter;
import org.springframework.config.java.Bean; import org.springframework.config.java.Bean;
import org.springframework.config.java.Configuration;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode; import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.core.io.FileSystemResource;
import org.springframework.util.Assert; import org.springframework.util.Assert;
final class BeanMethod { /**
* Represents a {@link Configuration} class method marked with the {@link Bean} annotation.
*
* @author Chris Beams
*/
final class BeanMethod implements BeanMetadataElement {
private final String name; private final String name;
private final int modifiers; private final int modifiers;
private final ModelClass returnType; private final ModelClass returnType;
private final List<Annotation> annotations = new ArrayList<Annotation>(); private final List<Annotation> annotations = new ArrayList<Annotation>();
private transient ConfigurationClass declaringClass; private transient ConfigurationClass declaringClass;
private transient int lineNumber; private transient Object source;
public BeanMethod(String name, int modifiers, ModelClass returnType, Annotation... annotations) { public BeanMethod(String name, int modifiers, ModelClass returnType, Annotation... annotations) {
Assert.hasText(name); Assert.hasText(name);
@ -104,7 +111,7 @@ final class BeanMethod {
/** /**
* Set up a bi-directional relationship between this method and its declaring class. * Set up a bi-directional relationship between this method and its declaring class.
* *
* @see ConfigurationClass#addMethod(BeanMethod) * @see ConfigurationClass#addBeanMethod(BeanMethod)
*/ */
public void setDeclaringClass(ConfigurationClass declaringClass) { public void setDeclaringClass(ConfigurationClass declaringClass) {
this.declaringClass = declaringClass; this.declaringClass = declaringClass;
@ -114,12 +121,16 @@ final class BeanMethod {
return declaringClass; return declaringClass;
} }
public void setLineNumber(int lineNumber) { public void setSource(Object source) {
this.lineNumber = lineNumber; this.source = source;
} }
public int getLineNumber() { public Object getSource() {
return lineNumber; return source;
}
public Location getLocation() {
return new Location(declaringClass.getLocation().getResource(), getSource());
} }
public void validate(ProblemReporter problemReporter) { public void validate(ProblemReporter problemReporter) {
@ -188,7 +199,7 @@ final class BeanMethod {
public class PrivateMethodError extends Problem { public class PrivateMethodError extends Problem {
public PrivateMethodError() { public PrivateMethodError() {
super(format("method '%s' may not be private", getName()), super(format("method '%s' may not be private", getName()),
new Location(new FileSystemResource("/dev/null"))); BeanMethod.this.getLocation());
} }
} }
@ -196,7 +207,7 @@ final class BeanMethod {
public class FinalMethodError extends Problem { public class FinalMethodError extends Problem {
public FinalMethodError() { public FinalMethodError() {
super(format("method '%s' may not be final. remove the final modifier to continue", getName()), super(format("method '%s' may not be final. remove the final modifier to continue", getName()),
new Location(new FileSystemResource("/dev/null"))); BeanMethod.this.getLocation());
} }
} }
@ -204,7 +215,7 @@ final class BeanMethod {
public InvalidScopedProxyDeclarationError(BeanMethod method) { public InvalidScopedProxyDeclarationError(BeanMethod method) {
super(format("method %s contains an invalid annotation declaration: scoped proxies " super(format("method %s contains an invalid annotation declaration: scoped proxies "
+ "cannot be created for singleton/prototype beans", method.getName()), + "cannot be created for singleton/prototype beans", method.getName()),
new Location(new FileSystemResource("/dev/null"))); BeanMethod.this.getLocation());
} }
} }

View File

@ -17,21 +17,16 @@ package org.springframework.config.java.support;
import static java.lang.String.*; import static java.lang.String.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import org.springframework.beans.factory.parsing.Location;
import org.springframework.beans.factory.parsing.Problem; import org.springframework.beans.factory.parsing.Problem;
import org.springframework.beans.factory.parsing.ProblemReporter; import org.springframework.beans.factory.parsing.ProblemReporter;
import org.springframework.config.java.Bean; import org.springframework.config.java.Bean;
import org.springframework.config.java.Configuration; import org.springframework.config.java.Configuration;
import org.springframework.core.io.FileSystemResource;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import sun.security.x509.Extension;
/** /**
* Abstract representation of a user-defined {@link Configuration @Configuration} class. * Abstract representation of a user-defined {@link Configuration @Configuration} class.
@ -41,107 +36,51 @@ import sun.security.x509.Extension;
* (for the purpose of tooling with Spring IDE). * (for the purpose of tooling with Spring IDE).
* *
* @author Chris Beams * @author Chris Beams
* @see ConfigurationModel
* @see BeanMethod
* @see ConfigurationParser
*/ */
final class ConfigurationClass extends ModelClass { final class ConfigurationClass extends ModelClass {
private String beanName; private String beanName;
private int modifiers; private int modifiers;
private Configuration configurationAnnotation;
private Configuration metadata;
private HashSet<BeanMethod> methods = new HashSet<BeanMethod>(); private HashSet<BeanMethod> methods = new HashSet<BeanMethod>();
private HashSet<Annotation> pluginAnnotations = new HashSet<Annotation>();
private ConfigurationClass declaringClass; private ConfigurationClass declaringClass;
public ConfigurationClass() {
}
// TODO: get rid of constructors used only for testing. put in testing util.
/**
* Creates a new ConfigurationClass named <var>className.</var>
*
* @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(BeanMethod method) {
method.setDeclaringClass(this);
methods.add(method);
return this;
}
public String getBeanName() { public String getBeanName() {
return beanName == null ? getName() : beanName; return beanName == null ? getName() : beanName;
} }
public ConfigurationClass setBeanName(String id) { public void setBeanName(String id) {
this.beanName = id; this.beanName = id;
return this;
} }
public Set<BeanMethod> getMethods() { public int getModifiers() {
return modifiers;
}
public void setModifiers(int modifiers) {
Assert.isTrue(modifiers >= 0, "modifiers must be non-negative");
this.modifiers = modifiers;
}
public Configuration getConfigurationAnnotation() {
return this.configurationAnnotation;
}
public void setConfigurationAnnotation(Configuration configAnno) {
Assert.notNull(configAnno, "configuration annotation must be non-null");
this.configurationAnnotation = configAnno;
}
public Set<BeanMethod> getBeanMethods() {
return methods; return methods;
} }
public Annotation[] getPluginAnnotations() { public ConfigurationClass addBeanMethod(BeanMethod method) {
return pluginAnnotations.toArray(new Annotation[pluginAnnotations.size()]); method.setDeclaringClass(this);
} methods.add(method);
/**
* Add a {@link Extension @Plugin}-annotated annotation to this configuration class.
*
* @param pluginAnno type-level <code>Plugin</code> annotation
*/
public ConfigurationClass addPluginAnnotation(Annotation pluginAnno) {
pluginAnnotations.add(pluginAnno);
return this;
}
public ConfigurationClass setDeclaringClass(ConfigurationClass configurationClass) {
this.declaringClass = configurationClass;
return this; return this;
} }
@ -149,29 +88,13 @@ final class ConfigurationClass extends ModelClass {
return declaringClass; return declaringClass;
} }
public int getModifiers() { public void setDeclaringClass(ConfigurationClass configurationClass) {
return modifiers; this.declaringClass = configurationClass;
}
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(ProblemReporter problemReporter) { public void validate(ProblemReporter problemReporter) {
// configuration classes must be annotated with @Configuration // configuration classes must be annotated with @Configuration
if (metadata == null) if (configurationAnnotation == null)
problemReporter.error(new NonAnnotatedConfigurationProblem()); problemReporter.error(new NonAnnotatedConfigurationProblem());
// a configuration class may not be final (CGLIB limitation) // a configuration class may not be final (CGLIB limitation)
@ -193,10 +116,9 @@ final class ConfigurationClass extends ModelClass {
int result = super.hashCode(); int result = super.hashCode();
result = prime * result + ((declaringClass == null) ? 0 : declaringClass.hashCode()); result = prime * result + ((declaringClass == null) ? 0 : declaringClass.hashCode());
result = prime * result + ((beanName == null) ? 0 : beanName.hashCode()); result = prime * result + ((beanName == null) ? 0 : beanName.hashCode());
result = prime * result + ((metadata == null) ? 0 : metadata.hashCode()); result = prime * result + ((configurationAnnotation == null) ? 0 : configurationAnnotation.hashCode());
result = prime * result + ((methods == null) ? 0 : methods.hashCode()); result = prime * result + ((methods == null) ? 0 : methods.hashCode());
result = prime * result + modifiers; result = prime * result + modifiers;
result = prime * result + ((pluginAnnotations == null) ? 0 : pluginAnnotations.hashCode());
return result; return result;
} }
@ -219,10 +141,10 @@ final class ConfigurationClass extends ModelClass {
return false; return false;
} else if (!beanName.equals(other.beanName)) } else if (!beanName.equals(other.beanName))
return false; return false;
if (metadata == null) { if (configurationAnnotation == null) {
if (other.metadata != null) if (other.configurationAnnotation != null)
return false; return false;
} else if (!metadata.equals(other.metadata)) } else if (!configurationAnnotation.equals(other.configurationAnnotation))
return false; return false;
if (methods == null) { if (methods == null) {
if (other.methods != null) if (other.methods != null)
@ -231,37 +153,30 @@ final class ConfigurationClass extends ModelClass {
return false; return false;
if (modifiers != other.modifiers) if (modifiers != other.modifiers)
return false; return false;
if (pluginAnnotations == null) {
if (other.pluginAnnotations != null)
return false;
} else if (!pluginAnnotations.equals(other.pluginAnnotations))
return false;
return true; return true;
} }
/** Configuration classes must be annotated with {@link Configuration @Configuration}. */ /** Configuration classes must be annotated with {@link Configuration @Configuration}. */
public class NonAnnotatedConfigurationProblem extends Problem { class NonAnnotatedConfigurationProblem extends Problem {
public NonAnnotatedConfigurationProblem() { NonAnnotatedConfigurationProblem() {
super(format("%s was specified as a @Configuration class but was not actually annotated " + super(format("%s was specified as a @Configuration class but was not actually annotated " +
"with @Configuration. Annotate the class or do not attempt to process it.", "with @Configuration. Annotate the class or do not attempt to process it.",
getSimpleName()), getSimpleName()),
new Location(new FileSystemResource("/dev/null")) ConfigurationClass.this.getLocation());
);
} }
} }
/** Configuration classes must be non-final to accommodate CGLIB subclassing. */ /** Configuration classes must be non-final to accommodate CGLIB subclassing. */
public class FinalConfigurationProblem extends Problem { class FinalConfigurationProblem extends Problem {
public FinalConfigurationProblem() { FinalConfigurationProblem() {
super(format("@Configuration class [%s] may not be final. Remove the final modifier to continue.", super(format("@Configuration class [%s] may not be final. Remove the final modifier to continue.",
ConfigurationClass.this.getSimpleName()), getSimpleName()),
new Location(new FileSystemResource("/dev/null")) ConfigurationClass.this.getLocation());
);
} }
} }

View File

@ -48,7 +48,6 @@ class ConfigurationClassMethodVisitor extends MethodAdapter {
private final ArrayList<Annotation> annotations = new ArrayList<Annotation>(); private final ArrayList<Annotation> annotations = new ArrayList<Annotation>();
private final ClassLoader classLoader; private final ClassLoader classLoader;
private boolean isModelMethod = false;
private int lineNumber; private int lineNumber;
/** /**
@ -104,24 +103,20 @@ class ConfigurationClassMethodVisitor extends MethodAdapter {
/** /**
* Parses through all {@link #annotations} on this method in order to determine whether * Parses through all {@link #annotations} on this method in order to determine whether
* it is a {@link Bean} method or not and if so adds it to the enclosing {@link #configClass}. * it is a {@link Bean} method and if so adds it to the enclosing {@link #configClass}.
*/ */
@Override @Override
public void visitEnd() { public void visitEnd() {
for (Annotation anno : annotations) { for (Annotation anno : annotations) {
if (anno.annotationType().equals(Bean.class)) { if (Bean.class.equals(anno.annotationType())) {
isModelMethod = true; // this method is annotated with @Bean -> add it to the ConfigurationClass model
Annotation[] annoArray = annotations.toArray(new Annotation[] {});
BeanMethod method = new BeanMethod(methodName, modifiers, returnType, annoArray);
method.setSource(lineNumber);
configClass.addBeanMethod(method);
break; break;
} }
} }
if (!isModelMethod)
return;
Annotation[] annoArray = annotations.toArray(new Annotation[] {});
BeanMethod method = new BeanMethod(methodName, modifiers, returnType, annoArray);
method.setLineNumber(lineNumber);
configClass.addMethod(method);
} }
/** /**

View File

@ -17,10 +17,8 @@ package org.springframework.config.java.support;
import static java.lang.String.*; import static java.lang.String.*;
import static org.springframework.config.java.support.MutableAnnotationUtils.*; import static org.springframework.config.java.support.MutableAnnotationUtils.*;
import static org.springframework.config.java.support.Util.*;
import static org.springframework.util.ClassUtils.*; import static org.springframework.util.ClassUtils.*;
import java.lang.annotation.Annotation;
import java.util.HashMap; import java.util.HashMap;
import java.util.Stack; import java.util.Stack;
@ -29,13 +27,12 @@ import org.springframework.asm.ClassAdapter;
import org.springframework.asm.ClassReader; import org.springframework.asm.ClassReader;
import org.springframework.asm.MethodVisitor; import org.springframework.asm.MethodVisitor;
import org.springframework.asm.Opcodes; import org.springframework.asm.Opcodes;
import org.springframework.asm.commons.EmptyVisitor;
import org.springframework.beans.factory.parsing.Location; import org.springframework.beans.factory.parsing.Location;
import org.springframework.beans.factory.parsing.Problem; import org.springframework.beans.factory.parsing.Problem;
import org.springframework.beans.factory.parsing.ProblemReporter; import org.springframework.beans.factory.parsing.ProblemReporter;
import org.springframework.config.java.Configuration; import org.springframework.config.java.Configuration;
import org.springframework.config.java.Import; import org.springframework.config.java.Import;
import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.ClassPathResource;
/** /**
@ -51,10 +48,11 @@ class ConfigurationClassVisitor extends ClassAdapter {
private final ConfigurationClass configClass; private final ConfigurationClass configClass;
private final ConfigurationModel model; private final ConfigurationModel model;
private final ProblemReporter problemReporter; private final ProblemReporter problemReporter;
private final ClassLoader classLoader;
private final HashMap<String, ConfigurationClass> innerClasses = new HashMap<String, ConfigurationClass>(); private final HashMap<String, ConfigurationClass> innerClasses = new HashMap<String, ConfigurationClass>();
private boolean processInnerClasses = true; private boolean processInnerClasses = true;
private final ClassLoader classLoader;
public ConfigurationClassVisitor(ConfigurationClass configClass, ConfigurationModel model, public ConfigurationClassVisitor(ConfigurationClass configClass, ConfigurationModel model,
ProblemReporter problemReporter, ClassLoader classLoader) { ProblemReporter problemReporter, ClassLoader classLoader) {
@ -120,78 +118,24 @@ class ConfigurationClassVisitor extends ClassAdapter {
if (Configuration.class.getName().equals(annoTypeName)) { if (Configuration.class.getName().equals(annoTypeName)) {
Configuration mutableConfiguration = createMutableAnnotation(Configuration.class); Configuration mutableConfiguration = createMutableAnnotation(Configuration.class);
configClass.setMetadata(mutableConfiguration); configClass.setConfigurationAnnotation(mutableConfiguration);
return new MutableAnnotationVisitor(mutableConfiguration, classLoader); return new MutableAnnotationVisitor(mutableConfiguration, classLoader);
} }
if (Import.class.getName().equals(annoTypeName)) { if (Import.class.getName().equals(annoTypeName)) {
ImportStack importStack = ImportStackHolder.getImportStack(); ImportStack importStack = ImportStackHolder.getImportStack();
if (importStack.contains(configClass)) { if (!importStack.contains(configClass)) {
//throw new CircularImportException(configClass, importStack); importStack.push(configClass);
problemReporter.error(new CircularImportProblem(configClass, importStack)); return new ImportAnnotationVisitor(model, problemReporter, classLoader);
return new EmptyVisitor();
} }
importStack.push(configClass); problemReporter.error(new CircularImportProblem(configClass, importStack));
return new ImportAnnotationVisitor(model, problemReporter, classLoader);
} }
/* ------------------------------------- return super.visitAnnotation(annoTypeDesc, visible);
// Detect @Extension annotations
// -------------------------------------
PluginAnnotationDetectingClassVisitor classVisitor = new PluginAnnotationDetectingClassVisitor(classLoader);
String className = AsmUtils.convertTypeDescriptorToClassName(annoTypeDesc);
String resourcePath = ClassUtils.convertClassNameToResourcePath(className);
ClassReader reader = AsmUtils.newClassReader(resourcePath, classLoader);
reader.accept(classVisitor, false);
if (!classVisitor.hasPluginAnnotation())
return super.visitAnnotation(annoTypeDesc, visible);
*/
Class<? extends Annotation> annoType = loadToolingSafeClass(annoTypeName, classLoader);
if (annoType == null)
return super.visitAnnotation(annoTypeDesc, visible);
Annotation pluginAnno = createMutableAnnotation(annoType);
configClass.addPluginAnnotation(pluginAnno);
return new MutableAnnotationVisitor(pluginAnno, classLoader);
} }
/* Support for @Extension annotation processing
private static class PluginAnnotationDetectingClassVisitor extends ClassAdapter {
private boolean hasPluginAnnotation = false;
private final Extension pluginAnnotation = createMutableAnnotation(Extension.class);
private final ClassLoader classLoader;
public PluginAnnotationDetectingClassVisitor(ClassLoader classLoader) {
super(AsmUtils.EMPTY_VISITOR);
this.classLoader = classLoader;
}
@Override
public AnnotationVisitor visitAnnotation(String typeDesc, boolean arg1) {
if (Extension.class.getName().equals(AsmUtils.convertTypeDescriptorToClassName(typeDesc))) {
hasPluginAnnotation = true;
return new MutableAnnotationVisitor(pluginAnnotation, classLoader);
}
return super.visitAnnotation(typeDesc, arg1);
}
public boolean hasPluginAnnotation() {
return hasPluginAnnotation;
}
public Extension getPluginAnnotation() {
return pluginAnnotation;
}
}
*/
/** /**
* Delegates all {@link Configuration @Configuration} class method parsing to * Delegates all {@link Configuration @Configuration} class method parsing to
* {@link ConfigurationClassMethodVisitor}. * {@link ConfigurationClassMethodVisitor}.
@ -241,7 +185,7 @@ class ConfigurationClassVisitor extends ClassAdapter {
innerConfigClass.setDeclaringClass(innerClasses.get(outerName)); innerConfigClass.setDeclaringClass(innerClasses.get(outerName));
// is the inner class a @Configuration class? If so, add it to the list // is the inner class a @Configuration class? If so, add it to the list
if (innerConfigClass.getMetadata() != null) if (innerConfigClass.getConfigurationAnnotation() != null)
innerClasses.put(name, innerConfigClass); innerClasses.put(name, innerConfigClass);
} }
@ -255,13 +199,14 @@ class ConfigurationClassVisitor extends ClassAdapter {
*/ */
class CircularImportProblem extends Problem { class CircularImportProblem extends Problem {
public CircularImportProblem(ConfigurationClass attemptedImport, Stack<ConfigurationClass> currentImportStack) { CircularImportProblem(ConfigurationClass attemptedImport, Stack<ConfigurationClass> importStack) {
super(format("A circular @Import has been detected: " + super(format("A circular @Import has been detected: " +
"Illegal attempt by @Configuration class '%s' to import class '%s' as '%s' is " + "Illegal attempt by @Configuration class '%s' to import class '%s' as '%s' is " +
"already present in the current import stack [%s]", "already present in the current import stack [%s]",
currentImportStack.peek().getSimpleName(), attemptedImport.getSimpleName(), importStack.peek().getSimpleName(), attemptedImport.getSimpleName(),
attemptedImport.getSimpleName(), currentImportStack), attemptedImport.getSimpleName(), importStack),
new Location(new FileSystemResource("/dev/null")) new Location(new ClassPathResource(convertClassNameToResourcePath(importStack.peek().getName())),
importStack.peek().getSource())
); );
} }

View File

@ -19,11 +19,8 @@ import static java.lang.String.*;
import java.util.ArrayList; import java.util.ArrayList;
import org.springframework.beans.factory.parsing.Location;
import org.springframework.beans.factory.parsing.Problem;
import org.springframework.beans.factory.parsing.ProblemReporter; import org.springframework.beans.factory.parsing.ProblemReporter;
import org.springframework.config.java.Configuration; import org.springframework.config.java.Configuration;
import org.springframework.core.io.FileSystemResource;
/** /**
@ -60,31 +57,6 @@ final class ConfigurationModel {
return this; return this;
} }
/**
* 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<ConfigurationClass> allConfigClasses = new ArrayList<ConfigurationClass>();
//
// for (ConfigurationClass configClass : configurationClasses)
// allConfigClasses.addAll(configClass.getSelfAndAllImports());
//
// return allConfigClasses.toArray(new ConfigurationClass[allConfigClasses.size()]);
// }
public ConfigurationClass[] getAllConfigurationClasses() { public ConfigurationClass[] getAllConfigurationClasses() {
return configurationClasses.toArray(new ConfigurationClass[configurationClasses.size()]); return configurationClasses.toArray(new ConfigurationClass[configurationClasses.size()]);
} }
@ -94,37 +66,8 @@ final class ConfigurationModel {
* <var>errors</var>. * <var>errors</var>.
* *
* @see ConfigurationClass#validate * @see ConfigurationClass#validate
* @see BeanMethod#validate
*/ */
public void validate(ProblemReporter problemReporter) { public void validate(ProblemReporter problemReporter) {
// user must specify at least one configuration
if (configurationClasses.isEmpty())
problemReporter.error(new EmptyModelError());
// TODO: prune this
// // check for any illegal @Bean overriding
// ConfigurationClass[] allClasses = getAllConfigurationClasses();
// for (int i = 0; i < allClasses.length; i++) {
// for (BeanMethod 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()))
// problemReporter.error(
// new Problem(
// allClasses[i].new IllegalBeanOverrideError(allClasses[j], method).getDescription(),
// new Location(new ClassPathResource(allClasses[i].getName().replace('.', '/').concat(".class")))
// )
// );
// }
// }
// 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) for (ConfigurationClass configClass : configurationClasses)
configClass.validate(problemReporter); configClass.validate(problemReporter);
} }
@ -159,13 +102,4 @@ final class ConfigurationModel {
return true; return true;
} }
public class EmptyModelError extends Problem {
public EmptyModelError() {
super(format("Configuration model was empty. Make sure at least one "
+ "@%s class has been specified.", Configuration.class.getSimpleName()),
new Location(new FileSystemResource("/dev/null")));
}
}
} }

View File

@ -74,7 +74,7 @@ class ConfigurationModelBeanDefinitionReader {
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass) { private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass) {
doLoadBeanDefinitionForConfigurationClass(configClass); doLoadBeanDefinitionForConfigurationClass(configClass);
for (BeanMethod method : configClass.getMethods()) for (BeanMethod method : configClass.getBeanMethods())
loadBeanDefinitionsForModelMethod(method); loadBeanDefinitionsForModelMethod(method);
// Annotation[] pluginAnnotations = configClass.getPluginAnnotations(); // Annotation[] pluginAnnotations = configClass.getPluginAnnotations();

View File

@ -31,10 +31,10 @@ import org.springframework.util.ClassUtils;
* from the concern of registering {@link BeanDefinition} objects based on the content of * from the concern of registering {@link BeanDefinition} objects based on the content of
* that model. * that model.
* *
* @see org.springframework.config.java.support.ConfigurationModel
* @see org.springframework.config.java.support.ConfigurationModelBeanDefinitionReader
*
* @author Chris Beams * @author Chris Beams
* @since 3.0
* @see ConfigurationModel
* @see ConfigurationModelBeanDefinitionReader
*/ */
public class ConfigurationParser { public class ConfigurationParser {

View File

@ -15,23 +15,28 @@
*/ */
package org.springframework.config.java.support; package org.springframework.config.java.support;
import static org.springframework.util.ClassUtils.*;
import org.springframework.beans.BeanMetadataElement; import org.springframework.beans.BeanMetadataElement;
import org.springframework.config.java.Configuration; import org.springframework.beans.factory.parsing.Location;
import org.springframework.util.Assert; import org.springframework.core.io.ClassPathResource;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
/** /**
* Abstract representation of a class, free from java reflection. Base class used within the * Representation of a class, free from java reflection,
* internal JavaConfig metamodel for representing {@link Configuration} classes. * populated by {@link ConfigurationParser}.
* *
* @author Chris Beams * @author Chris Beams
* @see ConfigurationModel
* @see ConfigurationClass
* @see ConfigurationParser
*/ */
class ModelClass implements BeanMetadataElement { class ModelClass implements BeanMetadataElement {
private String name; private String name;
private boolean isInterface; private boolean isInterface;
private String source; private transient Object source;
/** /**
* Creates a new and empty ModelClass instance. * Creates a new and empty ModelClass instance.
@ -98,8 +103,7 @@ class ModelClass implements BeanMetadataElement {
* Returns a resource path-formatted representation of the .java file that declares this * Returns a resource path-formatted representation of the .java file that declares this
* class * class
*/ */
// TODO: return type should be Object here. Spring IDE will return a JDT representation... public Object getSource() {
public String getSource() {
return source; return source;
} }
@ -109,8 +113,11 @@ class ModelClass implements BeanMetadataElement {
* @param source resource path to the .java file that declares this class. * @param source resource path to the .java file that declares this class.
*/ */
public void setSource(Object source) { public void setSource(Object source) {
Assert.isInstanceOf(String.class, source); this.source = source;
this.source = (String) source; }
public Location getLocation() {
return new Location(new ClassPathResource(convertClassNameToResourcePath(getName())), getSource());
} }
/** /**
@ -160,5 +167,3 @@ class ModelClass implements BeanMetadataElement {
} }
} }
// TODO: Consider eliminating in favor of just ConfigurationClass

View File

@ -32,10 +32,11 @@ import test.beans.TestBean;
* @author Chris Beams * @author Chris Beams
*/ */
public abstract class AbstractCircularImportDetectionTests { public abstract class AbstractCircularImportDetectionTests {
protected abstract ConfigurationParser newParser(); protected abstract ConfigurationParser newParser();
protected abstract String loadAsConfigurationSource(Class<?> clazz) throws Exception; protected abstract String loadAsConfigurationSource(Class<?> clazz) throws Exception;
@Test @Test
public void simpleCircularImportIsDetected() throws Exception { public void simpleCircularImportIsDetected() throws Exception {
boolean threw = false; boolean threw = false;

View File

@ -8,6 +8,7 @@ import org.junit.Test;
import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor; import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.config.java.Bean; import org.springframework.config.java.Bean;
import org.springframework.config.java.Configuration; import org.springframework.config.java.Configuration;
@ -49,6 +50,17 @@ public class BasicTests {
return factory; return factory;
} }
@Test(expected=BeanDefinitionParsingException.class)
public void testFinalBeanMethod() {
initBeanFactory(ConfigWithFinalBean.class);
}
@Configuration
static class ConfigWithFinalBean {
public final @Bean TestBean testBean() {
return new TestBean();
}
}
@Test @Test
public void simplestPossibleConfiguration() { public void simplestPossibleConfiguration() {
@ -61,8 +73,7 @@ public class BasicTests {
@Configuration @Configuration
static class SimplestPossibleConfig { static class SimplestPossibleConfig {
public @Bean public @Bean String stringBean() {
String stringBean() {
return "foo"; return "foo";
} }
} }
@ -82,15 +93,13 @@ public class BasicTests {
@Configuration @Configuration
static class ConfigWithPrototypeBean { static class ConfigWithPrototypeBean {
public @Bean public @Bean TestBean foo() {
TestBean foo() {
TestBean foo = new TestBean("foo"); TestBean foo = new TestBean("foo");
foo.setSpouse(bar()); foo.setSpouse(bar());
return foo; return foo;
} }
public @Bean public @Bean TestBean bar() {
TestBean bar() {
TestBean bar = new TestBean("bar"); TestBean bar = new TestBean("bar");
bar.setSpouse(baz()); bar.setSpouse(baz());
return bar; return bar;