Polish metadata annotation processor’s incremental build support

The main change in this commit is to introduce a new BuildHandler
abstraction. A BuildHandler is responsible for producing the metadata
for a build. Two implementations are provided; one for standard builds
and one for incremental builds. This change means that the annotation
processor is no longer concerned with the two different build types
and can use the same logic in each case.

The code for reading and writing metadata files has also been moved
out into a separate class, MetadataStore, to allow it to be easily
utilised from multiple places.

Closes gh-2313
This commit is contained in:
Andy Wilkinson
2015-01-28 11:10:07 +00:00
parent 8df43a8a79
commit 23c175f674
9 changed files with 434 additions and 277 deletions

View File

@@ -1,47 +0,0 @@
/*
* Copyright 2012-2015 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.configurationprocessor;
import java.util.Set;
import org.springframework.boot.configurationprocessor.metadata.ConfigurationMetadata;
/**
* Data object containing information about a finished build.
*
* @author Kris De Volder
*/
public class BuildResult {
public final ConfigurationMetadata metadata;
public final Set<String> processedTypes;
public final boolean isIncremental;
public BuildResult(boolean isIncremental, ConfigurationMetadata metadata,
Set<String> processedTypes) {
this.isIncremental = isIncremental;
this.metadata = metadata;
this.processedTypes = processedTypes;
}
public BuildResult(TestConfigurationMetadataAnnotationProcessor processor) {
this(processor.isIncremental(), processor.getMetadata(),
processor.processedSourceTypes);
}
}

View File

