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 public synchronized void addPropertyChangeListener(PropertyChangeListener listener) 216 { 217 /* do not register null. */ 218 listener.getClass(); 219 220 pcs.addPropertyChangeListener(listener); 221 } 222 223 /** 224 * Unregisters a listener. 225 * 226 * If <code>listener</code> has not been registered previously, 227 * nothing happens. Also, no exception is thrown if 228 * <code>listener</code> is <code>null</code>. 229 */ 230 public synchronized void removePropertyChangeListener(PropertyChangeListener listener) 231 { 232 if (listener != null) 233 pcs.removePropertyChangeListener(listener); 234 } 235 236 /** 237 * Adds a named logger. If a logger with the same name has 238 * already been registered, the method returns <code>false</code> 239 * without adding the logger. 240 * 241 * <p>The <code>LogManager</code> only keeps weak references 242 * to registered loggers. Therefore, names can become available 243 * after automatic garbage collection. 244 * 245 * @param logger the logger to be added. 246 * 247 * @return <code>true</code>if <code>logger</code> was added, 248 * <code>false</code> otherwise. 249 * 250 * @throws NullPointerException if <code>name</code> is 251 * <code>null</code>. 252 */ 253 public synchronized boolean addLogger(Logger logger) 254 { 255 /* To developers thinking about to remove the 'synchronized' 256 * declaration from this method: Please read the comment 257 * in java.util.logging.Logger.getLogger(String, String) 258 * and make sure that whatever you change wrt. synchronization 259 * does not endanger thread-safety of Logger.getLogger. 260 * The current implementation of Logger.getLogger assumes 261 * that LogManager does its synchronization on the globally 262 * shared instance of LogManager. 263 */ 264 String name; 265 WeakReference ref; 266 267 /* This will throw a NullPointerException if logger is null, 268 * as required by the API specification. 269 */ 270 name = logger.getName(); 271 272 ref = loggers.get(name); 273 if (ref != null) 274 { 275 if (ref.get() != null) 276 return false; 277 278 /* There has been a logger under this name in the past, 279 * but it has been garbage collected. 280 */ 281 loggers.remove(ref); 282 } 283 284 /* Adding a named logger requires a security permission. */ 285 if ((name != null) && ! name.equals("")) 286 checkAccess(); 287 288 Logger parent = findAncestor(logger); 289 loggers.put(name, new WeakReference<Logger>(logger)); 290 if (parent != logger.getParent()) 291 logger.setParent(parent); 292 293 // The level of the newly added logger must be specified. 294 // The easiest case is if there is a level for exactly this logger 295 // in the properties. If no such level exists the level needs to be 296 // searched along the hirachy. So if there is a new logger 'foo.blah.blub' 297 // and an existing parent logger 'foo' the properties 'foo.blah.blub.level' 298 // and 'foo.blah.level' need to be checked. If both do not exist in the 299 // properties the level of the new logger is set to 'null' (i.e. it uses the 300 // level of its parent 'foo'). 301 Level logLevel = logger.getLevel(); 302 String searchName = name; 303 String parentName = parent != null ? parent.getName() : ""; 304 while (logLevel == null && ! searchName.equals(parentName)) 305 { 306 logLevel = getLevelProperty(searchName + ".level", logLevel); 307 int index = searchName.lastIndexOf('.'); 308 if(index > -1) 309 searchName = searchName.substring(0,index); 310 else 311 searchName = ""; 312 } 313 logger.setLevel(logLevel); 314 315 /* It can happen that existing loggers should be children of 316 * the newly added logger. For example, assume that there 317 * already exist loggers under the names "", "foo", and "foo.bar.baz". 318 * When adding "foo.bar", the logger "foo.bar.baz" should change 319 * its parent to "foo.bar". 320 */ 321 for (Iterator iter = loggers.keySet().iterator(); iter.hasNext();) 322 { 323 Logger possChild = (Logger) ((WeakReference) loggers.get(iter.next())) 324 .get(); 325 if ((possChild == null) || (possChild == logger) 326 || (possChild.getParent() != parent)) 327 continue; 328 329 if (! possChild.getName().startsWith(name)) 330 continue; 331 332 if (possChild.getName().charAt(name.length()) != '.') 333 continue; 334 335 possChild.setParent(logger); 336 } 337 338 return true; 339 } 340 341 /** 342 * Finds the closest ancestor for a logger among the currently 343 * registered ones. For example, if the currently registered 344 * loggers have the names "", "foo", and "foo.bar", the result for 345 * "foo.bar.baz" will be the logger whose name is "foo.bar". 346 * 347 * @param child a logger for whose name no logger has been 348 * registered. 349 * 350 * @return the closest ancestor for <code>child</code>, 351 * or <code>null</code> if <code>child</code> 352 * is the root logger. 353 * 354 * @throws NullPointerException if <code>child</code> 355 * is <code>null</code>. 356 */ 357 private synchronized Logger findAncestor(Logger child) 358 { 359 String childName = child.getName(); 360 int childNameLength = childName.length(); 361 Logger best = Logger.root; 362 int bestNameLength = 0; 363 364 Logger cand; 365 int candNameLength; 366 367 if (child == Logger.root) 368 return null; 369 370 for (String candName : loggers.keySet()) 371 { 372 candNameLength = candName.length(); 373 374 if (candNameLength > bestNameLength 375 && childNameLength > candNameLength 376 && childName.startsWith(candName) 377 && childName.charAt(candNameLength) == '.') 378 { 379 cand = loggers.get(candName).get(); 380 if ((cand == null) || (cand == child)) 381 continue; 382 383 bestNameLength = candName.length(); 384 best = cand; 385 } 386 } 387 388 return best; 389 } 390 391 /** 392 * Returns a Logger given its name. 393 * 394 * @param name the name of the logger. 395 * 396 * @return a named Logger, or <code>null</code> if there is no 397 * logger with that name. 398 * 399 * @throw java.lang.NullPointerException if <code>name</code> 400 * is <code>null</code>. 401 */ 402 public synchronized Logger getLogger(String name) 403 { 404 WeakReference<Logger> ref; 405 406 /* Throw a NullPointerException if name is null. */ 407 name.getClass(); 408 409 ref = loggers.get(name); 410 if (ref != null) 411 return ref.get(); 412 else 413 return null; 414 } 415 416 /** 417 * Returns an Enumeration of currently registered Logger names. 418 * Since other threads can register loggers at any time, the 419 * result could be different any time this method is called. 420 * 421 * @return an Enumeration with the names of the currently 422 * registered Loggers. 423 */ 424 public synchronized Enumeration<String> getLoggerNames() 425 { 426 return Collections.enumeration(loggers.keySet()); 427 } 428 429 /** 430 * Resets the logging configuration by removing all handlers for 431 * registered named loggers and setting their level to <code>null</code>. 432 * The level of the root logger will be set to <code>Level.INFO</code>. 433 * 434 * @throws SecurityException if a security manager exists and 435 * the caller is not granted the permission to control 436 * the logging infrastructure. 437 */ 438 public synchronized void reset() throws SecurityException 439 { 440 /* Throw a SecurityException if the caller does not have the 441 * permission to control the logging infrastructure. 442 */ 443 checkAccess(); 444 445 properties = new Properties(); 446 447 Iterator<WeakReference<Logger>> iter = loggers.values().iterator(); 448 while (iter.hasNext()) 449 { 450 WeakReference<Logger> ref; 451 Logger logger; 452 453 ref = iter.next(); 454 if (ref != null) 455 { 456 logger = ref.get(); 457 458 if (logger == null) 459 iter.remove(); 460 else if (logger != Logger.root) 461 { 462 logger.resetLogger(); 463 logger.setLevel(null); 464 } 465 } 466 } 467 468 Logger.root.setLevel(Level.INFO); 469 Logger.root.resetLogger(); 470 } 471 472 /** 473 * Configures the logging framework by reading a configuration file. 474 * The name and location of this file are specified by the system 475 * property <code>java.util.logging.config.file</code>. If this 476 * property is not set, the URL 477 * "{gnu.classpath.home.url}/logging.properties" is taken, where 478 * "{gnu.classpath.home.url}" stands for the value of the system 479 * property <code>gnu.classpath.home.url</code>. 480 * 481 * <p>The task of configuring the framework is then delegated to 482 * {@link #readConfiguration(java.io.InputStream)}, which will 483 * notify registered listeners after having read the properties. 484 * 485 * @throws SecurityException if a security manager exists and 486 * the caller is not granted the permission to control 487 * the logging infrastructure, or if the caller is 488 * not granted the permission to read the configuration 489 * file. 490 * 491 * @throws IOException if there is a problem reading in the 492 * configuration file. 493 */ 494 public synchronized void readConfiguration() 495 throws IOException, SecurityException 496 { 497 String path; 498 InputStream inputStream; 499 500 path = System.getProperty("java.util.logging.config.file"); 501 if ((path == null) || (path.length() == 0)) 502 { 503 String url = (System.getProperty("gnu.classpath.home.url") 504 + "/logging.properties"); 505 try 506 { 507 inputStream = new URL(url).openStream(); 508 } 509 catch (Exception e) 510 { 511 inputStream=null; 512 } 513 514 // If no config file could be found use a default configuration. 515 if(inputStream == null) 516 { 517 String defaultConfig = "handlers = java.util.logging.ConsoleHandler \n" 518 + ".level=INFO \n"; 519 inputStream = new ByteArrayInputStream(defaultConfig.getBytes()); 520 } 521 } 522 else 523 inputStream = new java.io.FileInputStream(path); 524 525 try 526 { 527 readConfiguration(inputStream); 528 } 529 finally 530 { 531 // Close the stream in order to save 532 // resources such as file descriptors. 533 inputStream.close(); 534 } 535 } 536 537 public synchronized void readConfiguration(InputStream inputStream) 538 throws IOException, SecurityException 539 { 540 Properties newProperties; 541 Enumeration keys; 542 543 checkAccess(); 544 newProperties = new Properties(); 545 newProperties.load(inputStream); 546 reset(); 547 this.properties = newProperties; 548 keys = newProperties.propertyNames(); 549 550 while (keys.hasMoreElements()) 551 { 552 String key = ((String) keys.nextElement()).trim(); 553 String value = newProperties.getProperty(key); 554 555 if (value == null) 556 continue; 557 558 value = value.trim(); 559 560 if ("handlers".equals(key)) 561 { 562 // In Java 5 and earlier this was specified to be 563 // whitespace-separated, but in reality it also accepted 564 // commas (tomcat relied on this), and in Java 6 the 565 // documentation was updated to fit the implementation. 566 StringTokenizer tokenizer = new StringTokenizer(value, 567 " \t\n\r\f,"); 568 while (tokenizer.hasMoreTokens()) 569 { 570 String handlerName = tokenizer.nextToken(); 571 Handler handler = (Handler) 572 createInstance(handlerName, Handler.class, key); 573 // Tomcat also relies on the implementation ignoring 574 // items in 'handlers' which are not class names. 575 if (handler != null) 576 Logger.root.addHandler(handler); 577 } 578 } 579 580 if (key.endsWith(".level")) 581 { 582 String loggerName = key.substring(0, key.length() - 6); 583 Logger logger = getLogger(loggerName); 584 585 if (logger == null) 586 { 587 logger = Logger.getLogger(loggerName); 588 addLogger(logger); 589 } 590 Level level = null; 591 try 592 { 593 level = Level.parse(value); 594 } 595 catch (IllegalArgumentException e) 596 { 597 warn("bad level \'" + value + "\'", e); 598 } 599 if (level != null) 600 { 601 logger.setLevel(level); 602 } 603 continue; 604 } 605 } 606 607 /* The API specification does not talk about the 608 * property name that is distributed with the 609 * PropertyChangeEvent. With test code, it could 610 * be determined that the Sun J2SE 1.4 reference 611 * implementation uses null for the property name. 612 */ 613 pcs.firePropertyChange(null, null, null); 614 } 615 616 /** 617 * Returns the value of a configuration property as a String. 618 */ 619 public synchronized String getProperty(String name) 620 { 621 if (properties != null) 622 return properties.getProperty(name); 623 else 624 return null; 625 } 626 627 /** 628 * Returns the value of a configuration property as an integer. 629 * This function is a helper used by the Classpath implementation 630 * of java.util.logging, it is <em>not</em> specified in the 631 * logging API. 632 * 633 * @param name the name of the configuration property. 634 * 635 * @param defaultValue the value that will be returned if the 636 * property is not defined, or if its value is not an integer 637 * number. 638 */ 639 static int getIntProperty(String name, int defaultValue) 640 { 641 try 642 { 643 return Integer.parseInt(getLogManager().getProperty(name)); 644 } 645 catch (Exception ex) 646 { 647 return defaultValue; 648 } 649 } 650 651 /** 652 * Returns the value of a configuration property as an integer, 653 * provided it is inside the acceptable range. 654 * This function is a helper used by the Classpath implementation 655 * of java.util.logging, it is <em>not</em> specified in the 656 * logging API. 657 * 658 * @param name the name of the configuration property. 659 * 660 * @param minValue the lowest acceptable value. 661 * 662 * @param maxValue the highest acceptable value. 663 * 664 * @param defaultValue the value that will be returned if the 665 * property is not defined, or if its value is not an integer 666 * number, or if it is less than the minimum value, 667 * or if it is greater than the maximum value. 668 */ 669 static int getIntPropertyClamped(String name, int defaultValue, 670 int minValue, int maxValue) 671 { 672 int val = getIntProperty(name, defaultValue); 673 if ((val < minValue) || (val > maxValue)) 674 val = defaultValue; 675 return val; 676 } 677 678 /** 679 * Returns the value of a configuration property as a boolean. 680 * This function is a helper used by the Classpath implementation 681 * of java.util.logging, it is <em>not</em> specified in the 682 * logging API. 683 * 684 * @param name the name of the configuration property. 685 * 686 * @param defaultValue the value that will be returned if the 687 * property is not defined, or if its value is neither 688 * <code>"true"</code> nor <code>"false"</code>. 689 */ 690 static boolean getBooleanProperty(String name, boolean defaultValue) 691 { 692 try 693 { 694 return (Boolean.valueOf(getLogManager().getProperty(name))).booleanValue(); 695 } 696 catch (Exception ex) 697 { 698 return defaultValue; 699 } 700 } 701 702 /** 703 * Returns the value of a configuration property as a Level. 704 * This function is a helper used by the Classpath implementation 705 * of java.util.logging, it is <em>not</em> specified in the 706 * logging API. 707 * 708 * @param propertyName the name of the configuration property. 709 * 710 * @param defaultValue the value that will be returned if the 711 * property is not defined, or if 712 * {@link Level#parse(java.lang.String)} does not like 713 * the property value. 714 */ 715 static Level getLevelProperty(String propertyName, Level defaultValue) 716 { 717 try 718 { 719 String value = getLogManager().getProperty(propertyName); 720 if (value != null) 721 return Level.parse(getLogManager().getProperty(propertyName)); 722 else 723 return defaultValue; 724 } 725 catch (Exception ex) 726 { 727 return defaultValue; 728 } 729 } 730 731 /** 732 * Returns the value of a configuration property as a Class. 733 * This function is a helper used by the Classpath implementation 734 * of java.util.logging, it is <em>not</em> specified in the 735 * logging API. 736 * 737 * @param propertyName the name of the configuration property. 738 * 739 * @param defaultValue the value that will be returned if the 740 * property is not defined, or if it does not specify 741 * the name of a loadable class. 742 */ 743 static final Class getClassProperty(String propertyName, Class defaultValue) 744 { 745 String propertyValue = logManager.getProperty(propertyName); 746 747 if (propertyValue != null) 748 try 749 { 750 return locateClass(propertyValue); 751 } 752 catch (ClassNotFoundException e) 753 { 754 warn(propertyName + " = " + propertyValue, e); 755 } 756 757 return defaultValue; 758 } 759 760 static final Object getInstanceProperty(String propertyName, Class ofClass, 761 Class defaultClass) 762 { 763 Class klass = getClassProperty(propertyName, defaultClass); 764 if (klass == null) 765 return null; 766 767 try 768 { 769 Object obj = klass.newInstance(); 770 if (ofClass.isInstance(obj)) 771 return obj; 772 } 773 catch (InstantiationException e) 774 { 775 warn(propertyName + " = " + klass.getName(), e); 776 } 777 catch (IllegalAccessException e) 778 { 779 warn(propertyName + " = " + klass.getName(), e); 780 } 781 782 if (defaultClass == null) 783 return null; 784 785 try 786 { 787 return defaultClass.newInstance(); 788 } 789 catch (java.lang.InstantiationException ex) 790 { 791 throw new RuntimeException(ex.getMessage()); 792 } 793 catch (java.lang.IllegalAccessException ex) 794 { 795 throw new RuntimeException(ex.getMessage()); 796 } 797 } 798 799 /** 800 * An instance of <code>LoggingPermission("control")</code> 801 * that is shared between calls to <code>checkAccess()</code>. 802 */ 803 private static final LoggingPermission controlPermission = new LoggingPermission("control", 804 null); 805 806 /** 807 * Checks whether the current security context allows changing 808 * the configuration of the logging framework. For the security 809 * context to be trusted, it has to be granted 810 * a LoggingPermission("control"). 811 * 812 * @throws SecurityException if a security manager exists and 813 * the caller is not granted the permission to control 814 * the logging infrastructure. 815 */ 816 public void checkAccess() throws SecurityException 817 { 818 SecurityManager sm = System.getSecurityManager(); 819 if (sm != null) 820 sm.checkPermission(controlPermission); 821 } 822 823 /** 824 * Creates a new instance of a class specified by name and verifies 825 * that it is an instance (or subclass of) a given type. 826 * 827 * @param className the name of the class of which a new instance 828 * should be created. 829 * 830 * @param type the object created must be an instance of 831 * <code>type</code> or any subclass of <code>type</code> 832 * 833 * @param property the system property to reference in error 834 * messages 835 * 836 * @return the new instance, or <code>null</code> if 837 * <code>className</code> is <code>null</code>, if no class 838 * with that name could be found, if there was an error 839 * loading that class, or if the constructor of the class 840 * has thrown an exception. 841 */ 842 private static final Object createInstance(String className, Class type, 843 String property) 844 { 845 Class klass = null; 846 847 if ((className == null) || (className.length() == 0)) 848 return null; 849 850 try 851 { 852 klass = locateClass(className); 853 if (type.isAssignableFrom(klass)) 854 return klass.newInstance(); 855 warn(property, className, "not an instance of " + type.getName()); 856 } 857 catch (ClassNotFoundException e) 858 { 859 warn(property, className, "class not found", e); 860 } 861 catch (IllegalAccessException e) 862 { 863 warn(property, className, "illegal access", e); 864 } 865 catch (InstantiationException e) 866 { 867 warn(property, className, e); 868 } 869 catch (java.lang.LinkageError e) 870 { 871 warn(property, className, "linkage error", e); 872 } 873 874 return null; 875 } 876 877 private static final void warn(String property, String klass, Throwable t) 878 { 879 warn(property, klass, null, t); 880 } 881 882 private static final void warn(String property, String klass, String msg) 883 { 884 warn(property, klass, msg, null); 885 } 886 887 private static final void warn(String property, String klass, String msg, 888 Throwable t) 889 { 890 warn("error instantiating '" + klass + "' referenced by " + property + 891 (msg == null ? "" : ", " + msg), t); 892 } 893 894 /** 895 * All debug warnings go through this method. 896 */ 897 898 private static final void warn(String msg, Throwable t) 899 { 900 System.err.println("WARNING: " + msg); 901 if (t != null) 902 t.printStackTrace(System.err); 903 } 904 905 /** 906 * Locates a class by first checking the system class loader and 907 * then checking the context class loader. 908 * 909 * @param name the fully qualified name of the Class to locate 910 * @return Class the located Class 911 */ 912 913 private static Class locateClass(String name) throws ClassNotFoundException 914 { 915 // GCJ LOCAL 916 // Unfortunately this can be called during bootstrap when 917 // Thread.currentThread() will return null. 918 // See bug #27658 919 Thread t = Thread.currentThread(); 920 ClassLoader loader = (t == null) ? null : t.getContextClassLoader(); 921 try 922 { 923 return Class.forName(name, true, loader); 924 } 925 catch (ClassNotFoundException e) 926 { 927 loader = ClassLoader.getSystemClassLoader(); 928 return Class.forName(name, true, loader); 929 } 930 } 931 932 /** 933 * Return the logging bean. There is a single logging bean per 934 * VM instance. 935 * @since 1.5 936 */ 937 public static synchronized LoggingMXBean getLoggingMXBean() 938 { 939 if (loggingBean == null) 940 { 941 loggingBean = new LoggingMXBean() 942 { 943 public String getLoggerLevel(String logger) 944 { 945 LogManager mgr = getLogManager(); 946 Logger l = mgr.getLogger(logger); 947 if (l == null) 948 return null; 949 Level lev = l.getLevel(); 950 if (lev == null) 951 return ""; 952 return lev.getName(); 953 } 954 955 public List getLoggerNames() 956 { 957 LogManager mgr = getLogManager(); 958 // This is inefficient, but perhaps better for maintenance. 959 return Collections.list(mgr.getLoggerNames()); 960 } 961 962 public String getParentLoggerName(String logger) 963 { 964 LogManager mgr = getLogManager(); 965 Logger l = mgr.getLogger(logger); 966 if (l == null) 967 return null; 968 l = l.getParent(); 969 if (l == null) 970 return ""; 971 return l.getName(); 972 } 973 974 public void setLoggerLevel(String logger, String level) 975 { 976 LogManager mgr = getLogManager(); 977 Logger l = mgr.getLogger(logger); 978 if (l == null) 979 throw new IllegalArgumentException("no logger named " + logger); 980 Level newLevel; 981 if (level == null) 982 newLevel = null; 983 else 984 newLevel = Level.parse(level); 985 l.setLevel(newLevel); 986 } 987 }; 988 } 989 return loggingBean; 990 } 991}