Browse Source
This commit udpdates the metadata annotation processor so that change data from an incremental build is merged with the metadata from the previous build. Closes gh-2321pull/2400/merge
10 changed files with 710 additions and 61 deletions
@ -0,0 +1,47 @@
@@ -0,0 +1,47 @@
|
||||
/* |
||||
* 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); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,95 @@
@@ -0,0 +1,95 @@
|
||||
/* |
||||
* 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.io.File; |
||||
import java.io.FileInputStream; |
||||
import java.io.IOException; |
||||
import java.util.Set; |
||||
|
||||
import javax.annotation.processing.SupportedAnnotationTypes; |
||||
import javax.annotation.processing.SupportedSourceVersion; |
||||
import javax.lang.model.SourceVersion; |
||||
|
||||
import org.springframework.boot.configurationprocessor.metadata.ConfigurationMetadata; |
||||
import org.springframework.boot.configurationprocessor.metadata.JsonMarshaller; |
||||
|
||||
/** |
||||
* @author Stephane Nicoll |
||||
* @author Phillip Webb |
||||
* @author Andy Wilkinson |
||||
* @author Kris De Volder |
||||
*/ |
||||
@SupportedAnnotationTypes({ "*" }) |
||||
@SupportedSourceVersion(SourceVersion.RELEASE_6) |
||||
public class TestConfigurationMetadataAnnotationProcessor extends |
||||
ConfigurationMetadataAnnotationProcessor { |
||||
|
||||
static final String CONFIGURATION_PROPERTIES_ANNOTATION = "org.springframework.boot.configurationsample.ConfigurationProperties"; |
||||
|
||||
static final String NESTED_CONFIGURATION_PROPERTY_ANNOTATION = "org.springframework.boot.configurationsample.NestedConfigurationProperty"; |
||||
|
||||
private ConfigurationMetadata metadata; |
||||
|
||||
private final File outputLocation; |
||||
|
||||
public TestConfigurationMetadataAnnotationProcessor(File outputLocation) { |
||||
this.outputLocation = outputLocation; |
||||
} |
||||
|
||||
@Override |
||||
protected String configurationPropertiesAnnotation() { |
||||
return CONFIGURATION_PROPERTIES_ANNOTATION; |
||||
} |
||||
|
||||
@Override |
||||
protected String nestedConfigurationPropertyAnnotation() { |
||||
return NESTED_CONFIGURATION_PROPERTY_ANNOTATION; |
||||
} |
||||
|
||||
@Override |
||||
protected ConfigurationMetadata writeMetaData(ConfigurationMetadata metadata) { |
||||
super.writeMetaData(metadata); |
||||
try { |
||||
File metadataFile = new File(this.outputLocation, |
||||
"META-INF/spring-configuration-metadata.json"); |
||||
if (metadataFile.isFile()) { |
||||
this.metadata = new JsonMarshaller().read(new FileInputStream( |
||||
metadataFile)); |
||||
} |
||||
else { |
||||
this.metadata = metadata; |
||||
} |
||||
return this.metadata; |
||||
} |
||||
catch (IOException e) { |
||||
throw new RuntimeException("Failed to read metadata from disk", e); |
||||
} |
||||
} |
||||
|
||||
public ConfigurationMetadata getMetadata() { |
||||
return this.metadata; |
||||
} |
||||
|
||||
public Set<String> getProcessedTypes() { |
||||
return this.processedSourceTypes; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isIncremental() { |
||||
return super.isIncremental(); |
||||
} |
||||
} |
||||
@ -0,0 +1,187 @@
@@ -0,0 +1,187 @@
|
||||
/* |
||||
* 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.io.File; |
||||
import java.io.FileNotFoundException; |
||||
import java.io.FileReader; |
||||
import java.io.FileWriter; |
||||
import java.io.IOException; |
||||
import java.io.StringReader; |
||||
import java.io.UnsupportedEncodingException; |
||||
import java.util.Arrays; |
||||
import java.util.HashSet; |
||||
import java.util.LinkedHashSet; |
||||
import java.util.Set; |
||||
|
||||
import org.junit.Assert; |
||||
import org.junit.rules.TemporaryFolder; |
||||
import org.springframework.boot.configurationprocessor.TestCompiler.TestCompilationTask; |
||||
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; |
||||
|
||||
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. |
||||
* <p> |
||||
* Why a copy? Because when doing incremental build testing, we need to make modifications |
||||
* to the contents of the 'test project'. But we don't want to actually modify the |
||||
* original content itself. |
||||
* |
||||
* @author Kris De Volder |
||||
*/ |
||||
public class TestProject { |
||||
|
||||
private static final Class<?>[] ALWAYS_INCLUDE = { ConfigurationProperties.class, |
||||
NestedConfigurationProperty.class }; |
||||
|
||||
/** |
||||
* Contains copies of the original source so we can modify it safely to test |
||||
* incremental builds. |
||||
*/ |
||||
private File sourceFolder; |
||||
private TestCompiler compiler; |
||||
|
||||
private Set<File> sourceFiles = new LinkedHashSet<File>(); |
||||
|
||||
public TestProject(TemporaryFolder tempFolder, Class<?>... classes) |
||||
throws IOException { |
||||
this.sourceFolder = tempFolder.newFolder(); |
||||
this.compiler = new TestCompiler(tempFolder) { |
||||
@Override |
||||
protected File getSourceFolder() { |
||||
return TestProject.this.sourceFolder; |
||||
} |
||||
}; |
||||
Set<Class<?>> contents = new HashSet<Class<?>>(Arrays.asList(classes)); |
||||
contents.addAll(Arrays.asList(ALWAYS_INCLUDE)); |
||||
copySources(contents); |
||||
} |
||||
|
||||
private void copySources(Set<Class<?>> contents) throws IOException { |
||||
for (Class<?> klass : contents) { |
||||
copySources(klass); |
||||
} |
||||
} |
||||
|
||||
private void copySources(Class<?> klass) throws IOException { |
||||
File original = getOriginalSourceFile(klass); |
||||
File target = getSourceFile(klass); |
||||
target.getParentFile().mkdirs(); |
||||
FileCopyUtils.copy(original, target); |
||||
this.sourceFiles.add(target); |
||||
} |
||||
|
||||
public File getSourceFile(Class<?> klass) { |
||||
return new File(this.sourceFolder, sourcePathFor(klass)); |
||||
} |
||||
|
||||
public BuildResult 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); |
||||
} |
||||
|
||||
public BuildResult incrementalBuild(Class<?>... toRecompile) { |
||||
TestConfigurationMetadataAnnotationProcessor processor = new TestConfigurationMetadataAnnotationProcessor( |
||||
this.compiler.getOutputLocation()); |
||||
TestCompilationTask task = this.compiler.getTask(toRecompile); |
||||
task.call(processor); |
||||
return new BuildResult(processor); |
||||
} |
||||
|
||||
private void deleteFolderContents(File outputFolder) { |
||||
FileSystemUtils.deleteRecursively(outputFolder); |
||||
outputFolder.mkdirs(); |
||||
} |
||||
|
||||
/** |
||||
* Retrieve File relative to project's output folder. |
||||
*/ |
||||
public File getOutputFile(String relativePath) { |
||||
Assert.assertFalse(new File(relativePath).isAbsolute()); |
||||
return new File(this.compiler.getOutputLocation(), relativePath); |
||||
} |
||||
|
||||
/** |
||||
* Add source code at the end of file, just before last '}' |
||||
*/ |
||||
public void addSourceCode(Class<BarProperties> target, String text) throws Exception { |
||||
File targetFile = getSourceFile(target); |
||||
String contents = getContents(targetFile); |
||||
int insertAt = contents.lastIndexOf('}'); |
||||
contents = contents.substring(0, insertAt) + text + contents.substring(insertAt); |
||||
putContents(targetFile, contents); |
||||
} |
||||
|
||||
/** |
||||
* Delete source file for given class from project. |
||||
*/ |
||||
public void delete(Class<?> klass) { |
||||
File target = getSourceFile(klass); |
||||
target.delete(); |
||||
this.sourceFiles.remove(target); |
||||
} |
||||
|
||||
/** |
||||
* Restore source code of given class to its original contents. |
||||
*/ |
||||
public void revert(Class<?> klass) throws IOException { |
||||
Assert.assertTrue(getSourceFile(klass).exists()); |
||||
copySources(klass); |
||||
} |
||||
|
||||
/** |
||||
* Add source code of given class to this project. |
||||
*/ |
||||
public void add(Class<?> klass) throws IOException { |
||||
Assert.assertFalse(getSourceFile(klass).exists()); |
||||
copySources(klass); |
||||
} |
||||
|
||||
public void replaceText(Class<?> klass, String find, String replace) throws Exception { |
||||
File target = getSourceFile(klass); |
||||
String contents = getContents(target); |
||||
contents = contents.replace(find, replace); |
||||
putContents(target, contents); |
||||
} |
||||
|
||||
/** |
||||
* Find the 'original' source code for given test class. Clients or subclasses should |
||||
* have no need to know about these. They should work only with the copied source |
||||
* code. |
||||
*/ |
||||
private File getOriginalSourceFile(Class<?> klass) { |
||||
return new File(ORIGINAL_SOURCE_FOLDER, sourcePathFor(klass)); |
||||
} |
||||
|
||||
private static void putContents(File targetFile, String contents) |
||||
throws FileNotFoundException, IOException, UnsupportedEncodingException { |
||||
FileCopyUtils.copy(new StringReader(contents), new FileWriter(targetFile)); |
||||
} |
||||
|
||||
private static String getContents(File file) throws Exception { |
||||
return FileCopyUtils.copyToString(new FileReader(file)); |
||||
} |
||||
} |
||||
@ -0,0 +1,56 @@
@@ -0,0 +1,56 @@
|
||||
/* |
||||
* 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.configurationsample.incremental; |
||||
|
||||
import org.springframework.boot.configurationsample.ConfigurationProperties; |
||||
|
||||
@ConfigurationProperties("bar") |
||||
public class BarProperties { |
||||
|
||||
private String name; |
||||
|
||||
private String description; |
||||
|
||||
/** |
||||
* A nice counter description. |
||||
*/ |
||||
private Integer counter = 0; |
||||
|
||||
public String getName() { |
||||
return this.name; |
||||
} |
||||
|
||||
public void setName(String name) { |
||||
this.name = name; |
||||
} |
||||
|
||||
public String getDescription() { |
||||
return this.description; |
||||
} |
||||
|
||||
public void setDescription(String description) { |
||||
this.description = description; |
||||
} |
||||
|
||||
public Integer getCounter() { |
||||
return this.counter; |
||||
} |
||||
|
||||
public void setCounter(Integer counter) { |
||||
this.counter = counter; |
||||
} |
||||
} |
||||
@ -0,0 +1,57 @@
@@ -0,0 +1,57 @@
|
||||
/* |
||||
* 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.configurationsample.incremental; |
||||
|
||||
import org.springframework.boot.configurationsample.ConfigurationProperties; |
||||
|
||||
@ConfigurationProperties("foo") |
||||
public class FooProperties { |
||||
|
||||
private String name; |
||||
|
||||
private String description; |
||||
|
||||
/** |
||||
* A nice counter description. |
||||
*/ |
||||
private Integer counter = 0; |
||||
|
||||
public String getName() { |
||||
return this.name; |
||||
} |
||||
|
||||
public void setName(String name) { |
||||
this.name = name; |
||||
} |
||||
|
||||
public String getDescription() { |
||||
return this.description; |
||||
} |
||||
|
||||
public void setDescription(String description) { |
||||
this.description = description; |
||||
} |
||||
|
||||
public Integer getCounter() { |
||||
return this.counter; |
||||
} |
||||
|
||||
public void setCounter(Integer counter) { |
||||
this.counter = counter; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,56 @@
@@ -0,0 +1,56 @@
|
||||
/* |
||||
* 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.configurationsample.incremental; |
||||
|
||||
import org.springframework.boot.configurationsample.ConfigurationProperties; |
||||
|
||||
@ConfigurationProperties("bar") |
||||
public class RenamedBarProperties { |
||||
|
||||
private String name; |
||||
|
||||
private String description; |
||||
|
||||
/** |
||||
* A nice counter description. |
||||
*/ |
||||
private Integer counter = 0; |
||||
|
||||
public String getName() { |
||||
return this.name; |
||||
} |
||||
|
||||
public void setName(String name) { |
||||
this.name = name; |
||||
} |
||||
|
||||
public String getDescription() { |
||||
return this.description; |
||||
} |
||||
|
||||
public void setDescription(String description) { |
||||
this.description = description; |
||||
} |
||||
|
||||
public Integer getCounter() { |
||||
return this.counter; |
||||
} |
||||
|
||||
public void setCounter(Integer counter) { |
||||
this.counter = counter; |
||||
} |
||||
} |
||||
Loading…
Reference in new issue