org.simplejavamail.config.ConfigLoader Maven / Gradle / Ivy
Show all versions of core-module Show documentation
/*
* Copyright © 2009 Benny Bottema ([email protected])
*
* 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.simplejavamail.config;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.simplejavamail.api.email.ContentTransferEncoding;
import org.simplejavamail.api.email.config.DkimConfig;
import org.simplejavamail.api.mailer.config.LoadBalancingStrategy;
import org.simplejavamail.api.mailer.config.TransportStrategy;
import org.simplejavamail.internal.util.SimpleConversions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static java.util.Collections.unmodifiableMap;
import static java.util.regex.Pattern.compile;
import static org.simplejavamail.internal.util.MiscUtil.checkArgumentNotEmpty;
import static org.simplejavamail.internal.util.MiscUtil.valueNullOrEmpty;
import static org.simplejavamail.internal.util.Preconditions.assumeTrue;
/**
* Contains list of possible properties names and can produce a map of property values, if provided as file {@value #DEFAULT_CONFIG_FILENAME} on the
* classpath or as environment property.
*
* The following properties are allowed:
*
* - simplejavamail.javaxmail.debug
* - simplejavamail.transportstrategy
* - simplejavamail.smtp.host
* - simplejavamail.smtp.port
* - simplejavamail.smtp.username
* - simplejavamail.smtp.password
* - simplejavamail.disable.all.clientvalidation
* - simplejavamail.custom.sslfactory.class
* - simplejavamail.proxy.host
* - simplejavamail.proxy.port
* - simplejavamail.proxy.username
* - simplejavamail.proxy.password
* - simplejavamail.proxy.socks5bridge.port
* - simplejavamail.defaults.content.transfer.encoding
* - simplejavamail.defaults.subject
* - simplejavamail.defaults.from.name
* - simplejavamail.defaults.from.address
* - simplejavamail.defaults.replyto.name
* - simplejavamail.defaults.replyto.address
* - simplejavamail.defaults.bounceto.name
* - simplejavamail.defaults.bounceto.address
* - simplejavamail.defaults.to.name
* - simplejavamail.defaults.to.address
* - simplejavamail.defaults.cc.name
* - simplejavamail.defaults.cc.address
* - simplejavamail.defaults.bcc.name
* - simplejavamail.defaults.bcc.address
* - simplejavamail.defaults.poolsize
* - simplejavamail.defaults.poolsize.keepalivetime
* - simplejavamail.defaults.connectionpool.clusterkey.uuid
* - simplejavamail.defaults.connectionpool.coresize
* - simplejavamail.defaults.connectionpool.maxsize
* - simplejavamail.defaults.connectionpool.claimtimeout.millis
* - simplejavamail.defaults.connectionpool.expireafter.millis
* - simplejavamail.defaults.connectionpool.loadbalancing.strategy
* - simplejavamail.defaults.sessiontimeoutmillis
* - simplejavamail.defaults.trustallhosts
* - simplejavamail.defaults.trustedhosts
* - simplejavamail.defaults.verifyserveridentity
* - simplejavamail.transport.mode.logging.only
* - simplejavamail.opportunistic.tls
* - simplejavamail.smime.signing.keystore
* - simplejavamail.smime.signing.keystore_password
* - simplejavamail.smime.signing.key_alias
* - simplejavamail.smime.signing.key_password
* - simplejavamail.smime.encryption.certificate
* - simplejavamail.smime.signing.algorithm
* - simplejavamail.smime.encryption.key_encapsulation_algorithm
* - simplejavamail.smime.encryption.cipher
* - simplejavamail.dkim.signing.private_key_file_or_data
* - simplejavamail.dkim.signing.selector
* - simplejavamail.dkim.signing.signing_domain
* - simplejavamail.dkim.signing.use_length_param
* - simplejavamail.dkim.signing.excluded_headers_from_default_signing_list
* - simplejavamail.dkim.signing.header_canonicalization
* - simplejavamail.dkim.signing.body_canonicalization
* - simplejavamail.dkim.signing.algorithm
* - simplejavamail.embeddedimages.dynamicresolution.enable.dir
* - simplejavamail.embeddedimages.dynamicresolution.enable.url
* - simplejavamail.embeddedimages.dynamicresolution.enable.classpath
* - simplejavamail.embeddedimages.dynamicresolution.base.dir
* - simplejavamail.embeddedimages.dynamicresolution.base.url
* - simplejavamail.embeddedimages.dynamicresolution.base.classpath
* - simplejavamail.embeddedimages.dynamicresolution.outside.base.dir
* - simplejavamail.embeddedimages.dynamicresolution.outside.base.classpath
* - simplejavamail.embeddedimages.dynamicresolution.outside.base.url
* - simplejavamail.embeddedimages.dynamicresolution.mustbesuccesful
*
*/
public final class ConfigLoader {
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigLoader.class);
/**
* By default, the optional file {@value} will be loaded from classpath to load initial defaults.
*/
public static final String DEFAULT_CONFIG_FILENAME = "simplejavamail.properties";
/**
* This pattern recognizes extra property lines that should be loaded directly into JavaMail on the Session object.
*/
private static final Pattern EXTRA_PROPERTY_PATTERN = compile("^simplejavamail\\.extraproperties\\.(?.*)");
/**
* Initially try to load properties from "{@value #DEFAULT_CONFIG_FILENAME}".
*
* @see #loadProperties(String, boolean)
* @see #loadProperties(InputStream, boolean)
*/
private static final Map RESOLVED_PROPERTIES = new HashMap<>();
static {
// static initializer block, because loadProperties needs to modify RESOLVED_PROPERTIES while loading
// this is not possible when we are initializing the same field.
// RESOLVED_PROPERTIES = loadProperties(DEFAULT_CONFIG_FILENAME); <-- not possible
loadProperties(DEFAULT_CONFIG_FILENAME, false);
}
/**
* List of all the properties recognized by Simple Java Mail. Can be used to programmatically get, set or remove default values.
*
* @see simplejavamail.org
*/
public enum Property {
JAVAXMAIL_DEBUG("simplejavamail.javaxmail.debug"),
TRANSPORT_STRATEGY("simplejavamail.transportstrategy"),
SMTP_HOST("simplejavamail.smtp.host"),
SMTP_PORT("simplejavamail.smtp.port"),
SMTP_USERNAME("simplejavamail.smtp.username"),
SMTP_PASSWORD("simplejavamail.smtp.password"),
DISABLE_ALL_CLIENTVALIDATION("simplejavamail.disable.all.clientvalidation"),
CUSTOM_SSLFACTORY_CLASS("simplejavamail.custom.sslfactory.class"),
PROXY_HOST("simplejavamail.proxy.host"),
PROXY_PORT("simplejavamail.proxy.port"),
PROXY_USERNAME("simplejavamail.proxy.username"),
PROXY_PASSWORD("simplejavamail.proxy.password"),
PROXY_SOCKS5BRIDGE_PORT("simplejavamail.proxy.socks5bridge.port"),
DEFAULT_SUBJECT("simplejavamail.defaults.subject"),
DEFAULT_CONTENT_TRANSFER_ENCODING("simplejavamail.defaults.content.transfer.encoding"),
DEFAULT_FROM_NAME("simplejavamail.defaults.from.name"),
DEFAULT_FROM_ADDRESS("simplejavamail.defaults.from.address"),
DEFAULT_REPLYTO_NAME("simplejavamail.defaults.replyto.name"),
DEFAULT_REPLYTO_ADDRESS("simplejavamail.defaults.replyto.address"),
DEFAULT_BOUNCETO_NAME("simplejavamail.defaults.bounceto.name"),
DEFAULT_BOUNCETO_ADDRESS("simplejavamail.defaults.bounceto.address"),
DEFAULT_TO_NAME("simplejavamail.defaults.to.name"),
DEFAULT_TO_ADDRESS("simplejavamail.defaults.to.address"),
DEFAULT_CC_NAME("simplejavamail.defaults.cc.name"),
DEFAULT_CC_ADDRESS("simplejavamail.defaults.cc.address"),
DEFAULT_BCC_NAME("simplejavamail.defaults.bcc.name"),
DEFAULT_BCC_ADDRESS("simplejavamail.defaults.bcc.address"),
DEFAULT_POOL_SIZE("simplejavamail.defaults.poolsize"),
DEFAULT_CONNECTIONPOOL_CLUSTER_KEY("simplejavamail.defaults.connectionpool.clusterkey.uuid"),
DEFAULT_CONNECTIONPOOL_CORE_SIZE("simplejavamail.defaults.connectionpool.coresize"),
DEFAULT_CONNECTIONPOOL_MAX_SIZE("simplejavamail.defaults.connectionpool.maxsize"),
DEFAULT_CONNECTIONPOOL_CLAIMTIMEOUT_MILLIS("simplejavamail.defaults.connectionpool.claimtimeout.millis"),
DEFAULT_CONNECTIONPOOL_EXPIREAFTER_MILLIS("simplejavamail.defaults.connectionpool.expireafter.millis"),
DEFAULT_CONNECTIONPOOL_LOADBALANCING_STRATEGY("simplejavamail.defaults.connectionpool.loadbalancing.strategy"),
DEFAULT_POOL_KEEP_ALIVE_TIME("simplejavamail.defaults.poolsize.keepalivetime"),
DEFAULT_SESSION_TIMEOUT_MILLIS("simplejavamail.defaults.sessiontimeoutmillis"),
DEFAULT_TRUST_ALL_HOSTS("simplejavamail.defaults.trustallhosts"),
DEFAULT_TRUSTED_HOSTS("simplejavamail.defaults.trustedhosts"),
DEFAULT_VERIFY_SERVER_IDENTITY("simplejavamail.defaults.verifyserveridentity"),
TRANSPORT_MODE_LOGGING_ONLY("simplejavamail.transport.mode.logging.only"),
OPPORTUNISTIC_TLS("simplejavamail.opportunistic.tls"),
SMIME_SIGNING_KEYSTORE("simplejavamail.smime.signing.keystore"),
SMIME_SIGNING_KEYSTORE_PASSWORD("simplejavamail.smime.signing.keystore_password"),
SMIME_SIGNING_KEY_ALIAS("simplejavamail.smime.signing.key_alias"),
SMIME_SIGNING_KEY_PASSWORD("simplejavamail.smime.signing.key_password"),
SMIME_SIGNING_ALGORITHM("simplejavamail.smime.signing.algorithm"),
SMIME_ENCRYPTION_KEY_ENCAPSULATION_ALGORITHM("simplejavamail.smime.encryption.key_encapsulation_algorithm"),
SMIME_ENCRYPTION_CIPHER("simplejavamail.smime.encryption.cipher"),
DKIM_PRIVATE_KEY_FILE_OR_DATA("simplejavamail.dkim.signing.private_key_file_or_data"),
DKIM_SELECTOR("simplejavamail.dkim.signing.selector"),
DKIM_SIGNING_DOMAIN("simplejavamail.dkim.signing.signing_domain"),
DKIM_SIGNING_USE_LENGTH_PARAM("simplejavamail.dkim.signing.use_length_param"),
DKIM_EXCLUDED_HEADERS_FROM_DEFAULT_SIGNING_LIST("simplejavamail.dkim.signing.excluded_headers_from_default_signing_list"),
DKIM_SIGNING_HEADER_CANONICALIZATION("simplejavamail.dkim.signing.header_canonicalization"),
DKIM_SIGNING_BODY_CANONICALIZATION("simplejavamail.dkim.signing.body_canonicalization"),
DKIM_SIGNING_ALGORITHM("simplejavamail.dkim.signing.algorithm"),
SMIME_ENCRYPTION_CERTIFICATE("simplejavamail.smime.encryption.certificate"),
EMBEDDEDIMAGES_DYNAMICRESOLUTION_ENABLE_DIR("simplejavamail.embeddedimages.dynamicresolution.enable.dir"),
EMBEDDEDIMAGES_DYNAMICRESOLUTION_ENABLE_CLASSPATH("simplejavamail.embeddedimages.dynamicresolution.enable.classpath"),
EMBEDDEDIMAGES_DYNAMICRESOLUTION_ENABLE_URL("simplejavamail.embeddedimages.dynamicresolution.enable.url"),
EMBEDDEDIMAGES_DYNAMICRESOLUTION_BASE_DIR("simplejavamail.embeddedimages.dynamicresolution.base.dir"),
EMBEDDEDIMAGES_DYNAMICRESOLUTION_BASE_CLASSPATH("simplejavamail.embeddedimages.dynamicresolution.base.classpath"),
EMBEDDEDIMAGES_DYNAMICRESOLUTION_BASE_URL("simplejavamail.embeddedimages.dynamicresolution.base.url"),
EMBEDDEDIMAGES_DYNAMICRESOLUTION_OUTSIDE_BASE_DIR("simplejavamail.embeddedimages.dynamicresolution.outside.base.dir"),
EMBEDDEDIMAGES_DYNAMICRESOLUTION_OUTSIDE_BASE_URL("simplejavamail.embeddedimages.dynamicresolution.outside.base.classpath"),
EMBEDDEDIMAGES_DYNAMICRESOLUTION_OUTSIDE_BASE_CLASSPATH("simplejavamail.embeddedimages.dynamicresolution.outside.base.url"),
EMBEDDEDIMAGES_DYNAMICRESOLUTION_MUSTBESUCCESFUL("simplejavamail.embeddedimages.dynamicresolution.mustbesuccesful"),
EXTRA_PROPERTIES("simplejavamail.extraproperties.*");
private final String key;
Property(final String key) {
this.key = key;
}
public String key() {
return key;
}
}
private ConfigLoader() {
}
/**
* @return The value if not null or else the value from config file if provided or else null
.
*/
@Nullable
public static T valueOrProperty(final @Nullable T value, final Property property) {
return valueOrProperty(value, property, null);
}
/**
* See {@link #valueOrProperty(Object, Property, Object)}.
*/
@Nullable
public static String valueOrPropertyAsString(@Nullable final String value, @NotNull final Property property, @Nullable final String defaultValue) {
return SimpleConversions.convertToString(valueOrProperty(value, property, defaultValue));
}
/**
* See {@link #valueOrProperty(Object, Property, Object)}.
*/
@Nullable
public static Boolean valueOrPropertyAsBoolean(@Nullable final Boolean value, @NotNull final Property property, @Nullable final Boolean defaultValue) {
return SimpleConversions.convertToBoolean(valueOrProperty(value, property, defaultValue));
}
/**
* See {@link #valueOrProperty(Object, Property, Object)}.
*/
@Nullable
public static Integer valueOrPropertyAsInteger(@Nullable final Integer value, @NotNull final Property property, @Nullable final Integer defaultValue) {
return SimpleConversions.convertToInteger(valueOrProperty(value, property, defaultValue));
}
/**
* Returns the given value if not null and not empty, otherwise tries to resolve the given property and if still not found resort to the default value if
* provided.
*
* Null or blank values are never allowed, so they are always ignored.
*
* @return The value if not null or else the value from config file if provided or else defaultValue
.
*/
@Nullable
public static T valueOrProperty(@Nullable final T value, @NotNull final Property property, @Nullable final T defaultValue) {
if (!valueNullOrEmpty(value)) {
LOGGER.trace("using provided argument value {} for property {}", value, property);
return value;
} else if (hasProperty(property)) {
final T propertyValue = getProperty(property);
LOGGER.trace("using value {} from config file for property {}", propertyValue, property);
return propertyValue;
} else {
LOGGER.trace("no value provided as argument or in config file for property {}, using default value {}", property, defaultValue);
return defaultValue;
}
}
public static synchronized boolean hasProperty(final Property property) {
return !valueNullOrEmpty(RESOLVED_PROPERTIES.get(property));
}
@SuppressWarnings("unchecked")
@Nullable
public static synchronized T getProperty(final Property property) {
return (T) RESOLVED_PROPERTIES.get(property);
}
@Nullable
public static synchronized String getStringProperty(final Property property) {
return SimpleConversions.convertToString(RESOLVED_PROPERTIES.get(property));
}
@Nullable
public static synchronized Integer getIntegerProperty(final Property property) {
return SimpleConversions.convertToInteger(RESOLVED_PROPERTIES.get(property));
}
@Nullable
public static synchronized Boolean getBooleanProperty(final Property property) {
return SimpleConversions.convertToBoolean(RESOLVED_PROPERTIES.get(property));
}
/**
* Loads properties from property file on the classpath, if provided. Calling this method only has effect on new Email and Mailer instances after
* this.
*
* @param filename Any file that is on the classpath that holds a list of key=value pairs.
* @param addProperties Flag to indicate if the new properties should be added or replacing the old properties.
* @return The updated properties map that is used internally.
*/
public static Map loadProperties(final String filename, final boolean addProperties) {
final InputStream input = ConfigLoader.class.getClassLoader().getResourceAsStream(filename);
if (input != null) {
LOGGER.debug("Property file {} found on classpath, loading System properties and environment variables", filename);
return loadProperties(input, addProperties);
} else {
LOGGER.debug("Property file not found on classpath, loading System properties and environment variables");
return loadProperties(new Properties(), addProperties);
}
}
/**
* Loads properties from another properties source, in case you want to provide your own list.
*
* @param properties Your own list of properties
* @param addProperties Flag to indicate if the new properties should be added or replacing the old properties.
* @return The updated properties map that is used internally.
*/
public static Map loadProperties(final Properties properties, final boolean addProperties) {
if (!addProperties) {
RESOLVED_PROPERTIES.clear();
}
RESOLVED_PROPERTIES.putAll(readProperties(properties));
return unmodifiableMap(RESOLVED_PROPERTIES);
}
/**
* Loads properties from {@link InputStream}. Calling this method only has effect on new Email and Mailer instances after this.
*
* @param inputStream Source of property key=value pairs separated by newline \n characters.
* @param addProperties Flag to indicate if the new properties should be added or replacing the old properties.
* @return The updated properties map that is used internally.
*/
public static synchronized Map loadProperties(final @Nullable InputStream inputStream, final boolean addProperties) {
final Properties prop = new Properties();
try {
prop.load(checkArgumentNotEmpty(inputStream, "InputStream was null"));
} catch (final IOException e) {
throw new IllegalStateException("error reading properties file from inputstream", e);
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (final IOException e) {
LOGGER.error(e.getMessage(), e);
}
}
}
if (!addProperties) {
RESOLVED_PROPERTIES.clear();
}
RESOLVED_PROPERTIES.putAll(readProperties(prop));
return unmodifiableMap(RESOLVED_PROPERTIES);
}
/**
* @return All properties in priority of System property {@code >} Environment variable {@code >} File properties.
*/
private static Map readProperties(final @NotNull Properties fileProperties) {
final Properties filePropertiesLeft = new Properties();
filePropertiesLeft.putAll(fileProperties);
final Map resolvedProps = new HashMap<>();
for (final Property prop : Property.values()) {
String systemValue = System.getProperty(prop.key);
String envValue = System.getenv(prop.key.replace('.', '_').toUpperCase());
if (!valueNullOrEmpty(systemValue)) {
LOGGER.debug("{}: {}", prop.key, systemValue);
final Object parsedValue = parsePropertyValue(systemValue);
resolvedProps.put(prop, parsedValue);
filePropertiesLeft.remove(prop.key);
} else if (!valueNullOrEmpty(envValue)) {
LOGGER.debug("{}: {}", prop.key, envValue);
final Object parsedValue = parsePropertyValue(envValue);
resolvedProps.put(prop, parsedValue);
filePropertiesLeft.remove(prop.key);
} else {
final Object rawValue = filePropertiesLeft.remove(prop.key);
if (rawValue != null) {
if (rawValue instanceof String) {
resolvedProps.put(prop, parsePropertyValue((String) rawValue));
} else {
resolvedProps.put(prop, rawValue);
}
}
}
}
final Map extraProperties = new HashMap<>();
extraProperties.putAll(filterExtraJavaMailProperties(null, System.getProperties().entrySet()));
//noinspection unchecked,rawtypes
extraProperties.putAll(filterExtraJavaMailProperties(null, (Set) System.getenv().entrySet()));
extraProperties.putAll(filterExtraJavaMailProperties(filePropertiesLeft, fileProperties.entrySet()));
resolvedProps.put(Property.EXTRA_PROPERTIES, extraProperties);
if (!filePropertiesLeft.isEmpty()) {
throw new IllegalStateException("unknown properties provided " + filePropertiesLeft);
}
return resolvedProps;
}
private static Map filterExtraJavaMailProperties(@Nullable final Properties filePropertiesLeft, final Set> entries) {
final Map extraProperties = new HashMap<>();
for (Map.Entry