001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017package org.apache.logging.log4j.util; 018 019import java.io.IOException; 020import java.io.InputStream; 021import java.net.URL; 022import java.util.ArrayList; 023import java.util.List; 024import java.util.Map; 025import java.util.Properties; 026import java.util.concurrent.ConcurrentHashMap; 027 028/** 029 * <em>Consider this class private.</em> 030 * <p> 031 * Helps access properties. This utility provides a method to override system properties by specifying properties in a 032 * properties file. 033 * </p> 034 */ 035public final class PropertiesUtil { 036 037 private static final PropertiesUtil LOG4J_PROPERTIES = new PropertiesUtil("log4j2.component.properties"); 038 039 private final Properties props; 040 041 /** 042 * Constructs a PropertiesUtil using a given Properties object as its source of defined properties. 043 * 044 * @param props the Properties to use by default 045 */ 046 public PropertiesUtil(final Properties props) { 047 this.props = props; 048 } 049 050 /** 051 * Constructs a PropertiesUtil for a given properties file name on the classpath. The properties specified in this 052 * file are used by default. If a property is not defined in this file, then the equivalent system property is used. 053 * 054 * @param propertiesFileName the location of properties file to load 055 */ 056 public PropertiesUtil(final String propertiesFileName) { 057 final Properties properties = new Properties(); 058 for (final URL url : LoaderUtil.findResources(propertiesFileName)) { 059 try (final InputStream in = url.openStream()) { 060 properties.load(in); 061 } catch (final IOException ioe) { 062 LowLevelLogUtil.logException("Unable to read " + url.toString(), ioe); 063 } 064 } 065 this.props = properties; 066 } 067 068 /** 069 * Loads and closes the given property input stream. If an error occurs, log to the status logger. 070 * 071 * @param in a property input stream. 072 * @param source a source object describing the source, like a resource string or a URL. 073 * @return a new Properties object 074 */ 075 static Properties loadClose(final InputStream in, final Object source) { 076 final Properties props = new Properties(); 077 if (null != in) { 078 try { 079 props.load(in); 080 } catch (final IOException e) { 081 LowLevelLogUtil.logException("Unable to read " + source, e); 082 } finally { 083 try { 084 in.close(); 085 } catch (final IOException e) { 086 LowLevelLogUtil.logException("Unable to close " + source, e); 087 } 088 } 089 } 090 return props; 091 } 092 093 /** 094 * Returns the PropertiesUtil used by Log4j. 095 * 096 * @return the main Log4j PropertiesUtil instance. 097 */ 098 public static PropertiesUtil getProperties() { 099 return LOG4J_PROPERTIES; 100 } 101 102 /** 103 * Gets the named property as a boolean value. If the property matches the string {@code "true"} (case-insensitive), 104 * then it is returned as the boolean value {@code true}. Any other non-{@code null} text in the property is 105 * considered {@code false}. 106 * 107 * @param name the name of the property to look up 108 * @return the boolean value of the property or {@code false} if undefined. 109 */ 110 public boolean getBooleanProperty(final String name) { 111 return getBooleanProperty(name, false); 112 } 113 114 /** 115 * Gets the named property as a boolean value. 116 * 117 * @param name the name of the property to look up 118 * @param defaultValue the default value to use if the property is undefined 119 * @return the boolean value of the property or {@code defaultValue} if undefined. 120 */ 121 public boolean getBooleanProperty(final String name, final boolean defaultValue) { 122 final String prop = getStringProperty(name); 123 return (prop == null) ? defaultValue : "true".equalsIgnoreCase(prop); 124 } 125 126 /** 127 * Gets the named property as a double. 128 * 129 * @param name the name of the property to look up 130 * @param defaultValue the default value to use if the property is undefined 131 * @return the parsed double value of the property or {@code defaultValue} if it was undefined or could not be parsed. 132 */ 133 public double getDoubleProperty(final String name, final double defaultValue) { 134 final String prop = getStringProperty(name); 135 if (prop != null) { 136 try { 137 return Double.parseDouble(prop); 138 } catch (final Exception ignored) { 139 return defaultValue; 140 } 141 } 142 return defaultValue; 143 } 144 145 /** 146 * Gets the named property as an integer. 147 * 148 * @param name the name of the property to look up 149 * @param defaultValue the default value to use if the property is undefined 150 * @return the parsed integer value of the property or {@code defaultValue} if it was undefined or could not be 151 * parsed. 152 */ 153 public int getIntegerProperty(final String name, final int defaultValue) { 154 final String prop = getStringProperty(name); 155 if (prop != null) { 156 try { 157 return Integer.parseInt(prop); 158 } catch (final Exception ignored) { 159 return defaultValue; 160 } 161 } 162 return defaultValue; 163 } 164 165 /** 166 * Gets the named property as a long. 167 * 168 * @param name the name of the property to look up 169 * @param defaultValue the default value to use if the property is undefined 170 * @return the parsed long value of the property or {@code defaultValue} if it was undefined or could not be parsed. 171 */ 172 public long getLongProperty(final String name, final long defaultValue) { 173 final String prop = getStringProperty(name); 174 if (prop != null) { 175 try { 176 return Long.parseLong(prop); 177 } catch (final Exception ignored) { 178 return defaultValue; 179 } 180 } 181 return defaultValue; 182 } 183 184 /** 185 * Gets the named property as a String. 186 * 187 * @param name the name of the property to look up 188 * @return the String value of the property or {@code null} if undefined. 189 */ 190 public String getStringProperty(final String name) { 191 String prop = null; 192 try { 193 prop = System.getProperty(name); 194 } catch (final SecurityException ignored) { 195 // Ignore 196 } 197 return prop == null ? props.getProperty(name) : prop; 198 } 199 200 /** 201 * Gets the named property as a String. 202 * 203 * @param name the name of the property to look up 204 * @param defaultValue the default value to use if the property is undefined 205 * @return the String value of the property or {@code defaultValue} if undefined. 206 */ 207 public String getStringProperty(final String name, final String defaultValue) { 208 final String prop = getStringProperty(name); 209 return (prop == null) ? defaultValue : prop; 210 } 211 212 /** 213 * Return the system properties or an empty Properties object if an error occurs. 214 * 215 * @return The system properties. 216 */ 217 public static Properties getSystemProperties() { 218 try { 219 return new Properties(System.getProperties()); 220 } catch (final SecurityException ex) { 221 LowLevelLogUtil.logException("Unable to access system properties.", ex); 222 // Sandboxed - can't read System Properties 223 return new Properties(); 224 } 225 } 226 227 /** 228 * Extracts properties that start with or are equals to the specific prefix and returns them in a new Properties 229 * object with the prefix removed. 230 * 231 * @param properties The Properties to evaluate. 232 * @param prefix The prefix to extract. 233 * @return The subset of properties. 234 */ 235 public static Properties extractSubset(final Properties properties, final String prefix) { 236 final Properties subset = new Properties(); 237 238 if (prefix == null || prefix.length() == 0) { 239 return subset; 240 } 241 242 final String prefixToMatch = prefix.charAt(prefix.length() - 1) != '.' ? prefix + '.' : prefix; 243 244 final List<String> keys = new ArrayList<>(); 245 246 for (final String key : properties.stringPropertyNames()) { 247 if (key.startsWith(prefixToMatch)) { 248 subset.setProperty(key.substring(prefixToMatch.length()), properties.getProperty(key)); 249 keys.add(key); 250 } 251 } 252 for (final String key : keys) { 253 properties.remove(key); 254 } 255 256 return subset; 257 } 258 259 /** 260 * Partitions a properties map based on common key prefixes up to the first period. 261 * 262 * @param properties properties to partition 263 * @return the partitioned properties where each key is the common prefix (minus the period) and the values are 264 * new property maps without the prefix and period in the key 265 * @since 2.6 266 */ 267 public static Map<String, Properties> partitionOnCommonPrefixes(final Properties properties) { 268 final Map<String, Properties> parts = new ConcurrentHashMap<>(); 269 for (final String key : properties.stringPropertyNames()) { 270 final String prefix = key.substring(0, key.indexOf('.')); 271 if (!parts.containsKey(prefix)) { 272 parts.put(prefix, new Properties()); 273 } 274 parts.get(prefix).setProperty(key.substring(key.indexOf('.') + 1), properties.getProperty(key)); 275 } 276 return parts; 277 } 278 279 /** 280 * Returns true if system properties tell us we are running on Windows. 281 * @return true if system properties tell us we are running on Windows. 282 */ 283 public boolean isOsWindows() { 284 return getStringProperty("os.name").startsWith("Windows"); 285 } 286 287}