Clover coverage report - brownies library - 1.0-beta-1
Coverage timestamp: 月 8 16 2004 17:14:42 GMT+09:00
file stats: LOC: 703   Methods: 18
NCLOC: 276   Classes: 2
30 day Evaluation Version distributed via the Maven Jar Repository. Clover is not free. You have 30 days to evaluate it. Please visit http://www.thecortex.net/clover to obtain a licensed version of Clover
 
 Source file Conditionals Statements Methods TOTAL
PropertyConfigurator.java 0% 0% 0% 0%
coverage
 1   
 /*
 2   
  * Joey and its relative products are published under the terms
 3   
  * of the Apache Software License.
 4   
  */
 5   
 /*
 6   
  * Created on 2003/12/27
 7   
  */
 8   
 package org.asyrinx.brownie.log.log4j;
 9   
 
 10   
 import java.io.FileInputStream;
 11   
 import java.io.IOException;
 12   
 import java.util.Enumeration;
 13   
 import java.util.Hashtable;
 14   
 import java.util.Properties;
 15   
 import java.util.StringTokenizer;
 16   
 
 17   
 import org.apache.log4j.Appender;
 18   
 import org.apache.log4j.Layout;
 19   
 import org.apache.log4j.Level;
 20   
 import org.apache.log4j.LogManager;
 21   
 import org.apache.log4j.Logger;
 22   
 import org.apache.log4j.PatternLayout;
 23   
 import org.apache.log4j.config.PropertySetter;
 24   
 import org.apache.log4j.helpers.FileWatchdog;
 25   
 import org.apache.log4j.helpers.LogLog;
 26   
 import org.apache.log4j.helpers.OptionConverter;
 27   
 import org.apache.log4j.or.RendererMap;
 28   
 import org.apache.log4j.spi.Configurator;
 29   
 import org.apache.log4j.spi.LoggerFactory;
 30   
 import org.apache.log4j.spi.LoggerRepository;
 31   
 import org.apache.log4j.spi.OptionHandler;
 32   
 import org.apache.log4j.spi.RendererSupport;
 33   
 
 34   
 /**
 35   
  * @author akima
 36   
  */
 37   
 public class PropertyConfigurator implements Configurator {
 38   
 
 39   
     /**
 40   
      * Used internally to keep track of configured appenders.
 41   
      */
 42   
     protected Hashtable registry = new Hashtable(11);
 43   
 
 44   
     protected LoggerFactory loggerFactory = new DefaultCategoryFactory();
 45   
 
 46   
     protected static final String CATEGORY_PREFIX = "log4j.category.";
 47   
 
 48   
     protected static final String LOGGER_PREFIX = "log4j.logger.";
 49   
 
 50   
     protected static final String FACTORY_PREFIX = "log4j.factory";
 51   
 
 52   
     protected static final String ADDITIVITY_PREFIX = "log4j.additivity.";
 53   
 
 54   
     protected static final String ROOT_CATEGORY_PREFIX = "log4j.rootCategory";
 55   
 
 56   
     protected static final String ROOT_LOGGER_PREFIX = "log4j.rootLogger";
 57   
 
 58   
     protected static final String APPENDER_PREFIX = "log4j.appender.";
 59   
 
 60   
     protected static final String RENDERER_PREFIX = "log4j.renderer.";
 61   
 
 62   
     protected static final String THRESHOLD_PREFIX = "log4j.threshold";
 63   
 
 64   
     /**
 65   
      * Key for specifying the {@link org.apache.log4j.spi.LoggerFactory
 66   
      * LoggerFactory}. Currently set to "<code>log4j.loggerFactory</code>".
 67   
      */
 68   
     public static final String LOGGER_FACTORY_KEY = "log4j.loggerFactory";
 69   
 
 70   
     static final private String INTERNAL_ROOT_NAME = "root";
 71   
 
 72   
     /**
 73   
      * Read configuration from a file. <b>The existing configuration is not
 74   
      * cleared nor reset. </b> If you require a different behavior, then call
 75   
      * {@link  LogManager#resetConfigurationresetConfiguration}method before
 76   
      * calling <code>doConfigure</code>.
 77   
      * 
 78   
      * <p>
 79   
      * The configuration file consists of statements in the format
 80   
      * <code>key=value</code>. The syntax of different configuration elements
 81   
      * are discussed below.
 82   
      * 
 83   
      * <h3>Repository-wide threshold</h3>
 84   
      * 
 85   
      * <p>
 86   
      * The repository-wide threshold filters logging requests by level
 87   
      * regardless of logger. The syntax is:
 88   
      * 
 89   
      * <pre>
 90   
      * 
 91   
      *  
 92   
      *   
 93   
      *        log4j.threshold=[level]
 94   
      *        
 95   
      *   
 96   
      *  
 97   
      * </pre>
 98   
      * 
 99   
      * <p>
 100   
      * The level value can consist of the string values OFF, FATAL, ERROR, WARN,
 101   
      * INFO, DEBUG, ALL or a <em>custom level</em> value. A custom level value
 102   
      * can be specified in the form level#classname. By default the
 103   
      * repository-wide threshold is set to the lowest possible value, namely the
 104   
      * level <code>ALL</code>.
 105   
      * </p>
 106   
      * 
 107   
      * 
 108   
      * <h3>Appender configuration</h3>
 109   
      * 
 110   
      * <p>
 111   
      * Appender configuration syntax is:
 112   
      * 
 113   
      * <pre>
 114   
      * 
 115   
      *  
 116   
      *   
 117   
      *        # For appender named &lt;i&gt;appenderName&lt;/i&gt;, set its class.
 118   
      *        # Note: The appender name can contain dots.
 119   
      *        log4j.appender.appenderName=fully.qualified.name.of.appender.class
 120   
      *        
 121   
      *        # Set appender specific options.
 122   
      *        log4j.appender.appenderName.option1=value1
 123   
      *        ...
 124   
      *        log4j.appender.appenderName.optionN=valueN
 125   
      *        
 126   
      *   
 127   
      *  
 128   
      * </pre>
 129   
      * 
 130   
      * For each named appender you can configure its {@link Layout}. The syntax
 131   
      * for configuring an appender's layout is:
 132   
      * 
 133   
      * <pre>
 134   
      * 
 135   
      *  
 136   
      *   
 137   
      *        log4j.appender.appenderName.layout=fully.qualified.name.of.layout.class
 138   
      *        log4j.appender.appenderName.layout.option1=value1
 139   
      *        ....
 140   
      *        log4j.appender.appenderName.layout.optionN=valueN
 141   
      *        
 142   
      *   
 143   
      *  
 144   
      * </pre>
 145   
      * 
 146   
      * <h3>Configuring loggers</h3>
 147   
      * 
 148   
      * <p>
 149   
      * The syntax for configuring the root logger is:
 150   
      * 
 151   
      * <pre>
 152   
      * 
 153   
      *  
 154   
      *   
 155   
      *        log4j.rootLogger=[level], appenderName, appenderName, ...
 156   
      *        
 157   
      *   
 158   
      *  
 159   
      * </pre>
 160   
      * 
 161   
      * <p>
 162   
      * This syntax means that an optional <em>level</em> can be supplied
 163   
      * followed by appender names separated by commas.
 164   
      * 
 165   
      * <p>
 166   
      * The level value can consist of the string values OFF, FATAL, ERROR, WARN,
 167   
      * INFO, DEBUG, ALL or a <em>custom level</em> value. A custom level value
 168   
      * can be specified in the form <code>level#classname</code>.
 169   
      * 
 170   
      * <p>
 171   
      * If a level value is specified, then the root level is set to the
 172   
      * corresponding level. If no level value is specified, then the root level
 173   
      * remains untouched.
 174   
      * 
 175   
      * <p>
 176   
      * The root logger can be assigned multiple appenders.
 177   
      * 
 178   
      * <p>
 179   
      * Each <i>appenderName </i> (separated by commas) will be added to the root
 180   
      * logger. The named appender is defined using the appender syntax defined
 181   
      * above.
 182   
      * 
 183   
      * <p>
 184   
      * For non-root categories the syntax is almost the same:
 185   
      * 
 186   
      * <pre>
 187   
      * 
 188   
      *  
 189   
      *   
 190   
      *        log4j.logger.logger_name=[level|INHERITED|NULL], appenderName, appenderName, ...
 191   
      *        
 192   
      *   
 193   
      *  
 194   
      * </pre>
 195   
      * 
 196   
      * <p>
 197   
      * The meaning of the optional level value is discussed above in relation to
 198   
      * the root logger. In addition however, the value INHERITED can be
 199   
      * specified meaning that the named logger should inherit its level from the
 200   
      * logger hierarchy.
 201   
      * 
 202   
      * <p>
 203   
      * If no level value is supplied, then the level of the named logger remains
 204   
      * untouched.
 205   
      * 
 206   
      * <p>
 207   
      * By default categories inherit their level from the hierarchy. However, if
 208   
      * you set the level of a logger and later decide that that logger should
 209   
      * inherit its level, then you should specify INHERITED as the value for the
 210   
      * level value. NULL is a synonym for INHERITED.
 211   
      * 
 212   
      * <p>
 213   
      * Similar to the root logger syntax, each <i>appenderName </i> (separated
 214   
      * by commas) will be attached to the named logger.
 215   
      * 
 216   
      * <p>
 217   
      * See the <a href="../../../../manual.html#additivity">appender additivity
 218   
      * rule </a> in the user manual for the meaning of the
 219   
      * <code>additivity</code> flag.
 220   
      * 
 221   
      * <h3>ObjectRenderers</h3>
 222   
      * 
 223   
      * You can customize the way message objects of a given type are converted
 224   
      * to String before being logged. This is done by specifying an
 225   
      * {@link org.apache.log4j.or.ObjectRenderer ObjectRenderer}for the object
 226   
      * type would like to customize.
 227   
      * 
 228   
      * <p>
 229   
      * The syntax is:
 230   
      * 
 231   
      * <pre>
 232   
      * log4j.renderer.fully.qualified.name.of.rendered.class = fully.qualified.name.of.rendering.class
 233   
      * </pre>
 234   
      * 
 235   
      * As in,
 236   
      * 
 237   
      * <pre>
 238   
      * log4j.renderer.my.Fruit = my.FruitRenderer
 239   
      * </pre>
 240   
      * 
 241   
      * <h3>Logger Factories</h3>
 242   
      * 
 243   
      * The usage of custom logger factories is discouraged and no longer
 244   
      * documented.
 245   
      * 
 246   
      * <h3>Example</h3>
 247   
      * 
 248   
      * <p>
 249   
      * An example configuration is given below. Other configuration file
 250   
      * examples are given in the <code>examples</code> folder.
 251   
      * 
 252   
      * <pre>
 253   
      * 
 254   
      *  
 255   
      *   
 256   
      *        
 257   
      *        # Set options for appender named &quot;A1&quot;.
 258   
      *        # Appender &quot;A1&quot; will be a SyslogAppender
 259   
      *        log4j.appender.A1=org.apache.log4j.net.SyslogAppender
 260   
      *        
 261   
      *        # The syslog daemon resides on www.abc.net
 262   
      *        log4j.appender.A1.SyslogHost=www.abc.net
 263   
      *        
 264   
      *        # A1's layout is a PatternLayout, using the conversion pattern
 265   
      *        # &lt;b&gt;%r %-5p %c{2} %M.%L %x - %m\n&lt;/b&gt;. Thus, the log output will
 266   
      *        # include # the relative time since the start of the application in
 267   
      *        # milliseconds, followed by the level of the log request,
 268   
      *        # followed by the two rightmost components of the logger name,
 269   
      *        # followed by the callers method name, followed by the line number,
 270   
      *        # the nested disgnostic context and finally the message itself.
 271   
      *        # Refer to the documentation of {@link PatternLayout} for further information
 272   
      *        # on the syntax of the ConversionPattern key.
 273   
      *        log4j.appender.A1.layout=org.apache.log4j.PatternLayout
 274   
      *        log4j.appender.A1.layout.ConversionPattern=%-4r %-5p %c{2} %M.%L %x - %m\n
 275   
      *        
 276   
      *        # Set options for appender named &quot;A2&quot;
 277   
      *        # A2 should be a RollingFileAppender, with maximum file size of 10 MB
 278   
      *        # using at most one backup file. A2's layout is TTCC, using the
 279   
      *        # ISO8061 date format with context printing enabled.
 280   
      *        log4j.appender.A2=org.apache.log4j.RollingFileAppender
 281   
      *        log4j.appender.A2.MaxFileSize=10MB
 282   
      *        log4j.appender.A2.MaxBackupIndex=1
 283   
      *        log4j.appender.A2.layout=org.apache.log4j.TTCCLayout
 284   
      *        log4j.appender.A2.layout.ContextPrinting=enabled
 285   
      *        log4j.appender.A2.layout.DateFormat=ISO8601
 286   
      *        
 287   
      *        # Root logger set to DEBUG using the A2 appender defined above.
 288   
      *        log4j.rootLogger=DEBUG, A2
 289   
      *        
 290   
      *        # Logger definitions:
 291   
      *        # The SECURITY logger inherits is level from root. However, it's output
 292   
      *        # will go to A1 appender defined above. It's additivity is non-cumulative.
 293   
      *        log4j.logger.SECURITY=INHERIT, A1
 294   
      *        log4j.additivity.SECURITY=false
 295   
      *        
 296   
      *        # Only warnings or above will be logged for the logger &quot;SECURITY.access&quot;.
 297   
      *        # Output will go to A1.
 298   
      *        log4j.logger.SECURITY.access=WARN
 299   
      *        
 300   
      *        
 301   
      *        # The logger &quot;class.of.the.day&quot; inherits its level from the
 302   
      *        # logger hierarchy.  Output will go to the appender's of the root
 303   
      *        # logger, A2 in this case.
 304   
      *        log4j.logger.class.of.the.day=INHERIT
 305   
      *        
 306   
      *   
 307   
      *  
 308   
      * </pre>
 309   
      * 
 310   
      * <p>
 311   
      * Refer to the <b>setOption </b> method in each Appender and Layout for
 312   
      * class specific options.
 313   
      * 
 314   
      * <p>
 315   
      * Use the <code>#</code> or <code>!</code> characters at the beginning
 316   
      * of a line for comments.
 317   
      * 
 318   
      * <p>
 319   
      * 
 320   
      * @param configFileName
 321   
      *               The name of the configuration file where the configuration
 322   
      *               information is stored.
 323   
      *  
 324   
      */
 325  0
     public void doConfigure(String configFileName, LoggerRepository hierarchy) {
 326  0
         Properties props = new Properties();
 327  0
         try {
 328  0
             FileInputStream istream = new FileInputStream(configFileName);
 329  0
             props.load(istream);
 330  0
             istream.close();
 331   
         } catch (IOException e) {
 332  0
             LogLog.error("Could not read configuration file [" + configFileName
 333   
                     + "].", e);
 334  0
             LogLog.error("Ignoring configuration file [" + configFileName
 335   
                     + "].");
 336  0
             return;
 337   
         }
 338   
         // If we reach here, then the config file is alright.
 339  0
         doConfigure(props, hierarchy);
 340   
     }
 341   
 
 342   
     /**
 343   
      */
 344  0
     static public void configure(String configFilename) {
 345  0
         new PropertyConfigurator().doConfigure(configFilename, LogManager
 346   
                 .getLoggerRepository());
 347   
     }
 348   
 
 349   
     /**
 350   
      * Read configuration options from url <code>configURL</code>.
 351   
      * 
 352   
      * @since 0.8.2
 353   
      */
 354  0
     public static void configure(java.net.URL configURL) {
 355  0
         new PropertyConfigurator().doConfigure(configURL, LogManager
 356   
                 .getLoggerRepository());
 357   
     }
 358   
 
 359   
     /**
 360   
      * Read configuration options from <code>properties</code>.
 361   
      * 
 362   
      * See {@link #doConfigure(String, LoggerRepository)}for the expected
 363   
      * format.
 364   
      */
 365  0
     static public void configure(Properties properties) {
 366  0
         new PropertyConfigurator().doConfigure(properties, LogManager
 367   
                 .getLoggerRepository());
 368   
     }
 369   
 
 370   
     /**
 371   
      * Like {@link #configureAndWatch(String, long)}except that the default
 372   
      * delay as defined by {@link FileWatchdog#DEFAULT_DELAY}is used.
 373   
      * 
 374   
      * @param configFilename
 375   
      *               A file in key=value format.
 376   
      *  
 377   
      */
 378  0
     static public void configureAndWatch(String configFilename) {
 379  0
         configureAndWatch(configFilename, FileWatchdog.DEFAULT_DELAY);
 380   
     }
 381   
 
 382   
     /**
 383   
      * Read the configuration file <code>configFilename</code> if it exists.
 384   
      * Moreover, a thread will be created that will periodically check if
 385   
      * <code>configFilename</code> has been created or modified. The period is
 386   
      * determined by the <code>delay</code> argument. If a change or file
 387   
      * creation is detected, then <code>configFilename</code> is read to
 388   
      * configure log4j.
 389   
      * 
 390   
      * @param configFilename
 391   
      *               A file in key=value format.
 392   
      * @param delay
 393   
      *               The delay in milliseconds to wait between each check.
 394   
      */
 395  0
     static public void configureAndWatch(String configFilename, long delay) {
 396  0
         PropertyWatchdog pdog = new PropertyWatchdog(configFilename);
 397  0
         pdog.setDelay(delay);
 398  0
         pdog.start();
 399   
     }
 400   
 
 401   
     /**
 402   
      * Read configuration options from <code>properties</code>.
 403   
      * 
 404   
      * See {@link #doConfigure(String, LoggerRepository)}for the expected
 405   
      * format.
 406   
      * 
 407   
      * @deprecated
 408   
      */
 409  0
     public void doConfigure(Properties properties, LoggerRepository hierarchy) {
 410   
 
 411  0
         String value = properties.getProperty(LogLog.DEBUG_KEY);
 412  0
         if (value == null) {
 413  0
             value = properties.getProperty(LogLog.CONFIG_DEBUG_KEY);
 414  0
             if (value != null)
 415  0
                 LogLog
 416   
                         .warn("[log4j.configDebug] is deprecated. Use [log4j.debug] instead.");
 417   
         }
 418   
 
 419  0
         if (value != null) {
 420  0
             LogLog.setInternalDebugging(OptionConverter.toBoolean(value, true));
 421   
         }
 422   
 
 423  0
         String thresholdStr = OptionConverter.findAndSubst(THRESHOLD_PREFIX,
 424   
                 properties);
 425  0
         if (thresholdStr != null) {
 426  0
             hierarchy.setThreshold(OptionConverter.toLevel(thresholdStr,
 427   
                     Level.ALL));
 428  0
             LogLog.debug("Hierarchy threshold set to ["
 429   
                     + hierarchy.getThreshold() + "].");
 430   
         }
 431   
 
 432  0
         configureRootCategory(properties, hierarchy);
 433  0
         configureLoggerFactory(properties);
 434  0
         parseCatsAndRenderers(properties, hierarchy);
 435   
 
 436  0
         LogLog.debug("Finished configuring.");
 437   
         // We don't want to hold references to appenders preventing their
 438   
         // garbage collection.
 439  0
         registry.clear();
 440   
     }
 441   
 
 442   
     /**
 443   
      * Read configuration options from url <code>configURL</code>.
 444   
      */
 445  0
     public void doConfigure(java.net.URL configURL, LoggerRepository hierarchy) {
 446  0
         Properties props = new Properties();
 447  0
         LogLog.debug("Reading configuration from URL " + configURL);
 448  0
         try {
 449  0
             props.load(configURL.openStream());
 450   
         } catch (java.io.IOException e) {
 451  0
             LogLog.error("Could not read configuration file from URL ["
 452   
                     + configURL + "].", e);
 453  0
             LogLog.error("Ignoring configuration file [" + configURL + "].");
 454  0
             return;
 455   
         }
 456  0
         doConfigure(props, hierarchy);
 457   
     }
 458   
 
 459   
     // --------------------------------------------------------------------------
 460   
     // Internal stuff
 461   
     // --------------------------------------------------------------------------
 462   
 
 463   
     /**
 464   
      * Check the provided <code>Properties</code> object for a
 465   
      * {@link org.apache.log4j.spi.LoggerFactory LoggerFactory}entry specified
 466   
      * by {@link #LOGGER_FACTORY_KEY}. If such an entry exists, an attempt is
 467   
      * made to create an instance using the default constructor. This instance
 468   
      * is used for subsequent Category creations within this configurator.
 469   
      * 
 470   
      * @see #parseCatsAndRenderers
 471   
      */
 472  0
     protected void configureLoggerFactory(Properties props) {
 473  0
         String factoryClassName = OptionConverter.findAndSubst(
 474   
                 LOGGER_FACTORY_KEY, props);
 475  0
         if (factoryClassName != null) {
 476  0
             LogLog.debug("Setting category factory to [" + factoryClassName
 477   
                     + "].");
 478  0
             loggerFactory = (LoggerFactory) OptionConverter
 479   
                     .instantiateByClassName(factoryClassName,
 480   
                             LoggerFactory.class, loggerFactory);
 481  0
             PropertySetter.setProperties(loggerFactory, props, FACTORY_PREFIX
 482   
                     + ".");
 483   
         }
 484   
     }
 485   
 
 486   
     /*
 487   
      * void configureOptionHandler(OptionHandler oh, String prefix, Properties
 488   
      * props) { String[] options = oh.getOptionStrings(); if(options == null)
 489   
      * return;
 490   
      * 
 491   
      * String value; for(int i = 0; i < options.length; i++) { value =
 492   
      * OptionConverter.findAndSubst(prefix + options[i], props); LogLog.debug(
 493   
      * "Option " + options[i] + "=[" + (value == null? "N/A" : value)+"]."); //
 494   
      * Some option handlers assume that null value are not passed to them. // So
 495   
      * don't remove this check if(value != null) { oh.setOption(options[i],
 496   
      * value); } } oh.activateOptions(); }
 497   
      */
 498   
 
 499  0
     protected void configureRootCategory(Properties props,
 500   
             LoggerRepository hierarchy) {
 501  0
         String effectiveFrefix = ROOT_LOGGER_PREFIX;
 502  0
         String value = OptionConverter.findAndSubst(ROOT_LOGGER_PREFIX, props);
 503   
 
 504  0
         if (value == null) {
 505  0
             value = OptionConverter.findAndSubst(ROOT_CATEGORY_PREFIX, props);
 506  0
             effectiveFrefix = ROOT_CATEGORY_PREFIX;
 507   
         }
 508   
 
 509  0
         if (value == null)
 510  0
             LogLog.debug("Could not find root logger information. Is this OK?");
 511   
         else {
 512  0
             Logger root = hierarchy.getRootLogger();
 513  0
             synchronized (root) {
 514  0
                 parseCategory(props, root, effectiveFrefix, INTERNAL_ROOT_NAME,
 515   
                         value);
 516   
             }
 517   
         }
 518   
     }
 519   
 
 520   
     /**
 521   
      * Parse non-root elements, such non-root categories and renderers.
 522   
      */
 523  0
     protected void parseCatsAndRenderers(Properties props,
 524   
             LoggerRepository hierarchy) {
 525  0
         Enumeration enum = props.propertyNames();
 526  0
         while (enum.hasMoreElements()) {
 527  0
             String key = (String) enum.nextElement();
 528  0
             if (key.startsWith(CATEGORY_PREFIX)
 529   
                     || key.startsWith(LOGGER_PREFIX)) {
 530  0
                 String loggerName = null;
 531  0
                 if (key.startsWith(CATEGORY_PREFIX)) {
 532  0
                     loggerName = key.substring(CATEGORY_PREFIX.length());
 533  0
                 } else if (key.startsWith(LOGGER_PREFIX)) {
 534  0
                     loggerName = key.substring(LOGGER_PREFIX.length());
 535   
                 }
 536  0
                 String value = OptionConverter.findAndSubst(key, props);
 537  0
                 Logger logger = hierarchy.getLogger(loggerName, loggerFactory);
 538  0
                 synchronized (logger) {
 539  0
                     parseCategory(props, logger, key, loggerName, value);
 540  0
                     parseAdditivityForLogger(props, logger, loggerName);
 541   
                 }
 542  0
             } else if (key.startsWith(RENDERER_PREFIX)) {
 543  0
                 String renderedClass = key.substring(RENDERER_PREFIX.length());
 544  0
                 String renderingClass = OptionConverter
 545   
                         .findAndSubst(key, props);
 546  0
                 if (hierarchy instanceof RendererSupport) {
 547  0
                     RendererMap.addRenderer((RendererSupport) hierarchy,
 548   
                             renderedClass, renderingClass);
 549   
                 }
 550   
             }
 551   
         }
 552   
     }
 553   
 
 554   
     /**
 555   
      * Parse the additivity option for a non-root category.
 556   
      */
 557  0
     protected void parseAdditivityForLogger(Properties props, Logger cat,
 558   
             String loggerName) {
 559  0
         String value = OptionConverter.findAndSubst(ADDITIVITY_PREFIX
 560   
                 + loggerName, props);
 561  0
         LogLog.debug("Handling " + ADDITIVITY_PREFIX + loggerName + "=["
 562   
                 + value + "]");
 563   
         // touch additivity only if necessary
 564  0
         if ((value != null) && (!value.equals(""))) {
 565  0
             boolean additivity = OptionConverter.toBoolean(value, true);
 566  0
             LogLog.debug("Setting additivity for \"" + loggerName + "\" to "
 567   
                     + additivity);
 568  0
             cat.setAdditivity(additivity);
 569   
         }
 570   
     }
 571   
 
 572   
     /**
 573   
      * This method must work for the root category as well.
 574   
      */
 575  0
     protected void parseCategory(Properties props, Logger logger,
 576   
             String optionKey, String loggerName, String value) {
 577   
 
 578  0
         if (optionKey == null) {
 579   
             //do nothing
 580   
         }
 581   
 
 582  0
         LogLog.debug("Parsing for [" + loggerName + "] with value=[" + value
 583   
                 + "].");
 584   
         // We must skip over ',' but not white space
 585  0
         StringTokenizer st = new StringTokenizer(value, ",");
 586   
 
 587   
         // If value is not in the form ", appender.." or "", then we should set
 588   
         // the level of the loggeregory.
 589   
 
 590  0
         if (!(value.startsWith(",") || value.equals(""))) {
 591   
 
 592   
             // just to be on the safe side...
 593  0
             if (!st.hasMoreTokens())
 594  0
                 return;
 595   
 
 596  0
             String levelStr = st.nextToken();
 597  0
             LogLog.debug("Level token is [" + levelStr + "].");
 598   
 
 599   
             // If the level value is inherited, set category level value to
 600   
             // null. We also check that the user has not specified inherited for
 601   
             // the
 602   
             // root category.
 603  0
             if (INHERITED.equalsIgnoreCase(levelStr)
 604   
                     || NULL.equalsIgnoreCase(levelStr)) {
 605  0
                 if (loggerName.equals(INTERNAL_ROOT_NAME)) {
 606  0
                     LogLog.warn("The root logger cannot be set to null.");
 607   
                 } else {
 608  0
                     logger.setLevel(null);
 609   
                 }
 610   
             } else {
 611  0
                 logger.setLevel(OptionConverter.toLevel(levelStr, Level.DEBUG));
 612   
             }
 613  0
             LogLog.debug("Category " + loggerName + " set to "
 614   
                     + logger.getLevel());
 615   
         }
 616   
 
 617   
         // Begin by removing all existing appenders.
 618  0
         logger.removeAllAppenders();
 619   
 
 620  0
         Appender appender;
 621  0
         String appenderName;
 622  0
         while (st.hasMoreTokens()) {
 623  0
             appenderName = st.nextToken().trim();
 624  0
             if (appenderName == null || appenderName.equals(","))
 625  0
                 continue;
 626  0
             LogLog.debug("Parsing appender named \"" + appenderName + "\".");
 627  0
             appender = parseAppender(props, appenderName);
 628  0
             if (appender != null) {
 629  0
                 logger.addAppender(appender);
 630   
             }
 631   
         }
 632   
     }
 633   
 
 634  0
     protected Appender parseAppender(Properties props, String appenderName) {
 635  0
         Appender appender = registryGet(appenderName);
 636  0
         if ((appender != null)) {
 637  0
             LogLog.debug("Appender \"" + appenderName
 638   
                     + "\" was already parsed.");
 639  0
             return appender;
 640   
         }
 641   
         // Appender was not previously initialized.
 642  0
         String prefix = APPENDER_PREFIX + appenderName;
 643  0
         String layoutPrefix = prefix + ".layout";
 644   
 
 645  0
         appender = (Appender) OptionConverter.instantiateByKey(props, prefix,
 646   
                 org.apache.log4j.Appender.class, null);
 647  0
         if (appender == null) {
 648  0
             LogLog.error("Could not instantiate appender named \""
 649   
                     + appenderName + "\".");
 650  0
             return null;
 651   
         }
 652  0
         appender.setName(appenderName);
 653   
 
 654  0
         if (appender instanceof OptionHandler) {
 655  0
             if (appender.requiresLayout()) {
 656  0
                 Layout layout = (Layout) OptionConverter.instantiateByKey(
 657   
                         props, layoutPrefix, Layout.class, null);
 658  0
                 if (layout != null) {
 659  0
                     appender.setLayout(layout);
 660  0
                     LogLog.debug("Parsing layout options for \"" + appenderName
 661   
                             + "\".");
 662   
                     //configureOptionHandler(layout, layoutPrefix + ".",
 663   
                     // props);
 664  0
                     PropertySetter.setProperties(layout, props, layoutPrefix
 665   
                             + ".");
 666  0
                     LogLog
 667   
                             .debug("End of parsing for \"" + appenderName
 668   
                                     + "\".");
 669   
                 }
 670   
             }
 671   
             //configureOptionHandler((OptionHandler) appender, prefix + ".",
 672   
             // props);
 673  0
             PropertySetter.setProperties(appender, props, prefix + ".");
 674  0
             LogLog.debug("Parsed \"" + appenderName + "\" options.");
 675   
         }
 676  0
         registryPut(appender);
 677  0
         return appender;
 678   
     }
 679   
 
 680  0
     protected final void registryPut(Appender appender) {
 681  0
         registry.put(appender.getName(), appender);
 682   
     }
 683   
 
 684  0
     protected final Appender registryGet(String name) {
 685  0
         return (Appender) registry.get(name);
 686   
     }
 687   
 }
 688   
 
 689   
 class PropertyWatchdog extends FileWatchdog {
 690   
 
 691  0
     PropertyWatchdog(String filename) {
 692  0
         super(filename);
 693   
     }
 694   
 
 695   
     /**
 696   
      * Call {@link PropertyConfigurator#configure(String)}with the
 697   
      * <code>filename</code> to reconfigure log4j.
 698   
      */
 699  0
     public void doOnChange() {
 700  0
         new PropertyConfigurator().doConfigure(filename, LogManager
 701   
                 .getLoggerRepository());
 702   
     }
 703   
 }