Add processor to generate configuration meta-data

Adds an annotation processor to generates a JSON meta-data file at
compile time from @ConfigurationProperties items. Each meta-data file
can include an array or 'properties' and 'groups'.

A 'property' is a single item that may appear in a Spring Boot
'application.properties' file with a given value. For example,
'server.port' and 'server.context-path' are properties. Each property
may optionally include 'type' and 'description' attributes to provide
the data type (e.g. `java.lang.Integer`, `java.lang.String`) and
some short documentation (taken from the field javadoc) about what the
property is for. For consistency, the type of a primitive is translated
to its wrapper counterpart, i.e. `boolean` becomes `java.lang.Boolean`.

A 'group' provides a higher level grouping of properties. For example
the 'server.port' and 'server.context-path' properties are in the
'server' group.

Both 'property' and 'group' items may additional have 'sourceType' and
'sourceMethod' attributes to indicate the source that contributed them.

Users may use `META-INF/additional-spring-configuration-metadata.json`
to manually provide additionally meta-data that is not covered by
@ConfigurationProperties objects. The contents of this file will be
read and merged with harvested items. The complete meta-data file is
finally written to `META-INF/spring-configuration-metadata.json`.

See gh-1001
This commit is contained in:
Stephane Nicoll
2014-10-31 10:18:28 -07:00
committed by Phillip Webb
parent 45b579c439
commit 884c058e57
32 changed files with 2552 additions and 1 deletions

View File

