Browse Source

Circular @Imports are now handled by registering a Problem (CircularImportProblem) as an error with the current ProblemReporter. This eliminates the need for CircularImportException and is a more tooling-friendly approach.

git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@776 50f2f4bb-b051-0410-bef5-90022cba6387
pull/1/head
Chris Beams 17 years ago
parent
commit
0cf9358bf4
  1. 40
      org.springframework.config.java/src/main/java/org/springframework/config/java/support/CircularImportException.java
  2. 2
      org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationClassPostProcessor.java
  3. 51
      org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationClassVisitor.java
  4. 10
      org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationParser.java
  5. 7
      org.springframework.config.java/src/main/java/org/springframework/config/java/support/ImportAnnotationVisitor.java
  6. 5
      org.springframework.config.java/src/test/java/org/springframework/config/java/support/AbstractCircularImportDetectionTests.java
  7. 3
      org.springframework.config.java/src/test/java/org/springframework/config/java/support/AsmCircularImportDetectionTests.java

40
org.springframework.config.java/src/main/java/org/springframework/config/java/support/CircularImportException.java

@ -1,40 +0,0 @@ @@ -1,40 +0,0 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.config.java.support;
import static java.lang.String.*;
import java.util.Stack;
/**
* Thrown by {@link ConfigurationParser} upon detecting circular use of the {@link Import} annotation.
*
* @author Chris Beams
* @see Import
* @see ImportStack
* @see ImportStackHolder
*/
@SuppressWarnings("serial")
class CircularImportException extends IllegalStateException {
public CircularImportException(ConfigurationClass attemptedImport, Stack<ConfigurationClass> currentImportStack) {
super(format("A circular @Import has been detected: " +
"Illegal attempt by @Configuration class '%s' to import class '%s' as '%s' is " +
"already present in the current import stack [%s]",
currentImportStack.peek().getSimpleName(), attemptedImport.getSimpleName(),
attemptedImport.getSimpleName(), currentImportStack));
}
}

2
org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationClassPostProcessor.java

