Make ContextIdApplicationContextInitializer produce unique IDs

Closes gh-11023
pull/11290/merge
Andy Wilkinson 7 years ago
parent f2b15ce5d8
commit 2059922735

@ -47,7 +47,6 @@ content into your application. Rather, pick only the properties that you need.
spring.aop.proxy-target-class=true # Whether subclass-based (CGLIB) proxies are to be created (true), as opposed to standard Java interface-based proxies (false).
# IDENTITY ({sc-spring-boot}/context/ContextIdApplicationContextInitializer.{sc-ext}[ContextIdApplicationContextInitializer])
spring.application.index= # Application index.
spring.application.name= # Application name.
# ADMIN ({sc-spring-boot-autoconfigure}/admin/SpringApplicationAdminJmxAutoConfiguration.{sc-ext}[SpringApplicationAdminJmxAutoConfiguration])

@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@ -16,6 +16,8 @@
package org.springframework.boot.context;
import java.util.concurrent.atomic.AtomicLong;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
@ -24,73 +26,19 @@ import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.util.StringUtils;
/**
* {@link ApplicationContextInitializer} that set the Spring
* {@link ApplicationContext#getId() ApplicationContext ID}. The following environment
* properties will be consulted to create the ID:
* <ul>
* <li>spring.application.name</li>
* <li>vcap.application.name</li>
* <li>spring.config.name</li>
* </ul>
* If no property is set the ID 'application' will be used.
*
* <p>
* In addition the following environment properties will be consulted to append a relevant
* port or index:
*
* <ul>
* <li>spring.application.index</li>
* <li>vcap.application.instance_index</li>
* <li>PORT</li>
* </ul>
* {@link ApplicationContextInitializer} that sets the Spring
* {@link ApplicationContext#getId() ApplicationContext ID}. The
* {@code spring.application.name} property is used to create the ID. If the property is
* not set {@code application} is used.
*
* @author Dave Syer
* @author Andy Wilkinson
*/
public class ContextIdApplicationContextInitializer implements
ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
/**
* Placeholder pattern to resolve for application name. The following order is used to
* find the name:
* <ul>
* <li>{@code spring.application.name}</li>
* <li>{@code vcap.application.name}</li>
* <li>{@code spring.config.name}</li>
* </ul>
* This order allows the user defined name to take precedence over the platform
* defined name. If no property is defined {@code 'application'} will be used.
*/
private static final String NAME_PATTERN = "${spring.application.name:${vcap.application.name:${spring.config.name:application}}}";
/**
* Placeholder pattern to resolve for application index. The following order is used
* to find the name:
* <ul>
* <li>{@code vcap.application.instance_index}</li>
* <li>{@code spring.application.index}</li>
* <li>{@code server.port}</li>
* <li>{@code PORT}</li>
* </ul>
* This order favors a platform defined index over any user defined value.
*/
private static final String INDEX_PATTERN = "${vcap.application.instance_index:${spring.application.index:${server.port:${PORT:null}}}}";
private final String name;
private int order = Ordered.LOWEST_PRECEDENCE - 10;
public ContextIdApplicationContextInitializer() {
this(NAME_PATTERN);
}
/**
* Create a new {@link ContextIdApplicationContextInitializer} instance.
* @param name the name of the application (can include placeholders)
*/
public ContextIdApplicationContextInitializer(String name) {
this.name = name;
}
public void setOrder(int order) {
this.order = order;
}
@ -102,21 +50,46 @@ public class ContextIdApplicationContextInitializer implements
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
applicationContext.setId(getApplicationId(applicationContext.getEnvironment()));
ContextId contextId = getContextId(applicationContext);
applicationContext.setId(contextId.getId());
applicationContext.getBeanFactory().registerSingleton(ContextId.class.getName(),
contextId);
}
private ContextId getContextId(ConfigurableApplicationContext applicationContext) {
ApplicationContext parent = applicationContext.getParent();
if (parent != null && parent.containsBean(ContextId.class.getName())) {
return parent.getBean(ContextId.class).createChildId();
}
return new ContextId(getApplicationId(applicationContext.getEnvironment()));
}
private String getApplicationId(ConfigurableEnvironment environment) {
String name = environment.resolvePlaceholders(this.name);
String index = environment.resolvePlaceholders(INDEX_PATTERN);
String profiles = StringUtils
.arrayToCommaDelimitedString(environment.getActiveProfiles());
if (StringUtils.hasText(profiles)) {
name = name + ":" + profiles;
String name = environment.getProperty("spring.application.name");
return StringUtils.hasText(name) ? name : "application";
}
if (!"null".equals(index)) {
name = name + ":" + index;
/**
* The ID of a context.
*/
class ContextId {
private final AtomicLong children = new AtomicLong(0);
private final String id;
ContextId(String id) {
this.id = id;
}
ContextId createChildId() {
return new ContextId(this.id + "-" + this.children.incrementAndGet());
}
return name;
String getId() {
return this.id;
}
}
}

@ -143,12 +143,6 @@
"sourceType": "org.springframework.boot.context.ContextIdApplicationContextInitializer",
"description": "Application name."
},
{
"name": "spring.application.index",
"type": "java.lang.Integer",
"sourceType": "org.springframework.boot.context.ContextIdApplicationContextInitializer",
"description": "Application index."
},
{
"name": "spring.beaninfo.ignore",
"type": "java.lang.Boolean",

@ -1,5 +1,5 @@
/*
* Copyright 2012-2017 the original author or authors.
* Copyright 2012-2018 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.
@ -16,6 +16,11 @@
package org.springframework.boot.context;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.junit.After;
import org.junit.Test;
import org.springframework.context.ConfigurableApplicationContext;
@ -33,50 +38,67 @@ public class ContextIdApplicationContextInitializerTests {
private final ContextIdApplicationContextInitializer initializer = new ContextIdApplicationContextInitializer();
private List<ConfigurableApplicationContext> contexts = new ArrayList<>();
@After
public void closeContexts() {
Collections.reverse(this.contexts);
this.contexts.forEach(ConfigurableApplicationContext::close);
}
@Test
public void testDefaults() {
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext();
this.initializer.initialize(context);
public void singleContextWithDefaultName() {
ConfigurableApplicationContext context = createContext(null);
assertThat(context.getId()).isEqualTo("application");
}
@Test
public void testNameAndPort() {
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext();
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(context,
"spring.application.name=foo", "PORT=8080");
this.initializer.initialize(context);
assertThat(context.getId()).isEqualTo("foo:8080");
public void singleContextWithCustomName() {
ConfigurableApplicationContext context = createContext(null,
"spring.application.name=test");
assertThat(context.getId()).isEqualTo("test");
}
@Test
public void testNameAndProfiles() {
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext();
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(context,
"spring.application.name=foo", "spring.profiles.active=spam,bar",
"spring.application.index=12");
this.initializer.initialize(context);
assertThat(context.getId()).isEqualTo("foo:spam,bar:12");
public void linearHierarchy() {
ConfigurableApplicationContext grandparent = createContext(null);
ConfigurableApplicationContext parent = createContext(grandparent);
ConfigurableApplicationContext child = createContext(parent);
assertThat(child.getId()).isEqualTo("application-1-1");
}
@Test
public void testCloudFoundry() {
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext();
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(context,
"spring.config.name=foo", "PORT=8080", "vcap.application.name=bar",
"vcap.application.instance_index=2");
this.initializer.initialize(context);
assertThat(context.getId()).isEqualTo("bar:2");
public void complexHierarchy() {
ConfigurableApplicationContext grandparent = createContext(null);
ConfigurableApplicationContext parent1 = createContext(grandparent);
ConfigurableApplicationContext parent2 = createContext(grandparent);
ConfigurableApplicationContext child1_1 = createContext(parent1);
assertThat(child1_1.getId()).isEqualTo("application-1-1");
ConfigurableApplicationContext child1_2 = createContext(parent1);
assertThat(child1_2.getId()).isEqualTo("application-1-2");
ConfigurableApplicationContext child2_1 = createContext(parent2);
assertThat(child2_1.getId()).isEqualTo("application-2-1");
}
@Test
public void testExplicitNameIsChosenInFavorOfCloudFoundry() {
public void contextWithParentWithNoContextIdFallsBackToDefaultId() {
ConfigurableApplicationContext parent = new AnnotationConfigApplicationContext();
this.contexts.add(parent);
parent.refresh();
assertThat(createContext(parent).getId()).isEqualTo("application");
}
private ConfigurableApplicationContext createContext(
ConfigurableApplicationContext parent, String... properties) {
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext();
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(context,
"spring.application.name=spam", "spring.config.name=foo", "PORT=8080",
"vcap.application.name=bar", "vcap.application.instance_index=2");
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(context, properties);
if (parent != null) {
context.setParent(parent);
}
this.initializer.initialize(context);
assertThat(context.getId()).isEqualTo("spam:2");
context.refresh();
this.contexts.add(context);
return context;
}
}

Loading…
Cancel
Save