mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-09-03 04:26:12 +08:00
Automatically detect 'development' profile
Detect when an application is running in development (by the presence of a build file) and automatically add a 'development' profile. Additional detectors can be developed by implementing the `ProfileDetector` interface and registering with the `SpringApplication` Fixes gh-296
This commit is contained in:
parent
643295cc3c
commit
a97bcfe3cd
@ -20,6 +20,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@ -43,6 +44,8 @@ public class SampleSimpleApplication implements CommandLineRunner {
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
SpringApplication.run(SampleSimpleApplication.class, args);
|
||||
ConfigurableApplicationContext context = SpringApplication.run(
|
||||
SampleSimpleApplication.class, args);
|
||||
System.out.println(context.getEnvironment().acceptsProfiles("development"));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link ProfileDetector} that attempts to detect when the application is being developed
|
||||
* and adds a 'development' profile.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class DevelopmentProfileDetector implements ProfileDetector {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private static final String DEFAULT_PROFILE_NAME = "development";
|
||||
|
||||
private static final String EXECUTABLE_JAR_CLASS = "org.springframework.boot.loader.Launcher";
|
||||
|
||||
private static final String[] DEVELOPMENT_TIME_FILES = { "pom.xml", "build.gradle",
|
||||
"build.xml" };
|
||||
|
||||
private final String profileName;
|
||||
|
||||
public DevelopmentProfileDetector() {
|
||||
this(DEFAULT_PROFILE_NAME);
|
||||
}
|
||||
|
||||
public DevelopmentProfileDetector(String profileName) {
|
||||
Assert.notNull(profileName, "ProfileName must not be null");
|
||||
this.profileName = profileName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDetectedProfiles(ConfigurableEnvironment environment) {
|
||||
if (!isPackageAsJar() && isRunningInDevelopmentDirectory()) {
|
||||
environment.addActiveProfile(this.profileName);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isPackageAsJar() {
|
||||
if (ClassUtils.isPresent(EXECUTABLE_JAR_CLASS, null)) {
|
||||
this.logger.debug("Development profile not detected: "
|
||||
+ "running inside executable jar");
|
||||
return true;
|
||||
}
|
||||
String command = System.getProperty("sun.java.command");
|
||||
if (StringUtils.hasLength(command) && command.toLowerCase().contains(".jar")) {
|
||||
this.logger.debug("Development profile not detected: started from a jar");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isRunningInDevelopmentDirectory() {
|
||||
File userDir = getUserDir();
|
||||
if (userDir != null && userDir.exists()) {
|
||||
for (String developementTimeFile : DEVELOPMENT_TIME_FILES) {
|
||||
if (new File(userDir, developementTimeFile).exists()) {
|
||||
this.logger.debug("Development profile detected: file "
|
||||
+ developementTimeFile + " present");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected File getUserDir() {
|
||||
String userDir = System.getProperty("user.dir");
|
||||
return (userDir == null ? null : new File(userDir));
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
|
||||
/**
|
||||
* Strategy interface that can be used to detect active profiles.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public interface ProfileDetector {
|
||||
|
||||
/**
|
||||
* Add any detected profiles to the specified environment.
|
||||
* @param environment the environment
|
||||
*/
|
||||
void addDetectedProfiles(ConfigurableEnvironment environment);
|
||||
|
||||
}
|
@ -158,6 +158,9 @@ public class SpringApplication {
|
||||
private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
|
||||
"org.springframework.web.context.ConfigurableWebApplicationContext" };
|
||||
|
||||
private static final List<ProfileDetector> DEFAULT_PROFILE_DETECTORS = Arrays
|
||||
.<ProfileDetector> asList(new DevelopmentProfileDetector());
|
||||
|
||||
private final Log log = LogFactory.getLog(getClass());
|
||||
|
||||
private final Set<Object> sources = new LinkedHashSet<Object>();
|
||||
@ -190,6 +193,8 @@ public class SpringApplication {
|
||||
|
||||
private Set<String> profiles = new HashSet<String>();
|
||||
|
||||
private List<ProfileDetector> profileDetectors = DEFAULT_PROFILE_DETECTORS;
|
||||
|
||||
/**
|
||||
* Crate a new {@link SpringApplication} instance. The application context will load
|
||||
* beans from the specified sources (see {@link SpringApplication class-level}
|
||||
@ -308,9 +313,7 @@ public class SpringApplication {
|
||||
// Create and configure the environment
|
||||
ConfigurableEnvironment environment = getOrCreateEnvironment();
|
||||
addPropertySources(environment, args);
|
||||
for (String profile : this.profiles) {
|
||||
environment.addActiveProfile(profile);
|
||||
}
|
||||
setupProfiles(environment);
|
||||
|
||||
// Notify listeners of the environment creation
|
||||
multicaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(this,
|
||||
@ -461,6 +464,15 @@ public class SpringApplication {
|
||||
}
|
||||
}
|
||||
|
||||
protected void setupProfiles(ConfigurableEnvironment environment) {
|
||||
for (String profile : this.profiles) {
|
||||
environment.addActiveProfile(profile);
|
||||
}
|
||||
for (ProfileDetector profileDetector : this.profileDetectors) {
|
||||
profileDetector.addDetectedProfiles(environment);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a simple banner message to the console. Subclasses can override this method
|
||||
* to provide additional or alternative banners.
|
||||
@ -740,11 +752,23 @@ public class SpringApplication {
|
||||
/**
|
||||
* Set additional profile values to use (on top of those set in system or command line
|
||||
* properties).
|
||||
*
|
||||
* @param profiles the additional profiles to set
|
||||
*/
|
||||
public void setAdditionalProfiles(Collection<String> profiles) {
|
||||
this.profiles = new LinkedHashSet<String>(profiles);
|
||||
public void setAdditionalProfiles(String... profiles) {
|
||||
this.profiles = new LinkedHashSet<String>(Arrays.asList(profiles));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link ProfileDetector}s that will be used to attempt to detect
|
||||
* {@link Environment#acceptsProfiles(String...) active profiles}.
|
||||
* {@link DevelopmentProfileDetector} is used.
|
||||
* @param profileDetectors the profile detectors
|
||||
* @see #setAdditionalProfiles(String...)
|
||||
*/
|
||||
public void setProfileDetectors(ProfileDetector... profileDetectors) {
|
||||
Assert.notNull(profileDetectors, "Profile detectors must not be null");
|
||||
this.profileDetectors = new ArrayList<ProfileDetector>(
|
||||
Arrays.asList(profileDetectors));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -28,6 +28,7 @@ import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
import org.springframework.boot.ProfileDetector;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.context.initializer.ParentContextApplicationContextInitializer;
|
||||
import org.springframework.boot.context.initializer.ServletContextApplicationContextInitializer;
|
||||
@ -389,14 +390,26 @@ public class SpringApplicationBuilder {
|
||||
*/
|
||||
public SpringApplicationBuilder profiles(String... profiles) {
|
||||
this.additionalProfiles.addAll(Arrays.asList(profiles));
|
||||
this.application.setAdditionalProfiles(this.additionalProfiles);
|
||||
this.application.setAdditionalProfiles(this.additionalProfiles
|
||||
.toArray(new String[this.additionalProfiles.size()]));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link ProfileDetector}s that should be used for this app.
|
||||
* @param profileDetectors the profile detectors to set
|
||||
* @return the current builder
|
||||
*/
|
||||
public SpringApplicationBuilder profileDetectors(ProfileDetector... profileDetectors) {
|
||||
this.application.setProfileDetectors(profileDetectors);
|
||||
return this;
|
||||
}
|
||||
|
||||
private SpringApplicationBuilder additionalProfiles(
|
||||
Collection<String> additionalProfiles) {
|
||||
this.additionalProfiles = new LinkedHashSet<String>(additionalProfiles);
|
||||
this.application.setAdditionalProfiles(additionalProfiles);
|
||||
this.application.setAdditionalProfiles(additionalProfiles
|
||||
.toArray(new String[additionalProfiles.size()]));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -61,8 +61,7 @@ public class SpringApplicationContextLoader extends AbstractContextLoader {
|
||||
SpringApplication application = new SpringApplication();
|
||||
application.setSources(getSources(mergedConfig));
|
||||
if (!ObjectUtils.isEmpty(mergedConfig.getActiveProfiles())) {
|
||||
application.setAdditionalProfiles(Arrays.asList(mergedConfig
|
||||
.getActiveProfiles()));
|
||||
application.setAdditionalProfiles(mergedConfig.getActiveProfiles());
|
||||
}
|
||||
application.setDefaultProperties(getArgs(mergedConfig));
|
||||
List<ApplicationContextInitializer<?>> initializers = getInitializers(
|
||||
|
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
|
||||
/**
|
||||
* Tests for {@link DevelopmentProfileDetector}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class DevelopmentProfileDetectorTests {
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||
|
||||
private TestDevelopmentProfileDetector detector;
|
||||
|
||||
private ConfigurableEnvironment environment;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.detector = new TestDevelopmentProfileDetector();
|
||||
this.environment = mock(ConfigurableEnvironment.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notFound() {
|
||||
this.detector.addDetectedProfiles(this.environment);
|
||||
verifyZeroInteractions(this.environment);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void foundDueToMavenBuild() throws Exception {
|
||||
this.temporaryFolder.newFile("pom.xml").createNewFile();
|
||||
this.detector.addDetectedProfiles(this.environment);
|
||||
verify(this.environment).addActiveProfile("development");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void foundDueToGradleBuild() throws Exception {
|
||||
this.temporaryFolder.newFile("build.gradle").createNewFile();
|
||||
this.detector.addDetectedProfiles(this.environment);
|
||||
verify(this.environment).addActiveProfile("development");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void foundDueToAntBuild() throws Exception {
|
||||
this.temporaryFolder.newFile("build.xml").createNewFile();
|
||||
this.detector.addDetectedProfiles(this.environment);
|
||||
verify(this.environment).addActiveProfile("development");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void differentProfileName() throws Exception {
|
||||
this.detector = new TestDevelopmentProfileDetector("different");
|
||||
this.temporaryFolder.newFile("pom.xml").createNewFile();
|
||||
this.detector.addDetectedProfiles(this.environment);
|
||||
verify(this.environment).addActiveProfile("different");
|
||||
verifyNoMoreInteractions(this.environment);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notFoundWhenStartedFromJar() throws Exception {
|
||||
System.setProperty("sun.java.command", "something.jar");
|
||||
this.temporaryFolder.newFile("pom.xml").createNewFile();
|
||||
this.detector.setSkipPackageAsJar(false);
|
||||
this.detector.addDetectedProfiles(this.environment);
|
||||
verifyZeroInteractions(this.environment);
|
||||
}
|
||||
|
||||
private class TestDevelopmentProfileDetector extends DevelopmentProfileDetector {
|
||||
|
||||
private boolean skipPackageAsJar = true;
|
||||
|
||||
public TestDevelopmentProfileDetector() {
|
||||
super();
|
||||
}
|
||||
|
||||
public TestDevelopmentProfileDetector(String profileName) {
|
||||
super(profileName);
|
||||
}
|
||||
|
||||
public void setSkipPackageAsJar(boolean skipPackageAsJar) {
|
||||
this.skipPackageAsJar = skipPackageAsJar;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isPackageAsJar() {
|
||||
if (this.skipPackageAsJar) {
|
||||
// Unfortunately surefire uses a jar so we need to stub this out
|
||||
return false;
|
||||
}
|
||||
return super.isPackageAsJar();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected File getUserDir() {
|
||||
return DevelopmentProfileDetectorTests.this.temporaryFolder.getRoot();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -409,6 +409,29 @@ public class SpringApplicationTests {
|
||||
assertThat(System.getProperty("java.awt.headless"), equalTo("false"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setAdditionalProfiles() throws Exception {
|
||||
TestSpringApplication application = new TestSpringApplication(ExampleConfig.class);
|
||||
application.setWebEnvironment(false);
|
||||
application.setAdditionalProfiles("mine");
|
||||
this.context = application.run();
|
||||
assertThat(this.context.getEnvironment().acceptsProfiles("mine"), equalTo(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setProfileDetectors() throws Exception {
|
||||
TestSpringApplication application = new TestSpringApplication(ExampleConfig.class);
|
||||
application.setWebEnvironment(false);
|
||||
application.setProfileDetectors(new ProfileDetector() {
|
||||
@Override
|
||||
public void addDetectedProfiles(ConfigurableEnvironment environment) {
|
||||
environment.addActiveProfile("mine");
|
||||
}
|
||||
});
|
||||
this.context = application.run();
|
||||
assertThat(this.context.getEnvironment().acceptsProfiles("mine"), equalTo(true));
|
||||
}
|
||||
|
||||
private boolean hasPropertySource(ConfigurableEnvironment environment,
|
||||
Class<?> propertySourceClass, String name) {
|
||||
for (PropertySource<?> source : environment.getPropertySources()) {
|
||||
|
Loading…
Reference in New Issue
Block a user