org.springframework.boot.devtools.env.DevToolsHomePropertiesPostProcessor Maven / Gradle / Ivy
/*
* Copyright 2012-2023 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
*
* https://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.devtools.env;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.devtools.system.DevToolsEnablementDeducer;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.boot.env.PropertiesPropertySourceLoader;
import org.springframework.boot.env.PropertySourceLoader;
import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* {@link EnvironmentPostProcessor} to add devtools properties from the user's home
* directory.
*
* @author Phillip Webb
* @author Andy Wilkinson
* @author HaiTao Zhang
* @author Madhura Bhave
* @since 1.3.0
*/
public class DevToolsHomePropertiesPostProcessor implements EnvironmentPostProcessor {
private static final String LEGACY_FILE_NAME = ".spring-boot-devtools.properties";
private static final String[] FILE_NAMES = new String[] { "spring-boot-devtools.yml", "spring-boot-devtools.yaml",
"spring-boot-devtools.properties" };
private static final String CONFIG_PATH = "/.config/spring-boot/";
private static final Set PROPERTY_SOURCE_LOADERS;
private final Properties systemProperties;
private final Map environmentVariables;
static {
Set propertySourceLoaders = new HashSet<>();
propertySourceLoaders.add(new PropertiesPropertySourceLoader());
if (ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", null)) {
propertySourceLoaders.add(new YamlPropertySourceLoader());
}
PROPERTY_SOURCE_LOADERS = Collections.unmodifiableSet(propertySourceLoaders);
}
public DevToolsHomePropertiesPostProcessor() {
this(System.getenv(), System.getProperties());
}
DevToolsHomePropertiesPostProcessor(Map environmentVariables, Properties systemProperties) {
this.environmentVariables = environmentVariables;
this.systemProperties = systemProperties;
}
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
if (DevToolsEnablementDeducer.shouldEnable(Thread.currentThread())) {
List> propertySources = getPropertySources();
if (propertySources.isEmpty()) {
addPropertySource(propertySources, LEGACY_FILE_NAME, (file) -> "devtools-local");
}
propertySources.forEach(environment.getPropertySources()::addFirst);
}
}
private List> getPropertySources() {
List> propertySources = new ArrayList<>();
for (String fileName : FILE_NAMES) {
addPropertySource(propertySources, CONFIG_PATH + fileName, this::getPropertySourceName);
}
return propertySources;
}
private String getPropertySourceName(File file) {
return "devtools-local: [" + file.toURI() + "]";
}
private void addPropertySource(List> propertySources, String fileName,
Function propertySourceNamer) {
File home = getHomeDirectory();
File file = (home != null) ? new File(home, fileName) : null;
FileSystemResource resource = (file != null) ? new FileSystemResource(file) : null;
if (resource != null && resource.exists() && resource.isFile()) {
addPropertySource(propertySources, resource, propertySourceNamer);
}
}
private void addPropertySource(List> propertySources, FileSystemResource resource,
Function propertySourceNamer) {
try {
String name = propertySourceNamer.apply(resource.getFile());
for (PropertySourceLoader loader : PROPERTY_SOURCE_LOADERS) {
if (canLoadFileExtension(loader, resource.getFilename())) {
propertySources.addAll(loader.load(name, resource));
}
}
}
catch (IOException ex) {
throw new IllegalStateException("Unable to load " + resource.getFilename(), ex);
}
}
private boolean canLoadFileExtension(PropertySourceLoader loader, String name) {
return Arrays.stream(loader.getFileExtensions())
.anyMatch((fileExtension) -> StringUtils.endsWithIgnoreCase(name, fileExtension));
}
protected File getHomeDirectory() {
return getHomeDirectory(() -> this.environmentVariables.get("SPRING_DEVTOOLS_HOME"),
() -> this.systemProperties.getProperty("spring.devtools.home"),
() -> this.systemProperties.getProperty("user.home"));
}
@SafeVarargs
private final File getHomeDirectory(Supplier... pathSuppliers) {
for (Supplier pathSupplier : pathSuppliers) {
String path = pathSupplier.get();
if (StringUtils.hasText(path)) {
return new File(path);
}
}
return null;
}
}