View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.util;
18  
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.net.URL;
22  import java.nio.charset.Charset;
23  import java.util.ArrayList;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.Properties;
27  import java.util.concurrent.ConcurrentHashMap;
28  
29  /**
30   * <em>Consider this class private.</em>
31   * <p>
32   * Helps access properties. This utility provides a method to override system properties by specifying properties in a
33   * properties file.
34   * </p>
35   */
36  public final class PropertiesUtil {
37  
38      private static final PropertiesUtil LOG4J_PROPERTIES = new PropertiesUtil("log4j2.component.properties");
39  
40      private final Properties props;
41  
42      /**
43       * Constructs a PropertiesUtil using a given Properties object as its source of defined properties.
44       *
45       * @param props the Properties to use by default
46       */
47      public PropertiesUtil(final Properties props) {
48          this.props = props;
49      }
50  
51      /**
52       * Constructs a PropertiesUtil for a given properties file name on the classpath. The properties specified in this
53       * file are used by default. If a property is not defined in this file, then the equivalent system property is used.
54       *
55       * @param propertiesFileName the location of properties file to load
56       */
57      public PropertiesUtil(final String propertiesFileName) {
58          final Properties properties = new Properties();
59          for (final URL url : LoaderUtil.findResources(propertiesFileName)) {
60              try (final InputStream in = url.openStream()) {
61                  properties.load(in);
62              } catch (final IOException ioe) {
63                  LowLevelLogUtil.logException("Unable to read " + url.toString(), ioe);
64              }
65          }
66          this.props = properties;
67      }
68  
69      /**
70       * Loads and closes the given property input stream. If an error occurs, log to the status logger.
71       *
72       * @param in a property input stream.
73       * @param source a source object describing the source, like a resource string or a URL.
74       * @return a new Properties object
75       */
76      static Properties loadClose(final InputStream in, final Object source) {
77          final Properties props = new Properties();
78          if (null != in) {
79              try {
80                  props.load(in);
81              } catch (final IOException e) {
82                  LowLevelLogUtil.logException("Unable to read " + source, e);
83              } finally {
84                  try {
85                      in.close();
86                  } catch (final IOException e) {
87                      LowLevelLogUtil.logException("Unable to close " + source, e);
88                  }
89              }
90          }
91          return props;
92      }
93  
94      /**
95       * Returns the PropertiesUtil used by Log4j.
96       *
97       * @return the main Log4j PropertiesUtil instance.
98       */
99      public static PropertiesUtil getProperties() {
100         return LOG4J_PROPERTIES;
101     }
102 
103     /**
104      * Gets the named property as a boolean value. If the property matches the string {@code "true"} (case-insensitive),
105      * then it is returned as the boolean value {@code true}. Any other non-{@code null} text in the property is
106      * considered {@code false}.
107      *
108      * @param name the name of the property to look up
109      * @return the boolean value of the property or {@code false} if undefined.
110      */
111     public boolean getBooleanProperty(final String name) {
112         return getBooleanProperty(name, false);
113     }
114 
115     /**
116      * Gets the named property as a boolean value.
117      *
118      * @param name the name of the property to look up
119      * @param defaultValue the default value to use if the property is undefined
120      * @return the boolean value of the property or {@code defaultValue} if undefined.
121      */
122     public boolean getBooleanProperty(final String name, final boolean defaultValue) {
123         final String prop = getStringProperty(name);
124         return (prop == null) ? defaultValue : "true".equalsIgnoreCase(prop);
125     }
126 
127     /**
128      * Gets the named property as a Charset value.
129      *
130      * @param name the name of the property to look up
131      * @return the Charset value of the property or {@link Charset#defaultCharset()} if undefined.
132      */
133     public Charset getCharsetProperty(final String name) {
134         return getCharsetProperty(name, Charset.defaultCharset());
135     }
136 
137     /**
138      * Gets the named property as a Charset value.
139      *
140      * @param name the name of the property to look up
141      * @param defaultValue the default value to use if the property is undefined
142      * @return the Charset value of the property or {@code defaultValue} if undefined.
143      */
144     public Charset getCharsetProperty(final String name, final Charset defaultValue) {
145         final String prop = getStringProperty(name);
146         return prop == null ? defaultValue : Charset.forName(prop);
147     }
148 
149     /**
150      * Gets the named property as a double.
151      *
152      * @param name the name of the property to look up
153      * @param defaultValue the default value to use if the property is undefined
154      * @return the parsed double value of the property or {@code defaultValue} if it was undefined or could not be parsed.
155      */
156     public double getDoubleProperty(final String name, final double defaultValue) {
157         final String prop = getStringProperty(name);
158         if (prop != null) {
159             try {
160                 return Double.parseDouble(prop);
161             } catch (final Exception ignored) {
162                 return defaultValue;
163             }
164         }
165         return defaultValue;
166     }
167 
168     /**
169      * Gets the named property as an integer.
170      *
171      * @param name the name of the property to look up
172      * @param defaultValue the default value to use if the property is undefined
173      * @return the parsed integer value of the property or {@code defaultValue} if it was undefined or could not be
174      *         parsed.
175      */
176     public int getIntegerProperty(final String name, final int defaultValue) {
177         final String prop = getStringProperty(name);
178         if (prop != null) {
179             try {
180                 return Integer.parseInt(prop);
181             } catch (final Exception ignored) {
182                 return defaultValue;
183             }
184         }
185         return defaultValue;
186     }
187 
188     /**
189      * Gets the named property as a long.
190      *
191      * @param name the name of the property to look up
192      * @param defaultValue the default value to use if the property is undefined
193      * @return the parsed long value of the property or {@code defaultValue} if it was undefined or could not be parsed.
194      */
195     public long getLongProperty(final String name, final long defaultValue) {
196         final String prop = getStringProperty(name);
197         if (prop != null) {
198             try {
199                 return Long.parseLong(prop);
200             } catch (final Exception ignored) {
201                 return defaultValue;
202             }
203         }
204         return defaultValue;
205     }
206 
207     /**
208      * Gets the named property as a String.
209      *
210      * @param name the name of the property to look up
211      * @return the String value of the property or {@code null} if undefined.
212      */
213     public String getStringProperty(final String name) {
214         String prop = null;
215         try {
216             prop = System.getProperty(name);
217         } catch (final SecurityException ignored) {
218             // Ignore
219         }
220         return prop == null ? props.getProperty(name) : prop;
221     }
222 
223     /**
224      * Gets the named property as a String.
225      *
226      * @param name the name of the property to look up
227      * @param defaultValue the default value to use if the property is undefined
228      * @return the String value of the property or {@code defaultValue} if undefined.
229      */
230     public String getStringProperty(final String name, final String defaultValue) {
231         final String prop = getStringProperty(name);
232         return (prop == null) ? defaultValue : prop;
233     }
234 
235     /**
236      * Return the system properties or an empty Properties object if an error occurs.
237      *
238      * @return The system properties.
239      */
240     public static Properties getSystemProperties() {
241         try {
242             return new Properties(System.getProperties());
243         } catch (final SecurityException ex) {
244             LowLevelLogUtil.logException("Unable to access system properties.", ex);
245             // Sandboxed - can't read System Properties
246             return new Properties();
247         }
248     }
249 
250     /**
251      * Extracts properties that start with or are equals to the specific prefix and returns them in a new Properties
252      * object with the prefix removed.
253      *
254      * @param properties The Properties to evaluate.
255      * @param prefix The prefix to extract.
256      * @return The subset of properties.
257      */
258     public static Properties extractSubset(final Properties properties, final String prefix) {
259         final Properties subset = new Properties();
260 
261         if (prefix == null || prefix.length() == 0) {
262             return subset;
263         }
264 
265         final String prefixToMatch = prefix.charAt(prefix.length() - 1) != '.' ? prefix + '.' : prefix;
266 
267         final List<String> keys = new ArrayList<>();
268 
269         for (final String key : properties.stringPropertyNames()) {
270             if (key.startsWith(prefixToMatch)) {
271                 subset.setProperty(key.substring(prefixToMatch.length()), properties.getProperty(key));
272                 keys.add(key);
273             }
274         }
275         for (final String key : keys) {
276             properties.remove(key);
277         }
278 
279         return subset;
280     }
281 
282     /**
283      * Partitions a properties map based on common key prefixes up to the first period.
284      *
285      * @param properties properties to partition
286      * @return the partitioned properties where each key is the common prefix (minus the period) and the values are
287      * new property maps without the prefix and period in the key
288      * @since 2.6
289      */
290     public static Map<String, Properties> partitionOnCommonPrefixes(final Properties properties) {
291         final Map<String, Properties> parts = new ConcurrentHashMap<>();
292         for (final String key : properties.stringPropertyNames()) {
293             final String prefix = key.substring(0, key.indexOf('.'));
294             if (!parts.containsKey(prefix)) {
295                 parts.put(prefix, new Properties());
296             }
297             parts.get(prefix).setProperty(key.substring(key.indexOf('.') + 1), properties.getProperty(key));
298         }
299         return parts;
300     }
301 
302     /**
303      * Returns true if system properties tell us we are running on Windows.
304      * @return true if system properties tell us we are running on Windows.
305      */
306     public boolean isOsWindows() {
307         return getStringProperty("os.name").startsWith("Windows");
308     }
309 
310 }