Java Source Code: org.apache.juli.ClassLoaderLogManager


   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: 
  18: package org.apache.juli;
  19: 
  20: import java.io.File;
  21: import java.io.FileInputStream;
  22: import java.io.IOException;
  23: import java.io.InputStream;
  24: import java.net.URLClassLoader;
  25: import java.security.AccessController;
  26: import java.security.PrivilegedAction;
  27: import java.util.Collections;
  28: import java.util.Enumeration;
  29: import java.util.HashMap;
  30: import java.util.Iterator;
  31: import java.util.Map;
  32: import java.util.Properties;
  33: import java.util.StringTokenizer;
  34: import java.util.WeakHashMap;
  35: import java.util.logging.Handler;
  36: import java.util.logging.Level;
  37: import java.util.logging.LogManager;
  38: import java.util.logging.Logger;
  39: 
  40: 
  41: /**
  42:  * Per classloader LogManager implementation.
  43:  */
  44:	  public class ClassLoaderLogManager extends LogManager {
  45:
  46:
  47:    // -------------------------------------------------------------- Variables
  48:
  49:
  50:    /**
  51:     * Map containing the classloader information, keyed per classloader. A
  52:     * weak hashmap is used to ensure no classloader reference is leaked from 
  53:     * application redeployment.
  54:     */
  55:    protected final Map<ClassLoader, ClassLoaderLogInfo> classLoaderLoggers = 
  56:        new WeakHashMap<ClassLoader, ClassLoaderLogInfo>();
  57:
  58:    
  59:    /**
  60:     * This prefix is used to allow using prefixes for the properties names
  61:     * of handlers and their subcomponents.
  62:     */
  63:    protected ThreadLocal<String> prefix = new ThreadLocal<String>();
  64:
  65:    
  66:    // --------------------------------------------------------- Public Methods
  67:
  68:
  69:    /**
  70:     * Add the specified logger to the classloader local configuration.
  71:     * 
  72:     * @param logger The logger to be added
  73:     */
  74:	      public synchronized boolean addLogger(final Logger logger) {
  75:
  76:        final String loggerName = logger.getName();
  77:
  78:        ClassLoader classLoader = 
  79:            Thread.currentThread().getContextClassLoader();
  80:        ClassLoaderLogInfo info = getClassLoaderInfo(classLoader);
  81:	          if (info.loggers.containsKey(loggerName)) {
  82:            return false;
  83:        }
  84:        info.loggers.put(loggerName, logger);
  85:
  86:        // Apply initial level for new logger
  87:        final String levelString = getProperty(loggerName + ".level");
  88:	          if (levelString != null) {
  89:	              try {
  90:	                  AccessController.doPrivileged(new PrivilegedAction() {
  91:	                      public Object run() {
  92:                        logger.setLevel(Level.parse(levelString.trim()));
  93:                        return null;
  94:                    }
  95:                });
  96:            } catch (IllegalArgumentException e) {
  97:                // Leave level set to null
  98:            }
  99:        }
 100:
 101:        // If any parent loggers have levels definied, make sure they are
 102:        // instantiated
 103:        int dotIndex = loggerName.lastIndexOf('.');
 104:	          while (dotIndex >= 0) {
 105:            final String parentName = loggerName.substring(0, dotIndex);
 106:	              if (getProperty(parentName + ".level") != null) {
 107:                Logger.getLogger(parentName);
 108:                break;
 109:            }
 110:            dotIndex = loggerName.lastIndexOf('.', dotIndex - 1);
 111:        }
 112:
 113:        // Find associated node
 114:        LogNode node = info.rootNode.findNode(loggerName);
 115:        node.logger = logger;
 116:
 117:        // Set parent logger
 118:        Logger parentLogger = node.findParentLogger();
 119:	          if (parentLogger != null) {
 120:            doSetParentLogger(logger, parentLogger);
 121:        }
 122:
 123:        // Tell children we are their new parent
 124:        node.setParentLogger(logger);
 125:
 126:        // Add associated handlers, if any are defined using the .handlers property.
 127:        // In this case, handlers of the parent logger(s) will not be used
 128:        String handlers = getProperty(loggerName + ".handlers");
 129:	          if (handlers != null) {
 130:            logger.setUseParentHandlers(false);
 131:            StringTokenizer tok = new StringTokenizer(handlers, ",");
 132:	              while (tok.hasMoreTokens()) {
 133:                String handlerName = (tok.nextToken().trim());
 134:                Handler handler = null;
 135:                ClassLoader current = classLoader;
 136:	                  while (current != null) {
 137:                    info = (ClassLoaderLogInfo) classLoaderLoggers.get(current);
 138:	                      if (info != null) {
 139:                        handler = (Handler) info.handlers.get(handlerName);
 140:	                          if (handler != null) {
 141:                            break;
 142:                        }
 143:                    }
 144:                    current = current.getParent();
 145:                }
 146:	                  if (handler != null) {
 147:                    logger.addHandler(handler);
 148:                }
 149:            }
 150:        }
 151:
 152:        // Parse useParentHandlers to set if the logger should delegate to its parent.
 153:        // Unlike java.util.logging, the default is to not delegate if a list of handlers
 154:        // has been specified for the logger.
 155:        String useParentHandlersString = getProperty(loggerName + ".useParentHandlers");
 156:	          if (Boolean.valueOf(useParentHandlersString).booleanValue()) {
 157:            logger.setUseParentHandlers(true);
 158:        }
 159:        
 160:        return true;
 161:    }
 162:
 163:    
 164:    /**
 165:     * Get the logger associated with the specified name inside 
 166:     * the classloader local configuration. If this returns null,
 167:     * and the call originated for Logger.getLogger, a new
 168:     * logger with the specified name will be instantiated and
 169:     * added using addLogger.
 170:     * 
 171:     * @param name The name of the logger to retrieve
 172:     */
 173:	      public synchronized Logger getLogger(final String name) {
 174:        ClassLoader classLoader = Thread.currentThread()
 175:                .getContextClassLoader();
 176:        return (Logger) getClassLoaderInfo(classLoader).loggers.get(name);
 177:    }
 178:    
 179:    
 180:    /**
 181:     * Get an enumeration of the logger names currently defined in the 
 182:     * classloader local configuration.
 183:     */
 184:	      public synchronized Enumeration<String> getLoggerNames() {
 185:        ClassLoader classLoader = Thread.currentThread()
 186:                .getContextClassLoader();
 187:        return Collections.enumeration(getClassLoaderInfo(classLoader).loggers.keySet());
 188:    }
 189:
 190:    
 191:    /**
 192:     * Get the value of the specified property in the classloader local
 193:     * configuration.
 194:     * 
 195:     * @param name The property name
 196:     */    
 197:	      public String getProperty(String name) {
 198:        ClassLoader classLoader = Thread.currentThread()
 199:            .getContextClassLoader();
 200:        String prefix = (String) this.prefix.get();
 201:	          if (prefix != null) {
 202:            name = prefix + name;
 203:        }
 204:        ClassLoaderLogInfo info = getClassLoaderInfo(classLoader);
 205:        String result = info.props.getProperty(name);
 206:        // If the property was not found, and the current classloader had no 
 207:        // configuration (property list is empty), look for the parent classloader
 208:        // properties.
 209:	          if ((result == null) && (info.props.isEmpty())) {
 210:            ClassLoader current = classLoader.getParent();
 211:	              while (current != null) {
 212:                info = (ClassLoaderLogInfo) classLoaderLoggers.get(current);
 213:	                  if (info != null) {
 214:                    result = info.props.getProperty(name);
 215:	                      if ((result != null) || (!info.props.isEmpty())) {
 216:                        break;
 217:                    }
 218:                }
 219:                current = current.getParent();
 220:            }
 221:	              if (result == null) {
 222:                result = super.getProperty(name);
 223:            }
 224:        }
 225:        // Simple property replacement (mostly for folder names)
 226:	          if (result != null) {
 227:            result = replace(result);
 228:        }
 229:        return result;
 230:    }
 231:    
 232:    
 233:    public void readConfiguration()
 234:	          throws IOException, SecurityException {
 235:        
 236:        checkAccess();
 237:        
 238:        readConfiguration(Thread.currentThread().getContextClassLoader());
 239:        
 240:    }
 241:        
 242:    public void readConfiguration(InputStream is)
 243:	          throws IOException, SecurityException {
 244:        
 245:        checkAccess();
 246:        reset();
 247:
 248:        readConfiguration(is, Thread.currentThread().getContextClassLoader());
 249:    
 250:    }
 251:        
 252:    // ------------------------------------------------------ Protected Methods
 253:
 254:
 255:    /**
 256:     * Retrieve the configuration associated with the specified classloader. If
 257:     * it does not exist, it will be created.
 258:     * 
 259:     * @param classLoader The classloader for which we will retrieve or build the 
 260:     *                    configuration
 261:     */
 262:	      protected ClassLoaderLogInfo getClassLoaderInfo(ClassLoader classLoader) {
 263:        
 264:	          if (classLoader == null) {
 265:            classLoader = ClassLoader.getSystemClassLoader();
 266:        }
 267:        ClassLoaderLogInfo info = (ClassLoaderLogInfo) classLoaderLoggers
 268:                .get(classLoader);
 269:	          if (info == null) {
 270:            final ClassLoader classLoaderParam = classLoader;
 271:	              AccessController.doPrivileged(new PrivilegedAction() {
 272:	                  public Object run() {
 273:	                      try {
 274:                        readConfiguration(classLoaderParam);
 275:                    } catch (IOException e) {
 276:                        // Ignore
 277:                    }
 278:                    return null;
 279:                }
 280:            });
 281:            info = (ClassLoaderLogInfo) classLoaderLoggers.get(classLoader);
 282:        }
 283:        return info;
 284:    }
 285:
 286:    
 287:    /**
 288:     * Read configuration for the specified classloader.
 289:     * 
 290:     * @param classLoader 
 291:     * @throws IOException Errot
 292:     */
 293:    protected void readConfiguration(ClassLoader classLoader)
 294:	          throws IOException {
 295:        
 296:        InputStream is = null;
 297:        // Special case for URL classloaders which are used in containers: 
 298:        // only look in the local repositories to avoid redefining loggers 20 times
 299:        if ((classLoader instanceof URLClassLoader) 
 300:	                  && (((URLClassLoader) classLoader).findResource("logging.properties") != null)) {
 301:            is = classLoader.getResourceAsStream("logging.properties");
 302:        }
 303:	          if ((is == null) && (classLoader == ClassLoader.getSystemClassLoader())) {
 304:            String configFileStr = System.getProperty("java.util.logging.config.file");
 305:	              if (configFileStr != null) {
 306:	                  try {
 307:                    is = new FileInputStream(replace(configFileStr));
 308:                } catch (IOException e) {
 309:                    // Ignore
 310:                }
 311:            }
 312:            // Try the default JVM configuration
 313:	              if (is == null) {
 314:                File defaultFile = new File(new File(System.getProperty("java.home"), "lib"), 
 315:                    "logging.properties");
 316:	                  try {
 317:                    is = new FileInputStream(defaultFile);
 318:                } catch (IOException e) {
 319:                    // Critical problem, do something ...
 320:                }
 321:            }
 322:        }
 323:        
 324:        Logger localRootLogger = new RootLogger();
 325:	          if (is == null) {
 326:            // Retrieve the root logger of the parent classloader instead
 327:            ClassLoader current = classLoader.getParent();
 328:            ClassLoaderLogInfo info = null;
 329:	              while (current != null && info == null) {
 330:                info = getClassLoaderInfo(current);
 331:                current = current.getParent();
 332:            }
 333:	              if (info != null) {
 334:                localRootLogger.setParent(info.rootNode.logger);
 335:            }
 336:        }
 337:        ClassLoaderLogInfo info = 
 338:            new ClassLoaderLogInfo(new LogNode(null, localRootLogger));
 339:        classLoaderLoggers.put(classLoader, info);
 340:        
 341:	          if (is != null) {
 342:            readConfiguration(is, classLoader);
 343:        }
 344:        addLogger(localRootLogger);
 345:        
 346:    }
 347:    
 348:    
 349:    /**
 350:     * Load specified configuration.
 351:     * 
 352:     * @param is InputStream to the properties file
 353:     * @param classLoader for which the configuration will be loaded
 354:     * @throws IOException If something wrong happens during loading
 355:     */
 356:    protected void readConfiguration(InputStream is, ClassLoader classLoader)
 357:	          throws IOException {
 358:        
 359:        ClassLoaderLogInfo info = 
 360:            (ClassLoaderLogInfo) classLoaderLoggers.get(classLoader);
 361:        
 362:	          try {
 363:            info.props.load(is);
 364:        } catch (IOException e) {
 365:            // Report error
 366:            System.err.println("Configuration error");
 367:            e.printStackTrace();
 368:        } finally {
 369:	              try {
 370:                is.close();
 371:            } catch (Throwable t) {}
 372:        }
 373:        
 374:        // Create handlers for the root logger of this classloader
 375:        String rootHandlers = info.props.getProperty(".handlers");
 376:        String handlers = info.props.getProperty("handlers");
 377:        Logger localRootLogger = info.rootNode.logger;
 378:	          if (handlers != null) {
 379:            StringTokenizer tok = new StringTokenizer(handlers, ",");
 380:	              while (tok.hasMoreTokens()) {
 381:                String handlerName = (tok.nextToken().trim());
 382:                String handlerClassName = handlerName;
 383:                String prefix = "";
 384:	                  if (handlerClassName.length() <= 0) {
 385:                    continue;
 386:                }
 387:                // Parse and remove a prefix (prefix start with a digit, such as 
 388:                // "10WebappFooHanlder.")
 389:	                  if (Character.isDigit(handlerClassName.charAt(0))) {
 390:                    int pos = handlerClassName.indexOf('.');
 391:	                      if (pos >= 0) {
 392:                        prefix = handlerClassName.substring(0, pos + 1);
 393:                        handlerClassName = handlerClassName.substring(pos + 1);
 394:                    }
 395:                }
 396:	                  try {
 397:                    this.prefix.set(prefix);
 398:                    Handler handler = 
 399:                        (Handler) classLoader.loadClass(handlerClassName).newInstance();
 400:                    // The specification strongly implies all configuration should be done 
 401:                    // during the creation of the handler object.
 402:                    // This includes setting level, filter, formatter and encoding.
 403:                    this.prefix.set(null);
 404:                    info.handlers.put(handlerName, handler);
 405:	                      if (rootHandlers == null) {
 406:                        localRootLogger.addHandler(handler);
 407:                    }
 408:                } catch (Exception e) {
 409:                    // Report error
 410:                    System.err.println("Handler error");
 411:                    e.printStackTrace();
 412:                }
 413:            }
 414:            
 415:            // Add handlers to the root logger, if any are defined using the .handlers property.
 416:	              if (rootHandlers != null) {
 417:                StringTokenizer tok2 = new StringTokenizer(rootHandlers, ",");
 418:	                  while (tok2.hasMoreTokens()) {
 419:                    String handlerName = (tok2.nextToken().trim());
 420:                    Handler handler = (Handler) info.handlers.get(handlerName);
 421:	                      if (handler != null) {
 422:                        localRootLogger.addHandler(handler);
 423:                    }
 424:                }
 425:            }
 426:            
 427:        }
 428:        
 429:    }
 430:    
 431:    
 432:    /**
 433:     * Set parent child relationship between the two specified loggers.
 434:     * 
 435:     * @param logger
 436:     * @param parent
 437:     */
 438:    protected static void doSetParentLogger(final Logger logger,
 439:	              final Logger parent) {
 440:	          AccessController.doPrivileged(new PrivilegedAction() {
 441:	              public Object run() {
 442:                logger.setParent(parent);
 443:                return null;
 444:            }
 445:        });
 446:    }
 447:
 448:    
 449:    /**
 450:     * System property replacement in the given string.
 451:     * 
 452:     * @param str The original string
 453:     * @return the modified string
 454:     */
 455:	      protected String replace(String str) {
 456:        String result = str;
 457:	          if (result.startsWith("${")) {
 458:            int pos = result.indexOf('}');
 459:	              if (pos != -1) {
 460:                String propName = result.substring(2, pos);
 461:                String replacement = System.getProperty(propName);
 462:	                  if (replacement != null) {
 463:                    result = replacement + result.substring(pos + 1);
 464:                }
 465:            }
 466:        }
 467:        return result;
 468:    }
 469:    
 470:
 471:    // ---------------------------------------------------- LogNode Inner Class
 472:
 473:
 474:	      protected static final class LogNode {
 475:        Logger logger;
 476:
 477:        protected final Map<String, LogNode> children = 
 478:            new HashMap<String, LogNode>();
 479:
 480:        protected final LogNode parent;
 481:
 482:	          LogNode(final LogNode parent, final Logger logger) {
 483:            this.parent = parent;
 484:            this.logger = logger;
 485:        }
 486:
 487:	          LogNode(final LogNode parent) {
 488:            this(parent, null);
 489:        }
 490:
 491:	          LogNode findNode(String name) {
 492:            LogNode currentNode = this;
 493:	              if (logger.getName().equals(name)) {
 494:                return this;
 495:            }
 496:	              while (name != null) {
 497:                final int dotIndex = name.indexOf('.');
 498:                final String nextName;
 499:	                  if (dotIndex < 0) {
 500:                    nextName = name;
 501:                    name = null;
 502:                } else {
 503:                    nextName = name.substring(0, dotIndex);
 504:                    name = name.substring(dotIndex + 1);
 505:                }
 506:                LogNode childNode = (LogNode) currentNode.children
 507:                        .get(nextName);
 508:	                  if (childNode == null) {
 509:                    childNode = new LogNode(currentNode);
 510:                    currentNode.children.put(nextName, childNode);
 511:                }
 512:                currentNode = childNode;
 513:            }
 514:            return currentNode;
 515:        }
 516:
 517:	          Logger findParentLogger() {
 518:            Logger logger = null;
 519:            LogNode node = parent;
 520:	              while (node != null && logger == null) {
 521:                logger = node.logger;
 522:                node = node.parent;
 523:            }
 524:            return logger;
 525:        }
 526:
 527:	          void setParentLogger(final Logger parent) {
 528:            for (final Iterator iter = children.values().iterator(); iter
 529:	                      .hasNext();) {
 530:                final LogNode childNode = (LogNode) iter.next();
 531:	                  if (childNode.logger == null) {
 532:                    childNode.setParentLogger(parent);
 533:                } else {
 534:                    doSetParentLogger(childNode.logger, parent);
 535:                }
 536:            }
 537:        }
 538:
 539:    }
 540:
 541:
 542:    // -------------------------------------------- ClassLoaderInfo Inner Class
 543:
 544:
 545:	      protected static final class ClassLoaderLogInfo {
 546:        final LogNode rootNode;
 547:        final Map<String, Logger> loggers = new HashMap<String, Logger>();
 548:        final Map<String, Handler> handlers = new HashMap<String, Handler>();
 549:        final Properties props = new Properties();
 550:
 551:	          ClassLoaderLogInfo(final LogNode rootNode) {
 552:            this.rootNode = rootNode;
 553:        }
 554:
 555:    }
 556:
 557:
 558:    // ------------------------------------------------- RootLogger Inner Class
 559:
 560:
 561:    /**
 562:     * This class is needed to instantiate the root of each per classloader 
 563:     * hierarchy.
 564:     */
 565:	      protected class RootLogger extends Logger {
 566:	          public RootLogger() {
 567:            super("", null);
 568:        }
 569:    }
 570:
 571:
 572:}