@ -104,7 +104,7 @@ public class ConfigurationClassPostProcessor extends AbstractConfigurationClassP @@ -104,7 +104,7 @@ public class ConfigurationClassPostProcessor extends AbstractConfigurationClassP
*/
@Override
protected ConfigurationParser createConfigurationParser() {
return new ConfigurationParser(beanFactory.getBeanClassLoader());
return new ConfigurationParser(this.getProblemReporter(), beanFactory.getBeanClassLoader());
}
/**

51
org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationClassVisitor.java

@ -15,20 +15,27 @@ @@ -15,20 +15,27 @@
*/
package org.springframework.config.java.support;
import static java.lang.String.*;
import static org.springframework.config.java.support.MutableAnnotationUtils.*;
import static org.springframework.config.java.support.Util.*;
import static org.springframework.util.ClassUtils.*;
import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.Stack;
import org.springframework.asm.AnnotationVisitor;
import org.springframework.asm.ClassAdapter;
import org.springframework.asm.ClassReader;
import org.springframework.asm.MethodVisitor;
import org.springframework.asm.Opcodes;
import org.springframework.asm.commons.EmptyVisitor;
import org.springframework.beans.factory.parsing.Location;
import org.springframework.beans.factory.parsing.Problem;
import org.springframework.beans.factory.parsing.ProblemReporter;
import org.springframework.config.java.Configuration;
import org.springframework.config.java.Import;
import org.springframework.core.io.FileSystemResource;
/**
@ -43,15 +50,18 @@ class ConfigurationClassVisitor extends ClassAdapter { @@ -43,15 +50,18 @@ class ConfigurationClassVisitor extends ClassAdapter {
private final ConfigurationClass configClass;
private final ConfigurationModel model;
private final ProblemReporter problemReporter;
private final HashMap<String, ConfigurationClass> innerClasses = new HashMap<String, ConfigurationClass>();
private boolean processInnerClasses = true;
private final ClassLoader classLoader;
public ConfigurationClassVisitor(ConfigurationClass configClass, ConfigurationModel model, ClassLoader classLoader) {
public ConfigurationClassVisitor(ConfigurationClass configClass, ConfigurationModel model,
ProblemReporter problemReporter, ClassLoader classLoader) {
super(AsmUtils.EMPTY_VISITOR);
this.configClass = configClass;
this.model = model;
this.problemReporter = problemReporter;
this.classLoader = classLoader;
}
@ -86,7 +96,8 @@ class ConfigurationClassVisitor extends ClassAdapter { @@ -86,7 +96,8 @@ class ConfigurationClassVisitor extends ClassAdapter {
if (OBJECT_DESC.equals(superTypeDesc))
return;
ConfigurationClassVisitor visitor = new ConfigurationClassVisitor(configClass, model, classLoader);
ConfigurationClassVisitor visitor =
new ConfigurationClassVisitor(configClass, model, problemReporter, classLoader);
ClassReader reader = AsmUtils.newClassReader(superTypeDesc, classLoader);
reader.accept(visitor, false);
@ -113,15 +124,18 @@ class ConfigurationClassVisitor extends ClassAdapter { @@ -113,15 +124,18 @@ class ConfigurationClassVisitor extends ClassAdapter {
return new MutableAnnotationVisitor(mutableConfiguration, classLoader);
}
if (Import.class.getName().equals(annoTypeName)) {
if (Import.class.getName().equals(annoTypeName)) {
ImportStack importStack = ImportStackHolder.getImportStack();
if (importStack.contains(configClass))
throw new CircularImportException(configClass, importStack);
if (importStack.contains(configClass)) {
//throw new CircularImportException(configClass, importStack);
problemReporter.error(new CircularImportProblem(configClass, importStack));
return new EmptyVisitor();
}
importStack.push(configClass);
return new ImportAnnotationVisitor(model, classLoader);
return new ImportAnnotationVisitor(model, problemReporter, classLoader);
}
/* -------------------------------------
@ -217,7 +231,7 @@ class ConfigurationClassVisitor extends ClassAdapter { @@ -217,7 +231,7 @@ class ConfigurationClassVisitor extends ClassAdapter {
ConfigurationClass innerConfigClass = new ConfigurationClass();
ConfigurationClassVisitor ccVisitor =
new ConfigurationClassVisitor(innerConfigClass, new ConfigurationModel(), classLoader);
new ConfigurationClassVisitor(innerConfigClass, new ConfigurationModel(), problemReporter, classLoader);
ccVisitor.setProcessInnerClasses(false);
ClassReader reader = AsmUtils.newClassReader(name, classLoader);
@ -230,4 +244,27 @@ class ConfigurationClassVisitor extends ClassAdapter { @@ -230,4 +244,27 @@ class ConfigurationClassVisitor extends ClassAdapter {
if (innerConfigClass.getMetadata() != null)
innerClasses.put(name, innerConfigClass);
}
/**
* {@link Problem} registered upon detection of a circular {@link Import}.
*
* @see Import
* @see ImportStack
* @see ImportStackHolder
*/
class CircularImportProblem extends Problem {
public CircularImportProblem(ConfigurationClass attemptedImport, Stack<ConfigurationClass> currentImportStack) {
super(format("A circular @Import has been detected: " +
"Illegal attempt by @Configuration class '%s' to import class '%s' as '%s' is " +
"already present in the current import stack [%s]",
currentImportStack.peek().getSimpleName(), attemptedImport.getSimpleName(),
attemptedImport.getSimpleName(), currentImportStack),
new Location(new FileSystemResource("/dev/null"))
);
}
}
}

10
org.springframework.config.java/src/main/java/org/springframework/config/java/support/ConfigurationParser.java