@@ -0,0 +1,286 @@
/*
* 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.io.IOException;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.springframework.boot.configurationprocessor.metadata.ConfigurationMetadata;
import org.springframework.boot.configurationsample.method.EmptyTypeMethodConfig;
import org.springframework.boot.configurationsample.method.InvalidMethodConfig;
import org.springframework.boot.configurationsample.method.MethodAndClassConfig;
import org.springframework.boot.configurationsample.method.SimpleMethodConfig;
import org.springframework.boot.configurationsample.simple.HierarchicalProperties;
import org.springframework.boot.configurationsample.simple.NotAnnotated;
import org.springframework.boot.configurationsample.simple.SimpleCollectionProperties;
import org.springframework.boot.configurationsample.simple.SimplePrefixValueProperties;
import org.springframework.boot.configurationsample.simple.SimpleProperties;
import org.springframework.boot.configurationsample.simple.SimpleTypeProperties;
import org.springframework.boot.configurationsample.specific.InnerClassAnnotatedGetterConfig;
import org.springframework.boot.configurationsample.specific.InnerClassProperties;
import org.springframework.boot.configurationsample.specific.InnerClassRootConfig;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat;
import static org.springframework.boot.configurationprocessor.ConfigurationMetadataMatchers.containsGroup;
import static org.springframework.boot.configurationprocessor.ConfigurationMetadataMatchers.containsProperty;
/**
* Tests for {@link ConfigurationMetadataAnnotationProcessor}.
*
* @author Stephane Nicoll
* @author Phillip Webb
*/
public class ConfigurationMetadataAnnotationProcessorTests {
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
@Test
public void notAnnotated() throws Exception {
ConfigurationMetadata metadata = compile(NotAnnotated.class);
assertThat("No config metadata file should have been generated when "
+ "no metadata is discovered", metadata, nullValue());
}
@Test
public void simpleProperties() throws Exception {
ConfigurationMetadata metadata = compile(SimpleProperties.class);
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."));
assertThat(
metadata,
containsProperty("simple.flag", Boolean.class).fromSource(
SimpleProperties.class).withDescription("A simple flag."));
assertThat(metadata, containsProperty("simple.comparator"));
assertThat(metadata, not(containsProperty("simple.counter")));
assertThat(metadata, not(containsProperty("simple.size")));
}
@Test
public void simplePrefixValueProperties() throws Exception {
ConfigurationMetadata metadata = compile(SimplePrefixValueProperties.class);
assertThat(metadata,
containsGroup("simple").fromSource(SimplePrefixValueProperties.class));
assertThat(
metadata,
containsProperty("simple.name", String.class).fromSource(
SimplePrefixValueProperties.class));
}
@Test
public void simpleTypeProperties() throws Exception {
ConfigurationMetadata metadata = compile(SimpleTypeProperties.class);
assertThat(metadata,
containsGroup("simple.type").fromSource(SimpleTypeProperties.class));
assertThat(metadata, containsProperty("simple.type.my-string", String.class));
assertThat(metadata, containsProperty("simple.type.my-byte", Byte.class));
assertThat(metadata,
containsProperty("simple.type.my-primitive-byte", Byte.class));
assertThat(metadata, containsProperty("simple.type.my-char", Character.class));
assertThat(metadata,
containsProperty("simple.type.my-primitive-char", Character.class));
assertThat(metadata, containsProperty("simple.type.my-boolean", Boolean.class));
assertThat(metadata,
containsProperty("simple.type.my-primitive-boolean", Boolean.class));
assertThat(metadata, containsProperty("simple.type.my-short", Short.class));
assertThat(metadata,
containsProperty("simple.type.my-primitive-short", Short.class));
assertThat(metadata, containsProperty("simple.type.my-integer", Integer.class));
assertThat(metadata,
containsProperty("simple.type.my-primitive-integer", Integer.class));
assertThat(metadata, containsProperty("simple.type.my-long", Long.class));
assertThat(metadata,
containsProperty("simple.type.my-primitive-long", Long.class));
assertThat(metadata, containsProperty("simple.type.my-double", Double.class));
assertThat(metadata,
containsProperty("simple.type.my-primitive-double", Double.class));
assertThat(metadata, containsProperty("simple.type.my-float", Float.class));
assertThat(metadata,
containsProperty("simple.type.my-primitive-float", Float.class));
assertThat(metadata.getItems().size(), equalTo(18));
}
@Test
public void hierarchicalProperties() throws Exception {
ConfigurationMetadata metadata = compile(HierarchicalProperties.class);
assertThat(metadata,
containsGroup("hierarchical").fromSource(HierarchicalProperties.class));
assertThat(metadata, containsProperty("hierarchical.first", String.class)
.fromSource(HierarchicalProperties.class));
assertThat(metadata, containsProperty("hierarchical.second", String.class)
.fromSource(HierarchicalProperties.class));
assertThat(metadata, containsProperty("hierarchical.third", String.class)
.fromSource(HierarchicalProperties.class));
}
@Test
public void parseCollectionConfig() throws Exception {
ConfigurationMetadata metadata = compile(SimpleCollectionProperties.class);
// getter and setter
assertThat(
metadata,
containsProperty("collection.integers-to-names",
"java.util.Map<java.lang.Integer,java.lang.String>"));
assertThat(
metadata,
containsProperty("collection.longs",
"java.util.Collection<java.lang.Long>"));
assertThat(metadata,
containsProperty("collection.floats", "java.util.List<java.lang.Float>"));
// getter only
assertThat(
metadata,
containsProperty("collection.names-to-integers",
"java.util.Map<java.lang.String,java.lang.Integer>"));
assertThat(
metadata,
containsProperty("collection.bytes",
"java.util.Collection<java.lang.Byte>"));
assertThat(
metadata,
containsProperty("collection.doubles", "java.util.List<java.lang.Double>"));
}
@Test
public void simpleMethodConfig() throws Exception {
ConfigurationMetadata metadata = compile(SimpleMethodConfig.class);
assertThat(metadata, containsGroup("foo").fromSource(SimpleMethodConfig.class));
assertThat(
metadata,
containsProperty("foo.name", String.class).fromSource(
SimpleMethodConfig.Foo.class));
assertThat(
metadata,
containsProperty("foo.flag", Boolean.class).fromSource(
SimpleMethodConfig.Foo.class));
}
@Test
public void invalidMethodConfig() throws Exception {
ConfigurationMetadata metadata = compile(InvalidMethodConfig.class);
assertThat(
metadata,
containsProperty("something.name", String.class).fromSource(
InvalidMethodConfig.class));
assertThat(metadata, not(containsProperty("invalid.name")));
}
@Test
public void methodAndClassConfig() throws Exception {
ConfigurationMetadata metadata = compile(MethodAndClassConfig.class);
assertThat(
metadata,
containsProperty("conflict.name", String.class).fromSource(
MethodAndClassConfig.Foo.class));
assertThat(
metadata,
containsProperty("conflict.flag", Boolean.class).fromSource(
MethodAndClassConfig.Foo.class));
assertThat(
metadata,
containsProperty("conflict.value", String.class).fromSource(
MethodAndClassConfig.class));
}
@Test
public void emptyTypeMethodConfig() throws Exception {
ConfigurationMetadata metadata = compile(EmptyTypeMethodConfig.class);
assertThat(metadata, not(containsProperty("something.foo")));
}
@Test
public void innerClassRootConfig() throws Exception {
ConfigurationMetadata metadata = compile(InnerClassRootConfig.class);
assertThat(metadata, containsProperty("config.name"));
}
@Test
public void innerClassProperties() throws Exception {
ConfigurationMetadata metadata = compile(InnerClassProperties.class);
assertThat(metadata,
containsGroup("config").fromSource(InnerClassProperties.class));
assertThat(metadata,
containsGroup("config.first").ofType(InnerClassProperties.Foo.class)
.fromSource(InnerClassProperties.class));
assertThat(metadata, containsProperty("config.first.name"));
assertThat(metadata, containsProperty("config.first.bar.name"));
assertThat(metadata,
containsProperty("config.the-second", InnerClassProperties.Foo.class)
.fromSource(InnerClassProperties.class));
assertThat(metadata, containsProperty("config.the-second.name"));
assertThat(metadata, containsProperty("config.the-second.bar.name"));
assertThat(
metadata,
containsGroup("config.third").ofType(
InnerClassProperties.SimplePojo.class).fromSource(
InnerClassProperties.class));
assertThat(metadata, containsProperty("config.third.value"));
}
@Test
public void innerClassAnnotatedGetterConfig() throws Exception {
ConfigurationMetadata metadata = compile(InnerClassAnnotatedGetterConfig.class);
assertThat(metadata, containsProperty("specific.value"));
assertThat(metadata, containsProperty("foo.name"));
assertThat(metadata, not(containsProperty("specific.foo")));
}
private ConfigurationMetadata compile(Class<?>... types) throws IOException {
TestConfigurationMetadataAnnotationProcessor processor = new TestConfigurationMetadataAnnotationProcessor();
new TestCompiler(this.temporaryFolder).getTask(types).call(processor);
return processor.getMetadata();
}
@SupportedAnnotationTypes({ TestConfigurationMetadataAnnotationProcessor.CONFIGURATION_PROPERTIES_ANNOTATION })
@SupportedSourceVersion(SourceVersion.RELEASE_6)
private static class TestConfigurationMetadataAnnotationProcessor extends
ConfigurationMetadataAnnotationProcessor {
static final String CONFIGURATION_PROPERTIES_ANNOTATION = "org.springframework.boot.configurationsample.ConfigurationProperties";
private ConfigurationMetadata metadata;
@Override
protected String configurationPropertiesAnnotation() {
return CONFIGURATION_PROPERTIES_ANNOTATION;
}
@Override
protected void writeMetaData(ConfigurationMetadata metadata) {
this.metadata = metadata;
}
public ConfigurationMetadata getMetadata() {
return this.metadata;
}
}
}

