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.core.config;
18  
19  import org.apache.logging.log4j.Logger;
20  import org.apache.logging.log4j.core.config.plugins.PluginManager;
21  import org.apache.logging.log4j.core.config.plugins.PluginType;
22  import org.apache.logging.log4j.core.helpers.FileUtils;
23  import org.apache.logging.log4j.core.helpers.Loader;
24  import org.apache.logging.log4j.status.StatusLogger;
25  import org.xml.sax.InputSource;
26  
27  import java.io.File;
28  import java.io.FileInputStream;
29  import java.io.FileNotFoundException;
30  import java.io.IOException;
31  import java.io.InputStream;
32  import java.net.MalformedURLException;
33  import java.net.URI;
34  import java.net.URISyntaxException;
35  import java.net.URL;
36  import java.util.ArrayList;
37  import java.util.List;
38  import java.util.Map;
39  import java.util.Set;
40  import java.util.TreeSet;
41  
42  /**
43   * ConfigurationFactory allows the configuration implementation to be dynamically chosen in 1
44   * of 3 ways:
45   * 1. A system property named "log4j.configurationFactory" can be set with the name of the
46   * ConfigurationFactory to be used.
47   * 2. setConfigurationFactory can be called with the instance of the ConfigurationFactory to
48   * be used. This must be called before any other calls to Log4j.
49   * 3. A ConfigurationFactory implementation can be added to the classpath and configured as a
50   * plugin. The Order annotation should be used to configure the factory to be the first one
51   * inspected. See XMLConfigurationFactory for an example.
52   *
53   * If the ConfigurationFactory that was added returns null on a call to getConfiguration the
54   * any other ConfigurationFactories found as plugins will be called in their respective order.
55   * DefaultConfiguration is always called last if no configuration has been returned.
56   */
57  public abstract class ConfigurationFactory {
58      /**
59       * Allow the ConfigurationFactory class to be specified as a system property.
60       */
61      public static final String CONFIGURATION_FACTORY_PROPERTY = "log4j.configurationFactory";
62  
63      /**
64       * Allow the location of the configuration file to be specified as a system property.
65       */
66      public static final String CONFIGURATION_FILE_PROPERTY = "log4j.configurationFile";
67  
68      /**
69       * Allow subclasses access to the status logger without creating another instance.
70       */
71      protected static final Logger LOGGER = StatusLogger.getLogger();
72  
73      /**
74       * File name prefix for test configurations.
75       */
76      protected static final String TEST_PREFIX = "log4j2-test";
77  
78      /**
79       * File name prefix for standard configurations.
80       */
81      protected static final String DEFAULT_PREFIX = "log4j2";
82  
83      private static List<ConfigurationFactory> factories = new ArrayList<ConfigurationFactory>();
84  
85      private static ConfigurationFactory configFactory = new Factory();
86  
87      /**
88       * The configuration File.
89       */
90      protected File configFile = null;
91  
92      /**
93       * Return the ConfigurationFactory.
94       * @return the ConfigurationFactory.
95       */
96      public static ConfigurationFactory getInstance() {
97          String factoryClass = System.getProperty(CONFIGURATION_FACTORY_PROPERTY);
98          if (factoryClass != null) {
99              addFactory(factoryClass);
100         }
101         PluginManager manager = new PluginManager("ConfigurationFactory");
102         manager.collectPlugins();
103         Map<String, PluginType> plugins = manager.getPlugins();
104         Set<WeightedFactory> ordered = new TreeSet<WeightedFactory>();
105         for (PluginType type : plugins.values()) {
106             try {
107                 Class<ConfigurationFactory> clazz = type.getPluginClass();
108                 Order o = clazz.getAnnotation(Order.class);
109                 Integer weight = o.value();
110                 if (o != null) {
111                     ordered.add(new WeightedFactory(weight, clazz));
112                 }
113             } catch (Exception ex) {
114               LOGGER.warn("Unable to add class " + type.getPluginClass());
115             }
116         }
117         for (WeightedFactory wf : ordered) {
118             addFactory(wf.factoryClass);
119         }
120         return configFactory;
121     }
122 
123     private static void addFactory(String factoryClass) {
124         try {
125             Class clazz = Class.forName(factoryClass);
126             addFactory(clazz);
127         } catch (ClassNotFoundException ex) {
128             LOGGER.error("Unable to load class " + factoryClass, ex);
129         } catch (Exception ex) {
130             LOGGER.error("Unable to load class " + factoryClass, ex);
131         }
132     }
133 
134     private static void addFactory(Class factoryClass) {
135         try {
136             factories.add((ConfigurationFactory) factoryClass.newInstance());
137         } catch (Exception ex) {
138             LOGGER.error("Unable to create instance of " + factoryClass.getName(), ex);
139         }
140     }
141 
142     /**
143      * Set the configuration factory.
144      * @param factory the ConfigurationFactory.
145      */
146     public static void setConfigurationFactory(ConfigurationFactory factory) {
147         configFactory = factory;
148     }
149 
150     /**
151      * Reset the ConfigurationFactory to the default.
152      */
153     public static void resetConfigurationFactory() {
154         configFactory = new Factory();
155     }
156 
157     /**
158      * Remove the ConfigurationFactory.
159      * @param factory The factory to remove.
160      */
161     public static void removeConfigurationFactory(ConfigurationFactory factory) {
162         factories.remove(factory);
163     }
164 
165     protected abstract String[] getSupportedTypes();
166 
167     protected boolean isActive() {
168         return true;
169     }
170 
171     public abstract Configuration getConfiguration(InputSource source);
172 
173     /**
174      * Return the Configuration.
175      * @param name The configuration name.
176      * @param configLocation The configuration location.
177      * @return The Configuration.
178      */
179     public Configuration getConfiguration(String name, URI configLocation) {
180         if (!isActive()) {
181             return null;
182         }
183         if (configLocation != null) {
184             InputSource source = getInputFromURI(configLocation);
185             if (source != null) {
186                 return getConfiguration(source);
187             }
188         }
189         return null;
190     }
191 
192     /**
193      * Load the configuration from a URI.
194      * @param configLocation A URI representing the location of the configuration.
195      * @return The InputSource for the configuration.
196      */
197     protected InputSource getInputFromURI(URI configLocation) {
198         configFile = FileUtils.fileFromURI(configLocation);
199         if (configFile != null && configFile.exists() && configFile.canRead()) {
200             try {
201                 InputSource source = new InputSource(new FileInputStream(configFile));
202                 source.setSystemId(configLocation.getPath());
203                 return source;
204             } catch (FileNotFoundException ex) {
205                 LOGGER.error("Cannot locate file " + configLocation.getPath(), ex);
206             }
207         }
208         try {
209             InputSource source = new InputSource(configLocation.toURL().openStream());
210             source.setSystemId(configLocation.getPath());
211             return source;
212         } catch (MalformedURLException ex) {
213             LOGGER.error("Invalid URL " + configLocation.toString(), ex);
214         } catch (IOException ex) {
215             LOGGER.error("Unable to access " + configLocation.toString(), ex);
216         }
217         return null;
218     }
219 
220     /**
221      * Load the configuration from the location represented by the String.
222      * @param config The configuration location.
223      * @param loader The default ClassLoader to use.
224      * @return The InputSource to use to read the configuration.
225      */
226     protected InputSource getInputFromString(String config, ClassLoader loader) {
227         InputSource source;
228         try {
229             URL url = new URL(config);
230             source = new InputSource(url.openStream());
231             if (FileUtils.isFile(url)) {
232                 configFile = FileUtils.fileFromURI(url.toURI());
233             }
234             source.setSystemId(config);
235             return source;
236         } catch (Exception ex) {
237             source = getInputFromResource(config, loader);
238             if (source == null) {
239                 try {
240                     File file = new File(config);
241                     FileInputStream is = new FileInputStream(file);
242                     configFile = file;
243                     source = new InputSource(is);
244                     source.setSystemId(config);
245                 } catch (FileNotFoundException fnfe) {
246                     // Ignore the exception
247                 }
248             }
249         }
250         return source;
251     }
252 
253     /**
254      * Retrieve the configuration via the ClassLoader.
255      * @param resource The resource to load.
256      * @param loader The default ClassLoader to use.
257      * @return The InputSource for the configuration.
258      */
259     protected InputSource getInputFromResource(String resource, ClassLoader loader) {
260         URL url = Loader.getResource(resource, loader);
261         if (url == null) {
262             return null;
263         }
264         InputStream is = null;
265         try {
266             is = url.openStream();
267         } catch (IOException ioe) {
268             return null;
269         }
270         if (is == null) {
271             return null;
272         }
273         InputSource source = new InputSource(is);
274         if (FileUtils.isFile(url)) {
275             try {
276                 configFile = FileUtils.fileFromURI(url.toURI());
277             } catch (URISyntaxException ex) {
278                 // Just ignore the exception.
279             }
280         }
281         source.setSystemId(resource);
282         return source;
283     }
284 
285     /**
286      * Factory that chooses a ConfigurationFactory based on weighting.
287      */
288     private static class WeightedFactory implements Comparable<WeightedFactory> {
289         private int weight;
290         private Class<ConfigurationFactory> factoryClass;
291 
292         /**
293          * Constructor.
294          * @param weight The weight.
295          * @param clazz The class.
296          */
297         public WeightedFactory(int weight, Class<ConfigurationFactory> clazz) {
298             this.weight = weight;
299             this.factoryClass = clazz;
300         }
301 
302         public int compareTo(WeightedFactory wf) {
303             int w = wf.weight;
304             if (weight == w) {
305                 return 0;
306             } else if (weight > w) {
307                 return -1;
308             } else {
309                 return 1;
310             }
311         }
312     }
313 
314     /**
315      * Default Factory.
316      */
317     private static class Factory extends ConfigurationFactory {
318 
319         /**
320          * Default Factory Constructor.
321          * @param name The configuration name.
322          * @param configLocation The configuration location.
323          * @return The Configuration.
324          */
325         public Configuration getConfiguration(String name, URI configLocation) {
326 
327             if (configLocation == null) {
328                 String config = System.getProperty(CONFIGURATION_FILE_PROPERTY);
329                 if (config != null) {
330                     ClassLoader loader = this.getClass().getClassLoader();
331                     InputSource source = getInputFromString(config, loader);
332                     if (source != null) {
333                         for (ConfigurationFactory factory : factories) {
334                             String[] types = factory.getSupportedTypes();
335                             if (types != null) {
336                                 for (String type : types) {
337                                     if (type.equals("*") || config.endsWith(type)) {
338                                         Configuration c = factory.getConfiguration(source);
339                                         if (c != null) {
340                                             return c;
341                                         }
342                                     }
343                                 }
344                             }
345                         }
346                     }
347                 }
348             } else {
349                 for (ConfigurationFactory factory : factories) {
350                     Configuration config = factory.getConfiguration(name, configLocation);
351                     if (config != null) {
352                         return config;
353                     }
354                 }
355             }
356 
357             Configuration config = getConfiguration(true, name);
358             if (config == null) {
359                 config = getConfiguration(true, null);
360                 if (config == null) {
361                     config = getConfiguration(false, name);
362                     if (config == null) {
363                         config = getConfiguration(false, null);
364                     }
365                 }
366             }
367             return config != null ? config : new DefaultConfiguration();
368         }
369 
370         private Configuration getConfiguration(boolean isTest, String name) {
371             boolean named = (name != null && name.length() > 0);
372             ClassLoader loader = this.getClass().getClassLoader();
373             for (ConfigurationFactory factory : factories) {
374                 String configName;
375                 String prefix = isTest ? TEST_PREFIX : DEFAULT_PREFIX;
376                 String [] types = factory.getSupportedTypes();
377                 if (types == null) {
378                     continue;
379                 }
380 
381                 for (String suffix : types) {
382                     if (suffix.equals("*")) {
383                         continue;
384                     }
385                     configName = named ? prefix + name + suffix : prefix + suffix;
386 
387                     InputSource source = getInputFromResource(configName, loader);
388                     if (source != null) {
389                         return factory.getConfiguration(source);
390                     }
391                 }
392             }
393             return null;
394         }
395 
396         public String[] getSupportedTypes() {
397             return null;
398         }
399 
400         public Configuration getConfiguration(InputSource source) {
401             return null;
402         }
403     }
404 }