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 java.io.File;
26  import java.io.FileInputStream;
27  import java.io.FileNotFoundException;
28  import java.io.IOException;
29  import java.io.InputStream;
30  import java.net.MalformedURLException;
31  import java.net.URI;
32  import java.net.URISyntaxException;
33  import java.net.URL;
34  import java.util.ArrayList;
35  import java.util.List;
36  import java.util.Map;
37  import java.util.Set;
38  import java.util.TreeSet;
39  
40  /**
41   * ConfigurationFactory allows the configuration implementation to be
42   * dynamically chosen in 1 of 3 ways:
43   * <ol>
44   * <li>A system property named "log4j.configurationFactory" can be set with the
45   * name of the ConfigurationFactory to be used.</li>
46   * <li>
47   * {@linkplain #setConfigurationFactory(ConfigurationFactory)} can be called
48   * with the instance of the ConfigurationFactory to be used. This must be called
49   * before any other calls to Log4j.</li>
50   * <li>
51   * A ConfigurationFactory implementation can be added to the classpath and
52   * configured as a plugin. The Order annotation should be used to configure the
53   * factory to be the first one inspected. See
54   * {@linkplain XMLConfigurationFactory} for an example.</li>
55   * </ol>
56   * 
57   * If the ConfigurationFactory that was added returns null on a call to
58   * getConfiguration the any other ConfigurationFactories found as plugins will
59   * be called in their respective order. DefaultConfiguration is always called
60   * last if no configuration has been returned.
61   */
62  public abstract class ConfigurationFactory {
63      /**
64       * Allow the ConfigurationFactory class to be specified as a system property.
65       */
66      public static final String CONFIGURATION_FACTORY_PROPERTY = "log4j.configurationFactory";
67  
68      /**
69       * Allow the location of the configuration file to be specified as a system property.
70       */
71      public static final String CONFIGURATION_FILE_PROPERTY = "log4j.configurationFile";
72  
73      /**
74       * Allow subclasses access to the status logger without creating another instance.
75       */
76      protected static final Logger LOGGER = StatusLogger.getLogger();
77  
78      /**
79       * File name prefix for test configurations.
80       */
81      protected static final String TEST_PREFIX = "log4j2-test";
82  
83      /**
84       * File name prefix for standard configurations.
85       */
86      protected static final String DEFAULT_PREFIX = "log4j2";
87  
88      private static List<ConfigurationFactory> factories = new ArrayList<ConfigurationFactory>();
89  
90      private static ConfigurationFactory configFactory = new Factory();
91  
92      /**
93       * Returns 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(ConfigurationSource source);
172 
173     /**
174      * Returns 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             ConfigurationSource 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 ConfigurationSource for the configuration.
196      */
197     protected ConfigurationSource getInputFromURI(URI configLocation) {
198         File configFile = FileUtils.fileFromURI(configLocation);
199         if (configFile != null && configFile.exists() && configFile.canRead()) {
200             try {
201                 return new ConfigurationSource(new FileInputStream(configFile), configFile);
202             } catch (FileNotFoundException ex) {
203                 LOGGER.error("Cannot locate file " + configLocation.getPath(), ex);
204             }
205         }
206         String scheme = configLocation.getScheme();
207         if (scheme == null || scheme.equals("classloader")) {
208             ClassLoader loader = this.getClass().getClassLoader();
209             ConfigurationSource source = getInputFromResource(configLocation.getPath(), loader);
210             if (source != null) {
211                 return source;
212             }
213         }
214         try {
215             return new ConfigurationSource(configLocation.toURL().openStream(), configLocation.getPath());
216         } catch (MalformedURLException ex) {
217             LOGGER.error("Invalid URL " + configLocation.toString(), ex);
218         } catch (IOException ex) {
219             LOGGER.error("Unable to access " + configLocation.toString(), ex);
220         } catch (Exception ex) {
221             LOGGER.error("Unable to access " + configLocation.toString(), ex);
222         }
223         return null;
224     }
225 
226     /**
227      * Load the configuration from the location represented by the String.
228      * @param config The configuration location.
229      * @param loader The default ClassLoader to use.
230      * @return The InputSource to use to read the configuration.
231      */
232     protected ConfigurationSource getInputFromString(String config, ClassLoader loader) {
233         try {
234             URL url = new URL(config);
235             return new ConfigurationSource(url.openStream(), FileUtils.fileFromURI(url.toURI()));
236         } catch (Exception ex) {
237             ConfigurationSource source = getInputFromResource(config, loader);
238             if (source == null) {
239                 try {
240                     File file = new File(config);
241                     return new ConfigurationSource(new FileInputStream(file), file);
242                 } catch (FileNotFoundException fnfe) {
243                     // Ignore the exception
244                 }
245             }
246             return source;
247         }
248     }
249 
250     /**
251      * Retrieve the configuration via the ClassLoader.
252      * @param resource The resource to load.
253      * @param loader The default ClassLoader to use.
254      * @return The ConfigurationSource for the configuration.
255      */
256     protected ConfigurationSource getInputFromResource(String resource, ClassLoader loader) {
257         URL url = Loader.getResource(resource, loader);
258         if (url == null) {
259             return null;
260         }
261         InputStream is = null;
262         try {
263             is = url.openStream();
264         } catch (IOException ioe) {
265             return null;
266         }
267         if (is == null) {
268             return null;
269         }
270 
271         if (FileUtils.isFile(url)) {
272             try {
273                 return new ConfigurationSource(is, FileUtils.fileFromURI((url.toURI())));
274             } catch (URISyntaxException ex) {
275                 // Just ignore the exception.
276             }
277         }
278         return new ConfigurationSource(is, resource);
279     }
280 
281     /**
282      * Factory that chooses a ConfigurationFactory based on weighting.
283      */
284     private static class WeightedFactory implements Comparable<WeightedFactory> {
285         private int weight;
286         private Class<ConfigurationFactory> factoryClass;
287 
288         /**
289          * Constructor.
290          * @param weight The weight.
291          * @param clazz The class.
292          */
293         public WeightedFactory(int weight, Class<ConfigurationFactory> clazz) {
294             this.weight = weight;
295             this.factoryClass = clazz;
296         }
297 
298         public int compareTo(WeightedFactory wf) {
299             int w = wf.weight;
300             if (weight == w) {
301                 return 0;
302             } else if (weight > w) {
303                 return -1;
304             } else {
305                 return 1;
306             }
307         }
308     }
309 
310     /**
311      * Default Factory.
312      */
313     private static class Factory extends ConfigurationFactory {
314 
315         /**
316          * Default Factory Constructor.
317          * @param name The configuration name.
318          * @param configLocation The configuration location.
319          * @return The Configuration.
320          */
321         @Override
322         public Configuration getConfiguration(String name, URI configLocation) {
323 
324             if (configLocation == null) {
325                 String config = System.getProperty(CONFIGURATION_FILE_PROPERTY);
326                 if (config != null) {
327                     ClassLoader loader = this.getClass().getClassLoader();
328                     ConfigurationSource source = getInputFromString(config, loader);
329                     if (source != null) {
330                         for (ConfigurationFactory factory : factories) {
331                             String[] types = factory.getSupportedTypes();
332                             if (types != null) {
333                                 for (String type : types) {
334                                     if (type.equals("*") || config.endsWith(type)) {
335                                         Configuration c = factory.getConfiguration(source);
336                                         if (c != null) {
337                                             return c;
338                                         }
339                                     }
340                                 }
341                             }
342                         }
343                     }
344                 }
345             } else {
346                 for (ConfigurationFactory factory : factories) {
347                     String[] types = factory.getSupportedTypes();
348                     if (types != null) {
349                         for (String type : types) {
350                             if (type.equals("*") || configLocation.getPath().endsWith(type)) {
351                                 Configuration config = factory.getConfiguration(name, configLocation);
352                                 if (config != null) {
353                                     return config;
354                                 }
355                             }
356                         }
357                     }
358                 }
359             }
360 
361             Configuration config = getConfiguration(true, name);
362             if (config == null) {
363                 config = getConfiguration(true, null);
364                 if (config == null) {
365                     config = getConfiguration(false, name);
366                     if (config == null) {
367                         config = getConfiguration(false, null);
368                     }
369                 }
370             }
371             return config != null ? config : new DefaultConfiguration();
372         }
373 
374         private Configuration getConfiguration(boolean isTest, String name) {
375             boolean named = (name != null && name.length() > 0);
376             ClassLoader loader = this.getClass().getClassLoader();
377             for (ConfigurationFactory factory : factories) {
378                 String configName;
379                 String prefix = isTest ? TEST_PREFIX : DEFAULT_PREFIX;
380                 String [] types = factory.getSupportedTypes();
381                 if (types == null) {
382                     continue;
383                 }
384 
385                 for (String suffix : types) {
386                     if (suffix.equals("*")) {
387                         continue;
388                     }
389                     configName = named ? prefix + name + suffix : prefix + suffix;
390 
391                     ConfigurationSource source = getInputFromResource(configName, loader);
392                     if (source != null) {
393                         return factory.getConfiguration(source);
394                     }
395                 }
396             }
397             return null;
398         }
399 
400         @Override
401         public String[] getSupportedTypes() {
402             return null;
403         }
404 
405         @Override
406         public Configuration getConfiguration(ConfigurationSource source) {
407             if (source != null) {
408                 String config = source.getLocation();
409                 for (ConfigurationFactory factory : factories) {
410                     String[] types = factory.getSupportedTypes();
411                     if (types != null) {
412                         for (String type : types) {
413                             if (type.equals("*") || (config != null && config.endsWith(type))) {
414                                 Configuration c = factory.getConfiguration(source);
415                                 if (c != null) {
416                                     return c;
417                                 } else {
418                                     LOGGER.error("Cannot determine the ConfigurationFactory to use for {}", config);
419                                     return null;
420                                 }
421                             }
422                         }
423                     }
424                 }
425             }
426             LOGGER.error("Cannot process configuration, input source is null");
427             return null;
428         }
429     }
430 
431     public static class ConfigurationSource {
432 
433         private File file;
434 
435         private String location;
436 
437         private InputStream stream;
438 
439         public ConfigurationSource() {
440         }
441 
442         public ConfigurationSource(InputStream stream) {
443             this.stream = stream;
444             this .file = null;
445             this.location = null;
446         }
447 
448         public ConfigurationSource(InputStream stream, File file) {
449             this.stream = stream;
450             this.file = file;
451             this.location = file.getAbsolutePath();
452         }
453 
454         public ConfigurationSource(InputStream stream, String location) {
455             this.stream = stream;
456             this.location = location;
457             this.file = null;
458         }
459 
460         public File getFile() {
461             return file;
462         }
463 
464         public void setFile(File file) {
465             this.file = file;
466         }
467 
468         public String getLocation() {
469             return location;
470         }
471 
472         public void setLocation(String location) {
473             this.location = location;
474         }
475 
476         public InputStream getInputStream() {
477             return stream;
478         }
479 
480         public void setInputStream(InputStream stream) {
481             this.stream = stream;
482         }
483     }
484 }