/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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.apache.logging.log4j.util; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.concurrent.ConcurrentHashMap; /** * <em>Consider this class private.</em> * <p> * Helps access properties. This utility provides a method to override system properties by specifying properties in a * properties file. * </p> */ public final class PropertiesUtil { private static final PropertiesUtil LOG4J_PROPERTIES = new PropertiesUtil("log4j2.component.properties"); private final Properties props; /** * Constructs a PropertiesUtil using a given Properties object as its source of defined properties. * * @param props the Properties to use by default */ public PropertiesUtil(final Properties props) { this.props = props; } /** * Constructs a PropertiesUtil for a given properties file name on the classpath. The properties specified in this * file are used by default. If a property is not defined in this file, then the equivalent system property is used. * * @param propertiesFileName the location of properties file to load */ public PropertiesUtil(final String propertiesFileName) { final Properties properties = new Properties(); for (final URL url : LoaderUtil.findResources(propertiesFileName)) { try (final InputStream in = url.openStream()) { properties.load(in); } catch (final IOException ioe) { LowLevelLogUtil.logException("Unable to read " + url.toString(), ioe); } } this.props = properties; } /** * Loads and closes the given property input stream. If an error occurs, log to the status logger. * * @param in a property input stream. * @param source a source object describing the source, like a resource string or a URL. * @return a new Properties object */ static Properties loadClose(final InputStream in, final Object source) { final Properties props = new Properties(); if (null != in) { try { props.load(in); } catch (final IOException e) { LowLevelLogUtil.logException("Unable to read " + source, e); } finally { try { in.close(); } catch (final IOException e) { LowLevelLogUtil.logException("Unable to close " + source, e); } } } return props; } /** * Returns the PropertiesUtil used by Log4j. * * @return the main Log4j PropertiesUtil instance. */ public static PropertiesUtil getProperties() { return LOG4J_PROPERTIES; } /** * Gets the named property as a boolean value. If the property matches the string {@code "true"} (case-insensitive), * then it is returned as the boolean value {@code true}. Any other non-{@code null} text in the property is * considered {@code false}. * * @param name the name of the property to look up * @return the boolean value of the property or {@code false} if undefined. */ public boolean getBooleanProperty(final String name) { return getBooleanProperty(name, false); } /** * Gets the named property as a boolean value. * * @param name the name of the property to look up * @param defaultValue the default value to use if the property is undefined * @return the boolean value of the property or {@code defaultValue} if undefined. */ public boolean getBooleanProperty(final String name, final boolean defaultValue) { final String prop = getStringProperty(name); return (prop == null) ? defaultValue : "true".equalsIgnoreCase(prop); } /** * Gets the named property as a Charset value. * * @param name the name of the property to look up * @return the Charset value of the property or {@link Charset#defaultCharset()} if undefined. */ public Charset getCharsetProperty(final String name) { return getCharsetProperty(name, Charset.defaultCharset()); } /** * Gets the named property as a Charset value. * * @param name the name of the property to look up * @param defaultValue the default value to use if the property is undefined * @return the Charset value of the property or {@code defaultValue} if undefined. */ public Charset getCharsetProperty(final String name, final Charset defaultValue) { final String prop = getStringProperty(name); return prop == null ? defaultValue : Charset.forName(prop); } /** * Gets the named property as a double. * * @param name the name of the property to look up * @param defaultValue the default value to use if the property is undefined * @return the parsed double value of the property or {@code defaultValue} if it was undefined or could not be parsed. */ public double getDoubleProperty(final String name, final double defaultValue) { final String prop = getStringProperty(name); if (prop != null) { try { return Double.parseDouble(prop); } catch (final Exception ignored) { return defaultValue; } } return defaultValue; } /** * Gets the named property as an integer. * * @param name the name of the property to look up * @param defaultValue the default value to use if the property is undefined * @return the parsed integer value of the property or {@code defaultValue} if it was undefined or could not be * parsed. */ public int getIntegerProperty(final String name, final int defaultValue) { final String prop = getStringProperty(name); if (prop != null) { try { return Integer.parseInt(prop); } catch (final Exception ignored) { return defaultValue; } } return defaultValue; } /** * Gets the named property as a long. * * @param name the name of the property to look up * @param defaultValue the default value to use if the property is undefined * @return the parsed long value of the property or {@code defaultValue} if it was undefined or could not be parsed. */ public long getLongProperty(final String name, final long defaultValue) { final String prop = getStringProperty(name); if (prop != null) { try { return Long.parseLong(prop); } catch (final Exception ignored) { return defaultValue; } } return defaultValue; } /** * Gets the named property as a String. * * @param name the name of the property to look up * @return the String value of the property or {@code null} if undefined. */ public String getStringProperty(final String name) { String prop = null; try { prop = System.getProperty(name); } catch (final SecurityException ignored) { // Ignore } return prop == null ? props.getProperty(name) : prop; } /** * Gets the named property as a String. * * @param name the name of the property to look up * @param defaultValue the default value to use if the property is undefined * @return the String value of the property or {@code defaultValue} if undefined. */ public String getStringProperty(final String name, final String defaultValue) { final String prop = getStringProperty(name); return (prop == null) ? defaultValue : prop; } /** * Return the system properties or an empty Properties object if an error occurs. * * @return The system properties. */ public static Properties getSystemProperties() { try { return new Properties(System.getProperties()); } catch (final SecurityException ex) { LowLevelLogUtil.logException("Unable to access system properties.", ex); // Sandboxed - can't read System Properties return new Properties(); } } /** * Extracts properties that start with or are equals to the specific prefix and returns them in a new Properties * object with the prefix removed. * * @param properties The Properties to evaluate. * @param prefix The prefix to extract. * @return The subset of properties. */ public static Properties extractSubset(final Properties properties, final String prefix) { final Properties subset = new Properties(); if (prefix == null || prefix.length() == 0) { return subset; } final String prefixToMatch = prefix.charAt(prefix.length() - 1) != '.' ? prefix + '.' : prefix; final List<String> keys = new ArrayList<>(); for (final String key : properties.stringPropertyNames()) { if (key.startsWith(prefixToMatch)) { subset.setProperty(key.substring(prefixToMatch.length()), properties.getProperty(key)); keys.add(key); } } for (final String key : keys) { properties.remove(key); } return subset; } /** * Partitions a properties map based on common key prefixes up to the first period. * * @param properties properties to partition * @return the partitioned properties where each key is the common prefix (minus the period) and the values are * new property maps without the prefix and period in the key * @since 2.6 */ public static Map<String, Properties> partitionOnCommonPrefixes(final Properties properties) { final Map<String, Properties> parts = new ConcurrentHashMap<>(); for (final String key : properties.stringPropertyNames()) { final String prefix = key.substring(0, key.indexOf('.')); if (!parts.containsKey(prefix)) { parts.put(prefix, new Properties()); } parts.get(prefix).setProperty(key.substring(key.indexOf('.') + 1), properties.getProperty(key)); } return parts; } /** * Returns true if system properties tell us we are running on Windows. * @return true if system properties tell us we are running on Windows. */ public boolean isOsWindows() { return getStringProperty("os.name").startsWith("Windows"); } }