001/* LogManager.java -- a class for maintaining Loggers and managing 002 configuration properties 003 Copyright (C) 2002, 2005, 2006, 2007 Free Software Foundation, Inc. 004 005This file is part of GNU Classpath. 006 007GNU Classpath is free software; you can redistribute it and/or modify 008it under the terms of the GNU General Public License as published by 009the Free Software Foundation; either version 2, or (at your option) 010any later version. 011 012GNU Classpath is distributed in the hope that it will be useful, but 013WITHOUT ANY WARRANTY; without even the implied warranty of 014MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015General Public License for more details. 016 017You should have received a copy of the GNU General Public License 018along with GNU Classpath; see the file COPYING. If not, write to the 019Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02002110-1301 USA. 021 022Linking this library statically or dynamically with other modules is 023making a combined work based on this library. Thus, the terms and 024conditions of the GNU General Public License cover the whole 025combination. 026 027As a special exception, the copyright holders of this library give you 028permission to link this library with independent modules to produce an 029executable, regardless of the license terms of these independent 030modules, and to copy and distribute the resulting executable under 031terms of your choice, provided that you also meet, for each linked 032independent module, the terms and conditions of the license of that 033module. An independent module is a module which is not derived from 034or based on this library. If you modify this library, you may extend 035this exception to your version of the library, but you are not 036obligated to do so. If you do not wish to do so, delete this 037exception statement from your version. */ 038 039 040package java.util.logging; 041 042import gnu.classpath.SystemProperties; 043 044import java.beans.PropertyChangeListener; 045import java.beans.PropertyChangeSupport; 046import java.io.ByteArrayInputStream; 047import java.io.IOException; 048import java.io.InputStream; 049import java.lang.ref.WeakReference; 050import java.net.URL; 051import java.util.Collections; 052import java.util.Enumeration; 053import java.util.HashMap; 054import java.util.Iterator; 055import java.util.List; 056import java.util.Map; 057import java.util.Properties; 058import java.util.StringTokenizer; 059 060/** 061 * The <code>LogManager</code> maintains a hierarchical namespace 062 * of Logger objects and manages properties for configuring the logging 063 * framework. There exists only one single <code>LogManager</code> 064 * per virtual machine. This instance can be retrieved using the 065 * static method {@link #getLogManager()}. 066 * 067 * <p><strong>Configuration Process:</strong> The global LogManager 068 * object is created and configured when the class 069 * <code>java.util.logging.LogManager</code> is initialized. 070 * The configuration process includes the subsequent steps: 071 * 072 * <ul> 073 * <li>If the system property <code>java.util.logging.manager</code> 074 * is set to the name of a subclass of 075 * <code>java.util.logging.LogManager</code>, an instance of 076 * that subclass is created and becomes the global LogManager. 077 * Otherwise, a new instance of LogManager is created.</li> 078 * <li>The <code>LogManager</code> constructor tries to create 079 * a new instance of the class specified by the system 080 * property <code>java.util.logging.config.class</code>. 081 * Typically, the constructor of this class will call 082 * <code>LogManager.getLogManager().readConfiguration(java.io.InputStream)</code> 083 * for configuring the logging framework. 084 * The configuration process stops at this point if 085 * the system property <code>java.util.logging.config.class</code> 086 * is set (irrespective of whether the class constructor 087 * could be called or an exception was thrown).</li> 088 * 089 * <li>If the system property <code>java.util.logging.config.class</code> 090 * is <em>not</em> set, the configuration parameters are read in from 091 * a file and passed to 092 * {@link #readConfiguration(java.io.InputStream)}. 093 * The name and location of this file are specified by the system 094 * property <code>java.util.logging.config.file</code>.</li> 095 * <li>If the system property <code>java.util.logging.config.file</code> 096 * is not set, however, the contents of the URL 097 * "{gnu.classpath.home.url}/logging.properties" are passed to 098 * {@link #readConfiguration(java.io.InputStream)}. 099 * Here, "{gnu.classpath.home.url}" stands for the value of 100 * the system property <code>gnu.classpath.home.url</code>.</li> 101 * </ul> 102 * 103 * <p>The <code>LogManager</code> has a level of <code>INFO</code> by 104 * default, and this will be inherited by <code>Logger</code>s unless they 105 * override it either by properties or programmatically. 106 * 107 * @author Sascha Brawer (brawer@acm.org) 108 */ 109public class LogManager 110{ 111 /** 112 * The object name for the logging management bean. 113 * @since 1.5 114 */ 115 public static final String LOGGING_MXBEAN_NAME 116 = "java.util.logging:type=Logging"; 117 118 /** 119 * The singleton LogManager instance. 120 */ 121 private static LogManager logManager; 122 123 /** 124 * The singleton logging bean. 125 */ 126 private static LoggingMXBean loggingBean; 127 128 /** 129 * The registered named loggers; maps the name of a Logger to 130 * a WeakReference to it. 131 */ 132 private Map<String, WeakReference<Logger>> loggers; 133 134 /** 135 * The properties for the logging framework which have been 136 * read in last. 137 */ 138 private Properties properties; 139 140 /** 141 * A delegate object that provides support for handling 142 * PropertyChangeEvents. The API specification does not 143 * mention which bean should be the source in the distributed 144 * PropertyChangeEvents, but Mauve test code has determined that 145 * the Sun J2SE 1.4 reference implementation uses the LogManager 146 * class object. This is somewhat strange, as the class object 147 * is not the bean with which listeners have to register, but 148 * there is no reason for the GNU Classpath implementation to 149 * behave differently from the reference implementation in 150 * this case. 151 */ 152 private final PropertyChangeSupport pcs = new PropertyChangeSupport( /* source bean */ 153 LogManager.class); 154 155 protected LogManager() 156 { 157 loggers = new HashMap(); 158 } 159 160 /** 161 * Returns the globally shared LogManager instance. 162 */ 163 public static synchronized LogManager getLogManager() 164 { 165 if (logManager == null) 166 { 167 logManager = makeLogManager(); 168 initLogManager(); 169 } 170 return logManager; 171 } 172 173 private static final String MANAGER_PROPERTY = "java.util.logging.manager"; 174 175 private static LogManager makeLogManager() 176 { 177 String managerClassName = SystemProperties.getProperty(MANAGER_PROPERTY); 178 LogManager manager = (LogManager) createInstance 179 (managerClassName, LogManager.class, MANAGER_PROPERTY); 180 if (manager == null) 181 manager = new LogManager(); 182 return manager; 183 } 184 185 private static final String CONFIG_PROPERTY = "java.util.logging.config.class"; 186 187 private static void initLogManager() 188 { 189 LogManager manager = getLogManager(); 190 Logger.root.setLevel(Level.INFO); 191 manager.addLogger(Logger.root); 192 193 /* The Javadoc description of the class explains 194 * what is going on here. 195 */ 196 Object configurator = createInstance(System.getProperty(CONFIG_PROPERTY), 197 /* must be instance of */ Object.class, 198 CONFIG_PROPERTY); 199 200 try 201 { 202 if (configurator == null) 203 manager.readConfiguration(); 204 } 205 catch (IOException ex) 206 { 207 /* FIXME: Is it ok to ignore exceptions here? */ 208 } 209 } 210 211 /** 212 * Registers a listener which will be notified when the 213 * logging properties are re-read. 214 * 215 * @param listener the event listener to register. 216 * @throws NullPointerException if the listener is {@code null}. 217 * @throws SecurityException if a security manager exists and the 218 * calling code does not have the permission 219 * {@code LoggingPermission("control")}. 220 */ 221 public synchronized void addPropertyChangeListener(PropertyChangeListener listener) 222 { 223 if (listener == null) 224 throw new NullPointerException("Attempt to add null property change listener"); 225 226 SecurityManager sm = System.getSecurityManager(); 227 if (sm != null) 228 sm.checkPermission(new LoggingPermission("control", null)); 229 230 pcs.addPropertyChangeListener(listener); 231 } 232 233 /** 234 * Unregisters a listener. 235 * 236 * If <code>listener</code> has not been registered previously, 237 * nothing happens. Also, no exception is thrown if 238 * <code>listener</code> is <code>null</code>. 239 * 240 * @param listener the listener to remove. 241 * @throws SecurityException if a security manager exists and the 242 * calling code does not have the permission 243 * {@code LoggingPermission("control")}. 244 */ 245 public synchronized void removePropertyChangeListener(PropertyChangeListener listener) 246 { 247 if (listener != null) 248 { 249 SecurityManager sm = System.getSecurityManager(); 250 if (sm != null) 251 sm.checkPermission(new LoggingPermission("control", null)); 252 253 pcs.removePropertyChangeListener(listener); 254 } 255 } 256 257 /** 258 * Adds a named logger. If a logger with the same name has 259 * already been registered, the method returns <code>false</code> 260 * without adding the logger. 261 * 262 * <p>The <code>LogManager</code> only keeps weak references 263 * to registered loggers. Therefore, names can become available 264 * after automatic garbage collection. 265 * 266 * @param logger the logger to be added. 267 * 268 * @return <code>true</code>if <code>logger</code> was added, 269 * <code>false</code> otherwise. 270 * 271 * @throws NullPointerException if <code>name</code> is 272 * <code>null</code>. 273 */ 274 public synchronized boolean addLogger(Logger logger) 275 { 276 /* To developers thinking about to remove the 'synchronized' 277 * declaration from this method: Please read the comment 278 * in java.util.logging.Logger.getLogger(String, String) 279 * and make sure that whatever you change wrt. synchronization 280 * does not endanger thread-safety of Logger.getLogger. 281 * The current implementation of Logger.getLogger assumes 282 * that LogManager does its synchronization on the globally 283 * shared instance of LogManager. 284 */ 285 String name; 286 WeakReference ref; 287 288 /* This will throw a NullPointerException if logger is null, 289 * as required by the API specification. 290 */ 291 name = logger.getName(); 292 293 ref = loggers.get(name); 294 if (ref != null) 295 { 296 if (ref.get() != null) 297 return false; 298 299 /* There has been a logger under this name in the past, 300 * but it has been garbage collected. 301 */ 302 loggers.remove(ref); 303 } 304 305 /* Adding a named logger requires a security permission. */ 306 if ((name != null) && ! name.equals("")) 307 checkAccess(); 308 309 Logger parent = findAncestor(logger); 310 loggers.put(name, new WeakReference<Logger>(logger)); 311 if (parent != logger.getParent()) 312 logger.setParent(parent); 313 314 // The level of the newly added logger must be specified. 315 // The easiest case is if there is a level for exactly this logger 316 // in the properties. If no such level exists the level needs to be 317 // searched along the hirachy. So if there is a new logger 'foo.blah.blub' 318 // and an existing parent logger 'foo' the properties 'foo.blah.blub.level' 319 // and 'foo.blah.level' need to be checked. If both do not exist in the 320 // properties the level of the new logger is set to 'null' (i.e. it uses the 321 // level of its parent 'foo'). 322 Level logLevel = logger.getLevel(); 323 String searchName = name; 324 String parentName = parent != null ? parent.getName() : ""; 325 while (logLevel == null && ! searchName.equals(parentName)) 326 { 327 logLevel = getLevelProperty(searchName + ".level", logLevel); 328 int index = searchName.lastIndexOf('.'); 329 if(index > -1) 330 searchName = searchName.substring(0,index); 331 else 332 searchName = ""; 333 } 334 logger.setLevel(logLevel); 335 336 /* It can happen that existing loggers should be children of 337 * the newly added logger. For example, assume that there 338 * already exist loggers under the names "", "foo", and "foo.bar.baz". 339 * When adding "foo.bar", the logger "foo.bar.baz" should change 340 * its parent to "foo.bar". 341 */ 342 for (Iterator iter = loggers.keySet().iterator(); iter.hasNext();) 343 { 344 Logger possChild = (Logger) ((WeakReference) loggers.get(iter.next())) 345 .get(); 346 if ((possChild == null) || (possChild == logger) 347 || (possChild.getParent() != parent)) 348 continue; 349 350 if (! possChild.getName().startsWith(name)) 351 continue; 352 353 if (possChild.getName().charAt(name.length()) != '.') 354 continue; 355 356 possChild.setParent(logger); 357 } 358 359 return true; 360 } 361 362 /** 363 * Finds the closest ancestor for a logger among the currently 364 * registered ones. For example, if the currently registered 365 * loggers have the names "", "foo", and "foo.bar", the result for 366 * "foo.bar.baz" will be the logger whose name is "foo.bar". 367 * 368 * @param child a logger for whose name no logger has been 369 * registered. 370 * 371 * @return the closest ancestor for <code>child</code>, 372 * or <code>null</code> if <code>child</code> 373 * is the root logger. 374 * 375 * @throws NullPointerException if <code>child</code> 376 * is <code>null</code>. 377 */ 378 private synchronized Logger findAncestor(Logger child) 379 { 380 String childName = child.getName(); 381 int childNameLength = childName.length(); 382 Logger best = Logger.root; 383 int bestNameLength = 0; 384 385 Logger cand; 386 int candNameLength; 387 388 if (child == Logger.root) 389 return null; 390 391 for (String candName : loggers.keySet()) 392 { 393 candNameLength = candName.length(); 394 395 if (candNameLength > bestNameLength 396 && childNameLength > candNameLength 397 && childName.startsWith(candName) 398 && childName.charAt(candNameLength) == '.') 399 { 400 cand = loggers.get(candName).get(); 401 if ((cand == null) || (cand == child)) 402 continue; 403 404 bestNameLength = candName.length(); 405 best = cand; 406 } 407 } 408 409 return best; 410 } 411 412 /** 413 * Returns a Logger given its name. 414 * 415 * @param name the name of the logger. 416 * 417 * @return a named Logger, or <code>null</code> if there is no 418 * logger with that name. 419 * 420 * @throw java.lang.NullPointerException if <code>name</code> 421 * is <code>null</code>. 422 */ 423 public synchronized Logger getLogger(String name) 424 { 425 WeakReference<Logger> ref; 426 427 /* Throw a NullPointerException if name is null. */ 428 name.getClass(); 429 430 ref = loggers.get(name); 431 if (ref != null) 432 return ref.get(); 433 else 434 return null; 435 } 436 437 /** 438 * Returns an Enumeration of currently registered Logger names. 439 * Since other threads can register loggers at any time, the 440 * result could be different any time this method is called. 441 * 442 * @return an Enumeration with the names of the currently 443 * registered Loggers. 444 */ 445 public synchronized Enumeration<String> getLoggerNames() 446 { 447 return Collections.enumeration(loggers.keySet()); 448 } 449 450 /** 451 * Resets the logging configuration by removing all handlers for 452 * registered named loggers and setting their level to <code>null</code>. 453 * The level of the root logger will be set to <code>Level.INFO</code>. 454 * 455 * @throws SecurityException if a security manager exists and 456 * the caller is not granted the permission to control 457 * the logging infrastructure. 458 */ 459 public synchronized void reset() throws SecurityException 460 { 461 /* Throw a SecurityException if the caller does not have the 462 * permission to control the logging infrastructure. 463 */ 464 checkAccess(); 465 466 properties = new Properties(); 467 468 Iterator<WeakReference<Logger>> iter = loggers.values().iterator(); 469 while (iter.hasNext()) 470 { 471 WeakReference<Logger> ref; 472 Logger logger; 473 474 ref = iter.next(); 475 if (ref != null) 476 { 477 logger = ref.get(); 478 479 if (logger == null) 480 iter.remove(); 481 else if (logger != Logger.root) 482 { 483 logger.resetLogger(); 484 logger.setLevel(null); 485 } 486 } 487 } 488 489 Logger.root.setLevel(Level.INFO); 490 Logger.root.resetLogger(); 491 } 492 493 /** 494 * Configures the logging framework by reading a configuration file. 495 * The name and location of this file are specified by the system 496 * property <code>java.util.logging.config.file</code>. If this 497 * property is not set, the URL 498 * "{gnu.classpath.home.url}/logging.properties" is taken, where 499 * "{gnu.classpath.home.url}" stands for the value of the system 500 * property <code>gnu.classpath.home.url</code>. 501 * 502 * <p>The task of configuring the framework is then delegated to 503 * {@link #readConfiguration(java.io.InputStream)}, which will 504 * notify registered listeners after having read the properties. 505 * 506 * @throws SecurityException if a security manager exists and 507 * the caller is not granted the permission to control 508 * the logging infrastructure, or if the caller is 509 * not granted the permission to read the configuration 510 * file. 511 * 512 * @throws IOException if there is a problem reading in the 513 * configuration file. 514 */ 515 public synchronized void readConfiguration() 516 throws IOException, SecurityException 517 { 518 String path; 519 InputStream inputStream; 520 521 path = System.getProperty("java.util.logging.config.file"); 522 if ((path == null) || (path.length() == 0)) 523 { 524 String url = (System.getProperty("gnu.classpath.home.url") 525 + "/logging.properties"); 526 try 527 { 528 inputStream = new URL(url).openStream(); 529 } 530 catch (Exception e) 531 { 532 inputStream=null; 533 } 534 535 // If no config file could be found use a default configuration. 536 if(inputStream == null) 537 { 538 String defaultConfig = "handlers = java.util.logging.ConsoleHandler \n" 539 + ".level=INFO \n"; 540 inputStream = new ByteArrayInputStream(defaultConfig.getBytes()); 541 } 542 } 543 else 544 inputStream = new java.io.FileInputStream(path); 545 546 try 547 { 548 readConfiguration(inputStream); 549 } 550 finally 551 { 552 // Close the stream in order to save 553 // resources such as file descriptors. 554 inputStream.close(); 555 } 556 } 557 558 public synchronized void readConfiguration(InputStream inputStream) 559 throws IOException, SecurityException 560 { 561 Properties newProperties; 562 Enumeration keys; 563 564 checkAccess(); 565 newProperties = new Properties(); 566 newProperties.load(inputStream); 567 reset(); 568 this.properties = newProperties; 569 keys = newProperties.propertyNames(); 570 571 while (keys.hasMoreElements()) 572 { 573 String key = ((String) keys.nextElement()).trim(); 574 String value = newProperties.getProperty(key); 575 576 if (value == null) 577 continue; 578 579 value = value.trim(); 580 581 if ("handlers".equals(key)) 582 { 583 // In Java 5 and earlier this was specified to be 584 // whitespace-separated, but in reality it also accepted 585 // commas (tomcat relied on this), and in Java 6 the 586 // documentation was updated to fit the implementation. 587 StringTokenizer tokenizer = new StringTokenizer(value, 588 " \t\n\r\f,"); 589 while (tokenizer.hasMoreTokens()) 590 { 591 String handlerName = tokenizer.nextToken(); 592 Handler handler = (Handler) 593 createInstance(handlerName, Handler.class, key); 594 // Tomcat also relies on the implementation ignoring 595 // items in 'handlers' which are not class names. 596 if (handler != null) 597 Logger.root.addHandler(handler); 598 } 599 } 600 601 if (key.endsWith(".level")) 602 { 603 String loggerName = key.substring(0, key.length() - 6); 604 Logger logger = getLogger(loggerName); 605 606 if (logger == null) 607 { 608 logger = Logger.getLogger(loggerName); 609 addLogger(logger); 610 } 611 Level level = null; 612 try 613 { 614 level = Level.parse(value); 615 } 616 catch (IllegalArgumentException e) 617 { 618 warn("bad level \'" + value + "\'", e); 619 } 620 if (level != null) 621 { 622 logger.setLevel(level); 623 } 624 continue; 625 } 626 } 627 628 /* The API specification does not talk about the 629 * property name that is distributed with the 630 * PropertyChangeEvent. With test code, it could 631 * be determined that the Sun J2SE 1.4 reference 632 * implementation uses null for the property name. 633 */ 634 pcs.firePropertyChange(null, null, null); 635 } 636 637 /** 638 * Returns the value of a configuration property as a String. 639 */ 640 public synchronized String getProperty(String name) 641 { 642 if (properties != null) 643 return properties.getProperty(name); 644 else 645 return null; 646 } 647 648 /** 649 * Returns the value of a configuration property as an integer. 650 * This function is a helper used by the Classpath implementation 651 * of java.util.logging, it is <em>not</em> specified in the 652 * logging API. 653 * 654 * @param name the name of the configuration property. 655 * 656 * @param defaultValue the value that will be returned if the 657 * property is not defined, or if its value is not an integer 658 * number. 659 */ 660 static int getIntProperty(String name, int defaultValue) 661 { 662 try 663 { 664 return Integer.parseInt(getLogManager().getProperty(name)); 665 } 666 catch (Exception ex) 667 { 668 return defaultValue; 669 } 670 } 671 672 /** 673 * Returns the value of a configuration property as an integer, 674 * provided it is inside the acceptable range. 675 * This function is a helper used by the Classpath implementation 676 * of java.util.logging, it is <em>not</em> specified in the 677 * logging API. 678 * 679 * @param name the name of the configuration property. 680 * 681 * @param minValue the lowest acceptable value. 682 * 683 * @param maxValue the highest acceptable value. 684 * 685 * @param defaultValue the value that will be returned if the 686 * property is not defined, or if its value is not an integer 687 * number, or if it is less than the minimum value, 688 * or if it is greater than the maximum value. 689 */ 690 static int getIntPropertyClamped(String name, int defaultValue, 691 int minValue, int maxValue) 692 { 693 int val = getIntProperty(name, defaultValue); 694 if ((val < minValue) || (val > maxValue)) 695 val = defaultValue; 696 return val; 697 } 698 699 /** 700 * Returns the value of a configuration property as a boolean. 701 * This function is a helper used by the Classpath implementation 702 * of java.util.logging, it is <em>not</em> specified in the 703 * logging API. 704 * 705 * @param name the name of the configuration property. 706 * 707 * @param defaultValue the value that will be returned if the 708 * property is not defined, or if its value is neither 709 * <code>"true"</code> nor <code>"false"</code>. 710 */ 711 static boolean getBooleanProperty(String name, boolean defaultValue) 712 { 713 try 714 { 715 return (Boolean.valueOf(getLogManager().getProperty(name))).booleanValue(); 716 } 717 catch (Exception ex) 718 { 719 return defaultValue; 720 } 721 } 722 723 /** 724 * Returns the value of a configuration property as a Level. 725 * This function is a helper used by the Classpath implementation 726 * of java.util.logging, it is <em>not</em> specified in the 727 * logging API. 728 * 729 * @param propertyName the name of the configuration property. 730 * 731 * @param defaultValue the value that will be returned if the 732 * property is not defined, or if 733 * {@link Level#parse(java.lang.String)} does not like 734 * the property value. 735 */ 736 static Level getLevelProperty(String propertyName, Level defaultValue) 737 { 738 try 739 { 740 String value = getLogManager().getProperty(propertyName); 741 if (value != null) 742 return Level.parse(getLogManager().getProperty(propertyName)); 743 else 744 return defaultValue; 745 } 746 catch (Exception ex) 747 { 748 return defaultValue; 749 } 750 } 751 752 /** 753 * Returns the value of a configuration property as a Class. 754 * This function is a helper used by the Classpath implementation 755 * of java.util.logging, it is <em>not</em> specified in the 756 * logging API. 757 * 758 * @param propertyName the name of the configuration property. 759 * 760 * @param defaultValue the value that will be returned if the 761 * property is not defined, or if it does not specify 762 * the name of a loadable class. 763 */ 764 static final Class getClassProperty(String propertyName, Class defaultValue) 765 { 766 String propertyValue = logManager.getProperty(propertyName); 767 768 if (propertyValue != null) 769 try 770 { 771 return locateClass(propertyValue); 772 } 773 catch (ClassNotFoundException e) 774 { 775 warn(propertyName + " = " + propertyValue, e); 776 } 777 778 return defaultValue; 779 } 780 781 static final Object getInstanceProperty(String propertyName, Class ofClass, 782 Class defaultClass) 783 { 784 Class klass = getClassProperty(propertyName, defaultClass); 785 if (klass == null) 786 return null; 787 788 try 789 { 790 Object obj = klass.newInstance(); 791 if (ofClass.isInstance(obj)) 792 return obj; 793 } 794 catch (InstantiationException e) 795 { 796 warn(propertyName + " = " + klass.getName(), e); 797 } 798 catch (IllegalAccessException e) 799 { 800 warn(propertyName + " = " + klass.getName(), e); 801 } 802 803 if (defaultClass == null) 804 return null; 805 806 try 807 { 808 return defaultClass.newInstance(); 809 } 810 catch (java.lang.InstantiationException ex) 811 { 812 throw new RuntimeException(ex.getMessage()); 813 } 814 catch (java.lang.IllegalAccessException ex) 815 { 816 throw new RuntimeException(ex.getMessage()); 817 } 818 } 819 820 /** 821 * An instance of <code>LoggingPermission("control")</code> 822 * that is shared between calls to <code>checkAccess()</code>. 823 */ 824 private static final LoggingPermission controlPermission = new LoggingPermission("control", 825 null); 826 827 /** 828 * Checks whether the current security context allows changing 829 * the configuration of the logging framework. For the security 830 * context to be trusted, it has to be granted 831 * a LoggingPermission("control"). 832 * 833 * @throws SecurityException if a security manager exists and 834 * the caller is not granted the permission to control 835 * the logging infrastructure. 836 */ 837 public void checkAccess() throws SecurityException 838 { 839 SecurityManager sm = System.getSecurityManager(); 840 if (sm != null) 841 sm.checkPermission(controlPermission); 842 } 843 844 /** 845 * Creates a new instance of a class specified by name and verifies 846 * that it is an instance (or subclass of) a given type. 847 * 848 * @param className the name of the class of which a new instance 849 * should be created. 850 * 851 * @param type the object created must be an instance of 852 * <code>type</code> or any subclass of <code>type</code> 853 * 854 * @param property the system property to reference in error 855 * messages 856 * 857 * @return the new instance, or <code>null</code> if 858 * <code>className</code> is <code>null</code>, if no class 859 * with that name could be found, if there was an error 860 * loading that class, or if the constructor of the class 861 * has thrown an exception. 862 */ 863 private static final Object createInstance(String className, Class type, 864 String property) 865 { 866 Class klass = null; 867 868 if ((className == null) || (className.length() == 0)) 869 return null; 870 871 try 872 { 873 klass = locateClass(className); 874 if (type.isAssignableFrom(klass)) 875 return klass.newInstance(); 876 warn(property, className, "not an instance of " + type.getName()); 877 } 878 catch (ClassNotFoundException e) 879 { 880 warn(property, className, "class not found", e); 881 } 882 catch (IllegalAccessException e) 883 { 884 warn(property, className, "illegal access", e); 885 } 886 catch (InstantiationException e) 887 { 888 warn(property, className, e); 889 } 890 catch (java.lang.LinkageError e) 891 { 892 warn(property, className, "linkage error", e); 893 } 894 895 return null; 896 } 897 898 private static final void warn(String property, String klass, Throwable t) 899 { 900 warn(property, klass, null, t); 901 } 902 903 private static final void warn(String property, String klass, String msg) 904 { 905 warn(property, klass, msg, null); 906 } 907 908 private static final void warn(String property, String klass, String msg, 909 Throwable t) 910 { 911 warn("error instantiating '" + klass + "' referenced by " + property + 912 (msg == null ? "" : ", " + msg), t); 913 } 914 915 /** 916 * All debug warnings go through this method. 917 */ 918 919 private static final void warn(String msg, Throwable t) 920 { 921 System.err.println("WARNING: " + msg); 922 if (t != null) 923 t.printStackTrace(System.err); 924 } 925 926 /** 927 * Locates a class by first checking the system class loader and 928 * then checking the context class loader. 929 * 930 * @param name the fully qualified name of the Class to locate 931 * @return Class the located Class 932 */ 933 934 private static Class locateClass(String name) throws ClassNotFoundException 935 { 936 ClassLoader loader = Thread.currentThread().getContextClassLoader(); 937 try 938 { 939 return Class.forName(name, true, loader); 940 } 941 catch (ClassNotFoundException e) 942 { 943 loader = ClassLoader.getSystemClassLoader(); 944 return Class.forName(name, true, loader); 945 } 946 } 947 948 /** 949 * Return the logging bean. There is a single logging bean per 950 * VM instance. 951 * @since 1.5 952 */ 953 public static synchronized LoggingMXBean getLoggingMXBean() 954 { 955 if (loggingBean == null) 956 { 957 loggingBean = new LoggingMXBean() 958 { 959 public String getLoggerLevel(String logger) 960 { 961 LogManager mgr = getLogManager(); 962 Logger l = mgr.getLogger(logger); 963 if (l == null) 964 return null; 965 Level lev = l.getLevel(); 966 if (lev == null) 967 return ""; 968 return lev.getName(); 969 } 970 971 public List getLoggerNames() 972 { 973 LogManager mgr = getLogManager(); 974 // This is inefficient, but perhaps better for maintenance. 975 return Collections.list(mgr.getLoggerNames()); 976 } 977 978 public String getParentLoggerName(String logger) 979 { 980 LogManager mgr = getLogManager(); 981 Logger l = mgr.getLogger(logger); 982 if (l == null) 983 return null; 984 l = l.getParent(); 985 if (l == null) 986 return ""; 987 return l.getName(); 988 } 989 990 public void setLoggerLevel(String logger, String level) 991 { 992 LogManager mgr = getLogManager(); 993 Logger l = mgr.getLogger(logger); 994 if (l == null) 995 throw new IllegalArgumentException("no logger named " + logger); 996 Level newLevel; 997 if (level == null) 998 newLevel = null; 999 else 1000 newLevel = Level.parse(level); 1001 l.setLevel(newLevel); 1002 } 1003 }; 1004 } 1005 return loggingBean; 1006 } 1007}