diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointAutoConfiguration.java index e8142d7551..88a7113ab2 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointAutoConfiguration.java @@ -105,16 +105,22 @@ public class EndpointAutoConfiguration { @Bean @ConditionalOnMissingBean public HealthEndpoint healthEndpoint() { - // The default sensitivity depends on whether all the endpoints by default are - // secure or not. User can always override with endpoints.health.sensitive. - boolean secure = this.management != null && this.management.getSecurity() != null - && this.management.getSecurity().isEnabled(); HealthEndpoint endpoint = new HealthEndpoint(this.healthAggregator, this.healthIndicators); - endpoint.setSensitive(secure); + endpoint.setSensitive(isHealthEndpointSensitive()); return endpoint; } + /** + * The default health endpoint sensitivity depends on whether all the endpoints by + * default are secure or not. User can always override with + * {@literal endpoints.health.sensitive}. + */ + private boolean isHealthEndpointSensitive() { + return (this.management != null) && (this.management.getSecurity() != null) + && this.management.getSecurity().isEnabled(); + } + @Bean @ConditionalOnMissingBean public BeansEndpoint beansEndpoint() { diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementSecurityAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementSecurityAutoConfiguration.java index 8c156ffa04..1aaa9f0df3 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementSecurityAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementSecurityAutoConfiguration.java @@ -224,7 +224,6 @@ public class ManagementSecurityAutoConfiguration { @Override protected void configure(HttpSecurity http) throws Exception { - // secure endpoints String[] paths = getEndpointPaths(this.endpointHandlerMapping); if (paths.length > 0 && this.management.getSecurity().isEnabled()) { @@ -237,27 +236,15 @@ public class ManagementSecurityAutoConfiguration { http.requestMatchers().antMatchers(paths); String[] endpointPaths = this.server.getPathsArray(getEndpointPaths( this.endpointHandlerMapping, false)); - ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry authorizeRequests = http - .authorizeRequests(); - authorizeRequests.antMatchers(endpointPaths).permitAll(); - if (this.endpointHandlerMapping != null) { - authorizeRequests.requestMatchers( - new PrincipalHandlerRequestMatcher()).permitAll(); - } - authorizeRequests.anyRequest().hasRole( - this.management.getSecurity().getRole()); + configureAuthorizeRequests(endpointPaths, http.authorizeRequests()); http.httpBasic(); - // No cookies for management endpoints by default http.csrf().disable(); http.sessionManagement().sessionCreationPolicy( this.management.getSecurity().getSessions()); - SpringBootWebSecurityConfiguration.configureHeaders(http.headers(), this.security.getHeaders()); - } - } private AuthenticationEntryPoint entryPoint() { @@ -266,12 +253,25 @@ public class ManagementSecurityAutoConfiguration { return entryPoint; } + private void configureAuthorizeRequests( + String[] endpointPaths, + ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry requests) { + requests.antMatchers(endpointPaths).permitAll(); + if (this.endpointHandlerMapping != null) { + requests.requestMatchers(new PrincipalHandlerRequestMatcher()) + .permitAll(); + } + requests.anyRequest().hasRole(this.management.getSecurity().getRole()); + } + private final class PrincipalHandlerRequestMatcher implements RequestMatcher { + @Override public boolean matches(HttpServletRequest request) { return ManagementWebSecurityConfigurerAdapter.this.endpointHandlerMapping .isPrincipalHandler(request); } + } } @@ -287,7 +287,6 @@ public class ManagementSecurityAutoConfiguration { if (endpointHandlerMapping == null) { return NO_PATHS; } - Set endpoints = endpointHandlerMapping.getEndpoints(); List paths = new ArrayList(endpoints.size()); for (MvcEndpoint endpoint : endpoints) { diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpoint.java index f0a729ad52..f4599a7166 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpoint.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpoint.java @@ -119,21 +119,17 @@ public class ConfigurationPropertiesReportEndpoint extends * {@link Map}. */ protected Map extract(ApplicationContext context) { - - Map result = new HashMap(); - Map beans = new HashMap( - context.getBeansWithAnnotation(ConfigurationProperties.class)); - ConfigurationBeanFactoryMetaData beanFactoryMetaData = null; - if (context.getBeanNamesForType(ConfigurationBeanFactoryMetaData.class).length == 1) { - beanFactoryMetaData = context.getBean(ConfigurationBeanFactoryMetaData.class); - beans.putAll(beanFactoryMetaData - .getBeansWithFactoryAnnotation(ConfigurationProperties.class)); - } - // Serialize beans into map structure and sanitize values ObjectMapper mapper = new ObjectMapper(); configureObjectMapper(mapper); + return extract(context, mapper); + } + private Map extract(ApplicationContext context, ObjectMapper mapper) { + Map result = new HashMap(); + ConfigurationBeanFactoryMetaData beanFactoryMetaData = getBeanFactoryMetaData(context); + Map beans = getConfigurationPropertiesBeans(context, + beanFactoryMetaData); for (Map.Entry entry : beans.entrySet()) { String beanName = entry.getKey(); Object bean = entry.getValue(); @@ -143,14 +139,34 @@ public class ConfigurationPropertiesReportEndpoint extends root.put("properties", sanitize(safeSerialize(mapper, bean, prefix))); result.put(beanName, root); } - if (context.getParent() != null) { - result.put("parent", extract(context.getParent())); + result.put("parent", extract(context.getParent(), mapper)); } - return result; } + private ConfigurationBeanFactoryMetaData getBeanFactoryMetaData( + ApplicationContext context) { + Map beans = context + .getBeansOfType(ConfigurationBeanFactoryMetaData.class); + if (beans.size() == 1) { + return beans.values().iterator().next(); + } + return null; + } + + private Map getConfigurationPropertiesBeans( + ApplicationContext context, + ConfigurationBeanFactoryMetaData beanFactoryMetaData) { + Map beans = new HashMap(); + beans.putAll(context.getBeansWithAnnotation(ConfigurationProperties.class)); + if (beanFactoryMetaData != null) { + beans.putAll(beanFactoryMetaData + .getBeansWithFactoryAnnotation(ConfigurationProperties.class)); + } + return beans; + } + /** * Cautiously serialize the bean to a map (returning a map with an error message * instead of throwing an exception if there is a problem). @@ -166,7 +182,7 @@ public class ConfigurationPropertiesReportEndpoint extends this.metadata.extractMap(bean, prefix), Map.class)); return result; } - catch (Exception e) { + catch (Exception ex) { return new HashMap(Collections. singletonMap( "error", "Cannot serialize '" + prefix + "'")); } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/HealthEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/HealthEndpoint.java index fbd22cf013..6ce6e559ec 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/HealthEndpoint.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/HealthEndpoint.java @@ -48,10 +48,8 @@ public class HealthEndpoint extends AbstractEndpoint { public HealthEndpoint(HealthAggregator healthAggregator, Map healthIndicators) { super("health", false, true); - Assert.notNull(healthAggregator, "HealthAggregator must not be null"); Assert.notNull(healthIndicators, "HealthIndicators must not be null"); - CompositeHealthIndicator healthIndicator = new CompositeHealthIndicator( healthAggregator); for (Map.Entry h : healthIndicators.entrySet()) { diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/EndpointHandlerMapping.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/EndpointHandlerMapping.java index 3ef70fe38d..c3bdfc0fc8 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/EndpointHandlerMapping.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/EndpointHandlerMapping.java @@ -18,8 +18,10 @@ package org.springframework.boot.actuate.endpoint.mvc; import java.lang.reflect.Method; import java.security.Principal; +import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; +import java.util.List; import java.util.Set; import javax.servlet.http.HttpServletRequest; @@ -41,7 +43,6 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl * The semantics of {@code @RequestMapping} should be identical to a normal * {@code @Controller}, but the endpoints should not be annotated as {@code @Controller} * (otherwise they will be mapped by the normal MVC mechanisms). - * *

* One of the aims of the mapping is to support endpoints that work as HTTP endpoints but * can still provide useful service interfaces when there is no HTTP server (and no Spring @@ -97,59 +98,38 @@ public class EndpointHandlerMapping extends RequestMappingHandlerMapping impleme @Override protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) { - if (mapping == null) { return; } - - Set defaultPatterns = mapping.getPatternsCondition().getPatterns(); - String[] patterns = new String[defaultPatterns.isEmpty() ? 1 : defaultPatterns - .size()]; - - String path = ""; - Object bean = handler; - if (bean instanceof String) { - bean = getApplicationContext().getBean((String) handler); - } - if (bean instanceof MvcEndpoint) { - MvcEndpoint endpoint = (MvcEndpoint) bean; - path = endpoint.getPath(); - } - - int i = 0; - String prefix = StringUtils.hasText(this.prefix) ? this.prefix + path : path; - if (defaultPatterns.isEmpty()) { - patterns[0] = prefix; - } - else { - for (String pattern : defaultPatterns) { - patterns[i] = prefix + pattern; - i++; - } - } - PatternsRequestCondition patternsInfo = new PatternsRequestCondition(patterns); - - RequestMappingInfo modified = new RequestMappingInfo(patternsInfo, - mapping.getMethodsCondition(), mapping.getParamsCondition(), - mapping.getHeadersCondition(), mapping.getConsumesCondition(), - mapping.getProducesCondition(), mapping.getCustomCondition()); - + String[] patterns = getPatterns(handler, mapping); if (handlesPrincipal(method)) { this.principalHandlers.add(new HandlerMethod(handler, method)); } - - super.registerHandlerMethod(handler, method, modified); + super.registerHandlerMethod(handler, method, withNewPatterns(mapping, patterns)); } - public boolean isPrincipalHandler(HttpServletRequest request) { - HandlerExecutionChain handler; - try { - handler = getHandler(request); + private String[] getPatterns(Object handler, RequestMappingInfo mapping) { + String path = getPath(handler); + String prefix = StringUtils.hasText(this.prefix) ? this.prefix + path : path; + Set defaultPatterns = mapping.getPatternsCondition().getPatterns(); + if (defaultPatterns.isEmpty()) { + return new String[] { prefix }; } - catch (Exception e) { - return false; + List patterns = new ArrayList(defaultPatterns); + for (int i = 0; i < patterns.size(); i++) { + patterns.set(i, prefix + patterns.get(i)); } - return (handler != null && this.principalHandlers.contains(handler.getHandler())); + return patterns.toArray(new String[patterns.size()]); + } + + private String getPath(Object handler) { + if (handler instanceof String) { + handler = getApplicationContext().getBean((String) handler); + } + if (handler instanceof MvcEndpoint) { + return ((MvcEndpoint) handler).getPath(); + } + return ""; } private boolean handlesPrincipal(Method method) { @@ -161,6 +141,32 @@ public class EndpointHandlerMapping extends RequestMappingHandlerMapping impleme return false; } + private RequestMappingInfo withNewPatterns(RequestMappingInfo mapping, + String[] patternStrings) { + PatternsRequestCondition patterns = new PatternsRequestCondition(patternStrings); + return new RequestMappingInfo(patterns, mapping.getMethodsCondition(), + mapping.getParamsCondition(), mapping.getHeadersCondition(), + mapping.getConsumesCondition(), mapping.getProducesCondition(), + mapping.getCustomCondition()); + } + + /** + * Returns {@code true} if the given request is mapped to an endpoint and to a method + * that includes a {@link Principal} argument. + * @param request the http request + * @return {@code true} if the request is + */ + public boolean isPrincipalHandler(HttpServletRequest request) { + try { + HandlerExecutionChain handlerChain = getHandler(request); + Object handler = (handlerChain == null ? null : handlerChain.getHandler()); + return (handler != null && this.principalHandlers.contains(handler)); + } + catch (Exception ex) { + return false; + } + } + /** * @param prefix the prefix to set */ diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/HealthMvcEndpointAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/HealthMvcEndpointAutoConfigurationTests.java index 7ef321e071..787ad9d3ed 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/HealthMvcEndpointAutoConfigurationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/HealthMvcEndpointAutoConfigurationTests.java @@ -32,6 +32,8 @@ import org.springframework.web.context.support.AnnotationConfigWebApplicationCon import static org.junit.Assert.assertEquals; /** + * Tests for {@link EndpointWebMvcAutoConfiguration} of the {@link HealthMvcEndpoint}. + * * @author Dave Syer */ public class HealthMvcEndpointAutoConfigurationTests { diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpointMethodAnnotationsTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpointMethodAnnotationsTests.java index fd4ee134bf..6c1c050f6f 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpointMethodAnnotationsTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpointMethodAnnotationsTests.java @@ -31,6 +31,11 @@ import org.springframework.context.annotation.Configuration; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +/** + * Tests for {@link ConfigurationPropertiesReportEndpoint} when used with bean methods. + * + * @author Dave Syer + */ public class ConfigurationPropertiesReportEndpointMethodAnnotationsTests { private AnnotationConfigApplicationContext context; diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpointParentTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpointParentTests.java index 97adecce74..36c8ee7a75 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpointParentTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpointParentTests.java @@ -32,6 +32,12 @@ import org.springframework.context.annotation.Configuration; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +/** + * Tests for {@link ConfigurationPropertiesReportEndpoint} when used with a parent + * context. + * + * @author Dave Syer + */ public class ConfigurationPropertiesReportEndpointParentTests { private AnnotationConfigApplicationContext context; diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpointSerializationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpointSerializationTests.java index 8c954d49c8..a3ed21b83a 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpointSerializationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpointSerializationTests.java @@ -34,6 +34,11 @@ import org.springframework.context.annotation.Import; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +/** + * Tests for {@link ConfigurationPropertiesReportEndpoint} serialization. + * + * @author Dave Syer + */ public class ConfigurationPropertiesReportEndpointSerializationTests { private AnnotationConfigApplicationContext context; diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpointTests.java index 31c9028a80..87e2cf0d05 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpointTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ConfigurationPropertiesReportEndpointTests.java @@ -31,6 +31,11 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; +/** + * Tests for {@link ConfigurationPropertiesReportEndpoint}. + * + * @author Dave Syer + */ public class ConfigurationPropertiesReportEndpointTests extends AbstractEndpointTests { diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationPackages.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationPackages.java index e9cc06fe10..c2de9a13cb 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationPackages.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationPackages.java @@ -18,7 +18,9 @@ package org.springframework.boot.autoconfigure; import java.util.ArrayList; import java.util.Arrays; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -26,7 +28,6 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConstructorArgumentValues; -import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; @@ -90,13 +91,12 @@ public abstract class AutoConfigurationPackages { * @param packageNames the package names to set */ public static void register(BeanDefinitionRegistry registry, String... packageNames) { - if (registry.containsBeanDefinition(BEAN)) { BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN); ConstructorArgumentValues constructorArguments = beanDefinition .getConstructorArgumentValues(); constructorArguments.addIndexedArgumentValue(0, - augmentBasePackages(constructorArguments, packageNames)); + addBasePackages(constructorArguments, packageNames)); } else { GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); @@ -108,17 +108,14 @@ public abstract class AutoConfigurationPackages { } } - private static String[] augmentBasePackages( + private static String[] addBasePackages( ConstructorArgumentValues constructorArguments, String[] packageNames) { - - ValueHolder valueHolder = constructorArguments.getIndexedArgumentValue(0, - List.class); - - List packages = new ArrayList( - Arrays.asList((String[]) valueHolder.getValue())); - packages.addAll(Arrays.asList(packageNames)); - - return packages.toArray(new String[packages.size()]); + String[] existing = (String[]) constructorArguments.getIndexedArgumentValue(0, + String[].class).getValue(); + Set merged = new LinkedHashSet(); + merged.addAll(Arrays.asList(existing)); + merged.addAll(Arrays.asList(packageNames)); + return merged.toArray(new String[merged.size()]); } /** diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoProperties.java index 39b1b319e9..52be3ce3ba 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoProperties.java @@ -165,12 +165,12 @@ public class MongoProperties { public MongoClient createMongoClient(MongoClientOptions options) throws UnknownHostException { try { - if (customAddress() || customCredentials()) { + if (hasCustomAddress() || hasCustomCredentials()) { if (options == null) { options = MongoClientOptions.builder().build(); } List credentials = null; - if (customCredentials()) { + if (hasCustomCredentials()) { credentials = Arrays.asList(MongoCredential.createMongoCRCredential( this.username, getMongoClientDatabase(), this.password)); } @@ -187,11 +187,11 @@ public class MongoProperties { } } - private boolean customAddress() { + private boolean hasCustomAddress() { return this.host != null || this.port != null; } - private boolean customCredentials() { + private boolean hasCustomCredentials() { return this.username != null && this.password != null; } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationPackagesTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationPackagesTests.java index f22f52fc97..1d58c448a3 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationPackagesTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationPackagesTests.java @@ -23,8 +23,8 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.springframework.boot.autoconfigure.AutoConfigurationPackages.Registrar; -import org.springframework.boot.autoconfigure.packages.one.FirstConfiguration; -import org.springframework.boot.autoconfigure.packages.two.SecondConfiguration; +import org.springframework.boot.autoconfigure.packagestest.one.FirstConfiguration; +import org.springframework.boot.autoconfigure.packagestest.two.SecondConfiguration; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -66,16 +66,12 @@ public class AutoConfigurationPackagesTests { @Test public void detectsMultipleAutoConfigurationPackages() { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( FirstConfiguration.class, SecondConfiguration.class); - List packages = AutoConfigurationPackages.get(context.getBeanFactory()); - - assertThat( - packages, - hasItems(FirstConfiguration.class.getPackage().getName(), - SecondConfiguration.class.getPackage().getName())); + Package package1 = FirstConfiguration.class.getPackage(); + Package package2 = SecondConfiguration.class.getPackage(); + assertThat(packages, hasItems(package1.getName(), package2.getName())); assertThat(packages, hasSize(2)); } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoPropertiesTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoPropertiesTests.java index 986b0d2db9..e0bd334d62 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoPropertiesTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoPropertiesTests.java @@ -78,24 +78,20 @@ public class MongoPropertiesTests { MongoProperties properties = new MongoProperties(); properties.setUsername("user"); properties.setPassword("secret".toCharArray()); - MongoClient client = properties.createMongoClient(null); - assertMongoCredential(client.getCredentialsList().get(0), "user", "secret"); } @Test public void uriCanBeCustomized() throws UnknownHostException { MongoProperties properties = new MongoProperties(); - properties - .setUri("mongodb://user:secret@mongo1.example.com:12345,mongo2.example.com:23456/test"); + properties.setUri("mongodb://user:secret@mongo1.example.com:12345," + + "mongo2.example.com:23456/test"); MongoClient client = properties.createMongoClient(null); - List allAddresses = client.getAllAddress(); assertEquals(2, allAddresses.size()); assertServerAddress(allAddresses.get(0), "mongo1.example.com", 12345); assertServerAddress(allAddresses.get(1), "mongo2.example.com", 23456); - List credentialsList = client.getCredentialsList(); assertEquals(1, credentialsList.size()); assertMongoCredential(credentialsList.get(0), "user", "secret"); diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/packages/one/FirstConfiguration.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/packagestest/one/FirstConfiguration.java similarity index 78% rename from spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/packages/one/FirstConfiguration.java rename to spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/packagestest/one/FirstConfiguration.java index 618c8cedff..259be2cb13 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/packages/one/FirstConfiguration.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/packagestest/one/FirstConfiguration.java @@ -14,12 +14,18 @@ * limitations under the License. */ -package org.springframework.boot.autoconfigure.packages.one; +package org.springframework.boot.autoconfigure.packagestest.one; +import org.springframework.boot.autoconfigure.AutoConfigurationPackagesTests; import org.springframework.boot.autoconfigure.AutoConfigurationPackagesTests.TestRegistrar; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; +/** + * Sample configuration used in {@link AutoConfigurationPackagesTests}. + * + * @author Oliver Gierke + */ @Configuration @Import(TestRegistrar.class) public class FirstConfiguration { diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/packages/two/SecondConfiguration.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/packagestest/two/SecondConfiguration.java similarity index 78% rename from spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/packages/two/SecondConfiguration.java rename to spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/packagestest/two/SecondConfiguration.java index c8c648a4f3..db0ababf5f 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/packages/two/SecondConfiguration.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/packagestest/two/SecondConfiguration.java @@ -14,12 +14,18 @@ * limitations under the License. */ -package org.springframework.boot.autoconfigure.packages.two; +package org.springframework.boot.autoconfigure.packagestest.two; +import org.springframework.boot.autoconfigure.AutoConfigurationPackagesTests; import org.springframework.boot.autoconfigure.AutoConfigurationPackagesTests.TestRegistrar; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; +/** + * Sample configuration used in {@link AutoConfigurationPackagesTests}. + * + * @author Oliver Gierke + */ @Configuration @Import(TestRegistrar.class) public class SecondConfiguration { diff --git a/spring-boot-dependencies/pom.xml b/spring-boot-dependencies/pom.xml index 2de7306bfd..935bfd50a9 100644 --- a/spring-boot-dependencies/pom.xml +++ b/spring-boot-dependencies/pom.xml @@ -1,6 +1,4 @@ - - + 4.0.0 org.springframework.boot spring-boot-dependencies @@ -1537,7 +1535,7 @@ true - spring-milestones diff --git a/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc b/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc index 1fb6e5a60c..932d5a9c1c 100644 --- a/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc +++ b/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc @@ -413,16 +413,15 @@ If you don't want to expose endpoints over HTTP you can set the management port [[production-ready-health-access-restrictions]] === Health endpoint anonymous access restrictions The information exposed by the health endpoint varies depending on whether or not it's -accessed anonymously. By default, when accessed anonymously, any details about the server's health -are hidden and the endpoint will simply indicate whether or not the server is up or -down. Furthermore, when accessed anonymously, the response is cached for a configurable -period to prevent the endpoint being used in a denial of service attack. +accessed anonymously. By default, when accessed anonymously, any details about the +server's health are hidden and the endpoint will simply indicate whether or not the server +is up or down. Furthermore, when accessed anonymously, the response is cached for a +configurable period to prevent the endpoint being used in a denial of service attack. The `endpoints.health.time-to-live` property is used to configure the caching period in milliseconds. It defaults to 1000, i.e. one second. The above-described restrictions can be disabled, thereby allowing anonymous users full -access to the health endpoint. To do so, set `endpoints.health.sensitive` -to `false`. +access to the health endpoint. To do so, set `endpoints.health.sensitive` to `false`. diff --git a/spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/client/GreetingService.java b/spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/client/GreetingService.java index de25204e0c..e03d15d76a 100644 --- a/spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/client/GreetingService.java +++ b/spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/client/GreetingService.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2013 the original author or authors. + * 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. diff --git a/spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/client/SimpleClientWebSocketHandler.java b/spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/client/SimpleClientWebSocketHandler.java index a3bbfc2f1c..828291de18 100644 --- a/spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/client/SimpleClientWebSocketHandler.java +++ b/spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/client/SimpleClientWebSocketHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2013 the original author or authors. + * 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. diff --git a/spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/client/SimpleGreetingService.java b/spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/client/SimpleGreetingService.java index 6a1e994c81..e08ec78e34 100644 --- a/spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/client/SimpleGreetingService.java +++ b/spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/client/SimpleGreetingService.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2013 the original author or authors. + * 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. diff --git a/spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/config/SampleWebSocketsApplication.java b/spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/config/SampleWebSocketsApplication.java index 2ae2ddfb56..963ea166e3 100644 --- a/spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/config/SampleWebSocketsApplication.java +++ b/spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/config/SampleWebSocketsApplication.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2013 the original author or authors. + * 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. diff --git a/spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/echo/DefaultEchoService.java b/spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/echo/DefaultEchoService.java index 87da340e54..c967613eb7 100644 --- a/spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/echo/DefaultEchoService.java +++ b/spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/echo/DefaultEchoService.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2013 the original author or authors. + * 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. diff --git a/spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/echo/EchoService.java b/spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/echo/EchoService.java index 59c0f06012..c07323cd56 100644 --- a/spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/echo/EchoService.java +++ b/spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/echo/EchoService.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2013 the original author or authors. + * 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. diff --git a/spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/echo/EchoWebSocketHandler.java b/spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/echo/EchoWebSocketHandler.java index 4f01f1288e..b97a5e01c5 100644 --- a/spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/echo/EchoWebSocketHandler.java +++ b/spring-boot-samples/spring-boot-sample-websocket-jetty/src/main/java/samples/websocket/echo/EchoWebSocketHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2013 the original author or authors. + * 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. diff --git a/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationBeanFactoryMetaData.java b/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationBeanFactoryMetaData.java index fa8d6a61c5..4566ce57db 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationBeanFactoryMetaData.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationBeanFactoryMetaData.java @@ -70,10 +70,7 @@ public class ConfigurationBeanFactoryMetaData implements BeanFactoryPostProcesso public A findFactoryAnnotation(String beanName, Class type) { Method method = findFactoryMethod(beanName); - if (method != null) { - return AnnotationUtils.findAnnotation(method, type); - } - return null; + return (method == null ? null : AnnotationUtils.findAnnotation(method, type)); } private Method findFactoryMethod(String beanName) { diff --git a/spring-boot/src/main/java/org/springframework/boot/json/BasicJsonParser.java b/spring-boot/src/main/java/org/springframework/boot/json/BasicJsonParser.java new file mode 100644 index 0000000000..49e2be2bf6 --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/json/BasicJsonParser.java @@ -0,0 +1,172 @@ +/* + * 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.json; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.springframework.util.StringUtils; + +/** + * Really basic JSON parser for when you have nothing else available. Comes with some + * limitations with respect to the JSON specification (e.g. only supports String values), + * so users will probably prefer to have a library handle things instead (Jackson or Snake + * YAML are supported). + * + * @author Dave Syer + * @see JsonParserFactory + * @since 1.2.0 + */ +public class BasicJsonParser implements JsonParser { + + @Override + public Map parseMap(String json) { + if (json != null) { + json = json.trim(); + if (json.startsWith("{")) { + return parseMapInternal(json); + } + else if (json.equals("")) { + return new HashMap(); + } + } + return null; + } + + @Override + public List parseList(String json) { + if (json != null) { + json = json.trim(); + if (json.startsWith("[")) { + return parseListInternal(json); + } + else if (json.trim().equals("")) { + return new ArrayList(); + } + } + return null; + } + + private List parseListInternal(String json) { + List list = new ArrayList(); + json = trimLeadingCharacter(trimTrailingCharacter(json, ']'), '['); + for (String value : tokenize(json)) { + list.add(parseInternal(value)); + } + return list; + } + + private Object parseInternal(String json) { + if (json.startsWith("[")) { + return parseListInternal(json); + } + + if (json.startsWith("{")) { + return parseMapInternal(json); + } + + if (json.startsWith("\"")) { + return trimTrailingCharacter(trimLeadingCharacter(json, '"'), '"'); + } + + try { + return Long.valueOf(json); + } + catch (NumberFormatException ex) { + // ignore + } + + try { + return Double.valueOf(json); + } + catch (NumberFormatException ex) { + // ignore + } + + return json; + } + + private static String trimTrailingCharacter(String string, char c) { + if (string.length() >= 0 && string.charAt(string.length() - 1) == c) { + return string.substring(0, string.length() - 1); + } + return string; + } + + private static String trimLeadingCharacter(String string, char c) { + if (string.length() >= 0 && string.charAt(0) == c) { + return string.substring(1); + } + return string; + } + + private Map parseMapInternal(String json) { + Map map = new LinkedHashMap(); + json = trimLeadingCharacter(trimTrailingCharacter(json, '}'), '{'); + for (String pair : tokenize(json)) { + String[] values = StringUtils.trimArrayElements(StringUtils.split(pair, ":")); + String key = trimLeadingCharacter(trimTrailingCharacter(values[0], '"'), '"'); + Object value = null; + if (values.length > 0) { + String string = trimLeadingCharacter( + trimTrailingCharacter(values[1], '"'), '"'); + value = parseInternal(string); + } + map.put(key, value); + } + return map; + } + + private List tokenize(String json) { + List list = new ArrayList(); + int index = 0; + int inObject = 0; + int inList = 0; + StringBuilder build = new StringBuilder(); + while (index < json.length()) { + char current = json.charAt(index); + if (current == '{') { + inObject++; + } + if (current == '}') { + inObject--; + } + if (current == '[') { + inList++; + } + if (current == ']') { + inList--; + } + if (current == ',' && inObject == 0 && inList == 0) { + list.add(build.toString()); + build.setLength(0); + } + else { + build.append(current); + } + index++; + } + if (build.length() > 0) { + list.add(build.toString()); + } + return list; + } + +} diff --git a/spring-boot/src/main/java/org/springframework/boot/json/JsonJsonParser.java b/spring-boot/src/main/java/org/springframework/boot/json/JsonJsonParser.java index 7184b75fc3..69a9e59cc0 100644 --- a/spring-boot/src/main/java/org/springframework/boot/json/JsonJsonParser.java +++ b/spring-boot/src/main/java/org/springframework/boot/json/JsonJsonParser.java @@ -21,12 +21,11 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import org.json.simple.JSONObject; -import org.json.simple.parser.JSONParser; -import org.json.simple.parser.ParseException; +import org.json.JSONArray; +import org.json.JSONObject; /** - * Thin wrapper to adapt {@link JSONObject} to a {@link JsonParser}. + * Thin wrapper to adapt {@link org.json.JSONObject} to a {@link JsonParser}. * * @author Dave Syer * @since 1.2.0 @@ -37,29 +36,50 @@ public class JsonJsonParser implements JsonParser { @Override public Map parseMap(String json) { Map map = new LinkedHashMap(); - try { - @SuppressWarnings("unchecked") - Map value = (Map) new JSONParser() - .parse(json); - map.putAll(value); - } - catch (ParseException e) { - throw new IllegalArgumentException("Cannot parse Json", e); - } + putAll(map, new JSONObject(json)); return map; } + private void putAll(Map map, JSONObject object) { + for (Object key : object.keySet()) { + String name = key.toString(); + Object value = object.get(name); + if (value instanceof JSONObject) { + Map nested = new LinkedHashMap(); + putAll(nested, (JSONObject) value); + value = nested; + } + if (value instanceof JSONArray) { + List nested = new ArrayList(); + addAll(nested, (JSONArray) value); + value = nested; + } + map.put(name, value); + } + } + + private void addAll(List list, JSONArray array) { + for (int i = 0; i < array.length(); i++) { + Object value = array.get(i); + if (value instanceof JSONObject) { + Map nested = new LinkedHashMap(); + putAll(nested, (JSONObject) value); + value = nested; + } + if (value instanceof JSONArray) { + List nested = new ArrayList(); + addAll(nested, (JSONArray) value); + value = nested; + } + list.add(value); + } + } + @Override public List parseList(String json) { List nested = new ArrayList(); - try { - @SuppressWarnings("unchecked") - List value = (List) new JSONParser().parse(json); - nested.addAll(value); - } - catch (ParseException e) { - throw new IllegalArgumentException("Cannot parse Json", e); - } + addAll(nested, new JSONArray(json)); return nested; } + } diff --git a/spring-boot/src/main/java/org/springframework/boot/json/JsonParserFactory.java b/spring-boot/src/main/java/org/springframework/boot/json/JsonParserFactory.java index c7ca26c1a7..9d6a55cd32 100644 --- a/spring-boot/src/main/java/org/springframework/boot/json/JsonParserFactory.java +++ b/spring-boot/src/main/java/org/springframework/boot/json/JsonParserFactory.java @@ -24,7 +24,9 @@ import org.springframework.util.ClassUtils; * @author Dave Syer * @see JacksonJsonParser * @see YamlJsonParser - * @see SimpleJsonParser + * @see JsonSimpleJsonParser + * @see JsonJsonParser + * @see BasicJsonParser */ public abstract class JsonParserFactory { @@ -39,19 +41,19 @@ public abstract class JsonParserFactory { if (ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", null)) { return new JacksonJsonParser(); } - if (ClassUtils.isPresent("org.json.JSONObject", null)) { - return new JsonJsonParser(); - } - if (ClassUtils.isPresent("org.json.simple.JSONObject", null)) { - return new SimpleJsonJsonParser(); - } if (ClassUtils.isPresent("com.google.gson.Gson", null)) { return new GsonJsonParser(); } if (ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", null)) { return new YamlJsonParser(); } - return new SimpleJsonParser(); + if (ClassUtils.isPresent("org.json.simple.JSONObject", null)) { + return new JsonSimpleJsonParser(); + } + if (ClassUtils.isPresent("org.json.JSONObject", null)) { + return new JsonJsonParser(); + } + return new BasicJsonParser(); } } diff --git a/spring-boot/src/main/java/org/springframework/boot/json/JsonSimpleJsonParser.java b/spring-boot/src/main/java/org/springframework/boot/json/JsonSimpleJsonParser.java new file mode 100644 index 0000000000..ccae1dff03 --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/json/JsonSimpleJsonParser.java @@ -0,0 +1,64 @@ +/* + * 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.json; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; + +/** + * Thin wrapper to adapt {@link org.json.simple.JSONObject} to a {@link JsonParser}. + * + * @author Dave Syer + * @since 1.2.0 + * @see JsonParserFactory + */ +public class JsonSimpleJsonParser implements JsonParser { + + @Override + public Map parseMap(String json) { + Map map = new LinkedHashMap(); + try { + @SuppressWarnings("unchecked") + Map value = (Map) new JSONParser() + .parse(json); + map.putAll(value); + } + catch (ParseException ex) { + throw new IllegalArgumentException("Cannot parse JSON", ex); + } + return map; + } + + @Override + public List parseList(String json) { + List nested = new ArrayList(); + try { + @SuppressWarnings("unchecked") + List value = (List) new JSONParser().parse(json); + nested.addAll(value); + } + catch (ParseException ex) { + throw new IllegalArgumentException("Cannot parse JSON", ex); + } + return nested; + } +} diff --git a/spring-boot/src/main/java/org/springframework/boot/json/SimpleJsonJsonParser.java b/spring-boot/src/main/java/org/springframework/boot/json/SimpleJsonJsonParser.java deleted file mode 100644 index b0c5193621..0000000000 --- a/spring-boot/src/main/java/org/springframework/boot/json/SimpleJsonJsonParser.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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.json; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import org.json.JSONArray; -import org.json.JSONObject; - -/** - * Thin wrapper to adapt {@link JSONObject} to a {@link JsonParser}. - * - * @author Dave Syer - * @since 1.2.0 - * @see JsonParserFactory - */ -public class SimpleJsonJsonParser implements JsonParser { - - @Override - public Map parseMap(String json) { - Map map = new LinkedHashMap(); - putAll(map, new JSONObject(json)); - return map; - } - - private void putAll(Map map, JSONObject object) { - for (Object key : object.keySet()) { - String name = key.toString(); - Object value = object.get(name); - if (value instanceof JSONObject) { - Map nested = new LinkedHashMap(); - putAll(nested, (JSONObject) value); - value = nested; - } - if (value instanceof JSONArray) { - List nested = new ArrayList(); - addAll(nested, (JSONArray) value); - value = nested; - } - map.put(name, value); - } - } - - private void addAll(List list, JSONArray array) { - for (int i = 0; i < array.length(); i++) { - Object value = array.get(i); - if (value instanceof JSONObject) { - Map nested = new LinkedHashMap(); - putAll(nested, (JSONObject) value); - value = nested; - } - if (value instanceof JSONArray) { - List nested = new ArrayList(); - addAll(nested, (JSONArray) value); - value = nested; - } - list.add(value); - } - } - - @Override - public List parseList(String json) { - List nested = new ArrayList(); - addAll(nested, new JSONArray(json)); - return nested; - } - -} diff --git a/spring-boot/src/main/java/org/springframework/boot/json/SimpleJsonParser.java b/spring-boot/src/main/java/org/springframework/boot/json/SimpleJsonParser.java index 2aa96e7422..391c37712e 100644 --- a/spring-boot/src/main/java/org/springframework/boot/json/SimpleJsonParser.java +++ b/spring-boot/src/main/java/org/springframework/boot/json/SimpleJsonParser.java @@ -16,14 +16,6 @@ package org.springframework.boot.json; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import org.springframework.util.StringUtils; - /** * Really basic JSON parser for when you have nothing else available. Comes with some * limitations with respect to the JSON specification (e.g. only supports String values), @@ -32,140 +24,8 @@ import org.springframework.util.StringUtils; * * @author Dave Syer * @see JsonParserFactory + * @deprecated since 1.2.0 in favor of {@link BasicJsonParser}. */ -public class SimpleJsonParser implements JsonParser { - - @Override - public Map parseMap(String json) { - if (json != null) { - json = json.trim(); - if (json.startsWith("{")) { - return parseMapInternal(json); - } - else if (json.equals("")) { - return new HashMap(); - } - } - return null; - } - - @Override - public List parseList(String json) { - if (json != null) { - json = json.trim(); - if (json.startsWith("[")) { - return parseListInternal(json); - } - else if (json.trim().equals("")) { - return new ArrayList(); - } - } - return null; - } - - private List parseListInternal(String json) { - List list = new ArrayList(); - json = trimLeadingCharacter(trimTrailingCharacter(json, ']'), '['); - for (String value : tokenize(json)) { - list.add(parseInternal(value)); - } - return list; - } - - private Object parseInternal(String json) { - if (json.startsWith("[")) { - return parseListInternal(json); - } - - if (json.startsWith("{")) { - return parseMapInternal(json); - } - - if (json.startsWith("\"")) { - return trimTrailingCharacter(trimLeadingCharacter(json, '"'), '"'); - } - - try { - return Long.valueOf(json); - } - catch (NumberFormatException ex) { - // ignore - } - - try { - return Double.valueOf(json); - } - catch (NumberFormatException ex) { - // ignore - } - - return json; - } - - private static String trimTrailingCharacter(String string, char c) { - if (string.length() >= 0 && string.charAt(string.length() - 1) == c) { - return string.substring(0, string.length() - 1); - } - return string; - } - - private static String trimLeadingCharacter(String string, char c) { - if (string.length() >= 0 && string.charAt(0) == c) { - return string.substring(1); - } - return string; - } - - private Map parseMapInternal(String json) { - Map map = new LinkedHashMap(); - json = trimLeadingCharacter(trimTrailingCharacter(json, '}'), '{'); - for (String pair : tokenize(json)) { - String[] values = StringUtils.trimArrayElements(StringUtils.split(pair, ":")); - String key = trimLeadingCharacter(trimTrailingCharacter(values[0], '"'), '"'); - Object value = null; - if (values.length > 0) { - String string = trimLeadingCharacter( - trimTrailingCharacter(values[1], '"'), '"'); - value = parseInternal(string); - } - map.put(key, value); - } - return map; - } - - private List tokenize(String json) { - List list = new ArrayList(); - int index = 0; - int inObject = 0; - int inList = 0; - StringBuilder build = new StringBuilder(); - while (index < json.length()) { - char current = json.charAt(index); - if (current == '{') { - inObject++; - } - if (current == '}') { - inObject--; - } - if (current == '[') { - inList++; - } - if (current == ']') { - inList--; - } - if (current == ',' && inObject == 0 && inList == 0) { - list.add(build.toString()); - build.setLength(0); - } - else { - build.append(current); - } - index++; - } - if (build.length() > 0) { - list.add(build.toString()); - } - return list; - } - +@Deprecated +public class SimpleJsonParser extends BasicJsonParser { } diff --git a/spring-boot/src/test/java/org/springframework/boot/json/SimpleJsonParserTests.java b/spring-boot/src/test/java/org/springframework/boot/json/AbstractJsonParserTests.java similarity index 94% rename from spring-boot/src/test/java/org/springframework/boot/json/SimpleJsonParserTests.java rename to spring-boot/src/test/java/org/springframework/boot/json/AbstractJsonParserTests.java index bf8eb3213c..dba5286b2f 100644 --- a/spring-boot/src/test/java/org/springframework/boot/json/SimpleJsonParserTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/json/AbstractJsonParserTests.java @@ -24,17 +24,15 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; /** - * Tests for {@link SimpleJsonParser}. + * Base for {@link JsonParser} tests. * * @author Dave Syer */ -public class SimpleJsonParserTests { +public abstract class AbstractJsonParserTests { private final JsonParser parser = getParser(); - protected JsonParser getParser() { - return new SimpleJsonParser(); - } + protected abstract JsonParser getParser(); @Test public void testSimpleMap() { diff --git a/spring-boot/src/test/java/org/springframework/boot/json/BasicJsonParserTests.java b/spring-boot/src/test/java/org/springframework/boot/json/BasicJsonParserTests.java new file mode 100644 index 0000000000..6db0194139 --- /dev/null +++ b/spring-boot/src/test/java/org/springframework/boot/json/BasicJsonParserTests.java @@ -0,0 +1,31 @@ +/* + * 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.json; + +/** + * Tests for {@link BasicJsonParser}. + * + * @author Dave Syer + */ +public class BasicJsonParserTests extends AbstractJsonParserTests { + + @Override + protected JsonParser getParser() { + return new BasicJsonParser(); + } + +} diff --git a/spring-boot/src/test/java/org/springframework/boot/json/GsonJsonParserTests.java b/spring-boot/src/test/java/org/springframework/boot/json/GsonJsonParserTests.java index 89a51e728a..7b525c3bfd 100644 --- a/spring-boot/src/test/java/org/springframework/boot/json/GsonJsonParserTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/json/GsonJsonParserTests.java @@ -21,10 +21,11 @@ package org.springframework.boot.json; * * @author Dave Syer */ -public class GsonJsonParserTests extends SimpleJsonParserTests { +public class GsonJsonParserTests extends AbstractJsonParserTests { @Override protected JsonParser getParser() { return new GsonJsonParser(); } + } diff --git a/spring-boot/src/test/java/org/springframework/boot/json/JacksonParserTests.java b/spring-boot/src/test/java/org/springframework/boot/json/JacksonParserTests.java index 1944a4847d..3927a75a05 100644 --- a/spring-boot/src/test/java/org/springframework/boot/json/JacksonParserTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/json/JacksonParserTests.java @@ -21,10 +21,11 @@ package org.springframework.boot.json; * * @author Dave Syer */ -public class JacksonParserTests extends SimpleJsonParserTests { +public class JacksonParserTests extends AbstractJsonParserTests { @Override protected JsonParser getParser() { return new JacksonJsonParser(); } + } diff --git a/spring-boot/src/test/java/org/springframework/boot/json/JsonJsonParserTests.java b/spring-boot/src/test/java/org/springframework/boot/json/JsonJsonParserTests.java index 2eb4d892ec..bc96f46787 100644 --- a/spring-boot/src/test/java/org/springframework/boot/json/JsonJsonParserTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/json/JsonJsonParserTests.java @@ -17,14 +17,15 @@ package org.springframework.boot.json; /** - * Tests for {@link JsonJsonParser}. + * Tests for {@link JsonSimpleJsonParser}. * * @author Dave Syer */ -public class JsonJsonParserTests extends SimpleJsonParserTests { +public class JsonJsonParserTests extends AbstractJsonParserTests { @Override protected JsonParser getParser() { - return new JsonJsonParser(); + return new JsonSimpleJsonParser(); } + } diff --git a/spring-boot/src/test/java/org/springframework/boot/json/SimpleJsonJsonParserTests.java b/spring-boot/src/test/java/org/springframework/boot/json/SimpleJsonJsonParserTests.java index 7587cc2d8a..9a4a5cf907 100644 --- a/spring-boot/src/test/java/org/springframework/boot/json/SimpleJsonJsonParserTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/json/SimpleJsonJsonParserTests.java @@ -17,14 +17,15 @@ package org.springframework.boot.json; /** - * Tests for {@link SimpleJsonJsonParser}. + * Tests for {@link JsonJsonParser}. * * @author Dave Syer */ -public class SimpleJsonJsonParserTests extends SimpleJsonParserTests { +public class SimpleJsonJsonParserTests extends AbstractJsonParserTests { @Override protected JsonParser getParser() { - return new SimpleJsonJsonParser(); + return new JsonJsonParser(); } + } diff --git a/spring-boot/src/test/java/org/springframework/boot/json/YamlJsonParserTests.java b/spring-boot/src/test/java/org/springframework/boot/json/YamlJsonParserTests.java index 3ab39fef9a..7015304ce6 100644 --- a/spring-boot/src/test/java/org/springframework/boot/json/YamlJsonParserTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/json/YamlJsonParserTests.java @@ -21,10 +21,11 @@ package org.springframework.boot.json; * * @author Dave Syer */ -public class YamlJsonParserTests extends SimpleJsonParserTests { +public class YamlJsonParserTests extends AbstractJsonParserTests { @Override protected JsonParser getParser() { return new YamlJsonParser(); } + }