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 */ 017 package org.apache.logging.log4j.core.config; 018 019 import org.apache.logging.log4j.Logger; 020 import org.apache.logging.log4j.core.config.plugins.PluginManager; 021 import org.apache.logging.log4j.core.config.plugins.PluginType; 022 import org.apache.logging.log4j.core.helpers.FileUtils; 023 import org.apache.logging.log4j.core.helpers.Loader; 024 import org.apache.logging.log4j.status.StatusLogger; 025 import java.io.File; 026 import java.io.FileInputStream; 027 import java.io.FileNotFoundException; 028 import java.io.IOException; 029 import java.io.InputStream; 030 import java.net.MalformedURLException; 031 import java.net.URI; 032 import java.net.URISyntaxException; 033 import java.net.URL; 034 import java.util.ArrayList; 035 import java.util.List; 036 import java.util.Map; 037 import java.util.Set; 038 import java.util.TreeSet; 039 040 /** 041 * ConfigurationFactory allows the configuration implementation to be 042 * dynamically chosen in 1 of 3 ways: 043 * <ol> 044 * <li>A system property named "log4j.configurationFactory" can be set with the 045 * name of the ConfigurationFactory to be used.</li> 046 * <li> 047 * {@linkplain #setConfigurationFactory(ConfigurationFactory)} can be called 048 * with the instance of the ConfigurationFactory to be used. This must be called 049 * before any other calls to Log4j.</li> 050 * <li> 051 * A ConfigurationFactory implementation can be added to the classpath and 052 * configured as a plugin. The Order annotation should be used to configure the 053 * factory to be the first one inspected. See 054 * {@linkplain XMLConfigurationFactory} for an example.</li> 055 * </ol> 056 * 057 * If the ConfigurationFactory that was added returns null on a call to 058 * getConfiguration the any other ConfigurationFactories found as plugins will 059 * be called in their respective order. DefaultConfiguration is always called 060 * last if no configuration has been returned. 061 */ 062 public abstract class ConfigurationFactory { 063 /** 064 * Allow the ConfigurationFactory class to be specified as a system property. 065 */ 066 public static final String CONFIGURATION_FACTORY_PROPERTY = "log4j.configurationFactory"; 067 068 /** 069 * Allow the location of the configuration file to be specified as a system property. 070 */ 071 public static final String CONFIGURATION_FILE_PROPERTY = "log4j.configurationFile"; 072 073 /** 074 * Allow subclasses access to the status logger without creating another instance. 075 */ 076 protected static final Logger LOGGER = StatusLogger.getLogger(); 077 078 /** 079 * File name prefix for test configurations. 080 */ 081 protected static final String TEST_PREFIX = "log4j2-test"; 082 083 /** 084 * File name prefix for standard configurations. 085 */ 086 protected static final String DEFAULT_PREFIX = "log4j2"; 087 088 private static List<ConfigurationFactory> factories = new ArrayList<ConfigurationFactory>(); 089 090 private static ConfigurationFactory configFactory = new Factory(); 091 092 /** 093 * Returns the ConfigurationFactory. 094 * @return the ConfigurationFactory. 095 */ 096 public static ConfigurationFactory getInstance() { 097 String factoryClass = System.getProperty(CONFIGURATION_FACTORY_PROPERTY); 098 if (factoryClass != null) { 099 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 }