From ca435512c0493a8c811c30d41890ba033c6c3558 Mon Sep 17 00:00:00 2001 From: Madhura Bhave Date: Fri, 20 Jan 2017 14:02:51 -0800 Subject: [PATCH] Introduce spring-boot-autoconfigure-processor Add an annotation processor that generates properties files for certain auto-configuration class annotations. Currently attribute values from @AutoConfigureOrder, @AutoConfigureBefore, @AutoConfigureAfter and @ConditionalOnClass annotations are stored. The properties file will allow optimizations to be added in the `spring-boot-autoconfigure` project. Primarily by removing the need to ASM parse as many `.class` files. See gh-7573 --- eclipse/spring-boot-project.setup | 2 +- spring-boot-actuator/pom.xml | 5 + spring-boot-autoconfigure/pom.xml | 5 + spring-boot-dependencies/pom.xml | 5 + spring-boot-devtools/pom.xml | 5 + .../boot/junit/compiler}/TestCompiler.java | 11 +- .../boot/junit/compiler/package-info.java | 20 ++ spring-boot-tools/pom.xml | 1 + .../pom.xml | 40 ++++ .../AutoConfigureAnnotationProcessor.java | 214 ++++++++++++++++++ .../javax.annotation.processing.Processor | 1 + ...AutoConfigureAnnotationProcessorTests.java | 117 ++++++++++ .../TestAutoConfigureAfter.java | 38 ++++ .../TestAutoConfigureBefore.java | 38 ++++ .../TestAutoConfigureOrder.java | 36 +++ .../TestClassConfiguration.java | 35 +++ ...stConditionMetdataAnnotationProcessor.java | 76 +++++++ .../TestConditionalOnClass.java | 39 ++++ .../TestConfiguration.java | 38 ++++ .../TestMethodConfiguration.java | 33 +++ .../TestOrderedClassConfiguration.java | 32 +++ .../pom.xml | 4 + ...ationMetadataAnnotationProcessorTests.java | 3 +- .../configurationprocessor/TestProject.java | 12 +- .../AbstractFieldValuesProcessorTests.java | 4 +- 25 files changed, 799 insertions(+), 15 deletions(-) rename {spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor => spring-boot-test-support/src/main/java/org/springframework/boot/junit/compiler}/TestCompiler.java (93%) create mode 100644 spring-boot-test-support/src/main/java/org/springframework/boot/junit/compiler/package-info.java create mode 100644 spring-boot-tools/spring-boot-autoconfigure-processor/pom.xml create mode 100644 spring-boot-tools/spring-boot-autoconfigure-processor/src/main/java/org/springframework/boot/autoconfigureprocessor/AutoConfigureAnnotationProcessor.java create mode 100644 spring-boot-tools/spring-boot-autoconfigure-processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor create mode 100644 spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/AutoConfigureAnnotationProcessorTests.java create mode 100644 spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestAutoConfigureAfter.java create mode 100644 spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestAutoConfigureBefore.java create mode 100644 spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestAutoConfigureOrder.java create mode 100644 spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestClassConfiguration.java create mode 100644 spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestConditionMetdataAnnotationProcessor.java create mode 100644 spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestConditionalOnClass.java create mode 100644 spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestConfiguration.java create mode 100644 spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestMethodConfiguration.java create mode 100644 spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestOrderedClassConfiguration.java diff --git a/eclipse/spring-boot-project.setup b/eclipse/spring-boot-project.setup index 9f83d7a96b..acd7baeb57 100644 --- a/eclipse/spring-boot-project.setup +++ b/eclipse/spring-boot-project.setup @@ -143,7 +143,7 @@ name="spring-boot-tools"> + pattern="spring-boot-(tools|antlib|configuration-.*|loader|.*-tools|.*-plugin|autoconfigure-processor)"/> diff --git a/spring-boot-actuator/pom.xml b/spring-boot-actuator/pom.xml index 3204b8fc08..9832d01777 100644 --- a/spring-boot-actuator/pom.xml +++ b/spring-boot-actuator/pom.xml @@ -274,6 +274,11 @@ true + + org.springframework.boot + spring-boot-autoconfigure-processor + true + org.springframework.boot spring-boot-configuration-processor diff --git a/spring-boot-autoconfigure/pom.xml b/spring-boot-autoconfigure/pom.xml index e55696c6c4..877d6a1775 100755 --- a/spring-boot-autoconfigure/pom.xml +++ b/spring-boot-autoconfigure/pom.xml @@ -608,6 +608,11 @@ true + + org.springframework.boot + spring-boot-autoconfigure-processor + true + org.springframework.boot spring-boot-configuration-processor diff --git a/spring-boot-dependencies/pom.xml b/spring-boot-dependencies/pom.xml index bbffcec697..de02d5ca4b 100644 --- a/spring-boot-dependencies/pom.xml +++ b/spring-boot-dependencies/pom.xml @@ -271,6 +271,11 @@ spring-boot-autoconfigure 1.5.0.BUILD-SNAPSHOT + + org.springframework.boot + spring-boot-autoconfigure-processor + 1.5.0.BUILD-SNAPSHOT + org.springframework.boot spring-boot-configuration-metadata diff --git a/spring-boot-devtools/pom.xml b/spring-boot-devtools/pom.xml index 9928841f27..a54da7d8e7 100644 --- a/spring-boot-devtools/pom.xml +++ b/spring-boot-devtools/pom.xml @@ -80,6 +80,11 @@ true + + org.springframework.boot + spring-boot-autoconfigure-processor + true + org.springframework.boot spring-boot-configuration-processor diff --git a/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/TestCompiler.java b/spring-boot-test-support/src/main/java/org/springframework/boot/junit/compiler/TestCompiler.java similarity index 93% rename from spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/TestCompiler.java rename to spring-boot-test-support/src/main/java/org/springframework/boot/junit/compiler/TestCompiler.java index 37143a6f45..53328c7a96 100644 --- a/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/TestCompiler.java +++ b/spring-boot-test-support/src/main/java/org/springframework/boot/junit/compiler/TestCompiler.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2017 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.configurationprocessor; +package org.springframework.boot.junit.compiler; import java.io.File; import java.io.IOException; @@ -40,7 +40,10 @@ import org.junit.rules.TemporaryFolder; */ public class TestCompiler { - public static final File ORIGINAL_SOURCE_FOLDER = new File("src/test/java"); + /** + * The default source folder. + */ + public static final File SOURCE_FOLDER = new File("src/test/java"); private final JavaCompiler compiler; @@ -100,7 +103,7 @@ public class TestCompiler { } protected File getSourceFolder() { - return ORIGINAL_SOURCE_FOLDER; + return SOURCE_FOLDER; } /** diff --git a/spring-boot-test-support/src/main/java/org/springframework/boot/junit/compiler/package-info.java b/spring-boot-test-support/src/main/java/org/springframework/boot/junit/compiler/package-info.java new file mode 100644 index 0000000000..e76ad73ef9 --- /dev/null +++ b/spring-boot-test-support/src/main/java/org/springframework/boot/junit/compiler/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2017 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. + */ + +/** + * Utilities to work with the Java compiler at test time. + */ +package org.springframework.boot.junit.compiler; diff --git a/spring-boot-tools/pom.xml b/spring-boot-tools/pom.xml index cbf35f7895..bfdd833c91 100644 --- a/spring-boot-tools/pom.xml +++ b/spring-boot-tools/pom.xml @@ -20,6 +20,7 @@ ${basedir}/.. + spring-boot-autoconfigure-processor spring-boot-configuration-metadata spring-boot-configuration-processor spring-boot-loader diff --git a/spring-boot-tools/spring-boot-autoconfigure-processor/pom.xml b/spring-boot-tools/spring-boot-autoconfigure-processor/pom.xml new file mode 100644 index 0000000000..2695eafa86 --- /dev/null +++ b/spring-boot-tools/spring-boot-autoconfigure-processor/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-tools + 1.5.0.BUILD-SNAPSHOT + + spring-boot-autoconfigure-processor + Spring Boot Auto-Configure Annotation Processor + Spring Auto-Configure Annotation Processor + http://projects.spring.io/spring-boot/ + + Pivotal Software, Inc. + http://www.spring.io + + + ${basedir}/../.. + + + + org.springframework.boot + spring-boot-test-support + test + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + none + + + + + diff --git a/spring-boot-tools/spring-boot-autoconfigure-processor/src/main/java/org/springframework/boot/autoconfigureprocessor/AutoConfigureAnnotationProcessor.java b/spring-boot-tools/spring-boot-autoconfigure-processor/src/main/java/org/springframework/boot/autoconfigureprocessor/AutoConfigureAnnotationProcessor.java new file mode 100644 index 0000000000..fe09512e66 --- /dev/null +++ b/spring-boot-tools/spring-boot-autoconfigure-processor/src/main/java/org/springframework/boot/autoconfigureprocessor/AutoConfigureAnnotationProcessor.java @@ -0,0 +1,214 @@ +/* + * Copyright 2012-2017 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.boot.autoconfigureprocessor; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeMirror; +import javax.tools.FileObject; +import javax.tools.StandardLocation; + +/** + * Annotation processor to store certain annotations from auto-configuration classes in a + * property file. + * + * @author Madhura Bhave + * @author Phillip Webb + */ +@SupportedAnnotationTypes({ "org.springframework.context.annotation.Configuration", + "org.springframework.boot.autoconfigure.condition.ConditionalOnClass", + "org.springframework.boot.autoconfigure.AutoConfigureBefore", + "org.springframework.boot.autoconfigure.AutoConfigureAfter", + "org.springframework.boot.autoconfigure.AutoConfigureOrder" }) +public class AutoConfigureAnnotationProcessor extends AbstractProcessor { + + protected static final String PROPERTIES_PATH = "META-INF/" + + "spring-autoconfigure-metadata.properties"; + + private Map annotations; + + private final Properties properties = new Properties(); + + public AutoConfigureAnnotationProcessor() { + Map annotations = new LinkedHashMap(); + addAnnotations(annotations); + this.annotations = Collections.unmodifiableMap(annotations); + } + + protected void addAnnotations(Map annotations) { + annotations.put("Configuration", + "org.springframework.context.annotation.Configuration"); + annotations.put("ConditionalOnClass", + "org.springframework.boot.autoconfigure.condition.ConditionalOnClass"); + annotations.put("AutoConfigureBefore", + "org.springframework.boot.autoconfigure.AutoConfigureBefore"); + annotations.put("AutoConfigureAfter", + "org.springframework.boot.autoconfigure.AutoConfigureAfter"); + annotations.put("AutoConfigureOrder", + "org.springframework.boot.autoconfigure.AutoConfigureOrder"); + } + + @Override + public boolean process(Set annotations, + RoundEnvironment roundEnv) { + for (Map.Entry entry : this.annotations.entrySet()) { + process(roundEnv, entry.getKey(), entry.getValue()); + } + if (roundEnv.processingOver()) { + try { + writeProperties(); + } + catch (Exception ex) { + throw new IllegalStateException("Failed to write metadata", ex); + } + } + return false; + } + + private void process(RoundEnvironment roundEnv, String propertyKey, + String annotationName) { + TypeElement annotationType = this.processingEnv.getElementUtils() + .getTypeElement(annotationName); + if (annotationType != null) { + for (Element element : roundEnv.getElementsAnnotatedWith(annotationType)) { + Element enclosingElement = element.getEnclosingElement(); + if (enclosingElement != null + && enclosingElement.getKind() == ElementKind.PACKAGE) { + processElement(element, propertyKey, annotationName); + } + } + } + } + + private void processElement(Element element, String propertyKey, + String annotationName) { + try { + String qualifiedName = getQualifiedName(element); + AnnotationMirror annotation = getAnnotation(element, annotationName); + if (qualifiedName != null && annotation != null) { + List values = getValues(annotation); + this.properties.put(qualifiedName + "." + propertyKey, + toCommaDelimitedString(values)); + this.properties.put(qualifiedName, ""); + } + } + catch (Exception ex) { + throw new IllegalStateException( + "Error processing configuration meta-data on " + element, ex); + } + } + + private AnnotationMirror getAnnotation(Element element, String type) { + if (element != null) { + for (AnnotationMirror annotation : element.getAnnotationMirrors()) { + if (type.equals(annotation.getAnnotationType().toString())) { + return annotation; + } + } + } + return null; + } + + private String toCommaDelimitedString(List list) { + StringBuilder result = new StringBuilder(); + for (Object item : list) { + result.append(result.length() != 0 ? "," : ""); + result.append(item); + } + return result.toString(); + } + + @SuppressWarnings("unchecked") + private List getValues(AnnotationMirror annotation) { + List result = new ArrayList(); + for (Map.Entry entry : annotation + .getElementValues().entrySet()) { + String attributeName = entry.getKey().getSimpleName().toString(); + if ("name".equals(attributeName) || "value".equals(attributeName)) { + Object value = entry.getValue().getValue(); + if (value instanceof List) { + for (AnnotationValue annotationValue : (List) value) { + result.add(annotationValue.getValue()); + } + } + else { + result.add(value); + } + } + } + return result; + } + + private String getQualifiedName(Element element) { + if (element != null) { + TypeElement enclosingElement = getEnclosingTypeElement(element.asType()); + if (enclosingElement != null) { + return getQualifiedName(enclosingElement) + "$" + + ((DeclaredType) element.asType()).asElement().getSimpleName() + .toString(); + } + if (element instanceof TypeElement) { + return ((TypeElement) element).getQualifiedName().toString(); + } + } + return null; + } + + private TypeElement getEnclosingTypeElement(TypeMirror type) { + if (type instanceof DeclaredType) { + DeclaredType declaredType = (DeclaredType) type; + Element enclosingElement = declaredType.asElement().getEnclosingElement(); + if (enclosingElement != null && enclosingElement instanceof TypeElement) { + return (TypeElement) enclosingElement; + } + } + return null; + } + + private void writeProperties() throws IOException { + if (!this.properties.isEmpty()) { + FileObject file = this.processingEnv.getFiler() + .createResource(StandardLocation.CLASS_OUTPUT, "", PROPERTIES_PATH); + OutputStream outputStream = file.openOutputStream(); + try { + this.properties.store(outputStream, null); + } + finally { + outputStream.close(); + } + } + } + +} diff --git a/spring-boot-tools/spring-boot-autoconfigure-processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/spring-boot-tools/spring-boot-autoconfigure-processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor new file mode 100644 index 0000000000..70a37f4e27 --- /dev/null +++ b/spring-boot-tools/spring-boot-autoconfigure-processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -0,0 +1 @@ +org.springframework.boot.autoconfigureprocessor.AutoConfigureAnnotationProcessor diff --git a/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/AutoConfigureAnnotationProcessorTests.java b/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/AutoConfigureAnnotationProcessorTests.java new file mode 100644 index 0000000000..26d4744e93 --- /dev/null +++ b/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/AutoConfigureAnnotationProcessorTests.java @@ -0,0 +1,117 @@ +/* + * Copyright 2012-2017 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.boot.autoconfigureprocessor; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; + +import org.springframework.boot.junit.compiler.TestCompiler; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link AutoConfigureAnnotationProcessor}. + * + * @author Madhura Bhave + */ +public class AutoConfigureAnnotationProcessorTests { + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private TestCompiler compiler; + + @Before + public void createCompiler() throws IOException { + this.compiler = new TestCompiler(this.temporaryFolder); + } + + @Test + public void annotatedClass() throws Exception { + Properties properties = compile(TestClassConfiguration.class); + System.out.println(properties); + assertThat(properties).hasSize(3); + assertThat(properties).containsEntry( + "org.springframework.boot.autoconfigureprocessor." + + "TestClassConfiguration.ConditionalOnClass", + "java.io.InputStream,java.io.OutputStream"); + assertThat(properties).containsKey( + "org.springframework.boot.autoconfigureprocessor.TestClassConfiguration"); + assertThat(properties).containsKey( + "org.springframework.boot.autoconfigureprocessor.TestClassConfiguration.Configuration"); + assertThat(properties).doesNotContainKey( + "org.springframework.boot.autoconfigureprocessor.TestClassConfiguration$Nested"); + } + + @Test + public void annotatedMethod() throws Exception { + Properties properties = compile(TestMethodConfiguration.class); + List matching = new ArrayList(); + for (Object key : properties.keySet()) { + if (key.toString().startsWith( + "org.springframework.boot.autoconfigureprocessor.TestMethodConfiguration")) { + matching.add(key.toString()); + } + } + assertThat(matching).hasSize(2) + .contains("org.springframework.boot.autoconfigureprocessor." + + "TestMethodConfiguration") + .contains("org.springframework.boot.autoconfigureprocessor." + + "TestMethodConfiguration.Configuration"); + } + + @Test + public void annotatedClassWithOrder() throws Exception { + Properties properties = compile(TestOrderedClassConfiguration.class); + assertThat(properties).containsEntry( + "org.springframework.boot.autoconfigureprocessor." + + "TestOrderedClassConfiguration.ConditionalOnClass", + "java.io.InputStream,java.io.OutputStream"); + assertThat(properties).containsEntry( + "org.springframework.boot.autoconfigureprocessor." + + "TestOrderedClassConfiguration.AutoConfigureBefore", + "test.before1,test.before2"); + assertThat(properties).containsEntry( + "org.springframework.boot.autoconfigureprocessor." + + "TestOrderedClassConfiguration.AutoConfigureAfter", + "java.io.ObjectInputStream"); + assertThat(properties) + .containsEntry( + "org.springframework.boot.autoconfigureprocessor." + + "TestOrderedClassConfiguration.AutoConfigureOrder", + "123"); + } + + private Properties compile(Class... types) throws IOException { + TestConditionMetdataAnnotationProcessor processor = new TestConditionMetdataAnnotationProcessor( + this.compiler.getOutputLocation()); + this.compiler.getTask(types).call(processor); + return processor.getWrittenProperties(); + } + +} diff --git a/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestAutoConfigureAfter.java b/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestAutoConfigureAfter.java new file mode 100644 index 0000000000..7f9334360a --- /dev/null +++ b/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestAutoConfigureAfter.java @@ -0,0 +1,38 @@ +/* + * Copyright 2012-2017 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.boot.autoconfigureprocessor; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Alternative to Spring Boot's {@code @AutoConfigureAfter} for testing (removes the need + * for a dependency on the real annotation). + * + * @author Phillip Webb + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE }) +public @interface TestAutoConfigureAfter { + + Class[] value() default {}; + + String[] name() default {}; + +} diff --git a/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestAutoConfigureBefore.java b/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestAutoConfigureBefore.java new file mode 100644 index 0000000000..53f306d557 --- /dev/null +++ b/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestAutoConfigureBefore.java @@ -0,0 +1,38 @@ +/* + * Copyright 2012-2017 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.boot.autoconfigureprocessor; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Alternative to Spring Boot's {@code @AutoConfigureBefore} for testing (removes the need + * for a dependency on the real annotation). + * + * @author Phillip Webb + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE }) +public @interface TestAutoConfigureBefore { + + Class[] value() default {}; + + String[] name() default {}; + +} diff --git a/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestAutoConfigureOrder.java b/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestAutoConfigureOrder.java new file mode 100644 index 0000000000..17caa158db --- /dev/null +++ b/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestAutoConfigureOrder.java @@ -0,0 +1,36 @@ +/* + * Copyright 2012-2017 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.boot.autoconfigureprocessor; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Alternative to Spring Boot's {@code @AutoConfigureOrder} for testing (removes the need + * for a dependency on the real annotation). + * + * @author Phillip Webb + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD }) +public @interface TestAutoConfigureOrder { + + int value() default Integer.MAX_VALUE; + +} diff --git a/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestClassConfiguration.java b/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestClassConfiguration.java new file mode 100644 index 0000000000..7ca07f4323 --- /dev/null +++ b/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestClassConfiguration.java @@ -0,0 +1,35 @@ +/* + * Copyright 2012-2017 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.boot.autoconfigureprocessor; + +import java.io.OutputStream; + +/** + * Test configuration with an annotated class. + * + * @author Madhura Bhave + */ +@TestConfiguration +@TestConditionalOnClass(name = "java.io.InputStream", value = OutputStream.class) +public class TestClassConfiguration { + + @TestAutoConfigureOrder + public static class Nested { + + } + +} diff --git a/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestConditionMetdataAnnotationProcessor.java b/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestConditionMetdataAnnotationProcessor.java new file mode 100644 index 0000000000..08d0c60d15 --- /dev/null +++ b/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestConditionMetdataAnnotationProcessor.java @@ -0,0 +1,76 @@ +/* + * Copyright 2012-2017 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.boot.autoconfigureprocessor; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Map; +import java.util.Properties; + +import javax.annotation.processing.SupportedAnnotationTypes; + +/** + * Version of {@link AutoConfigureAnnotationProcessor} used for testing. + * + * @author Madhura Bhave + */ +@SupportedAnnotationTypes({ + "org.springframework.boot.autoconfigureprocessor.TestConfiguration", + "org.springframework.boot.autoconfigureprocessor.TestConditionalOnClass", + "org.springframework.boot.autoconfigureprocessor.TestAutoConfigureBefore", + "org.springframework.boot.autoconfigureprocessor.TestAutoConfigureAfter", + "org.springframework.boot.autoconfigureprocessor.TestAutoConfigureOrder" }) +public class TestConditionMetdataAnnotationProcessor + extends AutoConfigureAnnotationProcessor { + + private final File outputLocation; + + public TestConditionMetdataAnnotationProcessor(File outputLocation) { + this.outputLocation = outputLocation; + } + + @Override + protected void addAnnotations(Map annotations) { + put(annotations, "Configuration", TestConfiguration.class); + put(annotations, "ConditionalOnClass", TestConditionalOnClass.class); + put(annotations, "AutoConfigureBefore", TestAutoConfigureBefore.class); + put(annotations, "AutoConfigureAfter", TestAutoConfigureAfter.class); + put(annotations, "AutoConfigureOrder", TestAutoConfigureOrder.class); + } + + private void put(Map annotations, String key, Class value) { + annotations.put(key, value.getName()); + } + + public Properties getWrittenProperties() throws IOException { + File file = new File(this.outputLocation, PROPERTIES_PATH); + if (!file.exists()) { + return null; + } + FileInputStream inputStream = new FileInputStream(file); + try { + Properties properties = new Properties(); + properties.load(inputStream); + return properties; + } + finally { + inputStream.close(); + } + } + +} diff --git a/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestConditionalOnClass.java b/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestConditionalOnClass.java new file mode 100644 index 0000000000..b946e2b2bf --- /dev/null +++ b/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestConditionalOnClass.java @@ -0,0 +1,39 @@ +/* + * Copyright 2012-2017 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.boot.autoconfigureprocessor; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Fake {@code @ConditionalOnClass} annotation used for testing. + * + * @author Madhura Bhave + */ +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface TestConditionalOnClass { + + Class[] value() default {}; + + String[] name() default {}; + +} diff --git a/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestConfiguration.java b/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestConfiguration.java new file mode 100644 index 0000000000..2015f31f5b --- /dev/null +++ b/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestConfiguration.java @@ -0,0 +1,38 @@ +/* + * Copyright 2012-2017 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.boot.autoconfigureprocessor; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Alternative to Spring's {@code @Configuration} for testing (removes the need for a + * dependency on the real annotation). + * + * @author Phillip Webb + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface TestConfiguration { + + String value() default ""; + +} diff --git a/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestMethodConfiguration.java b/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestMethodConfiguration.java new file mode 100644 index 0000000000..d2fe126d0c --- /dev/null +++ b/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestMethodConfiguration.java @@ -0,0 +1,33 @@ +/* + * Copyright 2012-2017 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.boot.autoconfigureprocessor; + +import java.io.OutputStream; + +/** + * Test configuration with an annoated method. + * + * @author Madhura Bhave + */ +@TestConfiguration +public class TestMethodConfiguration { + + @TestConditionalOnClass(name = "java.io.InputStream", value = OutputStream.class) + public Object method() { + return null; + } +} diff --git a/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestOrderedClassConfiguration.java b/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestOrderedClassConfiguration.java new file mode 100644 index 0000000000..625fd14ed0 --- /dev/null +++ b/spring-boot-tools/spring-boot-autoconfigure-processor/src/test/java/org/springframework/boot/autoconfigureprocessor/TestOrderedClassConfiguration.java @@ -0,0 +1,32 @@ +/* + * Copyright 2012-2017 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.boot.autoconfigureprocessor; + +import java.io.ObjectInputStream; +import java.io.OutputStream; + +/** + * Test configuration with an annotated class. + * + * @author Phillip Webb + */ +@TestAutoConfigureBefore(name = { "test.before1", "test.before2" }) +@TestAutoConfigureAfter(ObjectInputStream.class) +@TestConditionalOnClass(name = "java.io.InputStream", value = OutputStream.class) +@TestAutoConfigureOrder(123) +public class TestOrderedClassConfiguration { +} diff --git a/spring-boot-tools/spring-boot-configuration-processor/pom.xml b/spring-boot-tools/spring-boot-configuration-processor/pom.xml index 0db592f986..78aa0d1ea4 100644 --- a/spring-boot-tools/spring-boot-configuration-processor/pom.xml +++ b/spring-boot-tools/spring-boot-configuration-processor/pom.xml @@ -24,6 +24,10 @@ com.vaadin.external.google android-json + + org.springframework.boot + spring-boot-test-support + org.projectlombok diff --git a/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java b/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java index aa371a7a20..a145f0361e 100644 --- a/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java +++ b/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/ConfigurationMetadataAnnotationProcessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. @@ -68,6 +68,7 @@ import org.springframework.boot.configurationsample.specific.InnerClassRootConfi import org.springframework.boot.configurationsample.specific.InvalidAccessorProperties; import org.springframework.boot.configurationsample.specific.InvalidDoubleRegistrationProperties; import org.springframework.boot.configurationsample.specific.SimplePojo; +import org.springframework.boot.junit.compiler.TestCompiler; import org.springframework.util.FileCopyUtils; import static org.assertj.core.api.Assertions.assertThat; diff --git a/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/TestProject.java b/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/TestProject.java index 9225e09b12..ff5ec89fdd 100644 --- a/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/TestProject.java +++ b/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/TestProject.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. @@ -33,16 +33,14 @@ import java.util.Set; import org.junit.Assert; import org.junit.rules.TemporaryFolder; -import org.springframework.boot.configurationprocessor.TestCompiler.TestCompilationTask; import org.springframework.boot.configurationprocessor.metadata.ConfigurationMetadata; import org.springframework.boot.configurationsample.ConfigurationProperties; import org.springframework.boot.configurationsample.NestedConfigurationProperty; +import org.springframework.boot.junit.compiler.TestCompiler; +import org.springframework.boot.junit.compiler.TestCompiler.TestCompilationTask; import org.springframework.util.FileCopyUtils; import org.springframework.util.FileSystemUtils; -import static org.springframework.boot.configurationprocessor.TestCompiler.ORIGINAL_SOURCE_FOLDER; -import static org.springframework.boot.configurationprocessor.TestCompiler.sourcePathFor; - /** * A TestProject contains a copy of a subset of test sample code. *

@@ -96,7 +94,7 @@ public class TestProject { } public File getSourceFile(Class type) { - return new File(this.sourceFolder, sourcePathFor(type)); + return new File(this.sourceFolder, TestCompiler.sourcePathFor(type)); } public ConfigurationMetadata fullBuild() { @@ -192,7 +190,7 @@ public class TestProject { * code. */ private File getOriginalSourceFile(Class type) { - return new File(ORIGINAL_SOURCE_FOLDER, sourcePathFor(type)); + return new File(TestCompiler.SOURCE_FOLDER, TestCompiler.sourcePathFor(type)); } private static void putContents(File targetFile, String contents) diff --git a/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/fieldvalues/AbstractFieldValuesProcessorTests.java b/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/fieldvalues/AbstractFieldValuesProcessorTests.java index 54600b119c..1a6793cdec 100644 --- a/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/fieldvalues/AbstractFieldValuesProcessorTests.java +++ b/spring-boot-tools/spring-boot-configuration-processor/src/test/java/org/springframework/boot/configurationprocessor/fieldvalues/AbstractFieldValuesProcessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. @@ -33,8 +33,8 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; -import org.springframework.boot.configurationprocessor.TestCompiler; import org.springframework.boot.configurationsample.fieldvalues.FieldValues; +import org.springframework.boot.junit.compiler.TestCompiler; import static org.assertj.core.api.Assertions.assertThat;