View File

@@ -0,0 +1,161 @@
/*
* 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 org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.springframework.boot.configurationprocessor.metadata.ConfigurationMetadata;
import org.springframework.boot.configurationprocessor.metadata.ItemMetadata;
import org.springframework.boot.configurationprocessor.metadata.ItemMetadata.ItemType;
/**
* Hamcrest {@link Matcher} to help test {@link ConfigurationMetadata}.
*
* @author Phillip Webb
*/
public class ConfigurationMetadataMatchers {
public static ContainsItemMatcher containsGroup(String name) {
return new ContainsItemMatcher(ItemType.GROUP, name);
}
public static ContainsItemMatcher containsGroup(String name, Class<?> type) {
return new ContainsItemMatcher(ItemType.GROUP, name).ofType(type);
}
public static ContainsItemMatcher containsGroup(String name, String type) {
return new ContainsItemMatcher(ItemType.GROUP, name).ofDataType(type);
}
public static ContainsItemMatcher containsProperty(String name) {
return new ContainsItemMatcher(ItemType.PROPERTY, name);
}
public static ContainsItemMatcher containsProperty(String name, Class<?> type) {
return new ContainsItemMatcher(ItemType.PROPERTY, name).ofType(type);
}
public static ContainsItemMatcher containsProperty(String name, String type) {
return new ContainsItemMatcher(ItemType.PROPERTY, name).ofDataType(type);
}
public static class ContainsItemMatcher extends BaseMatcher<ConfigurationMetadata> {
private final ItemType itemType;
private final String name;
private final String type;
private final Class<?> sourceType;
private final String description;
public ContainsItemMatcher(ItemType itemType, String name) {
this(itemType, name, null, null, null);
}
public ContainsItemMatcher(ItemType itemType, String name, String type,
Class<?> sourceType, String description) {
this.itemType = itemType;
this.name = name;
this.type = type;
this.sourceType = sourceType;
this.description = description;
}
@Override
public boolean matches(Object item) {
ConfigurationMetadata metadata = (ConfigurationMetadata) item;
ItemMetadata itemMetadata = getFirstPropertyWithName(metadata, this.name);
if (itemMetadata == null) {
return false;
}
if (this.type != null && !this.type.equals(itemMetadata.getType())) {
return false;
}
if (this.sourceType != null
&& !this.sourceType.getName().equals(itemMetadata.getSourceType())) {
return false;
}
if (this.description != null
&& !this.description.equals(itemMetadata.getDescription())) {
return false;
}
return true;
}
@Override
public void describeMismatch(Object item, Description description) {
ConfigurationMetadata metadata = (ConfigurationMetadata) item;
ItemMetadata property = getFirstPropertyWithName(metadata, this.name);
if (property == null) {
description.appendText("missing property " + this.name);
}
else {
description.appendText("was property ").appendValue(property);
}
}
@Override
public void describeTo(Description description) {
description.appendText("metadata containing " + this.name);
if (this.type != null) {
description.appendText(" dataType ").appendValue(this.type);
}
if (this.sourceType != null) {
description.appendText(" sourceType ").appendValue(this.sourceType);
}
if (this.description != null) {
description.appendText(" description ").appendValue(this.description);
}
}
public ContainsItemMatcher ofType(Class<?> dataType) {
return new ContainsItemMatcher(this.itemType, this.name, dataType.getName(),
this.sourceType, this.description);
}
public ContainsItemMatcher ofDataType(String dataType) {
return new ContainsItemMatcher(this.itemType, this.name, dataType,
this.sourceType, this.description);
}
public ContainsItemMatcher fromSource(Class<?> sourceType) {
return new ContainsItemMatcher(this.itemType, this.name, this.type,
sourceType, this.description);
}
public ContainsItemMatcher withDescription(String description) {
return new ContainsItemMatcher(this.itemType, this.name, this.type,
this.sourceType, description);
}
private ItemMetadata getFirstPropertyWithName(ConfigurationMetadata metadata,
String name) {
for (ItemMetadata item : metadata.getItems()) {
if (item.isOfItemType(this.itemType) && name.equals(item.getName())) {
return item;
}
}
return null;
}
}
}

