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.util.ArrayList;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.Properties;
26  import java.util.concurrent.ConcurrentHashMap;
27  
28  /**
29   * <em>Consider this class private.</em>
30   * <p>
31   * Helps access properties. This utility provides a method to override system properties by specifying properties in a
32   * properties file.
33   * </p>
34   */
35  public final class PropertiesUtil {
36  
37      private static final PropertiesUtil LOG4J_PROPERTIES = new PropertiesUtil("log4j2.component.properties");
38  
39      private final Properties props;
40  
41      /**
42       * Constructs a PropertiesUtil using a given Properties object as its source of defined properties.
43       *
44       * @param props the Properties to use by default
45       */
46      public PropertiesUtil(final Properties props) {
47          this.props = props;
48      }
49  
50      /**
51       * Constructs a PropertiesUtil for a given properties file name on the classpath. The properties specified in this
52       * file are used by default. If a property is not defined in this file, then the equivalent system property is used.
53       *
54       * @param propertiesFileName the location of properties file to load
55       */
56      public PropertiesUtil(final String propertiesFileName) {
57          final Properties properties = new Properties();
58          for (final URL url : LoaderUtil.findResources(propertiesFileName)) {
59              try (final InputStream in = url.openStream()) {
60                  properties.load(in);
61              } catch (final IOException ioe) {
62                  LowLevelLogUtil.logException("Unable to read " + url.toString(), ioe);
63              }
64          }
65          this.props = properties;
66      }
67  
68      /**
69       * Loads and closes the given property input stream. If an error occurs, log to the status logger.
70       *
71       * @param in a property input stream.
72       * @param source a source object describing the source, like a resource string or a URL.
73       * @return a new Properties object
74       */
75      static Properties loadClose(final InputStream in, final Object source) {
76          final Properties props = new Properties();
77          if (null != in) {
78              try {
79                  props.load(in);
80              } catch (final IOException e) {
81                  LowLevelLogUtil.logException("Unable to read " + source, e);
82              } finally {
83                  try {
84                      in.close();
85                  } catch (final IOException e) {
86                      LowLevelLogUtil.logException("Unable to close " + source, e);
87                  }
88              }
89          }
90          return props;
91      }
92  
93      /**
94       * Returns the PropertiesUtil used by Log4j.
95       *
96       * @return the main Log4j PropertiesUtil instance.
97       */
98      public static PropertiesUtil getProperties() {
99          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 }