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-2313pull/2400/merge
parent
8df43a8a79
commit
23c175f674
@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2014 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.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.annotation.processing.ProcessingEnvironment;
|
||||||
|
import javax.annotation.processing.RoundEnvironment;
|
||||||
|
import javax.lang.model.element.Element;
|
||||||
|
import javax.lang.model.element.TypeElement;
|
||||||
|
|
||||||
|
import org.springframework.boot.configurationprocessor.metadata.ConfigurationMetadata;
|
||||||
|
import org.springframework.boot.configurationprocessor.metadata.ItemMetadata;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code BuildHandler} that provides incremental build support by merging the metadata
|
||||||
|
* from the current incremental build with any existing metadata.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
* @author Kris De Volder
|
||||||
|
* @since 1.2.2
|
||||||
|
*/
|
||||||
|
public class IncrementalBuildHandler extends StandardBuildHandler {
|
||||||
|
|
||||||
|
private final Set<String> processedSourceTypes = new HashSet<String>();
|
||||||
|
|
||||||
|
private final ProcessingEnvironment processingEnvironment;
|
||||||
|
|
||||||
|
private final ConfigurationMetadata existingMetadata;
|
||||||
|
|
||||||
|
private final TypeUtils typeUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@code IncrementalBuildTracker} that will merge the metadata produced
|
||||||
|
* by an incremental build with the given {@code existingMetadata}.
|
||||||
|
*
|
||||||
|
* @param processingEnvironment The processing environment of the build
|
||||||
|
* @param existingMetadata The existing metadata
|
||||||
|
*/
|
||||||
|
public IncrementalBuildHandler(ProcessingEnvironment processingEnvironment,
|
||||||
|
ConfigurationMetadata existingMetadata) {
|
||||||
|
this.existingMetadata = existingMetadata;
|
||||||
|
this.processingEnvironment = processingEnvironment;
|
||||||
|
this.typeUtils = new TypeUtils(processingEnvironment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void processing(RoundEnvironment environment) {
|
||||||
|
for (Element element : environment.getRootElements()) {
|
||||||
|
markAsProcessed(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigurationMetadata produceMetadata() {
|
||||||
|
ConfigurationMetadata metadata = super.produceMetadata();
|
||||||
|
mergeExistingMetadata(metadata);
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void markAsProcessed(Element element) {
|
||||||
|
if (element instanceof TypeElement) {
|
||||||
|
this.processedSourceTypes.add(this.typeUtils.getType(element));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mergeExistingMetadata(ConfigurationMetadata metadata) {
|
||||||
|
List<ItemMetadata> items = this.existingMetadata.getItems();
|
||||||
|
for (ItemMetadata oldItem : items) {
|
||||||
|
if (shouldBeMerged(oldItem)) {
|
||||||
|
metadata.add(oldItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldBeMerged(ItemMetadata itemMetadata) {
|
||||||
|
String sourceType = itemMetadata.getSourceType();
|
||||||
|
if (sourceType == null || deletedInCurrentBuild(sourceType)
|
||||||
|
|| processedInCurrentBuild(sourceType)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean deletedInCurrentBuild(String sourceType) {
|
||||||
|
return this.processingEnvironment.getElementUtils().getTypeElement(sourceType) == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean processedInCurrentBuild(String sourceType) {
|
||||||
|
return this.processedSourceTypes.contains(sourceType);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* 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.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import javax.annotation.processing.ProcessingEnvironment;
|
||||||
|
import javax.tools.FileObject;
|
||||||
|
import javax.tools.StandardLocation;
|
||||||
|
|
||||||
|
import org.springframework.boot.configurationprocessor.metadata.ConfigurationMetadata;
|
||||||
|
import org.springframework.boot.configurationprocessor.metadata.JsonMarshaller;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@code MetadataStore} is responsible for the storage of metadata on the filesystem
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
* @since 1.2.2
|
||||||
|
*/
|
||||||
|
public class MetadataStore {
|
||||||
|
|
||||||
|
static final String METADATA_PATH = "META-INF/spring-configuration-metadata.json";
|
||||||
|
|
||||||
|
private static final String ADDITIONAL_METADATA_PATH = "META-INF/additional-spring-configuration-metadata.json";
|
||||||
|
|
||||||
|
private static final String RESOURCES_FOLDER = "resources";
|
||||||
|
|
||||||
|
private static final String CLASSES_FOLDER = "classes";
|
||||||
|
|
||||||
|
private final ProcessingEnvironment environment;
|
||||||
|
|
||||||
|
public MetadataStore(ProcessingEnvironment environment) {
|
||||||
|
this.environment = environment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigurationMetadata readMetadata() {
|
||||||
|
try {
|
||||||
|
return readMetadata(getMetadataResource().openInputStream());
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeMetadata(ConfigurationMetadata metadata) throws IOException {
|
||||||
|
if (!metadata.getItems().isEmpty()) {
|
||||||
|
OutputStream outputStream = createMetadataResource().openOutputStream();
|
||||||
|
try {
|
||||||
|
new JsonMarshaller().write(metadata, outputStream);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
outputStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigurationMetadata readAdditionalMetadata() throws IOException {
|
||||||
|
return readMetadata(getAdditionalMetadataStream());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConfigurationMetadata readMetadata(InputStream in) throws IOException {
|
||||||
|
try {
|
||||||
|
return new JsonMarshaller().read(in);
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
in.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private FileObject getMetadataResource() throws IOException {
|
||||||
|
FileObject resource = this.environment.getFiler().getResource(
|
||||||
|
StandardLocation.CLASS_OUTPUT, "", METADATA_PATH);
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
private FileObject createMetadataResource() throws IOException {
|
||||||
|
FileObject resource = this.environment.getFiler().createResource(
|
||||||
|
StandardLocation.CLASS_OUTPUT, "", METADATA_PATH);
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
private InputStream getAdditionalMetadataStream() throws IOException {
|
||||||
|
// Most build systems will have copied the file to the class output location
|
||||||
|
FileObject fileObject = this.environment.getFiler().getResource(
|
||||||
|
StandardLocation.CLASS_OUTPUT, "", ADDITIONAL_METADATA_PATH);
|
||||||
|
File file = new File(fileObject.toUri());
|
||||||
|
if (!file.exists()) {
|
||||||
|
// Gradle keeps things separate
|
||||||
|
String path = file.getPath();
|
||||||
|
int index = path.lastIndexOf(CLASSES_FOLDER);
|
||||||
|
if (index >= 0) {
|
||||||
|
path = path.substring(0, index) + RESOURCES_FOLDER
|
||||||
|
+ path.substring(index + CLASSES_FOLDER.length());
|
||||||
|
file = new File(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (file.exists() ? new FileInputStream(file) : fileObject.toUri().toURL()
|
||||||
|
.openStream());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2014 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 javax.annotation.processing.RoundEnvironment;
|
||||||
|
|
||||||
|
import org.springframework.boot.configurationprocessor.metadata.ConfigurationMetadata;
|
||||||
|
import org.springframework.boot.configurationprocessor.metadata.ItemMetadata;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard implementation of {@code BuildHandler} that handles the state of a single
|
||||||
|
* build.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
* @since 1.2.2
|
||||||
|
*/
|
||||||
|
public class StandardBuildHandler implements BuildHandler {
|
||||||
|
|
||||||
|
private final ConfigurationMetadata metadata = new ConfigurationMetadata();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addGroup(String name, String type, String sourceType, String sourceMethod) {
|
||||||
|
this.metadata.add(ItemMetadata.newGroup(name, type, sourceType, sourceMethod));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addProperty(String prefix, String name, String type, String sourceType,
|
||||||
|
String sourceMethod, String description, Object defaultValue,
|
||||||
|
boolean deprecated) {
|
||||||
|
this.metadata.add(ItemMetadata.newProperty(prefix, name, type, sourceType,
|
||||||
|
sourceMethod, description, defaultValue, deprecated));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void processing(RoundEnvironment environment) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigurationMetadata produceMetadata() {
|
||||||
|
return this.metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
private String extra;
|
||||||
|
|
||||||
|
public String getExtra() {
|
||||||
|
return extra;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExtra(String extra) {
|
||||||
|
this.extra = extra;
|
||||||
|
}
|
Loading…
Reference in New Issue