com.lyders.properties.ApplicationProperties Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of application-properties Show documentation
Show all versions of application-properties Show documentation
This ApplicationProperties Java package provides a fast and easily configurable application.properties loader
that mimics some key features of the Spring Boot application.properties loader. This package allows for
multi-level overloading of properties based on a basename and optional suffix by searching the classpath,
optional configurable file paths along with the JNDI server container context.
The class
[ApplicationPropertiesConfig](src/main/java/com/lyders/application-properties/ApplicationPropertiesConfig.java)
controls the features of the overloading of properties such as the base name of the properties file, an optional
suffix that allows for the loading of environment-specific or scenario-specific property files as needed.
The following features are supported:
* Names of properties files can be customized
* Locations of properties files can be customized. The following path types are supported:
* class path
* file system paths
* JNDI environment naming context (e.g., Servlet/JSP running under Tomcat)
* Environment-specific property files can override the values in default properties files via a "suffix"
The newest version!
package com.lyders.properties;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.io.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.util.*;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.lyders.properties.ApplicationProperties.PATH_TYPE.*;
/**
* @author [email protected]
*/
@Data
public class ApplicationProperties extends HashMap implements Serializable {
private static final long serialVersionUID = 240517204195025182L;
private static final Log LOG = LogFactory.getLog(ApplicationProperties.class);
private static final String PROPERTY_SOURCE_ENV = "env";
private static final String PROPERTY_SOURCE_PROP = "prop";
public enum PATH_TYPE {
CLASSPATH_PREFIX("classpath:"),
FILEPATH_PREFIX("file:"),
SERVLET_PREFIX("servlet:");
public final String value;
PATH_TYPE(String value) {
this.value = value;
}
@Override
public String toString() {
return this.value;
}
}
public static final String PATH_TYPE_REGEX = "^([^:]+:)?([^:]+)$";
public static final String FAILED_TO_LOAD_PROPERTIES_FROM_CLASS_PATH_MSG_TEMPLATE = "Failed to load '%s' properties file named '%s' from path: %s";
private final ApplicationPropertiesConfig cfg;
private final String propertiesFileName;
private final String suffixedFileName;
private final LinkedHashMap sources = new LinkedHashMap<>();
private final HashMap cachedProps = new HashMap<>();
/* Overloaded constructor that passes null for config parameter of main constructor
* */
public ApplicationProperties() throws FileNotFoundException {
this(null);
}
/**
* Create an instance of the ApplicationProperties based on a given config and an optional list of paths used to control how property files are to be loaded
*
* @param cfg, config settings to use for this instance of ApplicationProperties
* @param pathTypeStrs, additional list of paths to append to the given list to paths in the cfg param
* @throws FileNotFoundException if a properties file could not be found
*/
public ApplicationProperties(ApplicationPropertiesConfig cfg, String... pathTypeStrs) throws FileNotFoundException {
if (cfg != null) {
// create a local copy of the given config so that we can add the additional paths without changing the given config
this.cfg = new ApplicationPropertiesConfig(cfg);
} else {
this.cfg = new ApplicationPropertiesConfig();
}
if (pathTypeStrs != null && pathTypeStrs.length > 0) {
// append additional paths to local copy of the given config
this.cfg.getPaths().addAll(Arrays.asList(pathTypeStrs));
}
propertiesFileName = this.cfg.getPropertiesFileName();
suffixedFileName = this.cfg.getSuffixFileName();
init();
}
void init() throws FileNotFoundException {
loadDefaults(this.cfg);
loadPropertiesFromPaths(this.cfg);
}
void loadPropertiesFromClassPath(String pathStr, String propertiesFileName) throws FileNotFoundException {
String filePathStr = Paths.get(pathStr, propertiesFileName).toString();
URL res = this.getClass().getClassLoader().getResource(filePathStr);
try (InputStream in = res.openStream()) {
Properties properties = new Properties();
properties.load(in);
logSourceFilePathAndProperties(res.toString(), properties);
putAll((Map) properties);
} catch (NullPointerException | IOException e) {
throw new FileNotFoundException(String.format(FAILED_TO_LOAD_PROPERTIES_FROM_CLASS_PATH_MSG_TEMPLATE, PATH_TYPE.CLASSPATH_PREFIX, propertiesFileName, pathStr));
}
}
void loadPropertiesFromFileSystem(String pathStr, String propertiesFileName) throws FileNotFoundException {
String filePathStr = Paths.get(pathStr, propertiesFileName).toAbsolutePath().toString();
try (FileReader fileReader = new FileReader(filePathStr, StandardCharsets.UTF_8)) {
Properties properties = new Properties();
properties.load(fileReader);
logSourceFilePathAndProperties(filePathStr, properties);
putAll((Map) properties);
} catch (IOException e) {
throw new FileNotFoundException(String.format(FAILED_TO_LOAD_PROPERTIES_FROM_CLASS_PATH_MSG_TEMPLATE, PATH_TYPE.FILEPATH_PREFIX, propertiesFileName, pathStr));
}
}
void loadPropertiesFromServletPath(String pathStr, String propertiesFileName) throws FileNotFoundException {
String filePathStr = Paths.get(cfg.getServletPropertiesBaseDirectory(), pathStr, propertiesFileName).toAbsolutePath().toString();
try (FileReader fileReader = new FileReader(filePathStr, StandardCharsets.UTF_8)) {
Properties properties = new Properties();
properties.load(fileReader);
logSourceFilePathAndProperties(filePathStr, properties);
putAll((Map) properties);
} catch (IOException e) {
throw new FileNotFoundException(String.format(FAILED_TO_LOAD_PROPERTIES_FROM_CLASS_PATH_MSG_TEMPLATE, PATH_TYPE.FILEPATH_PREFIX, propertiesFileName, pathStr));
}
}
void loadDefaults(ApplicationPropertiesConfig cfg) throws FileNotFoundException {
// should we try to load an application.properties file from the classpath if it exists and use it as the default set of properties?
if (cfg.isLoadClassPathRootPropertiesAsDefaults()) {
// load the property file from the classpath, if it exists
loadPropertiesFromClassPath("", cfg.getPropertiesFileName());
// additionally, if a suffixed file exists in the same place...then load it also to override the properties from the non-suffixed file
if (cfg.getSuffixFileName() != null) {
loadPropertiesFromClassPath("", cfg.getSuffixFileName());
}
}
}
void loadPropertiesFromPaths(ApplicationPropertiesConfig cfg) throws FileNotFoundException {
Pattern pathTypePattern = Pattern.compile(PATH_TYPE_REGEX);
if (!cfg.isLoadClassPathRootPropertiesAsDefaults() && cfg.getPaths().isEmpty()) {
throw new IllegalStateException("Failed to find any properties files because LoadClassPathRootPropertiesAsDefaults=NO and no file paths were given to search through");
}
// load properties file from each of the given paths if a file exists there
for (String p : cfg.getPaths()) {
Matcher matcher = pathTypePattern.matcher(p);
while (matcher.find()) {
String pathType = matcher.group(1);
if (pathType == null) {
pathType = FILEPATH_PREFIX.value;
}
String pathStr = matcher.group(2);
pathType = getPathTypeDefault(cfg, pathType);
loadFromPathType(pathType, pathStr);
}
}
}
private void loadFromPathType(String pathType, String pathStr) throws FileNotFoundException {
if (CLASSPATH_PREFIX.value.equals(pathType)) {
loadPropertiesFromClassPath(pathStr, propertiesFileName);
if (suffixedFileName != null) {
loadPropertiesFromClassPath(pathStr, suffixedFileName);
}
} else if (SERVLET_PREFIX.value.equals(pathType)) {
loadPropertiesFromServletPath(pathStr, propertiesFileName);
if (suffixedFileName != null) {
loadPropertiesFromServletPath(pathStr, suffixedFileName);
}
} else if (FILEPATH_PREFIX.value.equals(pathType)) {
loadPropertiesFromFileSystem(pathStr, propertiesFileName);
if (suffixedFileName != null) {
loadPropertiesFromFileSystem(pathStr, suffixedFileName);
}
} else {
throw new IllegalArgumentException("Unknown properties path type prefix: " + pathType);
}
}
private String getPathTypeDefault(ApplicationPropertiesConfig cfg, String pathType) {
if (StringUtils.isEmpty(pathType)) {
if (cfg.getServletContext() != null) {
pathType = SERVLET_PREFIX.value;
} else {
pathType = FILEPATH_PREFIX.value;
}
}
return pathType;
}
/* utility method to print out a list of all the final property values
* */
public void printAllProperties(Consumer f) {
for (Map.Entry entry : entrySet()) {
f.accept(entry.getKey() + ": " + entry.getValue());
}
}
/* utility method to print out a detailed list of which properties were loaded from each file
* */
public void printAllSourcesAndProperties(Consumer f) {
if (!cfg.isLogSourceFilePathsAndProperties()) {
f.accept("Logging of source files paths and properties is not enabled");
}
if (sources.isEmpty()) {
f.accept("No source files found");
}
int fileIdx = 0;
for (Map.Entry source : sources.entrySet()) {
String fileName = source.getKey();
f.accept(String.format("Source file %d: %s", ++fileIdx, fileName));
Properties properties = source.getValue();
Iterator