@ -17,6 +17,7 @@ package org.springframework.config.java.support; @@ -17,6 +17,7 @@ package org.springframework.config.java.support;
import org.springframework.asm.ClassReader;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.parsing.ProblemReporter;
import org.springframework.config.java.Configuration;
import org.springframework.util.ClassUtils;
@ -41,17 +42,18 @@ public class ConfigurationParser { @@ -41,17 +42,18 @@ public class ConfigurationParser {
* Model to be populated during calls to {@link #parse(Object, String)}
*/
private final ConfigurationModel model;
private final ProblemReporter problemReporter;
private final ClassLoader classLoader;
/**
* Creates a new parser instance that will be used to populate <var>model</var>.
*
* @param model model to be populated by each successive call to
* {@link #parse(Object, String)}
*/
public ConfigurationParser(ClassLoader classLoader) {
this.classLoader = classLoader;
public ConfigurationParser(ProblemReporter problemReporter, ClassLoader classLoader) {
this.model = new ConfigurationModel();
this.problemReporter = problemReporter;
this.classLoader = classLoader;
}
/**
@ -71,7 +73,7 @@ public class ConfigurationParser { @@ -71,7 +73,7 @@ public class ConfigurationParser {
ConfigurationClass configClass = new ConfigurationClass();
configClass.setBeanName(configurationId);
configClassReader.accept(new ConfigurationClassVisitor(configClass, model, classLoader), false);
configClassReader.accept(new ConfigurationClassVisitor(configClass, model, problemReporter, classLoader), false);
model.add(configClass);
}

7
org.springframework.config.java/src/main/java/org/springframework/config/java/support/ImportAnnotationVisitor.java

@ -24,6 +24,7 @@ import java.util.ArrayList; @@ -24,6 +24,7 @@ import java.util.ArrayList;
import org.springframework.asm.AnnotationVisitor;
import org.springframework.asm.ClassReader;
import org.springframework.asm.Type;
import org.springframework.beans.factory.parsing.ProblemReporter;
import org.springframework.config.java.Import;
import org.springframework.util.Assert;
@ -42,11 +43,13 @@ import org.springframework.util.Assert; @@ -42,11 +43,13 @@ import org.springframework.util.Assert;
class ImportAnnotationVisitor extends AnnotationAdapter {
private final ArrayList<String> classesToImport = new ArrayList<String>();
private final ConfigurationModel model;
private final ProblemReporter problemReporter;
private final ClassLoader classLoader;
public ImportAnnotationVisitor(ConfigurationModel model, ClassLoader classLoader) {
public ImportAnnotationVisitor(ConfigurationModel model, ProblemReporter problemReporter, ClassLoader classLoader) {
super(AsmUtils.EMPTY_VISITOR);
this.model = model;
this.problemReporter = problemReporter;
this.classLoader = classLoader;
}
@ -77,7 +80,7 @@ class ImportAnnotationVisitor extends AnnotationAdapter { @@ -77,7 +80,7 @@ class ImportAnnotationVisitor extends AnnotationAdapter {
ClassReader reader = newClassReader(convertClassNameToResourcePath(classToImport), classLoader);
reader.accept(new ConfigurationClassVisitor(configClass, model, classLoader), false);
reader.accept(new ConfigurationClassVisitor(configClass, model, problemReporter, classLoader), false);
model.add(configClass);
}

5
org.springframework.config.java/src/test/java/org/springframework/config/java/support/AbstractCircularImportDetectionTests.java

@ -18,6 +18,7 @@ package org.springframework.config.java.support; @@ -18,6 +18,7 @@ package org.springframework.config.java.support;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.config.java.Bean;
import org.springframework.config.java.Import;
@ -40,7 +41,7 @@ public abstract class AbstractCircularImportDetectionTests { @@ -40,7 +41,7 @@ public abstract class AbstractCircularImportDetectionTests {
boolean threw = false;
try {
newParser().parse(loadAsConfigurationSource(A.class), null);
} catch (CircularImportException ex) {
} catch (BeanDefinitionParsingException ex) {
assertTrue("Wrong message. Got: " + ex.getMessage(),
ex.getMessage().contains(
"Illegal attempt by @Configuration class 'AbstractCircularImportDetectionTests.B' " +
@ -57,7 +58,7 @@ public abstract class AbstractCircularImportDetectionTests { @@ -57,7 +58,7 @@ public abstract class AbstractCircularImportDetectionTests {
boolean threw = false;
try {
newParser().parse(loadAsConfigurationSource(X.class), null);
} catch (CircularImportException ex) {
} catch (BeanDefinitionParsingException ex) {
assertTrue("Wrong message. Got: " + ex.getMessage(),
ex.getMessage().contains(
"Illegal attempt by @Configuration class 'AbstractCircularImportDetectionTests.Z2' " +

3
org.springframework.config.java/src/test/java/org/springframework/config/java/support/AsmCircularImportDetectionTests.java

@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
*/
package org.springframework.config.java.support;
import org.springframework.beans.factory.parsing.FailFastProblemReporter;
import org.springframework.config.java.Import;
import org.springframework.util.ClassUtils;
@ -32,7 +33,7 @@ import org.springframework.util.ClassUtils; @@ -32,7 +33,7 @@ import org.springframework.util.ClassUtils;
public class AsmCircularImportDetectionTests extends AbstractCircularImportDetectionTests {
@Override
protected ConfigurationParser newParser() {
return new ConfigurationParser(ClassUtils.getDefaultClassLoader());
return new ConfigurationParser(new FailFastProblemReporter(), ClassUtils.getDefaultClassLoader());
}
@Override

Loading…
Cancel
Save