org.jboss.weld.config.WeldConfiguration Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source
* Copyright 2014, Red Hat, Inc., and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* 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.jboss.weld.config;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.security.AccessController;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Pattern;
import org.jboss.weld.bootstrap.api.Service;
import org.jboss.weld.bootstrap.api.ServiceRegistry;
import org.jboss.weld.bootstrap.spi.BeanDeploymentArchive;
import org.jboss.weld.bootstrap.spi.BootstrapConfiguration;
import org.jboss.weld.bootstrap.spi.Deployment;
import org.jboss.weld.configuration.spi.ExternalConfiguration;
import org.jboss.weld.exceptions.IllegalStateException;
import org.jboss.weld.logging.BeanLogger;
import org.jboss.weld.logging.ConfigurationLogger;
import org.jboss.weld.resources.WeldClassLoaderResourceLoader;
import org.jboss.weld.resources.spi.ResourceLoader;
import org.jboss.weld.resources.spi.ResourceLoadingException;
import org.jboss.weld.security.GetSystemPropertyAction;
import org.jboss.weld.util.Preconditions;
import org.jboss.weld.util.collections.ImmutableMap;
import org.jboss.weld.util.reflection.Reflections;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
/**
* Represents an immutable per-deployment Weld configuration.
*
*
* Each property may be set in three different sources (by priority in descending order):
*
*
* - In a properties file named `weld.properties`
* - As a system property
* - By a bootstrap configuration provided by an integrator
*
*
*
* For backwards compatibility there are some obsolete sources:
*
*
* - properties files
org.jboss.weld.executor.properties
and org.jboss.weld.bootstrap.properties
are also loaded for some
* configuration keys,
* - some system properties with obsolete keys are considered
*
*
*
* If a configuration key is set in multiple sources (e.g. as a system property and in a weld.properties
file), the value from the source with
* higher priority is taken, other values are ignored.
*
*
*
* If a configuration key is set multiple times in the same source (e.g. different weld.properties
files) and the values are different, the
* container automatically detects the problem and treats it as a deployment problem.
*
*
*
* Unsupported configuration keys are ignored. If an invalid value is set, the container automatically detects the problem and treats it as a deployment
* problem.
*
*
* @author Martin Kouba
* @see BootstrapConfiguration
* @see ConfigurationKey
*/
@SuppressWarnings("deprecation")
public class WeldConfiguration implements Service {
public static final String CONFIGURATION_FILE = "weld.properties";
private static final String EXECUTOR_CONFIGURATION_FILE = "org.jboss.weld.executor.properties";
private static final String BOOTSTRAP_CONFIGURATION_FILE = "org.jboss.weld.bootstrap.properties";
private static final String SYSTEM_PROPETIES = "system properties";
private static final String OBSOLETE_SYSTEM_PROPETIES = "obsolete system properties";
private static final String EXTERNAL_CONFIGURATION_CLASS_NAME = "org.jboss.weld.configuration.spi.ExternalConfiguration";
private static final String UNSAFE_PROXIES_MARKER = "META-INF/org.jboss.weld.enableUnsafeProxies";
private final Map properties;
private final File proxyDumpFilePath;
private final Pattern proxyIgnoreFinalMethodsPattern;
/**
*
* @param services
* @param deployment
*/
public WeldConfiguration(ServiceRegistry services, Deployment deployment) {
Preconditions.checkArgumentNotNull(deployment, "deployment");
this.properties = init(services, deployment);
this.proxyDumpFilePath = initProxyDumpFilePath();
this.proxyIgnoreFinalMethodsPattern = initProxyIgnoreFinalMethodsPattern();
StringBuilder logOuputBuilder = new StringBuilder("{");
String prefix = "";
for (ConfigurationKey key : properties.keySet()) {
logOuputBuilder.append(prefix);
prefix = ", ";
logOuputBuilder.append(key.get() + "=" + properties.get(key));
}
logOuputBuilder.append("}");
ConfigurationLogger.LOG.configurationInitialized(logOuputBuilder.toString());
}
/**
*
* @param key
* @return the property for the given key
* @throws IllegalStateException If the property type does not match the required type
*/
public String getStringProperty(ConfigurationKey key) {
return getProperty(key, String.class);
}
/**
*
* @param key
* @return the property for the given key
* @throws IllegalStateException If the property type does not match the required type
*/
public Boolean getBooleanProperty(ConfigurationKey key) {
return getProperty(key, Boolean.class);
}
/**
*
* @param key
* @return the property for the given key
* @throws IllegalStateException If the property type does not match the required type
*/
public Long getLongProperty(ConfigurationKey key) {
return getProperty(key, Long.class);
}
/**
*
* @param key
* @return the property for the given key
* @throws IllegalStateException If the property type does not match the required type
*/
public Integer getIntegerProperty(ConfigurationKey key) {
return getProperty(key, Integer.class);
}
/**
*
* @return the path or null
if the generated bytecode should not be dumped
* @see ConfigurationKey#PROXY_DUMP
*/
public File getProxyDumpFilePath() {
return proxyDumpFilePath;
}
/**
*
* @param className
* @return true
if the final methods declared on the given type should be ignored, false
otherwise
* @see ConfigurationKey#PROXY_IGNORE_FINAL_METHODS
*/
public boolean isFinalMethodIgnored(String className) {
return proxyIgnoreFinalMethodsPattern != null ? proxyIgnoreFinalMethodsPattern.matcher(className).matches() : false;
}
@Override
public void cleanup() {
if (properties != null) {
properties.clear();
}
}
/**
* Merge two maps of configuration properties. If the original contains a mapping for the same key, the new mapping is ignored.
*
* @param original
* @param toMerge
*/
static void merge(Map original, Map toMerge, String mergedSourceDescription) {
for (Entry entry : toMerge.entrySet()) {
Object existing = original.get(entry.getKey());
if (existing != null) {
ConfigurationLogger.LOG.configurationKeyAlreadySet(entry.getKey().get(), existing, entry.getValue(), mergedSourceDescription);
} else {
original.put(entry.getKey(), entry.getValue());
}
}
}
/**
*
* @param key
* @param requiredType
* @throws IllegalStateException If the configuration property type does not match the required type
*/
static void checkRequiredType(ConfigurationKey key, Class> requiredType) {
if (!key.isValidValueType(requiredType)) {
throw ConfigurationLogger.LOG.configurationPropertyTypeMismatch(key.getDefaultValue().getClass(), requiredType, key.get());
}
}
/**
*
* @param key
* @return the string value of the system property or null
*/
static String getSystemProperty(String key) {
try {
return AccessController.doPrivileged(new GetSystemPropertyAction(key));
} catch (Throwable ignore) {
return null;
}
}
private Map init(ServiceRegistry services, Deployment deployment) {
// 1. Properties files
// weld.properties
Map properties = readFileProperties(findPropertiesFiles(deployment, CONFIGURATION_FILE));
// org.jboss.weld.bootstrap.properties
merge(properties,
readObsoleteFileProperties(
findPropertiesFiles(deployment, BOOTSTRAP_CONFIGURATION_FILE),
ImmutableMap. builder().put("concurrentDeployment", ConfigurationKey.CONCURRENT_DEPLOYMENT)
.put("preloaderThreadPoolSize", ConfigurationKey.PRELOADER_THREAD_POOL_SIZE).build()), BOOTSTRAP_CONFIGURATION_FILE);
// org.jboss.weld.executor.properties
merge(properties,
readObsoleteFileProperties(
findPropertiesFiles(deployment, EXECUTOR_CONFIGURATION_FILE),
ImmutableMap. builder().put("threadPoolSize", ConfigurationKey.EXECUTOR_THREAD_POOL_SIZE)
.put("threadPoolDebug", ConfigurationKey.EXECUTOR_THREAD_POOL_DEBUG)
.put("threadPoolType", ConfigurationKey.EXECUTOR_THREAD_POOL_TYPE)
.put("threadPoolKeepAliveTime", ConfigurationKey.EXECUTOR_THREAD_POOL_KEEP_ALIVE_TIME).build()), EXECUTOR_CONFIGURATION_FILE);
// META-INF/org.jboss.weld.enableUnsafeProxies
if (!findPropertiesFiles(deployment, UNSAFE_PROXIES_MARKER).isEmpty()) {
merge(properties, ImmutableMap.of(ConfigurationKey.RELAXED_CONSTRUCTION, true), UNSAFE_PROXIES_MARKER);
}
// 2. System properties
merge(properties, getSystemProperties(), SYSTEM_PROPETIES);
merge(properties, getObsoleteSystemProperties(), OBSOLETE_SYSTEM_PROPETIES);
// 3. Integrator SPI
// ExternalConfiguration.getConfigurationProperties() map has precedence
merge(properties, processExternalConfiguration(getExternalConfigurationOptions(services)), "ExternalConfiguration");
merge(properties, processBootstrapConfiguration(services.get(BootstrapConfiguration.class)), BootstrapConfiguration.class.getSimpleName());
return properties;
}
private File initProxyDumpFilePath() {
String dumpPath = getStringProperty(ConfigurationKey.PROXY_DUMP);
if (!dumpPath.isEmpty()) {
File tmp = new File(dumpPath);
if (!tmp.isDirectory() && !tmp.mkdirs()) {
BeanLogger.LOG.directoryCannotBeCreated(tmp.toString());
return null;
} else {
return tmp;
}
}
return null;
}
private Pattern initProxyIgnoreFinalMethodsPattern() {
String ignore = getStringProperty(ConfigurationKey.PROXY_IGNORE_FINAL_METHODS);
if (!ignore.isEmpty()) {
return Pattern.compile(ignore);
}
return null;
}
@SuppressFBWarnings(value = "DMI_COLLECTION_OF_URLS", justification = "Only local URLs involved")
private Set findPropertiesFiles(Deployment deployment, String fileName) {
Set resourceLoaders = new HashSet();
Set files = new HashSet();
ResourceLoader deploymentResourceLoader = deployment.getServices().get(ResourceLoader.class);
if (deploymentResourceLoader != null) {
resourceLoaders.add(deploymentResourceLoader);
}
for (BeanDeploymentArchive archive : deployment.getBeanDeploymentArchives()) {
ResourceLoader resourceLoader = archive.getServices().get(ResourceLoader.class);
if (resourceLoader == null) {
ConfigurationLogger.LOG.resourceLoaderNotSpecifiedForArchive(archive);
continue;
}
resourceLoaders.add(resourceLoader);
}
for (ResourceLoader resourceLoader : resourceLoaders) {
URL file = resourceLoader.getResource(fileName);
if (file != null) {
files.add(file);
}
}
return files;
}
/**
* Iterate through the {@link ConfigurationKey#values()} and try to get a system property for every key. The value is automatically converted - a runtime
* exception may be thrown during conversion.
*
* @return all the properties set as system properties
*/
private Map getSystemProperties() {
Map found = new EnumMap(ConfigurationKey.class);
for (ConfigurationKey key : ConfigurationKey.values()) {
String property = getSystemProperty(key.get());
if (property != null) {
processKeyValue(found, key, property);
}
}
return found;
}
/**
* Try to get a system property for obsolete keys. The value is automatically converted - a runtime exception may be thrown during conversion.
*
* @return all the properties whose system property keys were different in previous versions
*/
private Map getObsoleteSystemProperties() {
Map found = new EnumMap(ConfigurationKey.class);
String concurrentDeployment = getSystemProperty("org.jboss.weld.bootstrap.properties.concurrentDeployment");
if (concurrentDeployment != null) {
processKeyValue(found, ConfigurationKey.CONCURRENT_DEPLOYMENT, concurrentDeployment);
found.put(ConfigurationKey.CONCURRENT_DEPLOYMENT, ConfigurationKey.CONCURRENT_DEPLOYMENT.convertValue(concurrentDeployment));
}
String preloaderThreadPoolSize = getSystemProperty("org.jboss.weld.bootstrap.properties.preloaderThreadPoolSize");
if (preloaderThreadPoolSize != null) {
found.put(ConfigurationKey.PRELOADER_THREAD_POOL_SIZE, ConfigurationKey.PRELOADER_THREAD_POOL_SIZE.convertValue(preloaderThreadPoolSize));
}
return found;
}
/**
* Read the set of property files. Keys and Values are automatically validated and converted.
*
* @param resourceLoader
* @return all the properties from the weld.properties file
*/
@SuppressFBWarnings(value = "DMI_COLLECTION_OF_URLS", justification = "Only local URLs involved")
private Map readFileProperties(Set files) {
Map found = new EnumMap(ConfigurationKey.class);
for (URL file : files) {
ConfigurationLogger.LOG.readingPropertiesFile(file);
Properties fileProperties = loadProperties(file);
for (String name : fileProperties.stringPropertyNames()) {
processKeyValue(found, name, fileProperties.getProperty(name));
}
}
return found;
}
@SuppressFBWarnings(value = "DMI_COLLECTION_OF_URLS", justification = "Only local URLs involved")
private Map readObsoleteFileProperties(Set files, Map nameToKeyMap) {
if (files.isEmpty()) {
return Collections.emptyMap();
}
Map found = new EnumMap(ConfigurationKey.class);
for (URL file : files) {
ConfigurationLogger.LOG.readingPropertiesFile(file);
Properties fileProperties = loadProperties(file);
for (String name : fileProperties.stringPropertyNames()) {
ConfigurationKey key = nameToKeyMap.get(name);
if (key != null) {
processKeyValue(found, key, fileProperties.getProperty(name));
} else {
ConfigurationLogger.LOG.unsupportedConfigurationKeyFound(name + " in " + fileProperties);
}
}
}
return found;
}
private Map processBootstrapConfiguration(BootstrapConfiguration bootstrapConfiguration) {
if (bootstrapConfiguration != null) {
Map found = new EnumMap(ConfigurationKey.class);
processKeyValue(found, ConfigurationKey.CONCURRENT_DEPLOYMENT, bootstrapConfiguration.isConcurrentDeploymentEnabled());
processKeyValue(found, ConfigurationKey.PRELOADER_THREAD_POOL_SIZE, bootstrapConfiguration.getPreloaderThreadPoolSize());
processKeyValue(found, ConfigurationKey.NON_PORTABLE_MODE, bootstrapConfiguration.isNonPortableModeEnabled());
return found;
}
return Collections.emptyMap();
}
private Map processExternalConfiguration(Map externalConfiguration) {
Map found = new EnumMap(ConfigurationKey.class);
for (Entry entry : externalConfiguration.entrySet()) {
processKeyValue(found, entry.getKey(), entry.getValue());
}
return found;
}
private Map getExternalConfigurationOptions(ServiceRegistry services) {
// to stay compatible with older SPI versions we first check if ExternalConfiguration is available before using the class
if (Reflections.isClassLoadable(EXTERNAL_CONFIGURATION_CLASS_NAME, WeldClassLoaderResourceLoader.INSTANCE)) {
final ExternalConfiguration externalConfiguration = services.get(ExternalConfiguration.class);
if (externalConfiguration != null) {
return externalConfiguration.getConfigurationProperties();
}
}
return Collections.emptyMap();
}
/**
* Process the given key and value. First validate the value and check if there's no different value for the same key in the same source - invalid and
* different values are treated as a deployment problem.
*
* @param properties
* @param key
* @param value
*/
private void processKeyValue(Map properties, ConfigurationKey key, Object value) {
if (value instanceof String) {
value = key.convertValue((String) value);
}
if (key.isValidValue(value)) {
Object previous = properties.put(key, value);
if (previous != null && !previous.equals(value)) {
throw ConfigurationLogger.LOG.configurationKeyHasDifferentValues(key.get(), previous, value);
}
} else {
throw ConfigurationLogger.LOG.invalidConfigurationPropertyValue(value, key.get());
}
}
/**
* Process the given string key and value. First try to convert the stringKey
- unsupported keys are ignored. Then delegate to
* {@link #processKeyValue(Map, ConfigurationKey, Object)}.
*
* @param properties
* @param stringKey
* @param value
*/
private void processKeyValue(Map properties, String stringKey, Object value) {
ConfigurationKey key = ConfigurationKey.fromString(stringKey);
if (key != null) {
processKeyValue(properties, key, value);
} else {
ConfigurationLogger.LOG.unsupportedConfigurationKeyFound(stringKey);
}
}
@SuppressWarnings("unchecked")
private T getProperty(ConfigurationKey key, Class requiredType) {
checkRequiredType(key, requiredType);
Object property = properties.get(key);
return (T) (property != null ? property : key.getDefaultValue());
}
private Properties loadProperties(URL url) {
Properties properties = new Properties();
try {
properties.load(url.openStream());
} catch (IOException e) {
throw new ResourceLoadingException(e);
}
return properties;
}
}