From e60efc7e55933d22ac5d614fd59af64aacabdb5f Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Tue, 4 Jun 2013 16:18:12 +0100 Subject: [PATCH] [bs-17], [bs-146] Add some ApplicationContextInitializers * One for Cloud Foundry and one for the application context ID * If app runs in Cloud Foundry vcap.application.* and vcap.services.* will be populated in the Environment * The ApplicationContext ID is set to something supposedly unique (e.g. name:index in a Cloud Foundry app) [#50968415] [#48153639] --- .../ManagementAutoConfiguration.java | 3 - .../ServiceBootstrapApplicationTests.java | 2 +- spring-bootstrap/pom.xml | 5 + .../bootstrap/config/JacksonParser.java | 51 +++++ .../bootstrap/config/JsonParser.java | 31 +++ .../bootstrap/config/SimpleJsonParser.java | 148 +++++++++++++ .../bootstrap/config/YamlParser.java | 43 ++++ ...ontextIdApplicationContextInitializer.java | 70 ++++++ .../VcapApplicationContextInitializer.java | 200 ++++++++++++++++++ .../main/resources/META-INF/spring.factories | 2 + .../bootstrap/config/JacksonParserTests.java | 28 +++ .../config/SimpleJsonParserTests.java | 73 +++++++ .../bootstrap/config/YamlParserTests.java | 28 +++ ...tIdApplicationContextInitializerTests.java | 77 +++++++ ...capApplicationContextInitializerTests.java | 56 +++++ 15 files changed, 813 insertions(+), 4 deletions(-) create mode 100644 spring-bootstrap/src/main/java/org/springframework/bootstrap/config/JacksonParser.java create mode 100644 spring-bootstrap/src/main/java/org/springframework/bootstrap/config/JsonParser.java create mode 100644 spring-bootstrap/src/main/java/org/springframework/bootstrap/config/SimpleJsonParser.java create mode 100644 spring-bootstrap/src/main/java/org/springframework/bootstrap/config/YamlParser.java create mode 100644 spring-bootstrap/src/main/java/org/springframework/bootstrap/context/initializer/ContextIdApplicationContextInitializer.java create mode 100644 spring-bootstrap/src/main/java/org/springframework/bootstrap/context/initializer/VcapApplicationContextInitializer.java create mode 100644 spring-bootstrap/src/test/java/org/springframework/bootstrap/config/JacksonParserTests.java create mode 100644 spring-bootstrap/src/test/java/org/springframework/bootstrap/config/SimpleJsonParserTests.java create mode 100644 spring-bootstrap/src/test/java/org/springframework/bootstrap/config/YamlParserTests.java create mode 100644 spring-bootstrap/src/test/java/org/springframework/bootstrap/context/initializer/ContextIdApplicationContextInitializerTests.java create mode 100644 spring-bootstrap/src/test/java/org/springframework/bootstrap/context/initializer/VcapApplicationContextInitializerTests.java diff --git a/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/autoconfigure/ManagementAutoConfiguration.java b/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/autoconfigure/ManagementAutoConfiguration.java index fc1684739e3..b6851879d82 100644 --- a/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/autoconfigure/ManagementAutoConfiguration.java +++ b/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/autoconfigure/ManagementAutoConfiguration.java @@ -40,7 +40,6 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.event.ContextClosedEvent; import org.springframework.context.event.ContextRefreshedEvent; -import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.util.ClassUtils; @@ -103,8 +102,6 @@ public class ManagementAutoConfiguration implements ApplicationContextAware { .getPort()) { AnnotationConfigEmbeddedWebApplicationContext context = new AnnotationConfigEmbeddedWebApplicationContext(); context.setParent(ManagementAutoConfiguration.this.parent); - context.setEnvironment((ConfigurableEnvironment) ManagementAutoConfiguration.this.parent - .getEnvironment()); context.register(assembleConfigClasses(ManagementAutoConfiguration.this.parent)); context.refresh(); ManagementAutoConfiguration.this.context = context; diff --git a/spring-bootstrap-samples/spring-bootstrap-actuator-sample/src/test/java/org/springframework/bootstrap/sample/test/ServiceBootstrapApplicationTests.java b/spring-bootstrap-samples/spring-bootstrap-actuator-sample/src/test/java/org/springframework/bootstrap/sample/test/ServiceBootstrapApplicationTests.java index 13d700a548a..9170072861a 100644 --- a/spring-bootstrap-samples/spring-bootstrap-actuator-sample/src/test/java/org/springframework/bootstrap/sample/test/ServiceBootstrapApplicationTests.java +++ b/spring-bootstrap-samples/spring-bootstrap-actuator-sample/src/test/java/org/springframework/bootstrap/sample/test/ServiceBootstrapApplicationTests.java @@ -138,7 +138,7 @@ public class ServiceBootstrapApplicationTests { @SuppressWarnings("unchecked") Map body = (Map) entity.getBody().get(0); assertTrue("Wrong body: " + body, - ((String) body.get("context")).startsWith("org.springframework")); + ((String) body.get("context")).startsWith("application")); } private RestTemplate getRestTemplate() { diff --git a/spring-bootstrap/pom.xml b/spring-bootstrap/pom.xml index d20ddf08a5d..a1a7848fbf2 100644 --- a/spring-bootstrap/pom.xml +++ b/spring-bootstrap/pom.xml @@ -39,6 +39,11 @@ snakeyaml true + + com.fasterxml.jackson.core + jackson-databind + true + org.apache.tomcat.embed tomcat-embed-core diff --git a/spring-bootstrap/src/main/java/org/springframework/bootstrap/config/JacksonParser.java b/spring-bootstrap/src/main/java/org/springframework/bootstrap/config/JacksonParser.java new file mode 100644 index 00000000000..361db5273ce --- /dev/null +++ b/spring-bootstrap/src/main/java/org/springframework/bootstrap/config/JacksonParser.java @@ -0,0 +1,51 @@ +/* + * Copyright 2012-2013 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.bootstrap.config; + +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * @author Dave Syer + * + */ +public class JacksonParser implements JsonParser { + + @Override + public Map parseMap(String json) { + try { + @SuppressWarnings("unchecked") + Map map = new ObjectMapper().readValue(json, Map.class); + return map; + } catch (Exception e) { + throw new IllegalArgumentException("Cannot parse JSON", e); + } + } + + @Override + public List parseList(String json) { + try { + @SuppressWarnings("unchecked") + List list = new ObjectMapper().readValue(json, List.class); + return list; + } catch (Exception e) { + throw new IllegalArgumentException("Cannot parse JSON", e); + } + } + +} diff --git a/spring-bootstrap/src/main/java/org/springframework/bootstrap/config/JsonParser.java b/spring-bootstrap/src/main/java/org/springframework/bootstrap/config/JsonParser.java new file mode 100644 index 00000000000..5dd77d7aff8 --- /dev/null +++ b/spring-bootstrap/src/main/java/org/springframework/bootstrap/config/JsonParser.java @@ -0,0 +1,31 @@ +/* + * Copyright 2012-2013 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.bootstrap.config; + +import java.util.List; +import java.util.Map; + +/** + * @author Dave Syer + * + */ +public interface JsonParser { + + Map parseMap(String json); + + List parseList(String json); + +} diff --git a/spring-bootstrap/src/main/java/org/springframework/bootstrap/config/SimpleJsonParser.java b/spring-bootstrap/src/main/java/org/springframework/bootstrap/config/SimpleJsonParser.java new file mode 100644 index 00000000000..91d46e67c20 --- /dev/null +++ b/spring-bootstrap/src/main/java/org/springframework/bootstrap/config/SimpleJsonParser.java @@ -0,0 +1,148 @@ +/* + * Copyright 2012-2013 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.bootstrap.config; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.springframework.util.ClassUtils; +import org.springframework.util.StringUtils; + +/** + * @author Dave Syer + * + */ +public class SimpleJsonParser implements JsonParser { + + public static JsonParser instance() { + if (ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", null)) { + return new YamlParser(); + } + if (ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", null)) { + return new JacksonParser(); + } + return new SimpleJsonParser(); + } + + @Override + public Map parseMap(String json) { + if (json.startsWith("{")) { + return parseMapInternal(json); + } else if (json.trim().equals("")) { + return new HashMap(); + } + return null; + } + + @Override + public List parseList(String json) { + 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, '"'), '"'); + } + return json; // TODO: numbers maybe? + } + + 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], '"'), '"'); + if (string.startsWith("{") && string.endsWith("}")) { + value = parseInternal(string); + } else { + value = string; + } + } + map.put(key, value); + } + return map; + } + + private List tokenize(String json) { + List list = new ArrayList(); + int index = 0; + int inObject = 0; + StringBuilder build = new StringBuilder(); + while (index < json.length()) { + char current = json.charAt(index); + if (current == '{') { + inObject++; + } + if (current == '}') { + inObject--; + } + if (current == ',' && inObject == 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-bootstrap/src/main/java/org/springframework/bootstrap/config/YamlParser.java b/spring-bootstrap/src/main/java/org/springframework/bootstrap/config/YamlParser.java new file mode 100644 index 00000000000..6c1f2eed06e --- /dev/null +++ b/spring-bootstrap/src/main/java/org/springframework/bootstrap/config/YamlParser.java @@ -0,0 +1,43 @@ +/* + * Copyright 2012-2013 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.bootstrap.config; + +import java.util.List; +import java.util.Map; + +import org.yaml.snakeyaml.Yaml; + +/** + * @author Dave Syer + * + */ +public class YamlParser implements JsonParser { + + @Override + public Map parseMap(String json) { + @SuppressWarnings("unchecked") + Map map = new Yaml().loadAs(json, Map.class); + return map; + } + + @Override + public List parseList(String json) { + @SuppressWarnings("unchecked") + List list = new Yaml().loadAs(json, List.class); + return list; + } + +} diff --git a/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/initializer/ContextIdApplicationContextInitializer.java b/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/initializer/ContextIdApplicationContextInitializer.java new file mode 100644 index 00000000000..7033e7a461d --- /dev/null +++ b/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/initializer/ContextIdApplicationContextInitializer.java @@ -0,0 +1,70 @@ +/* + * Copyright 2010-2012 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.bootstrap.context.initializer; + +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.Ordered; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.util.StringUtils; + +/** + * + * @author Dave Syer + */ +public class ContextIdApplicationContextInitializer implements + ApplicationContextInitializer, Ordered { + + private String name = "${spring.application.name:${vcap.application.name:${spring.config.name:application}}}"; + + private int index = -1; + + private int order = Integer.MAX_VALUE - 10; + + public void setOrder(int order) { + this.order = order; + } + + @Override + public int getOrder() { + return this.order; + } + + @Override + public void initialize(ConfigurableApplicationContext applicationContext) { + applicationContext.setId(getApplicationId(applicationContext.getEnvironment())); + } + + private String getApplicationId(ConfigurableEnvironment environment) { + String name = environment.resolvePlaceholders(this.name); + int index = environment.getProperty("PORT", Integer.class, this.index); + index = environment.getProperty("vcap.application.instance_index", Integer.class, + index); + index = environment.getProperty("spring.application.index", Integer.class, index); + if (index >= 0) { + name = name + ":" + index; + } else { + String profiles = StringUtils.arrayToCommaDelimitedString(environment + .getActiveProfiles()); + if (StringUtils.hasText(profiles)) { + name = name + ":" + profiles; + } + } + return name; + } + +} diff --git a/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/initializer/VcapApplicationContextInitializer.java b/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/initializer/VcapApplicationContextInitializer.java new file mode 100644 index 00000000000..9c667d1ba0d --- /dev/null +++ b/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/initializer/VcapApplicationContextInitializer.java @@ -0,0 +1,200 @@ +/* + * Copyright 2010-2012 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.bootstrap.context.initializer; + +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.springframework.bootstrap.config.JsonParser; +import org.springframework.bootstrap.config.SimpleJsonParser; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; +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 ApplicationContextInitializer} 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 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 smae 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
+ * ...
+ * 
+ * + * @author Dave Syer + */ +public class VcapApplicationContextInitializer implements + ApplicationContextInitializer, Ordered { + + private static final String VCAP_APPLICATION = "VCAP_APPLICATION"; + + private static final String VCAP_SERVICES = "VCAP_SERVICES"; + + private int order = Integer.MIN_VALUE + 11; + + private JsonParser parser = SimpleJsonParser.instance(); + + public void setOrder(int order) { + this.order = order; + } + + @Override + public int getOrder() { + return this.order; + } + + @Override + public void initialize(ConfigurableApplicationContext applicationContext) { + + ConfigurableEnvironment environment = applicationContext.getEnvironment(); + 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) { + Map map = this.parser.parseMap(environment.getProperty( + VCAP_APPLICATION, "{}")); + Properties properties = new Properties(); + properties.putAll(map); + return properties; + } + + private Properties getPropertiesFromServices(Environment environment) { + Map map = this.parser.parseMap(environment.getProperty( + VCAP_SERVICES, "{}")); + Properties properties = new Properties(); + 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); + } + } + return properties; + } + + private void flatten(Properties properties, Map input, String path) { + for (Entry entry : input.entrySet()) { + String key = entry.getKey(); + if (StringUtils.hasText(path)) { + if (key.startsWith("[")) { + key = path + key; + } else { + key = path + "." + key; + } + } + Object value = entry.getValue(); + if (value instanceof String) { + properties.put(key, value); + } else if (value instanceof Map) { + // Need a compound key + @SuppressWarnings("unchecked") + Map map = (Map) value; + flatten(properties, map, key); + } else if (value instanceof Collection) { + // Need a compound key + @SuppressWarnings("unchecked") + Collection collection = (Collection) value; + properties.put(key, + StringUtils.collectionToCommaDelimitedString(collection)); + int count = 0; + for (Object object : collection) { + flatten(properties, + Collections.singletonMap("[" + (count++) + "]", object), key); + } + } else { + properties.put(key, value == null ? "" : value); + } + } + } + +} diff --git a/spring-bootstrap/src/main/resources/META-INF/spring.factories b/spring-bootstrap/src/main/resources/META-INF/spring.factories index 287dc31897c..01f0bf0b16a 100644 --- a/spring-bootstrap/src/main/resources/META-INF/spring.factories +++ b/spring-bootstrap/src/main/resources/META-INF/spring.factories @@ -13,4 +13,6 @@ org.springframework.bootstrap.autoconfigure.web.WebMvcAutoConfiguration org.springframework.context.ApplicationContextInitializer=\ org.springframework.bootstrap.context.initializer.ConfigFileApplicationContextInitializer,\ org.springframework.bootstrap.context.initializer.LoggingApplicationContextInitializer,\ +org.springframework.bootstrap.context.initializer.VcapApplicationContextInitializer,\ +org.springframework.bootstrap.context.initializer.ContextIdApplicationContextInitializer,\ org.springframework.bootstrap.context.initializer.EnvironmentDelegateApplicationContextInitializer \ No newline at end of file diff --git a/spring-bootstrap/src/test/java/org/springframework/bootstrap/config/JacksonParserTests.java b/spring-bootstrap/src/test/java/org/springframework/bootstrap/config/JacksonParserTests.java new file mode 100644 index 00000000000..9b43f721227 --- /dev/null +++ b/spring-bootstrap/src/test/java/org/springframework/bootstrap/config/JacksonParserTests.java @@ -0,0 +1,28 @@ +/* + * Copyright 2012-2013 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.bootstrap.config; + +/** + * @author Dave Syer + * + */ +public class JacksonParserTests extends SimpleJsonParserTests { + + @Override + protected JsonParser getParser() { + return new JacksonParser(); + } +} diff --git a/spring-bootstrap/src/test/java/org/springframework/bootstrap/config/SimpleJsonParserTests.java b/spring-bootstrap/src/test/java/org/springframework/bootstrap/config/SimpleJsonParserTests.java new file mode 100644 index 00000000000..1caef14b4b5 --- /dev/null +++ b/spring-bootstrap/src/test/java/org/springframework/bootstrap/config/SimpleJsonParserTests.java @@ -0,0 +1,73 @@ +/* + * Copyright 2012-2013 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.bootstrap.config; + +import java.util.List; +import java.util.Map; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * @author Dave Syer + * + */ +public class SimpleJsonParserTests { + + private JsonParser parser = getParser(); + + protected JsonParser getParser() { + return new SimpleJsonParser(); + } + + @Test + public void testSimpleMap() { + Map map = this.parser.parseMap("{\"foo\":\"bar\",\"spam\":1}"); + assertEquals(2, map.size()); + assertEquals("bar", map.get("foo")); + assertEquals("1", map.get("spam").toString()); + } + + @Test + public void testEmptyMap() { + Map map = this.parser.parseMap("{}"); + assertEquals(0, map.size()); + } + + @Test + public void testSimpleList() { + List list = this.parser.parseList("[\"foo\",\"bar\",1]"); + assertEquals(3, list.size()); + assertEquals("bar", list.get(1)); + } + + @Test + public void testEmptyList() { + List list = this.parser.parseList("[]"); + assertEquals(0, list.size()); + } + + @SuppressWarnings("unchecked") + @Test + public void testListOfMaps() { + List list = this.parser + .parseList("[{\"foo\":\"bar\",\"spam\":1},{\"foo\":\"baz\",\"spam\":2}]"); + assertEquals(2, list.size()); + assertEquals(2, ((Map) list.get(1)).size()); + } + +} diff --git a/spring-bootstrap/src/test/java/org/springframework/bootstrap/config/YamlParserTests.java b/spring-bootstrap/src/test/java/org/springframework/bootstrap/config/YamlParserTests.java new file mode 100644 index 00000000000..e6a321b7a1b --- /dev/null +++ b/spring-bootstrap/src/test/java/org/springframework/bootstrap/config/YamlParserTests.java @@ -0,0 +1,28 @@ +/* + * Copyright 2012-2013 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.bootstrap.config; + +/** + * @author Dave Syer + * + */ +public class YamlParserTests extends SimpleJsonParserTests { + + @Override + protected JsonParser getParser() { + return new YamlParser(); + } +} diff --git a/spring-bootstrap/src/test/java/org/springframework/bootstrap/context/initializer/ContextIdApplicationContextInitializerTests.java b/spring-bootstrap/src/test/java/org/springframework/bootstrap/context/initializer/ContextIdApplicationContextInitializerTests.java new file mode 100644 index 00000000000..bf48cc58daf --- /dev/null +++ b/spring-bootstrap/src/test/java/org/springframework/bootstrap/context/initializer/ContextIdApplicationContextInitializerTests.java @@ -0,0 +1,77 @@ +/* + * Copyright 2012-2013 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.bootstrap.context.initializer; + +import org.junit.Test; +import org.springframework.bootstrap.TestUtils; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +import static org.junit.Assert.assertEquals; + +/** + * @author Dave Syer + * + */ +public class ContextIdApplicationContextInitializerTests { + + private ContextIdApplicationContextInitializer initializer = new ContextIdApplicationContextInitializer(); + + @Test + public void testDefaults() { + ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(); + this.initializer.initialize(context); + assertEquals("application", context.getId()); + } + + @Test + public void testNameAndPort() { + ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(); + TestUtils.addEnviroment(context, "spring.application.name:foo", "PORT:8080"); + this.initializer.initialize(context); + assertEquals("foo:8080", context.getId()); + } + + @Test + public void testNameAndProfiles() { + ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(); + TestUtils.addEnviroment(context, "spring.application.name:foo", + "spring.profiles.active: spam,bar"); + this.initializer.initialize(context); + assertEquals("foo:spam,bar", context.getId()); + } + + @Test + public void testCloudFoundry() { + ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(); + TestUtils.addEnviroment(context, "spring.config.name:foo", "PORT:8080", + "vcap.application.name:bar", "vcap.application.instance_index:2"); + this.initializer.initialize(context); + assertEquals("bar:2", context.getId()); + } + + @Test + public void testExplicitName() { + ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(); + TestUtils.addEnviroment(context, "spring.application.name:spam", + "spring.config.name:foo", "PORT:8080", + "vcap.application.application_name:bar", + "vcap.application.instance_index:2"); + this.initializer.initialize(context); + assertEquals("spam:2", context.getId()); + } + +} diff --git a/spring-bootstrap/src/test/java/org/springframework/bootstrap/context/initializer/VcapApplicationContextInitializerTests.java b/spring-bootstrap/src/test/java/org/springframework/bootstrap/context/initializer/VcapApplicationContextInitializerTests.java new file mode 100644 index 00000000000..795243648eb --- /dev/null +++ b/spring-bootstrap/src/test/java/org/springframework/bootstrap/context/initializer/VcapApplicationContextInitializerTests.java @@ -0,0 +1,56 @@ +/* + * Copyright 2012-2013 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.bootstrap.context.initializer; + +import org.junit.Test; +import org.springframework.bootstrap.TestUtils; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +import static org.junit.Assert.assertEquals; + +/** + * @author Dave Syer + * + */ +public class VcapApplicationContextInitializerTests { + + private VcapApplicationContextInitializer initializer = new VcapApplicationContextInitializer(); + + @Test + public void testApplicationProperties() { + ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(); + TestUtils + .addEnviroment( + context, + "VCAP_APPLICATION:{\"application_users\":[],\"instance_id\":\"bb7935245adf3e650dfb7c58a06e9ece\",\"instance_index\":0,\"version\":\"3464e092-1c13-462e-a47c-807c30318a50\",\"name\":\"foo\",\"uris\":[\"foo.cfapps.io\"],\"started_at\":\"2013-05-29 02:37:59 +0000\",\"started_at_timestamp\":1369795079,\"host\":\"0.0.0.0\",\"port\":61034,\"limits\":{\"mem\":128,\"disk\":1024,\"fds\":16384},\"version\":\"3464e092-1c13-462e-a47c-807c30318a50\",\"name\":\"dsyerenv\",\"uris\":[\"dsyerenv.cfapps.io\"],\"users\":[],\"start\":\"2013-05-29 02:37:59 +0000\",\"state_timestamp\":1369795079}"); + this.initializer.initialize(context); + assertEquals("bb7935245adf3e650dfb7c58a06e9ece", context.getEnvironment() + .getProperty("vcap.application.instance_id")); + } + + @Test + public void testServiceProperties() { + ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(); + TestUtils + .addEnviroment( + context, + "VCAP_SERVICES:{\"rds-mysql-n/a\":[{\"name\":\"mysql\",\"label\":\"rds-mysql-n/a\",\"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\"}}]}"); + this.initializer.initialize(context); + assertEquals("mysql", + context.getEnvironment().getProperty("vcap.services.mysql.name")); + } +}