View File

@@ -0,0 +1,96 @@
/*
* 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.io.File;
import java.io.IOException;
import java.util.Arrays;
import javax.annotation.processing.Processor;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
import org.junit.rules.TemporaryFolder;
/**
* Wrapper to make the {@link JavaCompiler} easier to use in tests.
*
* @author Stephane Nicoll
* @author Phillip Webb
*/
public class TestCompiler {
private final JavaCompiler compiler;
private final StandardJavaFileManager fileManager;
public TestCompiler(TemporaryFolder temporaryFolder) throws IOException {
this(ToolProvider.getSystemJavaCompiler(), temporaryFolder);
}
public TestCompiler(JavaCompiler compiler, TemporaryFolder temporaryFolder)
throws IOException {
this.compiler = compiler;
this.fileManager = compiler.getStandardFileManager(null, null, null);
Iterable<? extends File> temp = Arrays.asList(temporaryFolder.newFolder());
this.fileManager.setLocation(StandardLocation.CLASS_OUTPUT, temp);
this.fileManager.setLocation(StandardLocation.SOURCE_OUTPUT, temp);
}
public TestCompilationTask getTask(Class<?>... types) {
Iterable<? extends JavaFileObject> javaFileObjects = getJavaFileObjects(types);
return new TestCompilationTask(this.compiler.getTask(null, this.fileManager,
null, null, null, javaFileObjects));
}
private Iterable<? extends JavaFileObject> getJavaFileObjects(Class<?>... types) {
File[] files = new File[types.length];
for (int i = 0; i < types.length; i++) {
files[i] = getFile(types[i]);
}
return this.fileManager.getJavaFileObjects(files);
}
private File getFile(Class<?> type) {
return new File("src/test/java/" + type.getName().replace(".", "/") + ".java");
}
/**
* A compilation task.
*/
public static class TestCompilationTask {
private final CompilationTask task;
public TestCompilationTask(CompilationTask task) {
this.task = task;
}
public void call(Processor... processors) {
this.task.setProcessors(Arrays.asList(processors));
if (!this.task.call()) {
throw new IllegalStateException("Compilation failed");
}
}
}
}

