io.micronaut.context.env.DefaultEnvironment Maven / Gradle / Ivy
/*
* Copyright 2017-2020 original 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 io.micronaut.context.env;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import io.micronaut.context.ApplicationContextConfiguration;
import io.micronaut.context.exceptions.ConfigurationException;
import io.micronaut.core.convert.ConversionContext;
import io.micronaut.core.convert.TypeConverter;
import io.micronaut.core.io.ResourceLoader;
import io.micronaut.core.io.ResourceResolver;
import io.micronaut.core.io.file.FileSystemResourceLoader;
import io.micronaut.core.io.scan.CachingClassPathAnnotationScanner;
import io.micronaut.core.io.scan.ClassPathAnnotationScanner;
import io.micronaut.core.io.scan.ClassPathResourceLoader;
import io.micronaut.core.io.service.ServiceDefinition;
import io.micronaut.core.io.service.SoftServiceLoader;
import io.micronaut.core.naming.NameUtils;
import io.micronaut.core.order.OrderUtil;
import io.micronaut.core.reflect.ClassUtils;
import io.micronaut.core.util.StringUtils;
import io.micronaut.inject.BeanConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.lang.annotation.Annotation;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import java.util.stream.Stream;
/**
* The default implementation of the {@link Environment} interface. Configures a named environment.
*
* @author Graeme Rocher
* @author rvanderwerf
* @since 1.0
*/
public class DefaultEnvironment extends PropertySourcePropertyResolver implements Environment {
private static final String EC2_LINUX_HYPERVISOR_FILE = "/sys/hypervisor/uuid";
private static final String EC2_LINUX_BIOS_VENDOR_FILE = "/sys/devices/virtual/dmi/id/bios_vendor";
private static final String EC2_WINDOWS_HYPERVISOR_CMD = "wmic path win32_computersystemproduct get uuid";
private static final String FILE_SEPARATOR = ",";
private static final Logger LOG = LoggerFactory.getLogger(DefaultEnvironment.class);
private static final String AWS_LAMBDA_FUNCTION_NAME_ENV = "AWS_LAMBDA_FUNCTION_NAME";
private static final String K8S_ENV = "KUBERNETES_SERVICE_HOST";
private static final String PCF_ENV = "VCAP_SERVICES";
private static final String HEROKU_DYNO = "DYNO";
private static final String GOOGLE_APPENGINE_ENVIRONMENT = "GAE_ENV";
private static final int DEFAULT_READ_TIMEOUT = 500;
private static final int DEFAULT_CONNECT_TIMEOUT = 500;
private static final String GOOGLE_COMPUTE_METADATA = "http://metadata.google.internal";
private static final String ORACLE_CLOUD_ASSET_TAG_FILE = "/sys/devices/virtual/dmi/id/chassis_asset_tag";
private static final String ORACLE_CLOUD_WINDOWS_ASSET_TAG_CMD = "wmic systemenclosure get smbiosassettag";
private static final String DO_SYS_VENDOR_FILE = "/sys/devices/virtual/dmi/id/sys_vendor";
private static final Boolean DEDUCE_ENVIRONMENT_DEFAULT = true;
protected final ClassPathResourceLoader resourceLoader;
protected final List refreshablePropertySources = new ArrayList<>(10);
private EnvironmentsAndPackage environmentsAndPackage;
private final Set names;
private final ClassLoader classLoader;
private final Collection packages = new ConcurrentLinkedQueue<>();
private final ClassPathAnnotationScanner annotationScanner;
private Collection configurationIncludes = new HashSet<>(3);
private Collection configurationExcludes = new HashSet<>(3);
private final AtomicBoolean running = new AtomicBoolean(false);
private Collection propertySourceLoaderList;
private final Map loaderByFormatMap = new ConcurrentHashMap<>();
private final Map presenceCache = new ConcurrentHashMap<>();
private final AtomicBoolean reading = new AtomicBoolean(false);
private final Boolean deduceEnvironments;
private final ApplicationContextConfiguration configuration;
/**
* Construct a new environment for the given configuration.
*
* @param configuration The configuration
*/
public DefaultEnvironment(@NonNull ApplicationContextConfiguration configuration) {
super(configuration.getConversionService());
this.configuration = configuration;
Set environments = new LinkedHashSet<>(3);
List specifiedNames = configuration.getEnvironments();
this.deduceEnvironments = configuration.getDeduceEnvironments().orElse(null);
EnvironmentsAndPackage environmentsAndPackage = getEnvironmentsAndPackage(specifiedNames);
environments.addAll(environmentsAndPackage.enviroments);
String aPackage = environmentsAndPackage.aPackage;
if (aPackage != null) {
packages.add(aPackage);
}
environments.addAll(specifiedNames);
this.classLoader = configuration.getClassLoader();
this.names = environments;
if (LOG.isInfoEnabled() && !environments.isEmpty()) {
LOG.info("Established active environments: {}", environments);
}
this.resourceLoader = configuration.getResourceLoader();
this.annotationScanner = createAnnotationScanner(classLoader);
}
@Override
public boolean isPresent(String className) {
return presenceCache.computeIfAbsent(className, s -> ClassUtils.isPresent(className, getClassLoader()));
}
@Override
public PropertyPlaceholderResolver getPlaceholderResolver() {
return this.propertyPlaceholderResolver;
}
@Override
public Stream scan(Class extends Annotation> annotation) {
return annotationScanner.scan(annotation, getPackages());
}
@Override
public Stream scan(Class extends Annotation> annotation, String... packages) {
return annotationScanner.scan(annotation, packages);
}
@Override
public ClassLoader getClassLoader() {
return classLoader;
}
@Override
public boolean isActive(BeanConfiguration configuration) {
String name = configuration.getName();
return !configurationExcludes.contains(name) && (configurationIncludes.isEmpty() || configurationIncludes.contains(name));
}
@Override
public DefaultEnvironment addPropertySource(PropertySource propertySource) {
propertySources.put(propertySource.getName(), propertySource);
if (isRunning() && !reading.get()) {
resetCaches();
processPropertySource(propertySource, PropertySource.PropertyConvention.JAVA_PROPERTIES);
}
return this;
}
@Override
public Environment removePropertySource(PropertySource propertySource) {
propertySources.remove(propertySource.getName());
if (isRunning() && !reading.get()) {
resetCaches();
}
return this;
}
@Override
public DefaultEnvironment addPropertySource(String name, Map values) {
return (DefaultEnvironment) super.addPropertySource(name, values);
}
@Override
public Environment addPackage(String pkg) {
if (!this.packages.contains(pkg)) {
this.packages.add(pkg);
}
return this;
}
@Override
public Environment addConfigurationExcludes(@Nullable String... names) {
if (names != null) {
configurationExcludes.addAll(Arrays.asList(names));
}
return this;
}
@Override
public Environment addConfigurationIncludes(String... names) {
if (names != null) {
configurationIncludes.addAll(Arrays.asList(names));
}
return this;
}
@Override
public Collection getPackages() {
return Collections.unmodifiableCollection(packages);
}
@Override
public Set getActiveNames() {
return this.names;
}
@Override
public Collection getPropertySources() {
return Collections.unmodifiableCollection(this.propertySources.values());
}
@Override
public Environment start() {
if (running.compareAndSet(false, true)) {
if (LOG.isDebugEnabled()) {
LOG.debug("Starting environment {} for active names {}", this, getActiveNames());
}
if (reading.compareAndSet(false, true)) {
readPropertySources(getPropertySourceRootName());
reading.set(false);
}
}
return this;
}
@Override
public boolean isRunning() {
return running.get();
}
@Override
public Environment stop() {
running.set(false);
reading.set(false);
this.propertySources.values().removeAll(refreshablePropertySources);
synchronized (catalog) {
for (int i = 0; i < catalog.length; i++) {
catalog[i] = null;
}
resetCaches();
}
return this;
}
@Override
public Map refreshAndDiff() {
Map[] copiedCatalog = copyCatalog();
refresh();
return diffCatalog(copiedCatalog, catalog);
}
@Override
public Optional convert(Object object, Class targetType, ConversionContext context) {
return conversionService.convert(object, targetType, context);
}
@Override
public boolean canConvert(Class sourceType, Class targetType) {
return conversionService.canConvert(sourceType, targetType);
}
@Override
public Environment addConverter(Class sourceType, Class targetType, TypeConverter typeConverter) {
conversionService.addConverter(sourceType, targetType, typeConverter);
return this;
}
@Override
public Environment addConverter(Class sourceType, Class targetType, Function typeConverter) {
conversionService.addConverter(sourceType, targetType, typeConverter);
return this;
}
@Override
public Optional getResourceAsStream(String path) {
return resourceLoader.getResourceAsStream(path);
}
@Override
public Optional getResource(String path) {
return resourceLoader.getResource(path);
}
@Override
public Stream getResources(String path) {
return resourceLoader.getResources(path);
}
@Override
public boolean supportsPrefix(String path) {
return resourceLoader.supportsPrefix(path);
}
@Override
public ResourceLoader forBase(String basePath) {
return resourceLoader.forBase(basePath);
}
/**
* @return Whether environment names and packages should be deduced
*/
protected boolean shouldDeduceEnvironments() {
if (deduceEnvironments != null) {
if (LOG.isDebugEnabled()) {
LOG.debug("Environment deduction was set explicitly via builder to: " + deduceEnvironments);
}
return deduceEnvironments;
} else {
String deduceProperty = System.getProperty(Environment.DEDUCE_ENVIRONMENT_PROPERTY);
String deduceEnv = System.getenv(Environment.DEDUCE_ENVIRONMENT_ENV);
if (StringUtils.isNotEmpty(deduceEnv)) {
boolean deduce = Boolean.valueOf(deduceEnv);
if (LOG.isDebugEnabled()) {
LOG.debug("Environment deduction was set via environment variable to: " + deduce);
}
return deduce;
} else if (StringUtils.isNotEmpty(deduceProperty)) {
boolean deduce = Boolean.valueOf(deduceProperty);
if (LOG.isDebugEnabled()) {
LOG.debug("Environment deduction was set via system property to: " + deduce);
}
return deduce;
} else {
boolean deduceDefault = DEDUCE_ENVIRONMENT_DEFAULT;
if (LOG.isDebugEnabled()) {
LOG.debug("Environment deduction is using the default of: " + deduceDefault);
}
return deduceDefault;
}
}
}
/**
* Creates the default annotation scanner.
*
* @param classLoader The class loader
* @return The scanner
*/
protected ClassPathAnnotationScanner createAnnotationScanner(ClassLoader classLoader) {
return new CachingClassPathAnnotationScanner(classLoader);
}
/**
* @return The property source root name
*/
protected String getPropertySourceRootName() {
return DEFAULT_NAME;
}
/**
* @param name The name to read property sources
*/
protected void readPropertySources(String name) {
refreshablePropertySources.clear();
List propertySources = readPropertySourceList(name);
addDefaultPropertySources(propertySources);
String propertySourcesSystemProperty = System.getProperty(Environment.PROPERTY_SOURCES_KEY);
if (propertySourcesSystemProperty != null) {
propertySources.addAll(readPropertySourceListFromFiles(propertySourcesSystemProperty));
}
String propertySourcesEnv = readPropertySourceListKeyFromEnvironment();
if (propertySourcesEnv != null) {
propertySources.addAll(readPropertySourceListFromFiles(propertySourcesEnv));
}
refreshablePropertySources.addAll(propertySources);
propertySources.addAll(this.propertySources.values());
OrderUtil.sort(propertySources);
for (PropertySource propertySource : propertySources) {
if (LOG.isDebugEnabled()) {
LOG.debug("Processing property source: {}", propertySource.getName());
}
processPropertySource(propertySource, propertySource.getConvention());
}
}
/**
* Reads the value of MICRONAUT_CONFIG_FILES environment variable.
*
* @return The comma-separated list of files
*/
protected String readPropertySourceListKeyFromEnvironment() {
return System.getenv(StringUtils.convertDotToUnderscore(Environment.PROPERTY_SOURCES_KEY));
}
/**
* Resolve the property sources for files passed via system property and system env.
*
* @param files The comma separated list of files
* @return The list of property sources for each file
*/
protected List readPropertySourceListFromFiles(String files) {
List propertySources = new ArrayList<>();
Collection propertySourceLoaders = getPropertySourceLoaders();
Optional> filePathList = Optional.ofNullable(files)
.filter(value -> !value.isEmpty())
.map(value -> value.split(FILE_SEPARATOR))
.map(Arrays::asList)
.map(Collections::unmodifiableList);
filePathList.ifPresent(list -> {
if (!list.isEmpty()) {
int order = AbstractPropertySourceLoader.DEFAULT_POSITION + 50;
for (String filePath: list) {
if (!propertySourceLoaders.isEmpty()) {
String extension = NameUtils.extension(filePath);
String fileName = NameUtils.filename(filePath);
Optional propertySourceLoader = Optional.ofNullable(loaderByFormatMap.get(extension));
if (propertySourceLoader.isPresent()) {
if (LOG.isDebugEnabled()) {
LOG.debug("Reading property sources from loader: {}", propertySourceLoader);
}
Optional
© 2015 - 2025 Weber Informatics LLC | Privacy Policy