This commit is contained in:
Phillip Webb 2015-11-03 20:36:20 -08:00
parent fd1cbed51c
commit 6c2ea4648f
32 changed files with 190 additions and 141 deletions

View File

@ -20,13 +20,13 @@ package org.springframework.boot.actuate.metrics.writer;
* Simple writer for counters (metrics that increment).
*
* @author Dave Syer
* @since 1.3.0
*/
public interface CounterWriter {
/**
* Increment the value of a metric (or decrement if the delta is negative). The name
* of the delta is the name of the metric to increment.
*
* @param delta the amount to increment by
*/
void increment(Delta<?> delta);
@ -34,9 +34,8 @@ public interface CounterWriter {
/**
* Reset the value of a metric, usually to zero value. Implementations can discard the
* old values if desired, but may choose not to. This operation is optional (some
* implementations may not be able to fulfil the contract, in which case they should
* implementations may not be able to fulfill the contract, in which case they should
* simply do nothing).
*
* @param metricName the name to reset
*/
void reset(String metricName);

View File

@ -22,6 +22,7 @@ import org.springframework.boot.actuate.metrics.Metric;
* Writer for gauge values (simple metric with a number value).
*
* @author Dave Syer
* @since 1.3.0
*/
public interface GaugeWriter {

View File

@ -22,6 +22,8 @@ import org.springframework.boot.actuate.metrics.Metric;
* Basic strategy for write operations on {@link Metric} data.
*
* @author Dave Syer
* @see GaugeWriter
* @see CounterWriter
*/
public interface MetricWriter extends GaugeWriter, CounterWriter {

View File

@ -66,7 +66,7 @@ public class AuthenticationAuditListener implements
this.webListener.process(this, event);
}
else if (event instanceof AuthenticationSuccessEvent) {
onAuthenticationEvent((AuthenticationSuccessEvent) event);
onAuthenticationSuccessEvent((AuthenticationSuccessEvent) event);
}
}
@ -78,7 +78,7 @@ public class AuthenticationAuditListener implements
"AUTHENTICATION_FAILURE", data));
}
private void onAuthenticationEvent(AuthenticationSuccessEvent event) {
private void onAuthenticationSuccessEvent(AuthenticationSuccessEvent event) {
Map<String, Object> data = new HashMap<String, Object>();
if (event.getAuthentication().getDetails() != null) {
data.put("details", event.getAuthentication().getDetails());

View File

@ -22,6 +22,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* Configuration properties for OAuth2 Authorization server.
*
* @author Dave Syer
* @since 1.3.0
*/
@ConfigurationProperties("security.oauth2.authorization")
public class AuthorizationServerProperties {

View File

@ -94,13 +94,16 @@ public class OAuth2AuthorizationServerConfiguration
.scopes(this.details.getScope().toArray(new String[0]));
if (this.details.getAutoApproveScopes() != null) {
builder.autoApprove(this.details.getAutoApproveScopes().toArray(new String[0]));
builder.autoApprove(
this.details.getAutoApproveScopes().toArray(new String[0]));
}
if (this.details.getAccessTokenValiditySeconds() != null) {
builder.accessTokenValiditySeconds(this.details.getAccessTokenValiditySeconds());
builder.accessTokenValiditySeconds(
this.details.getAccessTokenValiditySeconds());
}
if (this.details.getRefreshTokenValiditySeconds() != null) {
builder.refreshTokenValiditySeconds(this.details.getRefreshTokenValiditySeconds());
builder.refreshTokenValiditySeconds(
this.details.getRefreshTokenValiditySeconds());
}
if (this.details.getRegisteredRedirectUri() != null) {
builder.redirectUris(

View File

@ -114,9 +114,7 @@ public class OAuth2ResourceServerConfiguration {
Environment environment = context.getEnvironment();
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(environment,
"security.oauth2.resource.");
String client = environment
.resolvePlaceholders("${security.oauth2.client.client-id:}");
if (StringUtils.hasText(client)) {
if (hasOAuthClientId(environment)) {
return ConditionOutcome.match("found client id");
}
if (!resolver.getSubProperties("jwt").isEmpty()) {
@ -137,6 +135,12 @@ public class OAuth2ResourceServerConfiguration {
+ "JWT resource nor authorization server");
}
private boolean hasOAuthClientId(Environment environment) {
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(environment,
"security.oauth2.client.");
return StringUtils.hasLength(resolver.getProperty("client-id", ""));
}
}
@ConditionalOnBean(AuthorizationServerEndpointsConfiguration.class)

View File

@ -140,4 +140,5 @@ public class UserInfoTokenServices implements ResourceServerTokenServices {
"Could not fetch user details");
}
}
}

View File

@ -32,6 +32,7 @@ import org.springframework.util.ReflectionUtils;
* {@link RestartListener} that prepares Log4J2 for an application restart.
*
* @author Andy Wilkinson
* @since 1.3.0
*/
public class Log4J2RestartListener implements RestartListener {

View File

@ -20,6 +20,7 @@ package org.springframework.boot.devtools.restart;
* Listener that is notified of application restarts.
*
* @author Andy Wilkinson
* @since 1.3.0
*/
public interface RestartListener {

View File

@ -411,6 +411,7 @@ and mark it as disabled. For example:
----
[[howto-add-a-servlet-filter-or-listener-using-scanning]]
==== Add Servlets, Filters, and Listeners using classpath scanning
`@WebServlet`, `@WebFilter`, and `@WebListener` annotated classes can be automatically

View File

@ -186,13 +186,14 @@ If the https://github.com/mikekelly/hal-browser[HAL Browser] is on the classpath
via its webjar (`org.webjars:hal-browser`), or via the `spring-data-rest-hal-browser` then
an HTML "`discovery page`", in the form of the HAL Browser, is also provided.
[[production-ready-endpoint-cors]]
=== CORS support
http://en.wikipedia.org/wiki/Cross-origin_resource_sharing[Cross-origin resource sharing]
(CORS) is a http://www.w3.org/TR/cors/[W3C specification] that allows you to specify in a
flexible way what kind of cross domain requests are authorized. Actuator's MVC endpoints
can be configured to support such scenario.
can be configured to support such scenarios.
CORS support is disabled by default and is only enabled once the
`endpoints.cors.allowed-origins` property has been set. The configuration below permits
@ -208,6 +209,7 @@ TIP: Check {sc-spring-boot-actuator}/autoconfigure/EndpointCorsProperties.{sc-ex
for a complete list of options.
[[production-ready-customizing-endpoints-programmatically]]
=== Adding custom endpoints
If you add a `@Bean` of type `Endpoint` then it will automatically be exposed over JMX and
@ -1076,16 +1078,13 @@ used by default if you are on Java 8 or if you are using Dropwizard metrics.
[[production-ready-metric-writers]]
=== Metric writers, exporters and aggregation
Spring Boot provides a couple of implementations of a marker interface
called `Exporter` which can be used to copy metric readings from the
in-memory buffers to a place where they can be analyzed and
displayed. Indeed, if you provide a `@Bean` that implements the
`MetricWriter` interface (or `GaugeWriter` for simple use cases) and
mark it `@ExportMetricWriter`, then it will automatically be hooked up
to an `Exporter` and fed metric updates every 5 seconds (configured
via `spring.metrics.export.delay-millis`). In addition, any
`MetricReader` that you define and mark as `@ExportMetricReader` will
Spring Boot provides a couple of implementations of a marker interface called `Exporter`
which can be used to copy metric readings from the in-memory buffers to a place where they
can be analyzed and displayed. Indeed, if you provide a `@Bean` that implements the
`MetricWriter` interface (or `GaugeWriter` for simple use cases) and mark it
`@ExportMetricWriter`, then it will automatically be hooked up to an `Exporter` and fed
metric updates every 5 seconds (configured via `spring.metrics.export.delay-millis`).
In addition, any `MetricReader` that you define and mark as `@ExportMetricReader` will
have its values exported by the default exporter.
The default exporter is a `MetricCopyExporter` which tries to optimize itself by not

View File

@ -330,16 +330,13 @@ To provide a concrete example, suppose you develop a `@Component` that uses a
----
On your application classpath (e.g. inside your jar) you can have an
`application.properties` that provides a sensible default property
value for `name`. When running in a new environment, an
`application.properties` can be provided outside of your jar that
overrides the `name`; and for one-off testing, you can launch with a
specific command line switch (e.g. `java -jar app.jar
--name="Spring"`).
`application.properties` that provides a sensible default property value for `name`. When
running in a new environment, an `application.properties` can be provided outside of your
jar that overrides the `name`; and for one-off testing, you can launch with a specific
command line switch (e.g. `java -jar app.jar --name="Spring"`).
[TIP]
====
The `SPRING_APPLICATION_JSON` properties can be supplied on the
command line with an environment variable. For example in a
UN{asterisk}X shell:
@ -366,6 +363,7 @@ or as a JNDI variable `java:comp/env/spring.application.json`.
====
[[boot-features-external-config-random-values]]
=== Configuring random values
The `RandomValuePropertySource` is useful for injecting random values (e.g. into secrets

View File

@ -27,9 +27,9 @@ work with other build systems (Ant for example), but they will not be particular
supported.
[[using-boot-dependency-management]]
=== Dependency management
Each release of Spring Boot provides a curated list of dependencies it supports. In
practice, you do not need to provide a version for any of these dependencies in your
build configuration as Spring Boot is managing that for you. When you upgrade Spring
@ -48,6 +48,7 @@ WARNING: Each release of Spring Boot is associated with a base version of the Sp
Framework so we **highly** recommend you to not specify its version on your own.
[[using-boot-maven]]
=== Maven
Maven users can inherit from the `spring-boot-starter-parent` project to obtain sensible
@ -105,6 +106,7 @@ TIP: Check the {github-code}/spring-boot-dependencies/pom.xml[`spring-boot-depen
for a list of supported properties.
[[using-boot-maven-without-a-parent]]
==== Using Spring Boot without the parent POM
Not everyone likes inheriting from the `spring-boot-starter-parent` POM. You may have your
@ -164,6 +166,7 @@ NOTE: In the example above, we specify a _BOM_ but any dependency type can be ov
that way.
[[using-boot-maven-java-version]]
==== Changing the Java version
The `spring-boot-starter-parent` chooses fairly conservative Java compatibility. If you

View File

@ -73,6 +73,7 @@
<module>spring-boot-sample-property-validation</module>
<module>spring-boot-sample-secure</module>
<module>spring-boot-sample-secure-oauth2</module>
<module>spring-boot-sample-secure-oauth2-resource</module>
<module>spring-boot-sample-secure-sso</module>
<module>spring-boot-sample-servlet</module>
<module>spring-boot-sample-session-redis</module>

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package sample.secure.oauth2;
package sample.secure.oauth2.resource;
import java.util.Date;

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package sample.secure.oauth2;
package sample.secure.oauth2.resource;
import org.springframework.data.repository.CrudRepository;

View File

@ -13,7 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.secure.oauth2;
package sample.secure.oauth2.resource;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@ -23,8 +24,9 @@ import org.springframework.security.oauth2.config.annotation.web.configuration.R
@SpringBootApplication
@EnableResourceServer
public class SampleSecureOAuth2ResourceApplication extends ResourceServerConfigurerAdapter {
public class SampleSecureOAuth2ResourceApplication
extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.antMatcher("/flights/**").authorizeRequests().anyRequest().authenticated();

View File

@ -1,13 +1,25 @@
package sample.secure.oauth2;
/*
* 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.
*/
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;
package sample.secure.oauth2.resource;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.boot.test.WebIntegrationTest;
@ -18,6 +30,11 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;
/**
* Series of automated integration tests to verify proper behavior of auto-configured,
* OAuth2-secured system
@ -36,6 +53,7 @@ public class SampleSecureOAuth2ResourceApplicationTests {
FilterChainProxy filterChain;
private MockMvc mvc;
@Before
public void setUp() {
this.mvc = webAppContextSetup(this.context).addFilters(this.filterChain).build();
@ -44,8 +62,8 @@ public class SampleSecureOAuth2ResourceApplicationTests {
@Test
public void homePageAvailable() throws Exception {
this.mvc.perform(get("/").accept(MediaTypes.HAL_JSON))
.andExpect(status().isOk()).andDo(print());
this.mvc.perform(get("/").accept(MediaTypes.HAL_JSON)).andExpect(status().isOk())
.andDo(print());
}
@Test

View File

@ -51,8 +51,7 @@ class TypeElementMembers {
private final Map<String, ExecutableElement> publicGetters = new LinkedHashMap<String, ExecutableElement>();
private final Map<String, List<ExecutableElement>> publicSetters =
new LinkedHashMap<String, List<ExecutableElement>>();
private final Map<String, List<ExecutableElement>> publicSetters = new LinkedHashMap<String, List<ExecutableElement>>();
TypeElementMembers(ProcessingEnvironment env, TypeElement element) {
this.env = env;
@ -84,7 +83,8 @@ class TypeElementMembers {
}
else if (isSetter(method)) {
String propertyName = getAccessorName(name);
List<ExecutableElement> matchingSetters = this.publicSetters.get(propertyName);
List<ExecutableElement> matchingSetters = this.publicSetters
.get(propertyName);
if (matchingSetters == null) {
matchingSetters = new ArrayList<ExecutableElement>();
this.publicSetters.put(propertyName, matchingSetters);
@ -97,7 +97,8 @@ class TypeElementMembers {
}
}
private ExecutableElement getMatchingSetter(List<ExecutableElement> candidates, TypeMirror type) {
private ExecutableElement getMatchingSetter(List<ExecutableElement> candidates,
TypeMirror type) {
for (ExecutableElement candidate : candidates) {
TypeMirror paramType = candidate.getParameters().get(0).asType();
if (this.env.getTypeUtils().isSameType(paramType, type)) {

View File

@ -40,7 +40,6 @@ import javax.lang.model.util.Types;
class TypeUtils {
private static final Map<TypeKind, Class<?>> PRIMITIVE_WRAPPERS;
private static final Map<String, TypeKind> WRAPPER_TO_PRIMITIVE;
static {
Map<TypeKind, Class<?>> wrappers = new HashMap<TypeKind, Class<?>>();
@ -53,16 +52,15 @@ class TypeUtils {
wrappers.put(TypeKind.LONG, Long.class);
wrappers.put(TypeKind.SHORT, Short.class);
PRIMITIVE_WRAPPERS = Collections.unmodifiableMap(wrappers);
}
private static final Map<String, TypeKind> WRAPPER_TO_PRIMITIVE;
static {
Map<String, TypeKind> primitives = new HashMap<String, TypeKind>();
primitives.put(Boolean.class.getName(), TypeKind.BOOLEAN);
primitives.put(Byte.class.getName(), TypeKind.BYTE);
primitives.put(Character.class.getName(), TypeKind.CHAR);
primitives.put(Double.class.getName(), TypeKind.DOUBLE);
primitives.put(Float.class.getName(), TypeKind.FLOAT);
primitives.put(Integer.class.getName(), TypeKind.INT);
primitives.put(Long.class.getName(), TypeKind.LONG);
primitives.put(Short.class.getName(), TypeKind.SHORT);
for (Map.Entry<TypeKind, Class<?>> entry : PRIMITIVE_WRAPPERS.entrySet()) {
primitives.put(entry.getValue().getName(), entry.getKey());
}
WRAPPER_TO_PRIMITIVE = primitives;
}
@ -111,7 +109,6 @@ class TypeUtils {
|| this.env.getTypeUtils().isAssignable(type, this.mapType);
}
public boolean isEnclosedIn(Element candidate, TypeElement element) {
if (candidate == null || element == null) {
return false;
@ -134,7 +131,8 @@ class TypeUtils {
public TypeMirror getWrapperOrPrimitiveFor(TypeMirror typeMirror) {
Class<?> candidate = getWrapperFor(typeMirror);
if (candidate != null) {
return this.env.getElementUtils().getTypeElement(candidate.getName()).asType();
return this.env.getElementUtils().getTypeElement(candidate.getName())
.asType();
}
TypeKind primitiveKind = getPrimitiveFor(typeMirror);
if (primitiveKind != null) {

View File

@ -205,11 +205,9 @@ public class ConfigurationMetadataAnnotationProcessorTests {
ConfigurationMetadata metadata = compile(type);
assertThat(metadata, containsGroup("not.deprecated").fromSource(type));
assertThat(metadata, containsProperty("not.deprecated.counter", Integer.class)
.withNoDeprecation()
.fromSource(type));
.withNoDeprecation().fromSource(type));
assertThat(metadata, containsProperty("not.deprecated.flag", Boolean.class)
.withNoDeprecation()
.fromSource(type));
.withNoDeprecation().fromSource(type));
}
@Test
@ -217,10 +215,10 @@ public class ConfigurationMetadataAnnotationProcessorTests {
Class<?> type = BoxingPojo.class;
ConfigurationMetadata metadata = compile(type);
assertThat(metadata, containsGroup("boxing").fromSource(type));
assertThat(metadata, containsProperty("boxing.flag", Boolean.class)
.fromSource(type));
assertThat(metadata, containsProperty("boxing.counter", Integer.class)
.fromSource(type));
assertThat(metadata,
containsProperty("boxing.flag", Boolean.class).fromSource(type));
assertThat(metadata,
containsProperty("boxing.counter", Integer.class).fromSource(type));
}
@Test
@ -366,12 +364,9 @@ public class ConfigurationMetadataAnnotationProcessorTests {
null, null, true, null);
writeAdditionalMetadata(property);
ConfigurationMetadata metadata = compile(SimpleProperties.class);
assertThat(metadata,
containsProperty("simple.flag", Boolean.class)
.fromSource(SimpleProperties.class)
.withDescription("A simple flag.")
.withDeprecation(null, null)
.withDefaultValue(is(true)));
assertThat(metadata, containsProperty("simple.flag", Boolean.class)
.fromSource(SimpleProperties.class).withDescription("A simple flag.")
.withDeprecation(null, null).withDefaultValue(is(true)));
assertThat(metadata.getItems().size(), is(4));
}

View File

@ -19,8 +19,8 @@ package org.springframework.boot.configurationsample.specific;
import org.springframework.boot.configurationsample.ConfigurationProperties;
/**
* Demonstrate the use of boxing/unboxing. Even if the type does not
* strictly match, it should still be detected.
* Demonstrate the use of boxing/unboxing. Even if the type does not strictly match, it
* should still be detected.
*
* @author Stephane Nicoll
*/

View File

@ -19,8 +19,8 @@ package org.springframework.boot.configurationsample.specific;
import org.springframework.boot.configurationsample.ConfigurationProperties;
/**
* Demonstrate that an unrelated setter is not taken into account
* to detect the deprecated flag.
* Demonstrate that an unrelated setter is not taken into account to detect the deprecated
* flag.
*
* @author Stephane Nicoll
*/

View File

@ -63,11 +63,9 @@ public class LaunchedURLClassLoader extends URLClassLoader {
}
/**
* Gets the resource with the given {@code name}.
* <p>
* Unlike a standard {@link ClassLoader}, this method will first search the root class
* loader. If the resource is not found, this method will call
* {@link #findResource(String)}.
* Gets the resource with the given {@code name}. Unlike a standard
* {@link ClassLoader}, this method will first search the root class loader. If the
* resource is not found, this method will call {@link #findResource(String)}.
*/
@Override
public URL getResource(String name) {
@ -104,11 +102,10 @@ public class LaunchedURLClassLoader extends URLClassLoader {
}
/**
* Gets the resources with the given {@code name}.
* <p>
* Returns a combination of the resources found by {@link #findResources(String)} and
* from {@link ClassLoader#getResources(String) getResources(String)} on the root
* class loader, if any.
* Gets the resources with the given {@code name}. Returns a combination of the
* resources found by {@link #findResources(String)} and from
* {@link ClassLoader#getResources(String) getResources(String)} on the root class
* loader, if any.
*/
@Override
public Enumeration<URL> getResources(String name) throws IOException {

View File

@ -40,9 +40,8 @@ import org.springframework.context.annotation.Import;
public @interface EnableConfigurationProperties {
/**
* Convenient way to quickly register {@link ConfigurationProperties} annotated
* beans with Spring. Standard Spring Beans will also be scanned regardless of
* this value.
* Convenient way to quickly register {@link ConfigurationProperties} annotated beans
* with Spring. Standard Spring Beans will also be scanned regardless of this value.
* @return {@link ConfigurationProperties} annotated beans to register
*/
Class<?>[] value() default {};

View File

@ -115,8 +115,9 @@ class EnableConfigurationPropertiesImportSelector implements ImportSelector {
ConfigurationProperties properties = AnnotationUtils.findAnnotation(type,
ConfigurationProperties.class);
Assert.notNull(properties, "No " + ConfigurationProperties.class.getSimpleName()
+ " annotation found on '" + type.getName() + "'.");
Assert.notNull(properties,
"No " + ConfigurationProperties.class.getSimpleName()
+ " annotation found on '" + type.getName() + "'.");
}
}

View File

@ -31,6 +31,7 @@ import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
@ -38,24 +39,29 @@ import org.springframework.web.context.support.StandardServletEnvironment;
/**
* An {@link EnvironmentPostProcessor} that parses JSON from
* <code>spring.application.json</code> or equivalently
* {@code spring.application.json} or equivalently
* {@link SpringApplicationJsonEnvironmentPostProcessor} and adds it as a map property
* source to the {@link Environment}. The new properties are added with higher priority
* than the system properties.
*
* @author Dave Syer
* @author Phillip Webb
* @since 1.3.0
*/
public class SpringApplicationJsonEnvironmentPostProcessor
implements EnvironmentPostProcessor, Ordered {
private static final Log logger = LogFactory
.getLog(SpringApplicationJsonEnvironmentPostProcessor.class);
private static final String SERVLET_ENVIRONMENT_CLASS = "org.springframework.web."
+ "context.support.StandardServletEnvironment";
/**
* The default order for the processor.
*/
public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 5;
private static final Log logger = LogFactory
.getLog(SpringApplicationJsonEnvironmentPostProcessor.class);
private int order = DEFAULT_ORDER;
@Override
@ -73,26 +79,22 @@ public class SpringApplicationJsonEnvironmentPostProcessor
String json = environment.resolvePlaceholders(
"${spring.application.json:${SPRING_APPLICATION_JSON:}}");
if (StringUtils.hasText(json)) {
try {
JsonParser parser = JsonParserFactory.getJsonParser();
Map<String, Object> map = parser.parseMap(json);
if (!map.isEmpty()) {
MapPropertySource source = new MapPropertySource(
"spring.application.json", flatten(map));
MutablePropertySources sources = environment.getPropertySources();
String name = findPropertySource(sources);
if (sources.contains(name)) {
sources.addBefore(name, source);
}
else {
sources.addFirst(source);
}
}
}
catch (Exception e) {
logger.warn("Cannot parse JSON for spring.application.json: " + json, e);
processJson(environment, json);
}
}
private void processJson(ConfigurableEnvironment environment, String json) {
try {
JsonParser parser = JsonParserFactory.getJsonParser();
Map<String, Object> map = parser.parseMap(json);
if (!map.isEmpty()) {
addJsonPropertySource(environment,
new MapPropertySource("spring.application.json", flatten(map)));
}
}
catch (Exception ex) {
logger.warn("Cannot parse JSON for spring.application.json: " + json, ex);
}
}
/**
@ -108,30 +110,20 @@ public class SpringApplicationJsonEnvironmentPostProcessor
private void flatten(String prefix, Map<String, Object> result,
Map<String, Object> map) {
if (prefix == null) {
prefix = "";
}
else {
prefix = prefix + ".";
}
for (String key : map.keySet()) {
String name = prefix + key;
Object value = map.get(key);
extract(name, result, value);
prefix = (prefix == null ? "" : prefix + ".");
for (Map.Entry<String, Object> entry : map.entrySet()) {
extract(prefix + entry.getKey(), result, entry.getValue());
}
}
@SuppressWarnings("unchecked")
private void extract(String name, Map<String, Object> result, Object value) {
if (value instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> nested = (Map<String, Object>) value;
flatten(name, result, nested);
flatten(name, result, (Map<String, Object>) value);
}
if (value instanceof Collection) {
@SuppressWarnings("unchecked")
Collection<Object> nested = (Collection<Object>) value;
int index = 0;
for (Object object : nested) {
for (Object object : (Collection<Object>) value) {
extract(name + "[" + index + "]", result, object);
index++;
}
@ -141,12 +133,21 @@ public class SpringApplicationJsonEnvironmentPostProcessor
}
}
private void addJsonPropertySource(ConfigurableEnvironment environment,
PropertySource<?> source) {
MutablePropertySources sources = environment.getPropertySources();
String name = findPropertySource(sources);
if (sources.contains(name)) {
sources.addBefore(name, source);
}
else {
sources.addFirst(source);
}
}
private String findPropertySource(MutablePropertySources sources) {
if (ClassUtils.isPresent(
"org.springframework.web.context.support.StandardServletEnvironment",
null)
&& sources
.contains(StandardServletEnvironment.JNDI_PROPERTY_SOURCE_NAME)) {
if (ClassUtils.isPresent(SERVLET_ENVIRONMENT_CLASS, null) && sources
.contains(StandardServletEnvironment.JNDI_PROPERTY_SOURCE_NAME)) {
return StandardServletEnvironment.JNDI_PROPERTY_SOURCE_NAME;
}

View File

@ -712,7 +712,6 @@ public class ConfigurationPropertiesBindingPostProcessorTests {
@EnableConfigurationProperties(PropertyWithoutConfigurationPropertiesAnnotation.class)
public static class ConfigurationPropertiesWithoutAnnotation {
}
public static class PropertyWithoutConfigurationPropertiesAnnotation {
@ -726,6 +725,7 @@ public class ConfigurationPropertiesBindingPostProcessorTests {
public void setName(String name) {
this.name = name;
}
}
}

View File

@ -415,41 +415,49 @@ public class EnableConfigurationPropertiesTests {
@Configuration
@EnableConfigurationProperties(TestProperties.class)
protected static class TestConfiguration {
}
@Configuration
@EnableConfigurationProperties(StrictTestProperties.class)
protected static class StrictTestConfiguration {
}
@Configuration
@EnableConfigurationProperties(EmbeddedTestProperties.class)
protected static class EmbeddedTestConfiguration {
}
@Configuration
@EnableConfigurationProperties(IgnoreNestedTestProperties.class)
protected static class IgnoreNestedTestConfiguration {
}
@Configuration
@EnableConfigurationProperties(ExceptionIfInvalidTestProperties.class)
protected static class ExceptionIfInvalidTestConfiguration {
}
@Configuration
@EnableConfigurationProperties(NoExceptionIfInvalidTestProperties.class)
protected static class NoExceptionIfInvalidTestConfiguration {
}
@Configuration
@EnableConfigurationProperties(DerivedProperties.class)
protected static class DerivedConfiguration {
}
@Configuration
@EnableConfigurationProperties(NestedProperties.class)
protected static class NestedConfiguration {
}
@Configuration
@ -467,6 +475,7 @@ public class EnableConfigurationPropertiesTests {
@Configuration
@ImportResource("org/springframework/boot/context/properties/testProperties.xml")
protected static class DefaultXmlConfiguration {
}
@EnableConfigurationProperties
@ -483,16 +492,19 @@ public class EnableConfigurationPropertiesTests {
@EnableConfigurationProperties(External.class)
@Configuration
public static class AnotherExampleConfig {
}
@EnableConfigurationProperties({ External.class, Another.class })
@Configuration
public static class FurtherExampleConfig {
}
@EnableConfigurationProperties({ SystemEnvVar.class })
@Configuration
public static class SystemExampleConfig {
}
@ConfigurationProperties(prefix = "external")
@ -507,6 +519,7 @@ public class EnableConfigurationPropertiesTests {
public void setName(String name) {
this.name = name;
}
}
@ConfigurationProperties(prefix = "another")
@ -521,11 +534,14 @@ public class EnableConfigurationPropertiesTests {
public void setName(String name) {
this.name = name;
}
}
@ConfigurationProperties(prefix = "spring_test_external")
public static class SystemEnvVar {
private String val;
public String getVal() {
return this.val;
}
@ -534,8 +550,6 @@ public class EnableConfigurationPropertiesTests {
this.val = val;
}
private String val;
}
@Component
@ -552,19 +566,20 @@ public class EnableConfigurationPropertiesTests {
public String getName() {
return this.properties.name;
}
}
@Configuration
@EnableConfigurationProperties(MoreProperties.class)
protected static class MoreConfiguration {
}
}
@Configuration
@EnableConfigurationProperties(InvalidConfiguration.class)
protected static class InvalidConfiguration {
}
}
@ConfigurationProperties
protected static class NestedProperties {
@ -605,6 +620,7 @@ public class EnableConfigurationPropertiesTests {
}
protected static class DerivedProperties extends BaseProperties {
}
@ConfigurationProperties
@ -638,14 +654,17 @@ public class EnableConfigurationPropertiesTests {
@ConfigurationProperties(ignoreUnknownFields = false)
protected static class StrictTestProperties extends TestProperties {
}
@ConfigurationProperties(prefix = "spring.foo")
protected static class EmbeddedTestProperties extends TestProperties {
}
@ConfigurationProperties(ignoreUnknownFields = false, ignoreNestedProperties = true)
protected static class IgnoreNestedTestProperties extends TestProperties {
}
@ConfigurationProperties
@ -704,6 +723,7 @@ public class EnableConfigurationPropertiesTests {
public void setName(String name) {
this.name = name;
}
}
@ConfigurationProperties(locations = "${binding.location:classpath:name.yml}")
@ -731,5 +751,7 @@ public class EnableConfigurationPropertiesTests {
public Map<String, String> getMymap() {
return this.mymap;
}
}
}

View File

@ -25,6 +25,8 @@ import org.springframework.core.env.StandardEnvironment;
import static org.junit.Assert.assertEquals;
/**
* Tests for {@link SpringApplicationJsonEnvironmentPostProcessor}.
*
* @author Dave Syer
*/
public class SpringApplicationJsonEnvironmentPostProcessorTests {

View File

@ -32,7 +32,6 @@ import static org.junit.Assert.assertNotNull;
*/
public class LoggingApplicationListenerIntegrationTests {
@Test
public void loggingSystemRegisteredInTheContext() {
ConfigurableApplicationContext context = new SpringApplicationBuilder(
@ -46,7 +45,6 @@ public class LoggingApplicationListenerIntegrationTests {
}
}
@Component
static class SampleService {
@ -56,7 +54,7 @@ public class LoggingApplicationListenerIntegrationTests {
SampleService(LoggingSystem loggingSystem) {
this.loggingSystem = loggingSystem;
}
}
}