diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java index 5335a4940c2..e274303a831 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java @@ -38,6 +38,7 @@ import org.apache.coyote.AbstractProtocol; import org.apache.coyote.ProtocolHandler; import org.apache.coyote.http11.AbstractHttp11Protocol; import org.springframework.boot.autoconfigure.web.ServerProperties.Session.Cookie; +import org.springframework.boot.cloud.CloudPlatform; import org.springframework.boot.context.embedded.Compression; import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer; import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer; @@ -55,7 +56,9 @@ import org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServle import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.DeprecatedConfigurationProperty; import org.springframework.boot.context.properties.NestedConfigurationProperty; +import org.springframework.context.EnvironmentAware; import org.springframework.core.Ordered; +import org.springframework.core.env.Environment; import org.springframework.util.StringUtils; /** @@ -70,7 +73,8 @@ import org.springframework.util.StringUtils; * @author Marcos Barbero */ @ConfigurationProperties(prefix = "server", ignoreUnknownFields = true) -public class ServerProperties implements EmbeddedServletContainerCustomizer, Ordered { +public class ServerProperties implements EmbeddedServletContainerCustomizer, + EnvironmentAware, Ordered { /** * Server HTTP port. @@ -106,7 +110,7 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer, Ord /** * If X-Forwarded-* headers should be applied to the HttpRequest. */ - private boolean useForwardHeaders; + private Boolean useForwardHeaders; private Session session = new Session(); @@ -125,11 +129,18 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer, Ord private final Undertow undertow = new Undertow(); + private Environment environment; + @Override public int getOrder() { return 0; } + @Override + public void setEnvironment(Environment environment) { + this.environment = environment; + } + @Override public void customize(ConfigurableEmbeddedServletContainer container) { if (getPort() != null) { @@ -280,14 +291,22 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer, Ord return this.contextParameters; } - public boolean isUseForwardHeaders() { + public Boolean isUseForwardHeaders() { return this.useForwardHeaders; } - public void setUseForwardHeaders(boolean useForwardHeaders) { + public void setUseForwardHeaders(Boolean useForwardHeaders) { this.useForwardHeaders = useForwardHeaders; } + protected final boolean getOrDeduceUseForwardHeaders() { + if (this.useForwardHeaders != null) { + return this.useForwardHeaders; + } + CloudPlatform platform = CloudPlatform.getActive(this.environment); + return (platform == null ? false : platform.isUsingForwardHeaders()); + } + /** * Get the session timeout. * @return the session timeout @@ -722,7 +741,7 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer, Ord // For back compatibility the valve is also enabled if protocol-header is set if (StringUtils.hasText(protocolHeader) || StringUtils.hasText(remoteIpHeader) - || properties.isUseForwardHeaders()) { + || properties.getOrDeduceUseForwardHeaders()) { RemoteIpValve valve = new RemoteIpValve(); valve.setProtocolHeader(StringUtils.hasLength(protocolHeader) ? protocolHeader : "X-Forwarded-Proto"); @@ -856,7 +875,7 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer, Ord void customizeJetty(ServerProperties serverProperties, JettyEmbeddedServletContainerFactory factory) { - factory.setUseForwardHeaders(serverProperties.isUseForwardHeaders()); + factory.setUseForwardHeaders(serverProperties.getOrDeduceUseForwardHeaders()); } } @@ -1007,7 +1026,7 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer, Ord factory.setAccessLogDirectory(this.accesslog.dir); factory.setAccessLogPattern(this.accesslog.pattern); factory.setAccessLogEnabled(this.accesslog.enabled); - factory.setUseForwardHeaders(serverProperties.isUseForwardHeaders()); + factory.setUseForwardHeaders(serverProperties.getOrDeduceUseForwardHeaders()); } public static class Accesslog { diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java index 298e6bdbf19..1dcae8c618c 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java @@ -42,6 +42,7 @@ import org.springframework.boot.context.embedded.ServletContextInitializer; import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory; +import org.springframework.mock.env.MockEnvironment; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; @@ -61,6 +62,7 @@ import static org.mockito.Mockito.verify; * @author Dave Syer * @author Stephane Nicoll * @author Andy Wilkinson + * @author Phillip Webb */ public class ServerPropertiesTests { @@ -242,10 +244,8 @@ public class ServerPropertiesTests { Map map = new HashMap(); map.put("server.display-name", "MyBootApp"); bindProperties(map); - TomcatEmbeddedServletContainerFactory container = new TomcatEmbeddedServletContainerFactory(); this.properties.customize(container); - assertEquals("MyBootApp", container.getDisplayName()); } @@ -277,6 +277,12 @@ public class ServerPropertiesTests { testRemoteIpValveConfigured(); } + @Test + public void deduceUseForwardHeadersTomcat() throws Exception { + this.properties.setEnvironment(new MockEnvironment().withProperty("DYNO", "-")); + testRemoteIpValveConfigured(); + } + private void testRemoteIpValveConfigured() { TomcatEmbeddedServletContainerFactory container = new TomcatEmbeddedServletContainerFactory(); this.properties.customize(container); @@ -321,6 +327,13 @@ public class ServerPropertiesTests { assertEquals("192.168.0.1", remoteIpValve.getInternalProxies()); } + @Test + public void defaultUseForwardHeadersUndertow() throws Exception { + UndertowEmbeddedServletContainerFactory container = spy(new UndertowEmbeddedServletContainerFactory()); + this.properties.customize(container); + verify(container).setUseForwardHeaders(false); + } + @Test public void setUseForwardHeadersUndertow() throws Exception { this.properties.setUseForwardHeaders(true); @@ -329,6 +342,21 @@ public class ServerPropertiesTests { verify(container).setUseForwardHeaders(true); } + @Test + public void deduceUseForwardHeadersUndertow() throws Exception { + this.properties.setEnvironment(new MockEnvironment().withProperty("DYNO", "-")); + UndertowEmbeddedServletContainerFactory container = spy(new UndertowEmbeddedServletContainerFactory()); + this.properties.customize(container); + verify(container).setUseForwardHeaders(true); + } + + @Test + public void defaultUseForwardHeadersJetty() throws Exception { + JettyEmbeddedServletContainerFactory container = spy(new JettyEmbeddedServletContainerFactory()); + this.properties.customize(container); + verify(container).setUseForwardHeaders(false); + } + @Test public void setUseForwardHeadersJetty() throws Exception { this.properties.setUseForwardHeaders(true); @@ -337,6 +365,14 @@ public class ServerPropertiesTests { verify(container).setUseForwardHeaders(true); } + @Test + public void deduceUseForwardHeadersJetty() throws Exception { + this.properties.setEnvironment(new MockEnvironment().withProperty("DYNO", "-")); + JettyEmbeddedServletContainerFactory container = spy(new JettyEmbeddedServletContainerFactory()); + this.properties.customize(container); + verify(container).setUseForwardHeaders(true); + } + private void bindProperties(Map map) { new RelaxedDataBinder(this.properties, "server").bind(new MutablePropertyValues( map)); diff --git a/spring-boot/src/main/java/org/springframework/boot/cloud/CloudFoundryVcapEnvironmentPostProcessor.java b/spring-boot/src/main/java/org/springframework/boot/cloud/CloudFoundryVcapEnvironmentPostProcessor.java new file mode 100644 index 00000000000..5b9bd2153a6 --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/cloud/CloudFoundryVcapEnvironmentPostProcessor.java @@ -0,0 +1,241 @@ +/* + * Copyright 2010-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. + */ + +package org.springframework.boot.cloud; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.context.config.ConfigFileEnvironmentPostProcessor; +import org.springframework.boot.env.EnvironmentPostProcessor; +import org.springframework.boot.json.JsonParser; +import org.springframework.boot.json.JsonParserFactory; +import org.springframework.core.Ordered; +import org.springframework.core.env.CommandLinePropertySource; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.Environment; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.core.env.PropertiesPropertySource; +import org.springframework.util.StringUtils; + +/** + * An {@link EnvironmentPostProcessor} that knows where to find VCAP (a.k.a. Cloud + * Foundry) meta data in the existing environment. It parses out the VCAP_APPLICATION and + * VCAP_SERVICES meta data and dumps it in a form that is easily consumed by + * {@link Environment} users. If the app is running in Cloud Foundry then both meta data + * items are JSON objects encoded in OS environment variables. VCAP_APPLICATION is a + * shallow hash with basic information about the application (name, instance id, instance + * index, etc.), and VCAP_SERVICES is a hash of lists where the keys are service labels + * and the values are lists of hashes of service instance meta data. Examples are: + * + *
+ * VCAP_APPLICATION: {"instance_id":"2ce0ac627a6c8e47e936d829a3a47b5b","instance_index":0,
+ *   "version":"0138c4a6-2a73-416b-aca0-572c09f7ca53","name":"foo",
+ *   "uris":["foo.cfapps.io"], ...}
+ * VCAP_SERVICES: {"rds-mysql-1.0":[{"name":"mysql","label":"rds-mysql-1.0","plan":"10mb",
+ *   "credentials":{"name":"d04fb13d27d964c62b267bbba1cffb9da","hostname":"mysql-service-public.clqg2e2w3ecf.us-east-1.rds.amazonaws.com",
+ *   "host":"mysql-service-public.clqg2e2w3ecf.us-east-1.rds.amazonaws.com","port":3306,"user":"urpRuqTf8Cpe6",
+ *   "username":"urpRuqTf8Cpe6","password":"pxLsGVpsC9A5S"}
+ * }]}
+ * 
+ * + * These objects are flattened into properties. The VCAP_APPLICATION object goes straight + * to {@code vcap.application.*} in a fairly obvious way, and the VCAP_SERVICES object is + * unwrapped so that it is a hash of objects with key equal to the service instance name + * (e.g. "mysql" in the example above), and value equal to that instances properties, and + * then flattened in the same way. E.g. + * + *
+ * vcap.application.instance_id: 2ce0ac627a6c8e47e936d829a3a47b5b
+ * vcap.application.version: 0138c4a6-2a73-416b-aca0-572c09f7ca53
+ * vcap.application.name: foo
+ * vcap.application.uris[0]: foo.cfapps.io
+ *
+ * vcap.services.mysql.name: mysql
+ * vcap.services.mysql.label: rds-mysql-1.0
+ * vcap.services.mysql.credentials.name: d04fb13d27d964c62b267bbba1cffb9da
+ * vcap.services.mysql.credentials.port: 3306
+ * vcap.services.mysql.credentials.host: mysql-service-public.clqg2e2w3ecf.us-east-1.rds.amazonaws.com
+ * vcap.services.mysql.credentials.username: urpRuqTf8Cpe6
+ * vcap.services.mysql.credentials.password: pxLsGVpsC9A5S
+ * ...
+ * 
+ * + * N.B. this initializer is mainly intended for informational use (the application and + * instance ids are particularly useful). For service binding you might find that Spring + * Cloud is more convenient and more robust against potential changes in Cloud Foundry. + * + * @author Dave Syer + * @author Andy Wilkinson + */ +public class CloudFoundryVcapEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered { + + private static final Log logger = LogFactory + .getLog(CloudFoundryVcapEnvironmentPostProcessor.class); + + private static final String VCAP_APPLICATION = "VCAP_APPLICATION"; + + private static final String VCAP_SERVICES = "VCAP_SERVICES"; + + // Before ConfigFileApplicationListener so values there can use these ones + private int order = ConfigFileEnvironmentPostProcessor.DEFAULT_ORDER - 1; + + private final JsonParser parser = JsonParserFactory.getJsonParser(); + + public void setOrder(int order) { + this.order = order; + } + + @Override + public int getOrder() { + return this.order; + } + + @Override + public void postProcessEnvironment(ConfigurableEnvironment environment, + SpringApplication application) { + if (CloudPlatform.CLOUD_FOUNDRY.isActive(environment)) { + Properties properties = new Properties(); + addWithPrefix(properties, getPropertiesFromApplication(environment), + "vcap.application."); + addWithPrefix(properties, getPropertiesFromServices(environment), + "vcap.services."); + MutablePropertySources propertySources = environment.getPropertySources(); + if (propertySources + .contains(CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME)) { + propertySources.addAfter( + CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME, + new PropertiesPropertySource("vcap", properties)); + } + else { + propertySources + .addFirst(new PropertiesPropertySource("vcap", properties)); + } + } + } + + private void addWithPrefix(Properties properties, Properties other, String prefix) { + for (String key : other.stringPropertyNames()) { + String prefixed = prefix + key; + properties.setProperty(prefixed, other.getProperty(key)); + } + } + + private Properties getPropertiesFromApplication(Environment environment) { + Properties properties = new Properties(); + try { + String property = environment.getProperty(VCAP_APPLICATION, "{}"); + Map map = this.parser.parseMap(property); + extractPropertiesFromApplication(properties, map); + } + catch (Exception ex) { + logger.error("Could not parse VCAP_APPLICATION", ex); + } + return properties; + } + + private Properties getPropertiesFromServices(Environment environment) { + Properties properties = new Properties(); + try { + String property = environment.getProperty(VCAP_SERVICES, "{}"); + Map map = this.parser.parseMap(property); + extractPropertiesFromServices(properties, map); + } + catch (Exception ex) { + logger.error("Could not parse VCAP_SERVICES", ex); + } + return properties; + } + + private void extractPropertiesFromApplication(Properties properties, + Map map) { + if (map != null) { + flatten(properties, map, ""); + } + } + + private void extractPropertiesFromServices(Properties properties, + Map map) { + if (map != null) { + for (Object services : map.values()) { + @SuppressWarnings("unchecked") + List list = (List) services; + for (Object object : list) { + @SuppressWarnings("unchecked") + Map service = (Map) object; + String key = (String) service.get("name"); + if (key == null) { + key = (String) service.get("label"); + } + flatten(properties, service, key); + } + } + } + } + + @SuppressWarnings("unchecked") + private void flatten(Properties properties, Map input, String path) { + for (Entry entry : input.entrySet()) { + String key = getFullKey(path, entry.getKey()); + Object value = entry.getValue(); + if (value instanceof Map) { + // Need a compound key + flatten(properties, (Map) value, key); + } + else if (value instanceof Collection) { + // Need a compound key + Collection collection = (Collection) value; + properties.put(key, + StringUtils.collectionToCommaDelimitedString(collection)); + int count = 0; + for (Object item : collection) { + String itemKey = "[" + (count++) + "]"; + flatten(properties, Collections.singletonMap(itemKey, item), key); + } + } + else if (value instanceof String) { + properties.put(key, value); + } + else if (value instanceof Number) { + properties.put(key, value.toString()); + } + else if (value instanceof Boolean) { + properties.put(key, value.toString()); + } + else { + properties.put(key, value == null ? "" : value); + } + } + } + + private String getFullKey(String path, String key) { + if (!StringUtils.hasText(path)) { + return key; + } + if (key.startsWith("[")) { + return path + key; + } + return path + "." + key; + } + +} diff --git a/spring-boot/src/main/java/org/springframework/boot/cloud/CloudPlatform.java b/spring-boot/src/main/java/org/springframework/boot/cloud/CloudPlatform.java new file mode 100644 index 00000000000..5f07071d0c7 --- /dev/null +++ b/spring-boot/src/main/java/org/springframework/boot/cloud/CloudPlatform.java @@ -0,0 +1,88 @@ +/* + * 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. + */ + +package org.springframework.boot.cloud; + +import org.springframework.core.env.Environment; + +/** + * Simple detection for well known cloud platforms. For more advanced cloud provider + * integration consider the Spring Clould project. + * + * @author Phillip Webb + * @since 1.3.0 + * @see "http://cloud.spring.io" + */ +public enum CloudPlatform { + + /** + * Cloud Foundry platform. + */ + CLOUD_FOUNDRY { + + @Override + public boolean isActive(Environment environment) { + return environment.containsProperty("VCAP_APPLICATION") + || environment.containsProperty("VCAP_SERVICES"); + } + + }, + + /** + * Heroku platform. + */ + HEROKU { + + @Override + public boolean isActive(Environment environment) { + return environment.containsProperty("DYNO"); + } + + }; + + /** + * Determines if the platform is active (i.e. the application is running in it). + * @param environment the environment + * @return if the platform is active. + */ + public abstract boolean isActive(Environment environment); + + /** + * Returns if the platform is behind a load balancer and uses + * {@literal X-Forwarded-For} headers. + * @return if {@literal X-Forwarded-For} headers are used + */ + public boolean isUsingForwardHeaders() { + return true; + } + + /** + * Returns the active {@link CloudPlatform} or {@code null} if one cannot be deduced. + * @param environment the environment + * @return the {@link CloudPlatform} or {@code null} + */ + public static CloudPlatform getActive(Environment environment) { + if (environment != null) { + for (CloudPlatform cloudPlatform : values()) { + if (cloudPlatform.isActive(environment)) { + return cloudPlatform; + } + } + } + return null; + } + +} diff --git a/spring-boot/src/main/java/org/springframework/boot/cloudfoundry/package-info.java b/spring-boot/src/main/java/org/springframework/boot/cloud/package-info.java similarity index 78% rename from spring-boot/src/main/java/org/springframework/boot/cloudfoundry/package-info.java rename to spring-boot/src/main/java/org/springframework/boot/cloud/package-info.java index ecb66184c4a..1d9c3ad8abd 100644 --- a/spring-boot/src/main/java/org/springframework/boot/cloudfoundry/package-info.java +++ b/spring-boot/src/main/java/org/springframework/boot/cloud/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2014 the original author or authors. + * 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. @@ -15,7 +15,7 @@ */ /** - * Support for Cloud Foundry PAAS based deployment. + * Low level support for Cloud deployments. */ -package org.springframework.boot.cloudfoundry; +package org.springframework.boot.cloud; diff --git a/spring-boot/src/main/java/org/springframework/boot/cloudfoundry/VcapEnvironmentPostProcessor.java b/spring-boot/src/main/java/org/springframework/boot/cloudfoundry/VcapEnvironmentPostProcessor.java index 44e4bc6e5fe..93f15b6d493 100644 --- a/spring-boot/src/main/java/org/springframework/boot/cloudfoundry/VcapEnvironmentPostProcessor.java +++ b/spring-boot/src/main/java/org/springframework/boot/cloudfoundry/VcapEnvironmentPostProcessor.java @@ -16,27 +16,9 @@ package org.springframework.boot.cloudfoundry; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Properties; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.context.config.ConfigFileEnvironmentPostProcessor; +import org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor; import org.springframework.boot.env.EnvironmentPostProcessor; -import org.springframework.boot.json.JsonParser; -import org.springframework.boot.json.JsonParserFactory; -import org.springframework.core.Ordered; -import org.springframework.core.env.CommandLinePropertySource; -import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; -import org.springframework.core.env.MutablePropertySources; -import org.springframework.core.env.PropertiesPropertySource; -import org.springframework.util.StringUtils; /** * An {@link EnvironmentPostProcessor} that knows where to find VCAP (a.k.a. Cloud @@ -87,156 +69,10 @@ import org.springframework.util.StringUtils; * * @author Dave Syer * @author Andy Wilkinson + * @deprecated since 1.3.0 in favor of CloudFoundryVcapEnvironmentPostProcessor */ -public class VcapEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered { - - private static final Log logger = LogFactory - .getLog(VcapEnvironmentPostProcessor.class); - - private static final String VCAP_APPLICATION = "VCAP_APPLICATION"; - - private static final String VCAP_SERVICES = "VCAP_SERVICES"; - - // Before ConfigFileApplicationListener so values there can use these ones - private int order = ConfigFileEnvironmentPostProcessor.DEFAULT_ORDER - 1; - - private final JsonParser parser = JsonParserFactory.getJsonParser(); - - public void setOrder(int order) { - this.order = order; - } - - @Override - public int getOrder() { - return this.order; - } - - @Override - public void postProcessEnvironment(ConfigurableEnvironment environment, - SpringApplication application) { - if (!environment.containsProperty(VCAP_APPLICATION) - && !environment.containsProperty(VCAP_SERVICES)) { - return; - } - Properties properties = new Properties(); - addWithPrefix(properties, getPropertiesFromApplication(environment), - "vcap.application."); - addWithPrefix(properties, getPropertiesFromServices(environment), - "vcap.services."); - MutablePropertySources propertySources = environment.getPropertySources(); - if (propertySources - .contains(CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME)) { - propertySources.addAfter( - CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME, - new PropertiesPropertySource("vcap", properties)); - } - else { - propertySources.addFirst(new PropertiesPropertySource("vcap", properties)); - } - } - - private void addWithPrefix(Properties properties, Properties other, String prefix) { - for (String key : other.stringPropertyNames()) { - String prefixed = prefix + key; - properties.setProperty(prefixed, other.getProperty(key)); - } - } - - private Properties getPropertiesFromApplication(Environment environment) { - Properties properties = new Properties(); - try { - Map map = this.parser.parseMap(environment.getProperty( - VCAP_APPLICATION, "{}")); - extractPropertiesFromApplication(properties, map); - } - catch (Exception ex) { - logger.error("Could not parse VCAP_APPLICATION", ex); - } - return properties; - } - - private Properties getPropertiesFromServices(Environment environment) { - Properties properties = new Properties(); - try { - Map map = this.parser.parseMap(environment.getProperty( - VCAP_SERVICES, "{}")); - extractPropertiesFromServices(properties, map); - } - catch (Exception ex) { - logger.error("Could not parse VCAP_SERVICES", ex); - } - return properties; - } - - private void extractPropertiesFromApplication(Properties properties, - Map map) { - if (map != null) { - flatten(properties, map, ""); - } - } - - private void extractPropertiesFromServices(Properties properties, - Map map) { - if (map != null) { - for (Object services : map.values()) { - @SuppressWarnings("unchecked") - List list = (List) services; - for (Object object : list) { - @SuppressWarnings("unchecked") - Map service = (Map) object; - String key = (String) service.get("name"); - if (key == null) { - key = (String) service.get("label"); - } - flatten(properties, service, key); - } - } - } - } - - @SuppressWarnings("unchecked") - private void flatten(Properties properties, Map input, String path) { - for (Entry entry : input.entrySet()) { - String key = getFullKey(path, entry.getKey()); - Object value = entry.getValue(); - if (value instanceof Map) { - // Need a compound key - flatten(properties, (Map) value, key); - } - else if (value instanceof Collection) { - // Need a compound key - Collection collection = (Collection) value; - properties.put(key, - StringUtils.collectionToCommaDelimitedString(collection)); - int count = 0; - for (Object item : collection) { - String itemKey = "[" + (count++) + "]"; - flatten(properties, Collections.singletonMap(itemKey, item), key); - } - } - else if (value instanceof String) { - properties.put(key, value); - } - else if (value instanceof Number) { - properties.put(key, value.toString()); - } - else if (value instanceof Boolean) { - properties.put(key, value.toString()); - } - else { - properties.put(key, value == null ? "" : value); - } - } - } - - private String getFullKey(String path, String key) { - if (!StringUtils.hasText(path)) { - return key; - } - if (key.startsWith("[")) { - return path + key; - } - return path + "." + key; - } +@Deprecated +public class VcapEnvironmentPostProcessor extends + CloudFoundryVcapEnvironmentPostProcessor { } diff --git a/spring-boot/src/main/resources/META-INF/spring.factories b/spring-boot/src/main/resources/META-INF/spring.factories index 02af0a6206c..ee6f17a1ea6 100644 --- a/spring-boot/src/main/resources/META-INF/spring.factories +++ b/spring-boot/src/main/resources/META-INF/spring.factories @@ -28,5 +28,5 @@ org.springframework.boot.logging.LoggingApplicationListener # Environment Post Processors org.springframework.boot.env.EnvironmentPostProcessor=\ -org.springframework.boot.cloudfoundry.VcapEnvironmentPostProcessor,\ -org.springframework.boot.context.config.ConfigFileEnvironmentPostProcessor \ No newline at end of file +org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\ +org.springframework.boot.context.config.ConfigFileEnvironmentPostProcessor diff --git a/spring-boot/src/test/java/org/springframework/boot/cloud/CloudPlatformTests.java b/spring-boot/src/test/java/org/springframework/boot/cloud/CloudPlatformTests.java new file mode 100644 index 00000000000..b1d714710fb --- /dev/null +++ b/spring-boot/src/test/java/org/springframework/boot/cloud/CloudPlatformTests.java @@ -0,0 +1,75 @@ +/* + * 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. + */ + +package org.springframework.boot.cloud; + +import org.junit.Test; +import org.springframework.core.env.Environment; +import org.springframework.mock.env.MockEnvironment; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertThat; + +/** + * Tests for {@link CloudPlatform}. + * + * @author Phillip Webb + */ +public class CloudPlatformTests { + + @Test + public void getActiveWhenEnvironmentIsNullShouldReturnNull() throws Exception { + CloudPlatform platform = CloudPlatform.getActive(null); + assertThat(platform, nullValue()); + } + + @Test + public void getActiveWhenNotInCloudShouldReturnNull() throws Exception { + Environment environment = new MockEnvironment(); + CloudPlatform platform = CloudPlatform.getActive(environment); + assertThat(platform, nullValue()); + + } + + @Test + public void getActiveWhenHasVcapApplicationShouldReturnCloudFoundry() + throws Exception { + Environment environment = new MockEnvironment().withProperty("VCAP_APPLICATION", + "---"); + CloudPlatform platform = CloudPlatform.getActive(environment); + assertThat(platform, equalTo(CloudPlatform.CLOUD_FOUNDRY)); + assertThat(platform.isActive(environment), equalTo(true)); + } + + @Test + public void getActiveWhenHasVcapServicesShouldReturnCloudFoundry() throws Exception { + Environment environment = new MockEnvironment().withProperty("VCAP_SERVICES", + "---"); + CloudPlatform platform = CloudPlatform.getActive(environment); + assertThat(platform, equalTo(CloudPlatform.CLOUD_FOUNDRY)); + assertThat(platform.isActive(environment), equalTo(true)); + } + + @Test + public void getActiveWhenHasDynoShouldReturnHeroku() throws Exception { + Environment environment = new MockEnvironment().withProperty("DYNO", "---"); + CloudPlatform platform = CloudPlatform.getActive(environment); + assertThat(platform, equalTo(CloudPlatform.HEROKU)); + assertThat(platform.isActive(environment), equalTo(true)); + } + +} diff --git a/spring-boot/src/test/java/org/springframework/boot/cloudfoundry/VcapEnvironmentPostProcessorTests.java b/spring-boot/src/test/java/org/springframework/boot/cloud/cloudfoundry/CloudFoundryVcapEnvironmentPostProcessorTests.java similarity index 93% rename from spring-boot/src/test/java/org/springframework/boot/cloudfoundry/VcapEnvironmentPostProcessorTests.java rename to spring-boot/src/test/java/org/springframework/boot/cloud/cloudfoundry/CloudFoundryVcapEnvironmentPostProcessorTests.java index ead065bf552..89cdb1b0f83 100644 --- a/spring-boot/src/test/java/org/springframework/boot/cloudfoundry/VcapEnvironmentPostProcessorTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/cloud/cloudfoundry/CloudFoundryVcapEnvironmentPostProcessorTests.java @@ -14,9 +14,10 @@ * limitations under the License. */ -package org.springframework.boot.cloudfoundry; +package org.springframework.boot.cloud.cloudfoundry; import org.junit.Test; +import org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor; import org.springframework.boot.test.EnvironmentTestUtils; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -25,14 +26,14 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; /** - * Tests for {@link VcapEnvironmentPostProcessor}. + * Tests for {@link CloudFoundryVcapEnvironmentPostProcessor}. * * @author Dave Syer * @author Andy Wilkinson */ -public class VcapEnvironmentPostProcessorTests { +public class CloudFoundryVcapEnvironmentPostProcessorTests { - private final VcapEnvironmentPostProcessor initializer = new VcapEnvironmentPostProcessor(); + private final CloudFoundryVcapEnvironmentPostProcessor initializer = new CloudFoundryVcapEnvironmentPostProcessor(); private final ConfigurableApplicationContext context = new AnnotationConfigApplicationContext();