@@ -57,9 +57,9 @@ import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.springframework.boot.configurationprocessor.ConfigurationMetadataAnnotationProcessor.METADATA_PATH;
import static org.springframework.boot.configurationprocessor.ConfigurationMetadataMatchers.containsGroup;
import static org.springframework.boot.configurationprocessor.ConfigurationMetadataMatchers.containsProperty;
import static org.springframework.boot.configurationprocessor.MetadataStore.METADATA_PATH;
/**
* Tests for {@link ConfigurationMetadataAnnotationProcessor}.
@@ -81,92 +81,6 @@ public class ConfigurationMetadataAnnotationProcessorTests {
this.compiler = new TestCompiler(this.temporaryFolder);
}
@Test
public void incrementalBuild() throws Exception {
TestProject project = new TestProject(this.temporaryFolder, FooProperties.class,
BarProperties.class);
assertFalse(project.getOutputFile(METADATA_PATH).exists());
BuildResult r = project.fullBuild();
assertFalse(r.isIncremental);
assertTrue(project.getOutputFile(METADATA_PATH).exists());
assertThat(r.metadata,
containsProperty("foo.counter").fromSource(FooProperties.class));
assertThat(r.metadata,
containsProperty("bar.counter").fromSource(BarProperties.class));
r = project.incrementalBuild(BarProperties.class);
assertTrue(r.isIncremental);
assertTrue(r.processedTypes.contains(BarProperties.class.getName()));
assertFalse(r.processedTypes.contains(FooProperties.class.getName()));
assertThat(r.metadata,
containsProperty("foo.counter").fromSource(FooProperties.class));
assertThat(r.metadata,
containsProperty("bar.counter").fromSource(BarProperties.class));
assertTrue(r.processedTypes.contains(BarProperties.class.getName()));
assertFalse(r.processedTypes.contains(FooProperties.class.getName()));
project.addSourceCode(BarProperties.class, " private String extra;\n" + " \n"
+ " public String getExtra() {\n" + " return extra;\n" + " }\n" + "\n"
+ " public void setExtra(String extra) {\n" + " this.extra = extra;\n"
+ " }\n");
r = project.incrementalBuild(BarProperties.class);
assertTrue(r.isIncremental);
assertThat(r.metadata, containsProperty("bar.extra"));
assertThat(r.metadata, containsProperty("foo.counter"));
assertThat(r.metadata, containsProperty("bar.counter"));
project.revert(BarProperties.class);
r = project.incrementalBuild(BarProperties.class);
assertTrue(r.isIncremental);
assertThat(r.metadata, not(containsProperty("bar.extra")));
assertThat(r.metadata, containsProperty("foo.counter"));
assertThat(r.metadata, containsProperty("bar.counter"));
}
@Test
public void incremenalBuildAnnotationRemoved() throws Exception {
TestProject project = new TestProject(this.temporaryFolder, FooProperties.class,
BarProperties.class);
BuildResult r = project.fullBuild();
assertThat(r.metadata, containsProperty("foo.counter"));
assertThat(r.metadata, containsProperty("bar.counter"));
project.replaceText(BarProperties.class, "@ConfigurationProperties",
"//@ConfigurationProperties");
r = project.incrementalBuild(BarProperties.class);
assertThat(r.metadata, containsProperty("foo.counter"));
assertThat(r.metadata, not(containsProperty("bar.counter")));
}
@Test
public void incremenalBuildTypeRenamed() throws Exception {
TestProject project = new TestProject(this.temporaryFolder, FooProperties.class,
BarProperties.class);
BuildResult r = project.fullBuild();
assertThat(r.metadata,
containsProperty("foo.counter").fromSource(FooProperties.class));
assertThat(r.metadata,
containsProperty("bar.counter").fromSource(BarProperties.class));
assertThat(r.metadata,
not(containsProperty("bar.counter")
.fromSource(RenamedBarProperties.class)));
project.delete(BarProperties.class);
project.add(RenamedBarProperties.class);
r = project.incrementalBuild(RenamedBarProperties.class);
assertThat(r.metadata,
containsProperty("foo.counter").fromSource(FooProperties.class));
assertThat(r.metadata,
not(containsProperty("bar.counter").fromSource(BarProperties.class)));
assertThat(r.metadata,
containsProperty("bar.counter").fromSource(RenamedBarProperties.class));
}
@Test
public void notAnnotated() throws Exception {
ConfigurationMetadata metadata = compile(NotAnnotated.class);
@@ -436,7 +350,80 @@ public class ConfigurationMetadataAnnotationProcessorTests {
assertThat(metadata,
containsProperty("foo", String.class)
.fromSource(AdditionalMetadata.class));
}
@Test
public void incrementalBuild() throws Exception {
TestProject project = new TestProject(this.temporaryFolder, FooProperties.class,
BarProperties.class);
assertFalse(project.getOutputFile(METADATA_PATH).exists());
ConfigurationMetadata metadata = project.fullBuild();
assertTrue(project.getOutputFile(METADATA_PATH).exists());
assertThat(metadata,
containsProperty("foo.counter").fromSource(FooProperties.class));
assertThat(metadata,
containsProperty("bar.counter").fromSource(BarProperties.class));
metadata = project.incrementalBuild(BarProperties.class);
assertThat(metadata,
containsProperty("foo.counter").fromSource(FooProperties.class));
assertThat(metadata,
containsProperty("bar.counter").fromSource(BarProperties.class));
project.addSourceCode(BarProperties.class,
BarProperties.class.getResourceAsStream("BarProperties.snippet"));
metadata = project.incrementalBuild(BarProperties.class);
assertThat(metadata, containsProperty("bar.extra"));
assertThat(metadata, containsProperty("foo.counter"));
assertThat(metadata, containsProperty("bar.counter"));
project.revert(BarProperties.class);
metadata = project.incrementalBuild(BarProperties.class);
assertThat(metadata, not(containsProperty("bar.extra")));
assertThat(metadata, containsProperty("foo.counter"));
assertThat(metadata, containsProperty("bar.counter"));
}
@Test
public void incremenalBuildAnnotationRemoved() throws Exception {
TestProject project = new TestProject(this.temporaryFolder, FooProperties.class,
BarProperties.class);
ConfigurationMetadata metadata = project.fullBuild();
assertThat(metadata, containsProperty("foo.counter"));
assertThat(metadata, containsProperty("bar.counter"));
project.replaceText(BarProperties.class, "@ConfigurationProperties",
"//@ConfigurationProperties");
metadata = project.incrementalBuild(BarProperties.class);
assertThat(metadata, containsProperty("foo.counter"));
assertThat(metadata, not(containsProperty("bar.counter")));
}
@Test
public void incremenalBuildTypeRenamed() throws Exception {
TestProject project = new TestProject(this.temporaryFolder, FooProperties.class,
BarProperties.class);
ConfigurationMetadata metadata = project.fullBuild();
assertThat(metadata,
containsProperty("foo.counter").fromSource(FooProperties.class));
assertThat(metadata,
containsProperty("bar.counter").fromSource(BarProperties.class));
assertThat(metadata,
not(containsProperty("bar.counter")
.fromSource(RenamedBarProperties.class)));
project.delete(BarProperties.class);
project.add(RenamedBarProperties.class);
metadata = project.incrementalBuild(RenamedBarProperties.class);
assertThat(metadata,
containsProperty("foo.counter").fromSource(FooProperties.class));
assertThat(metadata,
not(containsProperty("bar.counter").fromSource(BarProperties.class)));
assertThat(metadata,
containsProperty("bar.counter").fromSource(RenamedBarProperties.class));
}
private void assertSimpleLombokProperties(ConfigurationMetadata metadata,

View File

@@ -18,7 +18,6 @@ package org.springframework.boot.configurationprocessor;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Set;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
@@ -61,8 +60,8 @@ public class TestConfigurationMetadataAnnotationProcessor extends
}
@Override
protected ConfigurationMetadata writeMetaData(ConfigurationMetadata metadata) {
super.writeMetaData(metadata);
protected ConfigurationMetadata writeMetaData() {
super.writeMetaData();
try {
File metadataFile = new File(this.outputLocation,
"META-INF/spring-configuration-metadata.json");
@@ -71,7 +70,7 @@ public class TestConfigurationMetadataAnnotationProcessor extends
metadataFile));
}
else {
this.metadata = metadata;
this.metadata = new ConfigurationMetadata();
}
return this.metadata;
}
@@ -84,12 +83,4 @@ public class TestConfigurationMetadataAnnotationProcessor extends
return this.metadata;
}
public Set<String> getProcessedTypes() {
return this.processedSourceTypes;
}
@Override
public boolean isIncremental() {
return super.isIncremental();
}
}

View File

@@ -20,6 +20,8 @@ import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
@@ -30,9 +32,9 @@ 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.configurationsample.incremental.BarProperties;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.FileSystemUtils;
@@ -94,21 +96,21 @@ public class TestProject {
return new File(this.sourceFolder, sourcePathFor(klass));
}
public BuildResult fullBuild() {
public ConfigurationMetadata fullBuild() {
TestConfigurationMetadataAnnotationProcessor processor = new TestConfigurationMetadataAnnotationProcessor(
this.compiler.getOutputLocation());
TestCompilationTask task = this.compiler.getTask(this.sourceFiles);
deleteFolderContents(this.compiler.getOutputLocation());
task.call(processor);
return new BuildResult(processor);
return processor.getMetadata();
}
public BuildResult incrementalBuild(Class<?>... toRecompile) {
public ConfigurationMetadata incrementalBuild(Class<?>... toRecompile) {
TestConfigurationMetadataAnnotationProcessor processor = new TestConfigurationMetadataAnnotationProcessor(
this.compiler.getOutputLocation());
TestCompilationTask task = this.compiler.getTask(toRecompile);
task.call(processor);
return new BuildResult(processor);
return processor.getMetadata();
}
private void deleteFolderContents(File outputFolder) {
@@ -127,11 +129,15 @@ public class TestProject {
/**
* Add source code at the end of file, just before last '}'
*/
public void addSourceCode(Class<BarProperties> target, String text) throws Exception {
public void addSourceCode(Class<?> target, InputStream snippetStream)
throws Exception {
File targetFile = getSourceFile(target);
String contents = getContents(targetFile);
int insertAt = contents.lastIndexOf('}');
contents = contents.substring(0, insertAt) + text + contents.substring(insertAt);
String additionalSource = FileCopyUtils.copyToString(new InputStreamReader(
snippetStream));
contents = contents.substring(0, insertAt) + additionalSource
+ contents.substring(insertAt);
putContents(targetFile, contents);
}

View File

@@ -0,0 +1,9 @@
private String extra;
public String getExtra() {
return extra;
}
public void setExtra(String extra) {
this.extra = extra;
}