Add a defaultValue attribute to config meta-data
Update `ConfigurationMetadataAnnotationProcessor` to include the 'defaultValue' of a property when possible. For example the 'defaultValue' or 'server.port' is '8080'. Default values are detected by inspecting the field assignments of @ConfigurationProperties items. In order to detect field values some internals of the Java compiler are used. To save a dependency on 'tools.jar' internal javac classes are accessed using reflection. See gh-1001
This commit is contained in:
@@ -71,9 +71,10 @@ public class ConfigurationMetadataAnnotationProcessorTests {
|
||||
assertThat(metadata, containsGroup("simple").fromSource(SimpleProperties.class));
|
||||
assertThat(
|
||||
metadata,
|
||||
containsProperty("simple.the-name", String.class).fromSource(
|
||||
SimpleProperties.class).withDescription(
|
||||
"The name of this simple properties."));
|
||||
containsProperty("simple.the-name", String.class)
|
||||
.fromSource(SimpleProperties.class)
|
||||
.withDescription("The name of this simple properties.")
|
||||
.withDefaultValue("boot"));
|
||||
assertThat(
|
||||
metadata,
|
||||
containsProperty("simple.flag", Boolean.class).fromSource(
|
||||
@@ -171,7 +172,8 @@ public class ConfigurationMetadataAnnotationProcessorTests {
|
||||
@Test
|
||||
public void simpleMethodConfig() throws Exception {
|
||||
ConfigurationMetadata metadata = compile(SimpleMethodConfig.class);
|
||||
assertThat(metadata, containsGroup("foo").fromSource(SimpleMethodConfig.class));
|
||||
assertThat(metadata, containsGroup("foo")
|
||||
.fromSource(SimpleMethodConfig.class));
|
||||
assertThat(
|
||||
metadata,
|
||||
containsProperty("foo.name", String.class).fromSource(
|
||||
|
||||
@@ -66,17 +66,20 @@ public class ConfigurationMetadataMatchers {
|
||||
|
||||
private final String description;
|
||||
|
||||
private final Object defaultValue;
|
||||
|
||||
public ContainsItemMatcher(ItemType itemType, String name) {
|
||||
this(itemType, name, null, null, null);
|
||||
this(itemType, name, null, null, null, null);
|
||||
}
|
||||
|
||||
public ContainsItemMatcher(ItemType itemType, String name, String type,
|
||||
Class<?> sourceType, String description) {
|
||||
Class<?> sourceType, String description, Object defaultValue) {
|
||||
this.itemType = itemType;
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.sourceType = sourceType;
|
||||
this.description = description;
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -93,6 +96,10 @@ public class ConfigurationMetadataMatchers {
|
||||
&& !this.sourceType.getName().equals(itemMetadata.getSourceType())) {
|
||||
return false;
|
||||
}
|
||||
if (this.defaultValue != null
|
||||
&& !this.defaultValue.equals(itemMetadata.getDefaultValue())) {
|
||||
return false;
|
||||
}
|
||||
if (this.description != null
|
||||
&& !this.description.equals(itemMetadata.getDescription())) {
|
||||
return false;
|
||||
@@ -121,6 +128,9 @@ public class ConfigurationMetadataMatchers {
|
||||
if (this.sourceType != null) {
|
||||
description.appendText(" sourceType ").appendValue(this.sourceType);
|
||||
}
|
||||
if (this.defaultValue != null) {
|
||||
description.appendText(" defaultValue ").appendValue(this.defaultValue);
|
||||
}
|
||||
if (this.description != null) {
|
||||
description.appendText(" description ").appendValue(this.description);
|
||||
}
|
||||
@@ -128,22 +138,27 @@ public class ConfigurationMetadataMatchers {
|
||||
|
||||
public ContainsItemMatcher ofType(Class<?> dataType) {
|
||||
return new ContainsItemMatcher(this.itemType, this.name, dataType.getName(),
|
||||
this.sourceType, this.description);
|
||||
this.sourceType, this.description, this.defaultValue);
|
||||
}
|
||||
|
||||
public ContainsItemMatcher ofDataType(String dataType) {
|
||||
return new ContainsItemMatcher(this.itemType, this.name, dataType,
|
||||
this.sourceType, this.description);
|
||||
this.sourceType, this.description, this.defaultValue);
|
||||
}
|
||||
|
||||
public ContainsItemMatcher fromSource(Class<?> sourceType) {
|
||||
return new ContainsItemMatcher(this.itemType, this.name, this.type,
|
||||
sourceType, this.description);
|
||||
sourceType, this.description, this.defaultValue);
|
||||
}
|
||||
|
||||
public ContainsItemMatcher withDescription(String description) {
|
||||
return new ContainsItemMatcher(this.itemType, this.name, this.type,
|
||||
this.sourceType, description);
|
||||
this.sourceType, description, this.defaultValue);
|
||||
}
|
||||
|
||||
public Matcher<? super ConfigurationMetadata> withDefaultValue(Object defaultValue) {
|
||||
return new ContainsItemMatcher(this.itemType, this.name, this.type,
|
||||
this.sourceType, this.description, defaultValue);
|
||||
}
|
||||
|
||||
private ItemMetadata getFirstPropertyWithName(ConfigurationMetadata metadata,
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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.fieldvalues;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.processing.AbstractProcessor;
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.annotation.processing.RoundEnvironment;
|
||||
import javax.annotation.processing.SupportedAnnotationTypes;
|
||||
import javax.annotation.processing.SupportedSourceVersion;
|
||||
import javax.lang.model.SourceVersion;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
|
||||
import org.hamcrest.Matcher;
|
||||
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 static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@link FieldValuesParser} tests.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public abstract class AbstractFieldValuesProcessorTests {
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||
|
||||
protected abstract FieldValuesParser createProcessor(ProcessingEnvironment env);
|
||||
|
||||
@Test
|
||||
public void getFieldValues() throws Exception {
|
||||
TestProcessor processor = new TestProcessor();
|
||||
TestCompiler compiler = new TestCompiler(this.temporaryFolder);
|
||||
compiler.getTask(FieldValues.class).call(processor);
|
||||
Map<String, Object> values = processor.getValues();
|
||||
assertThat(values.get("string"), equalToObject("1"));
|
||||
assertThat(values.get("stringNone"), nullValue());
|
||||
assertThat(values.get("stringConst"), equalToObject("c"));
|
||||
assertThat(values.get("bool"), equalToObject(true));
|
||||
assertThat(values.get("boolNone"), equalToObject(false));
|
||||
assertThat(values.get("boolConst"), equalToObject(true));
|
||||
assertThat(values.get("boolObject"), equalToObject(true));
|
||||
assertThat(values.get("boolObjectNone"), nullValue());
|
||||
assertThat(values.get("boolObjectConst"), equalToObject(true));
|
||||
assertThat(values.get("integer"), equalToObject(1));
|
||||
assertThat(values.get("integerNone"), equalToObject(0));
|
||||
assertThat(values.get("integerConst"), equalToObject(2));
|
||||
assertThat(values.get("integerObject"), equalToObject(3));
|
||||
assertThat(values.get("integerObjectNone"), nullValue());
|
||||
assertThat(values.get("integerObjectConst"), equalToObject(4));
|
||||
assertThat(values.get("object"), equalToObject(123));
|
||||
assertThat(values.get("objectNone"), nullValue());
|
||||
assertThat(values.get("objectConst"), equalToObject("c"));
|
||||
assertThat(values.get("objectInstance"), nullValue());
|
||||
}
|
||||
|
||||
private Matcher<Object> equalToObject(Object object) {
|
||||
return equalTo(object);
|
||||
}
|
||||
|
||||
@SupportedAnnotationTypes({ "org.springframework.boot.configurationsample.ConfigurationProperties" })
|
||||
@SupportedSourceVersion(SourceVersion.RELEASE_6)
|
||||
private class TestProcessor extends AbstractProcessor {
|
||||
|
||||
private FieldValuesParser processor;
|
||||
|
||||
private Map<String, Object> values = new HashMap<String, Object>();
|
||||
|
||||
@Override
|
||||
public synchronized void init(ProcessingEnvironment env) {
|
||||
this.processor = createProcessor(env);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(Set<? extends TypeElement> annotations,
|
||||
RoundEnvironment roundEnv) {
|
||||
for (TypeElement annotation : annotations) {
|
||||
for (Element element : roundEnv.getElementsAnnotatedWith(annotation)) {
|
||||
if (element instanceof TypeElement) {
|
||||
try {
|
||||
this.values.putAll(this.processor
|
||||
.getFieldValues((TypeElement) element));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Map<String, Object> getValues() {
|
||||
return this.values;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.fieldvalues.javac;
|
||||
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
|
||||
import org.springframework.boot.configurationprocessor.fieldvalues.AbstractFieldValuesProcessorTests;
|
||||
import org.springframework.boot.configurationprocessor.fieldvalues.FieldValuesParser;
|
||||
|
||||
import static org.junit.Assume.assumeNoException;
|
||||
|
||||
/**
|
||||
* Tests for {@link JavaCompilerFieldValuesParser}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class JavaCompilerFieldValuesProcessorTests extends
|
||||
AbstractFieldValuesProcessorTests {
|
||||
|
||||
@Override
|
||||
protected FieldValuesParser createProcessor(ProcessingEnvironment env) {
|
||||
try {
|
||||
return new JavaCompilerFieldValuesParser(env);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
assumeNoException(ex);
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -38,10 +38,11 @@ public class JsonMarshallerTests {
|
||||
public void marshallAndUnmarshal() throws IOException {
|
||||
ConfigurationMetadata metadata = new ConfigurationMetadata();
|
||||
metadata.add(ItemMetadata.newProperty("a", "b", StringBuffer.class.getName(),
|
||||
InputStream.class.getName(), "sourceMethod", "desc"));
|
||||
metadata.add(ItemMetadata.newProperty("b.c.d", null, null, null, null, null));
|
||||
metadata.add(ItemMetadata.newProperty("c", null, null, null, null, null));
|
||||
metadata.add(ItemMetadata.newProperty("d", null, null, null, null, null));
|
||||
InputStream.class.getName(), "sourceMethod", "desc", "x"));
|
||||
metadata.add(ItemMetadata
|
||||
.newProperty("b.c.d", null, null, null, null, null, null));
|
||||
metadata.add(ItemMetadata.newProperty("c", null, null, null, null, null, 123));
|
||||
metadata.add(ItemMetadata.newProperty("d", null, null, null, null, null, true));
|
||||
metadata.add(ItemMetadata.newGroup("d", null, null, null));
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
JsonMarshaller marshaller = new JsonMarshaller();
|
||||
@@ -51,10 +52,10 @@ public class JsonMarshallerTests {
|
||||
outputStream.toByteArray()));
|
||||
assertThat(read,
|
||||
containsProperty("a.b", StringBuffer.class).fromSource(InputStream.class)
|
||||
.withDescription("desc"));
|
||||
.withDescription("desc").withDefaultValue("x"));
|
||||
assertThat(read, containsProperty("b.c.d"));
|
||||
assertThat(read, containsProperty("c"));
|
||||
assertThat(read, containsProperty("d"));
|
||||
assertThat(read, containsProperty("c").withDefaultValue(123));
|
||||
assertThat(read, containsProperty("d").withDefaultValue(true));
|
||||
assertThat(read, containsGroup("d"));
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.configurationsample.fieldvalues;
|
||||
|
||||
import org.springframework.boot.configurationsample.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* Sample object containing fields with initial values.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@ConfigurationProperties
|
||||
public class FieldValues {
|
||||
|
||||
private static final String STRING_CONST = "c";
|
||||
|
||||
private static final boolean BOOLEAN_CONST = true;
|
||||
|
||||
private static final Boolean BOOLEAN_OBJ_CONST = true;
|
||||
|
||||
private static final int INTEGER_CONST = 2;
|
||||
|
||||
private static final Integer INTEGER_OBJ_CONST = 4;
|
||||
|
||||
private String string = "1";
|
||||
|
||||
private String stringNone;
|
||||
|
||||
private String stringConst = STRING_CONST;
|
||||
|
||||
private boolean bool = true;
|
||||
|
||||
private boolean boolNone;
|
||||
|
||||
private boolean boolConst = BOOLEAN_CONST;
|
||||
|
||||
private Boolean boolObject = Boolean.TRUE;
|
||||
|
||||
private Boolean boolObjectNone;
|
||||
|
||||
private Boolean boolObjectConst = BOOLEAN_OBJ_CONST;
|
||||
|
||||
private int integer = 1;
|
||||
|
||||
private int integerNone;
|
||||
|
||||
private int integerConst = INTEGER_CONST;
|
||||
|
||||
private Integer integerObject = 3;
|
||||
|
||||
private Integer integerObjectNone;
|
||||
|
||||
private Integer integerObjectConst = INTEGER_OBJ_CONST;
|
||||
|
||||
private Object object = 123;
|
||||
|
||||
private Object objectNone;
|
||||
|
||||
private Object objectConst = STRING_CONST;
|
||||
|
||||
private Object objectInstance = new StringBuffer();
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user