View File

@@ -0,0 +1,61 @@
/*
* 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.metadata;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import org.junit.Test;
import static org.junit.Assert.assertThat;
import static org.springframework.boot.configurationprocessor.ConfigurationMetadataMatchers.containsGroup;
import static org.springframework.boot.configurationprocessor.ConfigurationMetadataMatchers.containsProperty;
/**
* Tests for {@link JsonMarshaller}.
*
* @author Phillip Webb
*/
public class JsonMarshallerTests {
@Test
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));
metadata.add(ItemMetadata.newGroup("d", null, null, null));
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
JsonMarshaller marshaller = new JsonMarshaller();
marshaller.write(metadata, outputStream);
System.out.println(outputStream);
ConfigurationMetadata read = marshaller.read(new ByteArrayInputStream(
outputStream.toByteArray()));
assertThat(read,
containsProperty("a.b", StringBuffer.class).fromSource(InputStream.class)
.withDescription("desc"));
assertThat(read, containsProperty("b.c.d"));
assertThat(read, containsProperty("c"));
assertThat(read, containsProperty("d"));
assertThat(read, containsGroup("d"));
}
}

View File

@@ -0,0 +1,41 @@
/*
* 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;
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 Boot's {@code @ConfigurationProperties} for testing (removes the
* need for a dependency on the real annotation).
*
* @author Stephane Nicoll
* @author Phillip Webb
*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ConfigurationProperties {
String value() default "";
String prefix() default "";
}

View File

@@ -0,0 +1,48 @@
/*
* 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.method;
import org.springframework.boot.configurationsample.ConfigurationProperties;
/**
* Sample for testing method configuration.
*
* @author Stephane Nicoll
*/
@ConfigurationProperties("something")
public class EmptyTypeMethodConfig {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
@ConfigurationProperties("something")
public Foo foo() {
return new Foo();
}
public static class Foo {
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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.method;
import org.springframework.boot.configurationsample.ConfigurationProperties;
/**
* Sample for testing invalid method configuration.
*
* @author Stephane Nicoll
*/
@ConfigurationProperties(prefix = "something")
public class InvalidMethodConfig {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
@ConfigurationProperties(prefix = "invalid")
InvalidMethodConfig foo() {
return new InvalidMethodConfig();
}
}

View File

@@ -0,0 +1,67 @@
/*
* 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.method;
import org.springframework.boot.configurationsample.ConfigurationProperties;
/**
* Sample for testing mixed method and class configuration.
*
* @author Stephane Nicoll
*/
@ConfigurationProperties("conflict")
public class MethodAndClassConfig {
private String value;
public String getValue() {
return this.value;
}
public void setValue(String value) {
this.value = value;
}
@ConfigurationProperties(prefix = "conflict")
public Foo foo() {
return new Foo();
}
public static class Foo {
private String name;
private boolean flag;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public boolean isFlag() {
return this.flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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.method;
import org.springframework.boot.configurationsample.ConfigurationProperties;
/**
* Sample for testing simple method configuration.
*
* @author Stephane Nicoll
*/
public class SimpleMethodConfig {
@ConfigurationProperties(prefix = "foo")
public Foo foo() {
return new Foo();
}
public static class Foo {
private String name;
private boolean flag;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public boolean isFlag() {
return this.flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
}

View File

@@ -0,0 +1,39 @@
/*
* 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.simple;
import org.springframework.boot.configurationsample.ConfigurationProperties;
/**
* Configuration properties with inherited values.
*
* @author Stephane Nicoll
*/
@ConfigurationProperties(prefix = "hierarchical")
public class HierarchicalProperties extends HierarchicalPropertiesParent {
private String third;
public String getThird() {
return this.third;
}
public void setThird(String third) {
this.third = third;
}
}

View File

@@ -0,0 +1,36 @@
/*
* 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.simple;
/**
* Grandparent for {@link HierarchicalProperties}.
*
* @author Stephane Nicoll
*/
public abstract class HierarchicalPropertiesGrandparent {
private String first;
public String getFirst() {
return this.first;
}
public void setFirst(String first) {
this.first = first;
}
}

View File

@@ -0,0 +1,49 @@
/*
* 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.simple;
/**
* Parent for {@link HierarchicalProperties}.
*
* @author Stephane Nicoll
*/
public abstract class HierarchicalPropertiesParent extends
HierarchicalPropertiesGrandparent {
private String second;
public String getSecond() {
return this.second;
}
public void setSecond(String second) {
this.second = second;
}
// Useless override
@Override
public String getFirst() {
return super.getFirst();
}
@Override
public void setFirst(String first) {
super.setFirst(first);
}
}

View File

@@ -0,0 +1,27 @@
/*
* 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.simple;
/**
* This has no annotation on purpose to check that no meta-data is generated.
*
* @author Stephane Nicoll
*/
public class NotAnnotated {
}

View File

@@ -0,0 +1,84 @@
/*
* 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.simple;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import org.springframework.boot.configurationsample.ConfigurationProperties;
/**
* Properties with collections.
*
* @author Stephane Nicoll
*/
@ConfigurationProperties(prefix = "collection")
public class SimpleCollectionProperties {
private Map<Integer, String> integersToNames;
private Collection<Long> longs;
private List<Float> floats;
private final Map<String, Integer> namesToIntegers = new HashMap<String, Integer>();
private final Collection<Byte> bytes = new LinkedHashSet<Byte>();
private final List<Double> doubles = new ArrayList<Double>();
public Map<Integer, String> getIntegersToNames() {
return this.integersToNames;
}
public void setIntegersToNames(Map<Integer, String> integersToNames) {
this.integersToNames = integersToNames;
}
public Collection<Long> getLongs() {
return this.longs;
}
public void setLongs(Collection<Long> longs) {
this.longs = longs;
}
public List<Float> getFloats() {
return this.floats;
}
public void setFloats(List<Float> floats) {
this.floats = floats;
}
public Map<String, Integer> getNamesToIntegers() {
return this.namesToIntegers;
}
public Collection<Byte> getBytes() {
return this.bytes;
}
public List<Double> getDoubles() {
return this.doubles;
}
}

View File

@@ -0,0 +1,39 @@
/*
* 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.simple;
import org.springframework.boot.configurationsample.ConfigurationProperties;
/**
* Properties with a simple prefix.
*
* @author Stephane Nicoll
*/
@ConfigurationProperties("simple")
public class SimplePrefixValueProperties {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@@ -0,0 +1,93 @@
/*
* 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.simple;
import java.beans.FeatureDescriptor;
import java.util.Comparator;
import org.springframework.boot.configurationsample.ConfigurationProperties;
/**
* Simple properties.
*
* @author Stephane Nicoll
*/
@ConfigurationProperties(prefix = "simple")
public class SimpleProperties {
/**
* The name of this simple properties.
*/
private String theName = "boot";
// isFlag is also detected
/**
* A simple flag.
*/
private boolean flag;
// An interface can still be injected because it might have a converter
private Comparator<?> comparator;
// There is only a getter on this instance but we don't know what to do with it ->
// ignored
private FeatureDescriptor featureDescriptor;
// There is only a setter on this "simple" property --> ignored
@SuppressWarnings("unused")
private Long counter;
// There is only a getter on this "simple" property --> ignored
private Integer size;
public String getTheName() {
return this.theName;
}
public void setTheName(String name) {
this.theName = name;
}
public boolean isFlag() {
return this.flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public Comparator<?> getComparator() {
return this.comparator;
}
public void setComparator(Comparator<?> comparator) {
this.comparator = comparator;
}
public FeatureDescriptor getFeatureDescriptor() {
return this.featureDescriptor;
}
public void setCounter(Long counter) {
this.counter = counter;
}
public Integer getSize() {
return this.size;
}
}

View File

@@ -0,0 +1,199 @@
/*
* 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.simple;
import org.springframework.boot.configurationsample.ConfigurationProperties;
/**
* Expose simple types to make sure these are detected properly.
*
* @author Stephane Nicoll
*/
@ConfigurationProperties(prefix = "simple.type")
public class SimpleTypeProperties {
private String myString;
private Byte myByte;
private byte myPrimitiveByte;
private Character myChar;
private char myPrimitiveChar;
private Boolean myBoolean;
private boolean myPrimitiveBoolean;
private Short myShort;
private short myPrimitiveShort;
private Integer myInteger;
private int myPrimitiveInteger;
private Long myLong;
private long myPrimitiveLong;
private Double myDouble;
private double myPrimitiveDouble;
private Float myFloat;
private float myPrimitiveFloat;
public String getMyString() {
return this.myString;
}
public void setMyString(String myString) {
this.myString = myString;
}
public Byte getMyByte() {
return this.myByte;
}
public void setMyByte(Byte myByte) {
this.myByte = myByte;
}
public byte getMyPrimitiveByte() {
return this.myPrimitiveByte;
}
public void setMyPrimitiveByte(byte myPrimitiveByte) {
this.myPrimitiveByte = myPrimitiveByte;
}
public Character getMyChar() {
return this.myChar;
}
public void setMyChar(Character myChar) {
this.myChar = myChar;
}
public char getMyPrimitiveChar() {
return this.myPrimitiveChar;
}
public void setMyPrimitiveChar(char myPrimitiveChar) {
this.myPrimitiveChar = myPrimitiveChar;
}
public Boolean getMyBoolean() {
return this.myBoolean;
}
public void setMyBoolean(Boolean myBoolean) {
this.myBoolean = myBoolean;
}
public boolean isMyPrimitiveBoolean() {
return this.myPrimitiveBoolean;
}
public void setMyPrimitiveBoolean(boolean myPrimitiveBoolean) {
this.myPrimitiveBoolean = myPrimitiveBoolean;
}
public Short getMyShort() {
return this.myShort;
}
public void setMyShort(Short myShort) {
this.myShort = myShort;
}
public short getMyPrimitiveShort() {
return this.myPrimitiveShort;
}
public void setMyPrimitiveShort(short myPrimitiveShort) {
this.myPrimitiveShort = myPrimitiveShort;
}
public Integer getMyInteger() {
return this.myInteger;
}
public void setMyInteger(Integer myInteger) {
this.myInteger = myInteger;
}
public int getMyPrimitiveInteger() {
return this.myPrimitiveInteger;
}
public void setMyPrimitiveInteger(int myPrimitiveInteger) {
this.myPrimitiveInteger = myPrimitiveInteger;
}
public Long getMyLong() {
return this.myLong;
}
public void setMyLong(Long myLong) {
this.myLong = myLong;
}
public long getMyPrimitiveLong() {
return this.myPrimitiveLong;
}
public void setMyPrimitiveLong(long myPrimitiveLong) {
this.myPrimitiveLong = myPrimitiveLong;
}
public Double getMyDouble() {
return this.myDouble;
}
public void setMyDouble(Double myDouble) {
this.myDouble = myDouble;
}
public double getMyPrimitiveDouble() {
return this.myPrimitiveDouble;
}
public void setMyPrimitiveDouble(double myPrimitiveDouble) {
this.myPrimitiveDouble = myPrimitiveDouble;
}
public Float getMyFloat() {
return this.myFloat;
}
public void setMyFloat(Float myFloat) {
this.myFloat = myFloat;
}
public float getMyPrimitiveFloat() {
return this.myPrimitiveFloat;
}
public void setMyPrimitiveFloat(float myPrimitiveFloat) {
this.myPrimitiveFloat = myPrimitiveFloat;
}
}

View File

@@ -0,0 +1,59 @@
/*
* 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.specific;
import org.springframework.boot.configurationsample.ConfigurationProperties;
/**
* Demonstrate that a method that exposes a root group within an annotated class is
* ignored as it should.
*
* @author Stephane Nicoll
*/
@ConfigurationProperties("specific")
public class InnerClassAnnotatedGetterConfig {
private String value;
public String getValue() {
return this.value;
}
public void setValue(String value) {
this.value = value;
}
@ConfigurationProperties("foo")
public Foo getFoo() {
return new Foo();
}
public static class Foo {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
}

View File

@@ -0,0 +1,89 @@
/*
* 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.specific;
import org.springframework.boot.configurationsample.ConfigurationProperties;
/**
* Demonstrate the auto-detection of a inner config classes.
*
* @author Stephane Nicoll
*/
@ConfigurationProperties(prefix = "config")
public class InnerClassProperties {
private final Foo first = new Foo();
private Foo second = new Foo();
private final SimplePojo third = new SimplePojo();
public Foo getFirst() {
return this.first;
}
public Foo getTheSecond() {
return this.second;
}
public void setTheSecond(Foo second) {
this.second = second;
}
public SimplePojo getThird() {
return this.third;
}
public static class Foo {
private String name;
private final Bar bar = new Bar();
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public Bar getBar() {
return this.bar;
}
public static class Bar {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
}
public static class SimplePojo extends
org.springframework.boot.configurationsample.specific.SimplePojo {
}
}

View File

@@ -0,0 +1,42 @@
/*
* 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.specific;
import org.springframework.boot.configurationsample.ConfigurationProperties;
/**
* Sample with a simple inner class config.
*
* @author Stephane Nicoll
*/
public class InnerClassRootConfig {
@ConfigurationProperties(prefix = "config")
static class Config {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
}

View File

@@ -0,0 +1,36 @@
/*
* 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.specific;
/**
* POJO for use with samples.
*
* @author Stephane Nicoll
*/
public class SimplePojo {
private int value;
public int getValue() {
return this.value;
}
public void setValue(int value) {
this.value = value;
}
}