diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/Bean.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/Bean.java index e9772936392..53a76f094ac 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/Bean.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/Bean.java @@ -22,10 +22,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import org.springframework.beans.factory.annotation.Autowire; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.support.AbstractBeanDefinition; - /** * Annotation to be applied to methods that create beans in a Spring context. The name of @@ -65,33 +61,39 @@ import org.springframework.beans.factory.support.AbstractBeanDefinition; @Documented 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; + // TODO: +// /** +// * 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; + + String[] name() default {}; - /** - * Bean aliases. - */ - String[] aliases() default {}; + // TODO: Prune aliases, favor name[] +// /** +// * Bean aliases. +// */ +// String[] aliases() default {}; - /** - * Scope: whether the bean is a singleton, prototype or custom scope. Default is - * singleton. - */ - String scope() default StandardScopes.SINGLETON; + // TODO: favor @Scope +// /** +// * Scope: whether the bean is a singleton, prototype or custom scope. Default is +// * singleton. +// */ +// String scope() default StandardScopes.SINGLETON; - /** - * Bean autowire strategy. - */ - Autowire autowire() default Autowire.INHERITED; + // TODO: prune autowiring? +// /** +// * Bean autowire strategy. +// */ +// Autowire autowire() default Autowire.INHERITED; // /** // * Bean lazy strategy. @@ -111,13 +113,14 @@ public @interface Bean { * Bean init method name. Normally this is not needed, as the initialization (with * parameterization) can be done directly through java code. */ - String initMethodName() default ""; + String initMethod() default ""; /** * Bean destroy method name. */ - String destroyMethodName() default ""; + String destroyMethod() default ""; + // TODO: Prune DependencyCheck // /** // * Bean dependency check strategy. // */ @@ -128,19 +131,23 @@ public @interface Bean { */ String[] dependsOn() default {}; + // TODO: Prune @Meta // /** // * 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; + // TODO: Prune allowOverriding +// /** +// * 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; +// + //String name() default ""; } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/BeanMethod.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/BeanMethod.java index 606ee570351..96077eb48d1 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/BeanMethod.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/BeanMethod.java @@ -16,16 +16,22 @@ package org.springframework.config.java; import static java.lang.String.*; +import static org.springframework.config.java.StandardScopes.*; import java.lang.annotation.Annotation; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; +import org.springframework.beans.factory.parsing.Location; +import org.springframework.beans.factory.parsing.Problem; +import org.springframework.beans.factory.parsing.ProblemReporter; +import org.springframework.context.annotation.Scope; +import org.springframework.core.io.FileSystemResource; import org.springframework.util.Assert; -public final class BeanMethod implements Validatable { +public final class BeanMethod { private final String name; private final int modifiers; @@ -65,9 +71,8 @@ public final class BeanMethod implements Validatable { } /** - * Returns the annotation on this method matching annoType or null - * IllegalStateException} if not present. - * + * @return the annotation on this method matching annoType or + * {@literal null} if not present. * @see #getRequiredAnnotation(Class) */ @SuppressWarnings("unchecked") @@ -80,9 +85,8 @@ public final class BeanMethod implements Validatable { } /** - * Returns the annotation on this method matching annoType or throws - * {@link IllegalStateException} if not present. - * + * @return the annotation on this method matching annoType + * @throws {@link IllegalStateException} if not present * @see #getAnnotation(Class) */ public T getRequiredAnnotation(Class annoType) { @@ -96,7 +100,7 @@ public final class BeanMethod implements Validatable { } /** - * Sets up 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) */ @@ -116,22 +120,20 @@ public final class BeanMethod implements Validatable { return lineNumber; } - public void validate(List errors) { + public void validate(ProblemReporter problemReporter) { if (Modifier.isPrivate(getModifiers())) - errors.add(new PrivateMethodError()); + problemReporter.error(new PrivateMethodError()); if (Modifier.isFinal(getModifiers())) - errors.add(new FinalMethodError()); + problemReporter.error(new FinalMethodError()); if (this.getAnnotation(ScopedProxy.class) == null) return; - Bean bean =this.getRequiredAnnotation(Bean.class); - - if (bean.scope().equals(StandardScopes.SINGLETON) - || bean.scope().equals(StandardScopes.PROTOTYPE)) - errors.add(new InvalidScopedProxyDeclarationError(this)); + Scope scope = this.getAnnotation(Scope.class); + if(scope == null || scope.equals(SINGLETON) || scope.equals(PROTOTYPE)) + problemReporter.error(new InvalidScopedProxyDeclarationError(this)); } @Override @@ -181,27 +183,17 @@ public final class BeanMethod implements Validatable { return true; } - /** JavaConfigMethods must be visible (non-private) in order to accommodate CGLIB. */ - public class PrivateMethodError extends UsageError { + /** {@link Bean} methods must be non-private in order to accommodate CGLIB. */ + public class PrivateMethodError extends Problem { public PrivateMethodError() { - super(getDeclaringClass(), getLineNumber()); - } - - @Override - public String getDescription() { - return format("method '%s' may not be private", getName()); + super(format("method '%s' may not be private", getName()), new Location(new FileSystemResource("/dev/null"))); } } - /** JavaConfigMethods must be extensible (non-final) in order to accommodate CGLIB. */ - public class FinalMethodError extends UsageError { + /** {@link Bean} methods must be non-final in order to accommodate CGLIB. */ + public class FinalMethodError extends Problem { public FinalMethodError() { - super(getDeclaringClass(), getLineNumber()); - } - - @Override - public String getDescription() { - return 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"))); } } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/BeanRegistrar.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/BeanRegistrar.java index b9852c839df..266123ef05c 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/BeanRegistrar.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/BeanRegistrar.java @@ -4,6 +4,8 @@ import static java.lang.String.*; import static org.springframework.util.StringUtils.*; import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -15,6 +17,7 @@ import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.context.annotation.Scope; import org.springframework.core.annotation.AnnotationUtils; @@ -42,20 +45,28 @@ public class BeanRegistrar implements BeanDefinitionRegistrar { beanDef.setFactoryMethodName(method.getName()); Bean bean = method.getRequiredAnnotation(Bean.class); - - Configuration defaults = configClass.getMetadata(); + + // TODO: prune defaults + //Configuration defaults = configClass.getMetadata(); // consider scoping - beanDef.setScope(bean.scope()); + Scope scope = method.getAnnotation(Scope.class); + if(scope != null) + beanDef.setScope(scope.value()); - // 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()); + // TODO: prune autowiring +// // 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(); + // consider name and any aliases + ArrayList names = new ArrayList(Arrays.asList(bean.name())); + String beanName = (names.size() > 0) ? names.remove(0) : method.getName(); + for (String alias : bean.name()) + registry.registerAlias(beanName, alias); // has this already been overriden (i.e.: via XML)? if (containsBeanDefinitionIncludingAncestry(beanName, registry)) { @@ -65,11 +76,12 @@ public class BeanRegistrar implements BeanDefinitionRegistrar { 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); - } + // TODO: Prune this +// // 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 " @@ -78,13 +90,6 @@ public class BeanRegistrar implements BeanDefinitionRegistrar { } } - // 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) @@ -96,12 +101,12 @@ public class BeanRegistrar implements BeanDefinitionRegistrar { // beanDef.setLazyInit(true); // does this bean have a custom init-method specified? - String initMethodName = bean.initMethodName(); + String initMethodName = bean.initMethod(); if (hasText(initMethodName)) beanDef.setInitMethodName(initMethodName); // does this bean have a custom destroy-method specified? - String destroyMethodName = bean.destroyMethodName(); + String destroyMethodName = bean.destroyMethod(); if (hasText(destroyMethodName)) beanDef.setDestroyMethodName(destroyMethodName); 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 1c6756f581b..d07c689d62d 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 @@ -22,8 +22,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import org.springframework.beans.factory.annotation.Autowire; -import org.springframework.beans.factory.annotation.Required; import org.springframework.stereotype.Component; @@ -44,9 +42,13 @@ import org.springframework.stereotype.Component; * fact that this annotation is meta-annotated with {@link Component @Component}. *

