This commit is contained in:
Phillip Webb 2015-06-25 12:00:48 -07:00
parent bedca634c9
commit 7879743b9f
9 changed files with 64 additions and 52 deletions

View File

@ -76,7 +76,7 @@ public class DevToolsProperties {
private long pollInterval = DEFAULT_RESTART_POLL_INTERVAL; private long pollInterval = DEFAULT_RESTART_POLL_INTERVAL;
/** /**
* Amount of quiet time (in milliseconds) requited without any classpath changes * Amount of quiet time (in milliseconds) required without any classpath changes
* before a restart is triggered. * before a restart is triggered.
*/ */
private long quietPeriod = DEFAULT_RESTART_QUIET_PERIOD; private long quietPeriod = DEFAULT_RESTART_QUIET_PERIOD;

View File

@ -261,6 +261,8 @@ following attributes:
provider for more details). provider for more details).
|=== |===
[[configuration-metadata-repeated-items]] [[configuration-metadata-repeated-items]]
==== Repeated meta-data items ==== Repeated meta-data items
It is perfectly acceptable for "`property`" and "`group`" objects with the same name to It is perfectly acceptable for "`property`" and "`group`" objects with the same name to
@ -282,7 +284,6 @@ property, you can provide additional meta-data that:
==== Value hints ==== Value hints
The `name` attribute of each hint refers to the `name` of a property. In the initial The `name` attribute of each hint refers to the `name` of a property. In the initial
example above, we provide 3 values for the `server.tomcat.compression` property: `on`, example above, we provide 3 values for the `server.tomcat.compression` property: `on`,
`off` and `force`. `off` and `force`.
@ -327,8 +328,9 @@ assistance for the keys, you could add the following to
NOTE: Of course, you should have an `Enum` for those two values instead. This is by far NOTE: Of course, you should have an `Enum` for those two values instead. This is by far
the most effective approach to auto-completion if your IDE supports it. the most effective approach to auto-completion if your IDE supports it.
==== Provider hints
==== Provider hints
Providers are a powerful way of attaching semantics to a property. We define in the section Providers are a powerful way of attaching semantics to a property. We define in the section
below the official providers that you can use for your own hints. Bare in mind however that below the official providers that you can use for your own hints. Bare in mind however that
your favorite IDE may implement some of these or none of them. It could eventually provide your favorite IDE may implement some of these or none of them. It could eventually provide
@ -369,8 +371,8 @@ can handle. If no provider for a given property is supported, no special content
assistance is provided either. assistance is provided either.
===== Any
===== Any
The **any** provider permits any additional values to be provided. Regular value The **any** provider permits any additional values to be provided. Regular value
validation based on the property type should be applied if this is supported. validation based on the property type should be applied if this is supported.
@ -402,8 +404,9 @@ other value is also allowed:
]} ]}
---- ----
===== Class reference
===== Class reference
The **class-reference** provider auto-completes classes available in the project. This The **class-reference** provider auto-completes classes available in the project. This
provider supports these parameters: provider supports these parameters:
@ -445,8 +448,9 @@ property that defines the `JspServlet` class name to use:
]} ]}
---- ----
===== Enum
===== Enum
The **enum** provider auto-completes the values of the `Enum` class referenced via the The **enum** provider auto-completes the values of the `Enum` class referenced via the
`target` parameter. This provider supports these parameters: `target` parameter. This provider supports these parameters:
@ -485,8 +489,8 @@ TIP: This is useful when you don't want your configuration classes to rely on cl
that may not be on the classpath. that may not be on the classpath.
===== Logger name
===== Logger name
The **logger-name** provider auto-completes valid logger names. Typically, package and The **logger-name** provider auto-completes valid logger names. Typically, package and
class names available in the current project can be auto-completed. Specific frameworks class names available in the current project can be auto-completed. Specific frameworks
may have extra magic logger names that could be supported as well. may have extra magic logger names that could be supported as well.
@ -552,8 +556,8 @@ level:
---- ----
===== Spring bean reference
===== Spring bean reference
The **spring-bean-reference** provider auto-completes the beans that are defined in The **spring-bean-reference** provider auto-completes the beans that are defined in
the configuration of the current project. This provider supports these parameters: the configuration of the current project. This provider supports these parameters:
@ -588,6 +592,8 @@ that defines the name of the `MBeanServer` bean to use:
]} ]}
---- ----
[[configuration-metadata-annotation-processor]] [[configuration-metadata-annotation-processor]]
=== Generating your own meta-data using the annotation processor === Generating your own meta-data using the annotation processor
You can easily generate your own configuration meta-data file from items annotated with You can easily generate your own configuration meta-data file from items annotated with
@ -635,6 +641,7 @@ annotation processor also supports the use of the `@Data`, `@Getter` and `@Sette
annotations. annotations.
[[configuration-metadata-nested-properties]] [[configuration-metadata-nested-properties]]
==== Nested properties ==== Nested properties
The annotation processor will automatically consider inner classes as nested properties. The annotation processor will automatically consider inner classes as nested properties.

View File

@ -44,8 +44,10 @@ public class ItemHint implements Comparable<ItemHint> {
public ItemHint(String name, List<ValueHint> values, List<ProviderHint> providers) { public ItemHint(String name, List<ValueHint> values, List<ProviderHint> providers) {
this.name = toCanonicalName(name); this.name = toCanonicalName(name);
this.values = (values != null ? new ArrayList<ValueHint>(values) : new ArrayList<ValueHint>()); this.values = (values != null ? new ArrayList<ValueHint>(values)
this.providers = (providers != null ? new ArrayList<ProviderHint>(providers) : new ArrayList<ProviderHint>()); : new ArrayList<ValueHint>());
this.providers = (providers != null ? new ArrayList<ProviderHint>(providers)
: new ArrayList<ProviderHint>());
} }
private String toCanonicalName(String name) { private String toCanonicalName(String name) {
@ -76,7 +78,8 @@ public class ItemHint implements Comparable<ItemHint> {
} }
public static ItemHint newHint(String name, ValueHint... values) { public static ItemHint newHint(String name, ValueHint... values) {
return new ItemHint(name, Arrays.asList(values), Collections.<ProviderHint>emptyList()); return new ItemHint(name, Arrays.asList(values),
Collections.<ProviderHint> emptyList());
} }
@Override @Override
@ -113,8 +116,10 @@ public class ItemHint implements Comparable<ItemHint> {
} }
public static class ProviderHint { public static class ProviderHint {
private final String name; private final String name;
private final Map<String,Object> parameters;
private final Map<String, Object> parameters;
public ProviderHint(String name, Map<String, Object> parameters) { public ProviderHint(String name, Map<String, Object> parameters) {
this.name = name; this.name = name;
@ -122,18 +127,19 @@ public class ItemHint implements Comparable<ItemHint> {
} }
public String getName() { public String getName() {
return name; return this.name;
} }
public Map<String, Object> getParameters() { public Map<String, Object> getParameters() {
return parameters; return this.parameters;
} }
@Override @Override
public String toString() { public String toString() {
return "Provider{" + "name='" + this.name + ", parameters=" + this.parameters + return "Provider{" + "name='" + this.name + ", parameters=" + this.parameters
'}'; + '}';
} }
} }
} }

View File

@ -110,10 +110,13 @@ public class JsonMarshaller {
for (ItemHint.ProviderHint providerHint : hint.getProviders()) { for (ItemHint.ProviderHint providerHint : hint.getProviders()) {
JSONObject providerHintObject = new JSONOrderedObject(); JSONObject providerHintObject = new JSONOrderedObject();
providerHintObject.put("name", providerHint.getName()); providerHintObject.put("name", providerHint.getName());
if (providerHint.getParameters() != null && !providerHint.getParameters().isEmpty()) { if (providerHint.getParameters() != null
&& !providerHint.getParameters().isEmpty()) {
JSONObject parametersObject = new JSONOrderedObject(); JSONObject parametersObject = new JSONOrderedObject();
for (Map.Entry<String, Object> entry : providerHint.getParameters().entrySet()) { for (Map.Entry<String, Object> entry : providerHint.getParameters()
parametersObject.put(entry.getKey(), extractItemValue(entry.getValue())); .entrySet()) {
parametersObject.put(entry.getKey(),
extractItemValue(entry.getValue()));
} }
providerHintObject.put("parameters", parametersObject); providerHintObject.put("parameters", parametersObject);
} }
@ -218,7 +221,7 @@ public class JsonMarshaller {
private ItemHint.ProviderHint toProviderHint(JSONObject object) { private ItemHint.ProviderHint toProviderHint(JSONObject object) {
String name = object.getString("name"); String name = object.getString("name");
Map<String,Object> parameters = new HashMap<String,Object>(); Map<String, Object> parameters = new HashMap<String, Object>();
if (object.has("parameters")) { if (object.has("parameters")) {
JSONObject parametersObject = object.getJSONObject("parameters"); JSONObject parametersObject = object.getJSONObject("parameters");
for (Object k : parametersObject.keySet()) { for (Object k : parametersObject.keySet()) {

View File

@ -330,7 +330,6 @@ public class ConfigurationMetadataAnnotationProcessorTests {
@Test @Test
public void mergingOfAdditionalProperty() throws Exception { public void mergingOfAdditionalProperty() throws Exception {
File additionalMetadataFile = createAdditionalMetadataFile(); File additionalMetadataFile = createAdditionalMetadataFile();
JSONObject property = new JSONObject(); JSONObject property = new JSONObject();
property.put("name", "foo"); property.put("name", "foo");
property.put("type", "java.lang.String"); property.put("type", "java.lang.String");
@ -339,12 +338,9 @@ public class ConfigurationMetadataAnnotationProcessorTests {
properties.put(property); properties.put(property);
JSONObject additionalMetadata = new JSONObject(); JSONObject additionalMetadata = new JSONObject();
additionalMetadata.put("properties", properties); additionalMetadata.put("properties", properties);
writeMetadata(additionalMetadataFile, additionalMetadata); writeMetadata(additionalMetadataFile, additionalMetadata);
ConfigurationMetadata metadata = compile(SimpleProperties.class); ConfigurationMetadata metadata = compile(SimpleProperties.class);
assertThat(metadata, containsProperty("simple.comparator")); assertThat(metadata, containsProperty("simple.comparator"));
assertThat(metadata, assertThat(metadata,
containsProperty("foo", String.class) containsProperty("foo", String.class)
.fromSource(AdditionalMetadata.class)); .fromSource(AdditionalMetadata.class));
@ -354,7 +350,6 @@ public class ConfigurationMetadataAnnotationProcessorTests {
public void mergingOfSimpleHint() throws Exception { public void mergingOfSimpleHint() throws Exception {
writeAdditionalHints(ItemHint.newHint("simple.the-name", new ItemHint.ValueHint( writeAdditionalHints(ItemHint.newHint("simple.the-name", new ItemHint.ValueHint(
"boot", "Bla bla"), new ItemHint.ValueHint("spring", null))); "boot", "Bla bla"), new ItemHint.ValueHint("spring", null)));
ConfigurationMetadata metadata = compile(SimpleProperties.class); ConfigurationMetadata metadata = compile(SimpleProperties.class);
assertThat(metadata, assertThat(metadata,
containsHint("simple.the-name").withValue(0, "boot", "Bla bla") containsHint("simple.the-name").withValue(0, "boot", "Bla bla")
@ -365,7 +360,6 @@ public class ConfigurationMetadataAnnotationProcessorTests {
public void mergingOfHintWithNonCanonicalName() throws Exception { public void mergingOfHintWithNonCanonicalName() throws Exception {
writeAdditionalHints(ItemHint.newHint("simple.theName", new ItemHint.ValueHint( writeAdditionalHints(ItemHint.newHint("simple.theName", new ItemHint.ValueHint(
"boot", "Bla bla"))); "boot", "Bla bla")));
ConfigurationMetadata metadata = compile(SimpleProperties.class); ConfigurationMetadata metadata = compile(SimpleProperties.class);
assertThat(metadata, assertThat(metadata,
containsHint("simple.the-name").withValue(0, "boot", "Bla bla")); containsHint("simple.the-name").withValue(0, "boot", "Bla bla"));
@ -373,14 +367,14 @@ public class ConfigurationMetadataAnnotationProcessorTests {
@Test @Test
public void mergingOfHintWithProvider() throws Exception { public void mergingOfHintWithProvider() throws Exception {
writeAdditionalHints( writeAdditionalHints(new ItemHint("simple.theName",
new ItemHint("simple.theName", Collections.<ItemHint.ValueHint>emptyList(), Arrays.asList( Collections.<ItemHint.ValueHint> emptyList(), Arrays.asList(
new ItemHint.ProviderHint("first", Collections.<String,Object>singletonMap("target", "org.foo")), new ItemHint.ProviderHint("first", Collections
new ItemHint.ProviderHint("second", null)) .<String, Object> singletonMap("target", "org.foo")),
)); new ItemHint.ProviderHint("second", null))));
ConfigurationMetadata metadata = compile(SimpleProperties.class); ConfigurationMetadata metadata = compile(SimpleProperties.class);
assertThat(metadata, containsHint("simple.the-name") assertThat(metadata,
containsHint("simple.the-name")
.withProvider("first", "target", "org.foo") .withProvider("first", "target", "org.foo")
.withProvider("second")); .withProvider("second"));
} }

View File

@ -212,10 +212,12 @@ public class ConfigurationMetadataMatchers {
private final List<ProviderHintMatcher> providers; private final List<ProviderHintMatcher> providers;
public ContainsHintMatcher(String name) { public ContainsHintMatcher(String name) {
this(name, new ArrayList<ValueHintMatcher>(), new ArrayList<ProviderHintMatcher>()); this(name, new ArrayList<ValueHintMatcher>(),
new ArrayList<ProviderHintMatcher>());
} }
public ContainsHintMatcher(String name, List<ValueHintMatcher> values, List<ProviderHintMatcher> providers) { public ContainsHintMatcher(String name, List<ValueHintMatcher> values,
List<ProviderHintMatcher> providers) {
this.name = name; this.name = name;
this.values = values; this.values = values;
this.providers = providers; this.providers = providers;
@ -273,14 +275,17 @@ public class ConfigurationMetadataMatchers {
return new ContainsHintMatcher(this.name, values, this.providers); return new ContainsHintMatcher(this.name, values, this.providers);
} }
public ContainsHintMatcher withProvider(int index, String provider, Map<String,Object> parameters) { public ContainsHintMatcher withProvider(int index, String provider,
List<ProviderHintMatcher> providers = new ArrayList<ProviderHintMatcher>(this.providers); Map<String, Object> parameters) {
List<ProviderHintMatcher> providers = new ArrayList<ProviderHintMatcher>(
this.providers);
providers.add(new ProviderHintMatcher(index, provider, parameters)); providers.add(new ProviderHintMatcher(index, provider, parameters));
return new ContainsHintMatcher(this.name, this.values, providers); return new ContainsHintMatcher(this.name, this.values, providers);
} }
public ContainsHintMatcher withProvider(String provider, String key, Object value) { public ContainsHintMatcher withProvider(String provider, String key, Object value) {
return withProvider(this.providers.size(), provider, Collections.singletonMap(key, value)); return withProvider(this.providers.size(), provider,
Collections.singletonMap(key, value));
} }
public ContainsHintMatcher withProvider(String provider) { public ContainsHintMatcher withProvider(String provider) {
@ -359,9 +364,8 @@ public class ConfigurationMetadataMatchers {
if (this.index + 1 > hint.getProviders().size()) { if (this.index + 1 > hint.getProviders().size()) {
return false; return false;
} }
ItemHint.ProviderHint providerHint = hint.getProviders().get(index); ItemHint.ProviderHint providerHint = hint.getProviders().get(this.index);
if (this.name != null if (this.name != null && !this.name.equals(providerHint.getName())) {
&& !this.name.equals(providerHint.getName())) {
return false; return false;
} }
if (this.parameters != null) { if (this.parameters != null) {

View File

@ -58,8 +58,8 @@ public class JsonMarshallerTests {
metadata.add(ItemHint.newHint("a.b")); metadata.add(ItemHint.newHint("a.b"));
metadata.add(ItemHint.newHint("c", new ItemHint.ValueHint(123, "hey"), metadata.add(ItemHint.newHint("c", new ItemHint.ValueHint(123, "hey"),
new ItemHint.ValueHint(456, null))); new ItemHint.ValueHint(456, null)));
metadata.add(new ItemHint("d", null, Arrays.asList( metadata.add(new ItemHint("d", null, Arrays.asList(new ItemHint.ProviderHint(
new ItemHint.ProviderHint("first", Collections.<String,Object>singletonMap("target", "foo")), "first", Collections.<String, Object> singletonMap("target", "foo")),
new ItemHint.ProviderHint("second", null)))); new ItemHint.ProviderHint("second", null))));
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
JsonMarshaller marshaller = new JsonMarshaller(); JsonMarshaller marshaller = new JsonMarshaller();
@ -81,8 +81,7 @@ public class JsonMarshallerTests {
assertThat(read, containsHint("a.b")); assertThat(read, containsHint("a.b"));
assertThat(read, assertThat(read,
containsHint("c").withValue(0, 123, "hey").withValue(1, 456, null)); containsHint("c").withValue(0, 123, "hey").withValue(1, 456, null));
assertThat(read, containsHint("d") assertThat(read, containsHint("d").withProvider("first", "target", "foo")
.withProvider("first", "target", "foo")
.withProvider("second")); .withProvider("second"));
} }

View File

@ -206,9 +206,9 @@ public class LoggingApplicationListener implements GenericApplicationListener {
system.initialize(logConfig, logFile); system.initialize(logConfig, logFile);
} }
catch (Exception ex) { catch (Exception ex) {
System.err // NOTE: We can't use the logger here to report the problem
.println("Logging system failed to initialize using configuration from '" System.err.println("Logging system failed to initialize "
+ logConfig + "'"); + "using configuration from '" + logConfig + "'");
ex.printStackTrace(System.err); ex.printStackTrace(System.err);
throw new IllegalStateException(ex); throw new IllegalStateException(ex);
} }

View File

@ -108,10 +108,9 @@ public class OutputCapture implements TestRule {
/** /**
* Verify that the output is matched by the supplied {@code matcher}. Verification is * Verify that the output is matched by the supplied {@code matcher}. Verification is
* performed after the test method has executed. * performed after the test method has executed.
*
* @param matcher the matcher * @param matcher the matcher
*/ */
public void expect(Matcher<String> matcher) { public void expect(Matcher<? super String> matcher) {
this.matchers.add(matcher); this.matchers.add(matcher);
} }