* + * May be used in conjunction with {@link Lazy} + * * @author Rod Johnson * @author Chris Beams * @since 3.0 + * @see Lazy + * @see Bean */ @Component @Target( { ElementType.TYPE }) @@ -55,22 +57,25 @@ import org.springframework.stereotype.Component; @Documented public @interface Configuration { - /** - * Configuration name. Allow different variants, such as test, production etc. Default - * will always match. - * - * @return - */ - String[] names() default ""; + // TODO: consider pruning @Configuration(name[]) +// /** +// * Configuration name. Allow different variants, such as test, production etc. Default +// * will always match. +// * +// * @return +// */ +// String[] name() default ""; - /** - * Specifies the default autowiring strategy. - * - * @see Autowire - * @return - */ - Autowire defaultAutowire() default Autowire.INHERITED; + // TODO: Prune defaultAutowire +// /** +// * Specifies the default autowiring strategy. +// * +// * @see Autowire +// * @return +// */ +// Autowire defaultAutowire() default Autowire.INHERITED; + // TODO: Prune DependencyCheck // /** // * Dependency check strategy. By default, the dependency check is // * unspecified, that is the default Spring option will apply. In most cases, @@ -81,6 +86,8 @@ public @interface Configuration { // */ // DependencyCheck defaultDependencyCheck() default DependencyCheck.UNSPECIFIED; // + + // TODO: Favor @Lazy at the @Configuration class level. Should have @Target(TYPE, METHOD) // /** // * Bean instantiation strategy. By default, it is unspecified. // * @@ -89,14 +96,16 @@ public @interface Configuration { // */ // Lazy defaultLazy() default Lazy.UNSPECIFIED; - /** - * Do we autowire with aspects from the enclosing factory scope? - */ - boolean useFactoryAspects() default false; + // TODO: prune useFactoryAspects +// /** +// * 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; + // TODO: this is the default, and needs to be switched off at annotation-config +// /** +// * 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 c5a1e1b5115..a73dde096e3 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 @@ -21,9 +21,12 @@ import static java.lang.String.*; import java.lang.annotation.Annotation; import java.lang.reflect.Modifier; import java.util.HashSet; -import java.util.List; import java.util.Set; +import org.springframework.beans.factory.parsing.Location; +import org.springframework.beans.factory.parsing.Problem; +import org.springframework.beans.factory.parsing.ProblemReporter; +import org.springframework.core.io.FileSystemResource; import org.springframework.util.Assert; import sun.security.x509.Extension; @@ -47,7 +50,7 @@ import sun.security.x509.Extension; */ // 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 { +public final class ConfigurationClass extends ModelClass { private String beanName; @@ -174,18 +177,18 @@ public final class ConfigurationClass extends ModelClass implements Validatable return this; } - public void validate(List errors) { + public void validate(ProblemReporter problemReporter) { // configuration classes must be annotated with @Configuration if (metadata == null) - errors.add(new NonAnnotatedConfigurationError()); + problemReporter.error(new NonAnnotatedConfigurationError()); // a configuration class may not be final (CGLIB limitation) if (Modifier.isFinal(modifiers)) - errors.add(new FinalConfigurationError()); + problemReporter.error(new FinalConfigurationError()); for (BeanMethod method : methods) - method.validate(errors); + method.validate(problemReporter); } @@ -248,94 +251,27 @@ public final class ConfigurationClass extends ModelClass implements Validatable /** Configuration classes must be annotated with {@link Configuration @Configuration}. */ - public class NonAnnotatedConfigurationError extends UsageError { + public class NonAnnotatedConfigurationError extends Problem { public NonAnnotatedConfigurationError() { - super(ConfigurationClass.this, -1); + super( + 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()), + new Location(new FileSystemResource("/dev/null")) + ); } - @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 class FinalConfigurationError extends Problem { public FinalConfigurationError() { - super(ConfigurationClass.this, -1); + super( + format("@%s class may not be final. Remove the final modifier to continue.", + Configuration.class.getSimpleName()), + new Location(new FileSystemResource("/dev/null")) + ); } - - @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 BeanMethod 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, BeanMethod 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 BeanMethod getMethod(String methodName) { - - for (BeanMethod 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 9a8403ff8b6..2fe2db831ae 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 @@ -18,7 +18,11 @@ package org.springframework.config.java; import static java.lang.String.*; import java.util.ArrayList; -import java.util.List; + +import org.springframework.beans.factory.parsing.Location; +import org.springframework.beans.factory.parsing.Problem; +import org.springframework.beans.factory.parsing.ProblemReporter; +import org.springframework.core.io.FileSystemResource; @@ -39,7 +43,7 @@ import java.util.List; * @author Chris Beams * @see org.springframework.config.java.internal.parsing.ConfigurationParser */ -public final class ConfigurationModel implements Validatable { +public final class ConfigurationModel { /* list is used because order and collection equality matters. */ private final ArrayList configurationClasses = new ArrayList(); @@ -89,35 +93,40 @@ public final class ConfigurationModel implements Validatable { * Recurses through the model validating each object along the way and aggregating any * errors. * - * @see ConfigurationClass#validate(java.util.List) - * @see BeanMethod#validate(java.util.List) - * @see UsageError + * @see ConfigurationClass#validate + * @see BeanMethod#validate */ - public void validate(List errors) { + public void validate(ProblemReporter problemReporter) { // user must specify at least one configuration if (configurationClasses.isEmpty()) - errors.add(new EmptyModelError()); + problemReporter.error(new EmptyModelError()); - // 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())) - errors.add(allClasses[i].new IllegalBeanOverrideError(allClasses[j], method)); - } - } + // 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) - configClass.validate(errors); + configClass.validate(problemReporter); } @Override @@ -151,15 +160,13 @@ public final class ConfigurationModel implements Validatable { } - public class EmptyModelError extends UsageError { + public class EmptyModelError extends Problem { 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()); + 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")) + ); } } diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/InvalidScopedProxyDeclarationError.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/InvalidScopedProxyDeclarationError.java index 4fc9dbe2e3d..78894e33c05 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/InvalidScopedProxyDeclarationError.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/InvalidScopedProxyDeclarationError.java @@ -15,19 +15,19 @@ */ package org.springframework.config.java; +import org.springframework.beans.factory.parsing.Location; +import org.springframework.beans.factory.parsing.Problem; +import org.springframework.core.io.FileSystemResource; -public class InvalidScopedProxyDeclarationError extends UsageError { - private final BeanMethod method; +public class InvalidScopedProxyDeclarationError extends Problem { public InvalidScopedProxyDeclarationError(BeanMethod method) { - super(method.getDeclaringClass(), method.getLineNumber()); - this.method = method; + super( + String.format("method %s contains an invalid annotation declaration: @%s " + + "cannot be used on a singleton/prototype bean", method.getName(), ScopedProxy.class + .getSimpleName()), + new Location(new FileSystemResource("/dev/null")) + ); } - @Override - public String getDescription() { - return String.format("method %s contains an invalid annotation declaration: @%s " - + "cannot be used on a singleton/prototype bean", method.getName(), ScopedProxy.class - .getSimpleName()); - } } 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 deleted file mode 100644 index 4fda73f97b8..00000000000 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/MalformedConfigurationException.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2002-2008 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.config.java; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - - -/** - * TODO: rename to UsageException / move outside .internal? - * - * @author Chris Beams - */ -@SuppressWarnings("serial") -public class MalformedConfigurationException extends RuntimeException { - - private final List errors; - - public MalformedConfigurationException(String message) { - super(message); - this.errors = new ArrayList(); - } - - 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; - - 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(); - - sb.append("\n"); - - if (errors.length == 1) - sb.append("A usage error has "); - else - sb.append(errors.length + " usage errors have "); - - 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'); - } - - return sb.toString(); - } - -} 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 deleted file mode 100644 index d7d9122d1a0..00000000000 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/UsageError.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2002-2008 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.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 - */ -public abstract class UsageError { - - 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; - } - - /** - * 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(); - - return String.format("%s:%d: %s", clazz.getSource(), lineNumber, getDescription()); - } - - /** - * 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/Validatable.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/Validatable.java deleted file mode 100644 index b2260baadea..00000000000 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/Validatable.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.springframework.config.java; - -import java.util.List; - - -/** - * Indicates a type is able to be validated for errors. - * - * @author Chris Beams - */ -interface Validatable { - - /** - * 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/internal/enhancement/ConfigurationEnhancer.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/internal/enhancement/ConfigurationEnhancer.java index b2221d47022..77f16a50182 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 @@ -174,6 +174,7 @@ public class ConfigurationEnhancer { * @return original subclass instance unless superclass is annnotated with @Aspect, in * which case a subclass of the subclass is returned */ + // TODO: try to implement with modifications to AbstractAspectJAdvisorFactory#isAspect private Class nestOneClassDeeperIfAspect(Class superclass, Class origSubclass) { boolean superclassIsAnAspect = false; diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/support/AbstractConfigurationClassProcessor.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/support/AbstractConfigurationClassProcessor.java index 9d18b8cd266..141d86de60b 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/support/AbstractConfigurationClassProcessor.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/support/AbstractConfigurationClassProcessor.java @@ -16,11 +16,13 @@ package org.springframework.config.java.support; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.parsing.BeanDefinitionParsingException; +import org.springframework.beans.factory.parsing.FailFastProblemReporter; +import org.springframework.beans.factory.parsing.ProblemReporter; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.config.java.Bean; import org.springframework.config.java.Configuration; import org.springframework.config.java.ConfigurationModel; -import org.springframework.config.java.MalformedConfigurationException; import org.springframework.config.java.internal.parsing.ConfigurationParser; @@ -44,6 +46,14 @@ import org.springframework.config.java.internal.parsing.ConfigurationParser; */ public abstract class AbstractConfigurationClassProcessor { + /** + * Used to register any problems detected with {@link Configuration} or {@link Bean} + * declarations. For instance, a Bean method marked as {@literal final} is illegal + * and would be reported as a problem. Defaults to {@link FailFastProblemReporter}, + * but is overridable with {@link #setProblemReporter} + */ + private ProblemReporter problemReporter = new FailFastProblemReporter(); + /** * Populate and return a registry containing all {@link Configuration} bean definitions * to be processed. @@ -65,12 +75,27 @@ public abstract class AbstractConfigurationClassProcessor { /** * Validate the given model and handle any errors. Implementations may choose to throw - * {@link MalformedConfigurationException}, or in the case of tooling register problems + * {@link BeanDefinitionParsingException}, or in the case of tooling register problems * with the UI. * @param configModel {@link ConfigurationModel} to validate */ protected abstract void validateModel(ConfigurationModel configModel); + /** + * Override the default {@link ProblemReporter}. + * @param problemReporter custom problem reporter + */ + protected final void setProblemReporter(ProblemReporter problemReporter) { + this.problemReporter = problemReporter; + } + + /** + * Get the currently registered {@link ProblemReporter}. + */ + protected final ProblemReporter getProblemReporter() { + return problemReporter; + } + /** * Build and validate a {@link ConfigurationModel} based on the registry of * {@link Configuration} classes provided by {@link #getConfigurationBeanDefinitions}, diff --git a/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationClassPostProcessor.java b/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationClassPostProcessor.java index 267562fcdac..d045a4c19db 100644 --- a/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationClassPostProcessor.java +++ b/org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationClassPostProcessor.java @@ -18,7 +18,6 @@ package org.springframework.config.java.support; import static java.lang.String.*; import java.io.IOException; -import java.util.ArrayList; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -31,8 +30,6 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.config.java.Bean; import org.springframework.config.java.Configuration; import org.springframework.config.java.ConfigurationModel; -import org.springframework.config.java.MalformedConfigurationException; -import org.springframework.config.java.UsageError; import org.springframework.config.java.internal.enhancement.ConfigurationEnhancer; import org.springframework.config.java.internal.parsing.ConfigurationParser; import org.springframework.core.Ordered; @@ -136,15 +133,12 @@ public class ConfigurationClassPostProcessor extends AbstractConfigurationClassP } /** - * Validates the given model. - * @throws MalformedConfigurationException if any errors are detected + * Validates the given model. Any problems found are delegated + * to {@link #getProblemReporter()}. */ @Override protected void validateModel(ConfigurationModel model) { - ArrayList errors = new ArrayList(); - model.validate(errors); - if (errors.size() > 0) - throw new MalformedConfigurationException(errors.toArray(new UsageError[] {})); + model.validate(this.getProblemReporter()); } /** 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 0fe4e339475..07c4bad512d 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 @@ -19,12 +19,10 @@ import static java.lang.String.*; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionReader; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.GenericBeanDefinition; -import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.support.SimpleBeanDefinitionRegistry; import org.springframework.config.java.Bean; import org.springframework.config.java.BeanMethod; @@ -96,18 +94,18 @@ class ConfigurationModelBeanDefinitionReader { * @param beanDefs */ private void doLoadBeanDefinitionForConfigurationClass(ConfigurationClass configClass) { - Configuration metadata = configClass.getMetadata(); - // TODO: think about implications with annotation-config - 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"); - registry.registerBeanDefinition(beanName, requiredAnnotationPostProcessor); - } + // TODO: prune support for @Required +// 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"); +// registry.registerBeanDefinition(beanName, requiredAnnotationPostProcessor); +// } GenericBeanDefinition configBeanDef = new GenericBeanDefinition(); configBeanDef.setBeanClassName(configClass.getName()); 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 479f05230c8..ed6729c0ad6 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 @@ -11,9 +11,9 @@ import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.config.java.Bean; import org.springframework.config.java.Configuration; -import org.springframework.config.java.MalformedConfigurationException; import org.springframework.config.java.StandardScopes; import org.springframework.config.java.support.ConfigurationClassPostProcessor; +import org.springframework.context.annotation.Scope; import test.beans.ITestBean; import test.beans.TestBean; @@ -96,68 +96,10 @@ public class BasicTests { return bar; } - @Bean(scope = StandardScopes.PROTOTYPE) + @Bean @Scope(StandardScopes.PROTOTYPE) public TestBean baz() { return new TestBean("bar"); } } - - @Test - public void legalBeanOverriding() { - { - BeanFactory factory = initBeanFactory(ConfigWithBeanThatAllowsOverriding.class, ConfigWithBeanOverride.class); - - TestBean testBean = factory.getBean("testBean", TestBean.class); - - assertThat(testBean.getName(), equalTo("overridden")); - } - - // now try it the other way around - order matters! - - { - BeanFactory factory = initBeanFactory(ConfigWithBeanOverride.class, ConfigWithBeanThatAllowsOverriding.class); - - TestBean testBean = factory.getBean("testBean", TestBean.class); - - assertThat(testBean.getName(), equalTo("original")); - } - - } - - @Test(expected=MalformedConfigurationException.class) - public void illegalBeanOverriding() { - initBeanFactory(ConfigWithBeanThatDisallowsOverriding.class, ConfigWithBeanOverride.class); - } - - @Test - public void illegalBeanOverriding2() { - // should be okay when the class that disallows overriding is the one doing the overriding - initBeanFactory(ConfigWithBeanOverride.class, ConfigWithBeanThatDisallowsOverriding.class); - } - - @Configuration - static class ConfigWithBeanThatAllowsOverriding { - @Bean - public TestBean testBean() { - return new TestBean("original"); - } - } - - @Configuration - static class ConfigWithBeanThatDisallowsOverriding { - @Bean(allowOverriding = false) - public TestBean testBean() { - return new TestBean("original"); - } - } - - @Configuration - static class ConfigWithBeanOverride { - @Bean - public TestBean testBean() { - return new TestBean("overridden"); - } - } - } diff --git a/org.springframework.config.java/src/test/java/test/feature/lifecycle/scoping/ScopingTests.java b/org.springframework.config.java/src/test/java/test/feature/lifecycle/scoping/ScopingTests.java index a5e0eb40bd3..99f21a4e90d 100644 --- a/org.springframework.config.java/src/test/java/test/feature/lifecycle/scoping/ScopingTests.java +++ b/org.springframework.config.java/src/test/java/test/feature/lifecycle/scoping/ScopingTests.java @@ -24,11 +24,10 @@ import org.junit.Before; import org.junit.Test; import org.springframework.aop.scope.ScopedObject; import org.springframework.beans.factory.config.Scope; +import org.springframework.beans.factory.parsing.BeanDefinitionParsingException; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.config.java.Bean; import org.springframework.config.java.Configuration; -import org.springframework.config.java.InvalidScopedProxyDeclarationError; -import org.springframework.config.java.MalformedConfigurationException; import org.springframework.config.java.ScopedProxy; import org.springframework.config.java.StandardScopes; import org.springframework.config.java.support.ConfigurationClassPostProcessor; @@ -118,8 +117,8 @@ public class ScopingTests { try { createContext(null, InvalidProxyOnPredefinedScopesConfiguration.class); fail("exception expected"); - } catch (MalformedConfigurationException ex) { - assertTrue(ex.containsError(InvalidScopedProxyDeclarationError.class)); + } catch (BeanDefinitionParsingException ex) { + assertTrue(ex.getMessage().contains("cannot be used on a singleton/prototype bean")); } } @@ -241,7 +240,8 @@ public class ScopingTests { @Configuration static class ScopeTestConfiguration { - @Bean(scope = StandardScopes.SESSION) + @Bean + @org.springframework.context.annotation.Scope(StandardScopes.SESSION) @ScopedProxy public Foo foo() { return new Foo(); @@ -326,21 +326,24 @@ public class ScopingTests { @Configuration public static class ScopedConfigurationClass { - @Bean(scope = SCOPE) + @Bean + @org.springframework.context.annotation.Scope(SCOPE) public TestBean scopedClass() { TestBean tb = new TestBean(); tb.setName(flag); return tb; } - @Bean(scope = SCOPE) + @Bean + @org.springframework.context.annotation.Scope(SCOPE) public ITestBean scopedInterface() { TestBean tb = new TestBean(); tb.setName(flag); return tb; } - @Bean(scope = SCOPE) + @Bean + @org.springframework.context.annotation.Scope(SCOPE) @ScopedProxy(proxyTargetClass = false) public ITestBean scopedProxyInterface() { TestBean tb = new TestBean(); @@ -349,7 +352,8 @@ public class ScopingTests { } @ScopedProxy - @Bean(scope = SCOPE) + @Bean + @org.springframework.context.annotation.Scope(SCOPE) public TestBean scopedProxyClass() { TestBean tb = new TestBean(); tb.setName(flag);