1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.logging;
18
19
20 import java.io.BufferedReader;
21 import java.io.FileOutputStream;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.InputStreamReader;
25 import java.io.PrintStream;
26 import java.lang.reflect.InvocationTargetException;
27 import java.lang.reflect.Method;
28 import java.net.URL;
29 import java.security.AccessController;
30 import java.security.PrivilegedAction;
31 import java.util.Enumeration;
32 import java.util.Hashtable;
33 import java.util.Properties;
34
35
36 /***
37 * <p>Factory for creating {@link Log} instances, with discovery and
38 * configuration features similar to that employed by standard Java APIs
39 * such as JAXP.</p>
40 *
41 * <p><strong>IMPLEMENTATION NOTE</strong> - This implementation is heavily
42 * based on the SAXParserFactory and DocumentBuilderFactory implementations
43 * (corresponding to the JAXP pluggability APIs) found in Apache Xerces.</p>
44 *
45 * @author Craig R. McClanahan
46 * @author Costin Manolache
47 * @author Richard A. Sitze
48 * @version $Revision: 399431 $ $Date: 2006-05-03 21:58:34 +0100 (Wed, 03 May 2006) $
49 */
50
51 public abstract class LogFactory {
52
53
54
55
56 /***
57 * The name (<code>priority</code>) of the key in the config file used to
58 * specify the priority of that particular config file. The associated value
59 * is a floating-point number; higher values take priority over lower values.
60 */
61 public static final String PRIORITY_KEY = "priority";
62
63 /***
64 * The name (<code>use_tccl</code>) of the key in the config file used
65 * to specify whether logging classes should be loaded via the thread
66 * context class loader (TCCL), or not. By default, the TCCL is used.
67 */
68 public static final String TCCL_KEY = "use_tccl";
69
70 /***
71 * The name (<code>org.apache.commons.logging.LogFactory</code>) of the property
72 * used to identify the LogFactory implementation
73 * class name. This can be used as a system property, or as an entry in a
74 * configuration properties file.
75 */
76 public static final String FACTORY_PROPERTY =
77 "org.apache.commons.logging.LogFactory";
78
79 /***
80 * The fully qualified class name of the fallback <code>LogFactory</code>
81 * implementation class to use, if no other can be found.
82 */
83 public static final String FACTORY_DEFAULT =
84 "org.apache.commons.logging.impl.LogFactoryImpl";
85
86 /***
87 * The name (<code>commons-logging.properties</code>) of the properties file to search for.
88 */
89 public static final String FACTORY_PROPERTIES =
90 "commons-logging.properties";
91
92 /***
93 * JDK1.3+ <a href="http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#Service%20Provider">
94 * 'Service Provider' specification</a>.
95 *
96 */
97 protected static final String SERVICE_ID =
98 "META-INF/services/org.apache.commons.logging.LogFactory";
99
100 /***
101 * The name (<code>org.apache.commons.logging.diagnostics.dest</code>)
102 * of the property used to enable internal commons-logging
103 * diagnostic output, in order to get information on what logging
104 * implementations are being discovered, what classloaders they
105 * are loaded through, etc.
106 * <p>
107 * If a system property of this name is set then the value is
108 * assumed to be the name of a file. The special strings
109 * STDOUT or STDERR (case-sensitive) indicate output to
110 * System.out and System.err respectively.
111 * <p>
112 * Diagnostic logging should be used only to debug problematic
113 * configurations and should not be set in normal production use.
114 */
115 public static final String DIAGNOSTICS_DEST_PROPERTY =
116 "org.apache.commons.logging.diagnostics.dest";
117
118 /***
119 * When null (the usual case), no diagnostic output will be
120 * generated by LogFactory or LogFactoryImpl. When non-null,
121 * interesting events will be written to the specified object.
122 */
123 private static PrintStream diagnosticsStream = null;
124
125 /***
126 * A string that gets prefixed to every message output by the
127 * logDiagnostic method, so that users can clearly see which
128 * LogFactory class is generating the output.
129 */
130 private static String diagnosticPrefix;
131
132 /***
133 * <p>Setting this system property
134 * (<code>org.apache.commons.logging.LogFactory.HashtableImpl</code>)
135 * value allows the <code>Hashtable</code> used to store
136 * classloaders to be substituted by an alternative implementation.
137 * </p>
138 * <p>
139 * <strong>Note:</strong> <code>LogFactory</code> will print:
140 * <code><pre>
141 * [ERROR] LogFactory: Load of custom hashtable failed</em>
142 * </pre></code>
143 * to system error and then continue using a standard Hashtable.
144 * </p>
145 * <p>
146 * <strong>Usage:</strong> Set this property when Java is invoked
147 * and <code>LogFactory</code> will attempt to load a new instance
148 * of the given implementation class.
149 * For example, running the following ant scriplet:
150 * <code><pre>
151 * <java classname="${test.runner}" fork="yes" failonerror="${test.failonerror}">
152 * ...
153 * <sysproperty
154 * key="org.apache.commons.logging.LogFactory.HashtableImpl"
155 * value="org.apache.commons.logging.AltHashtable"/>
156 * </java>
157 * </pre></code>
158 * will mean that <code>LogFactory</code> will load an instance of
159 * <code>org.apache.commons.logging.AltHashtable</code>.
160 * </p>
161 * <p>
162 * A typical use case is to allow a custom
163 * Hashtable implementation using weak references to be substituted.
164 * This will allow classloaders to be garbage collected without
165 * the need to release them (on 1.3+ JVMs only, of course ;)
166 * </p>
167 */
168 public static final String HASHTABLE_IMPLEMENTATION_PROPERTY =
169 "org.apache.commons.logging.LogFactory.HashtableImpl";
170 /*** Name used to load the weak hashtable implementation by names */
171 private static final String WEAK_HASHTABLE_CLASSNAME =
172 "org.apache.commons.logging.impl.WeakHashtable";
173
174 /***
175 * A reference to the classloader that loaded this class. This is the
176 * same as LogFactory.class.getClassLoader(). However computing this
177 * value isn't quite as simple as that, as we potentially need to use
178 * AccessControllers etc. It's more efficient to compute it once and
179 * cache it here.
180 */
181 private static ClassLoader thisClassLoader;
182
183
184
185
186 /***
187 * Protected constructor that is not available for public use.
188 */
189 protected LogFactory() {
190 }
191
192
193
194
195 /***
196 * Return the configuration attribute with the specified name (if any),
197 * or <code>null</code> if there is no such attribute.
198 *
199 * @param name Name of the attribute to return
200 */
201 public abstract Object getAttribute(String name);
202
203
204 /***
205 * Return an array containing the names of all currently defined
206 * configuration attributes. If there are no such attributes, a zero
207 * length array is returned.
208 */
209 public abstract String[] getAttributeNames();
210
211
212 /***
213 * Convenience method to derive a name from the specified class and
214 * call <code>getInstance(String)</code> with it.
215 *
216 * @param clazz Class for which a suitable Log name will be derived
217 *
218 * @exception LogConfigurationException if a suitable <code>Log</code>
219 * instance cannot be returned
220 */
221 public abstract Log getInstance(Class clazz)
222 throws LogConfigurationException;
223
224
225 /***
226 * <p>Construct (if necessary) and return a <code>Log</code> instance,
227 * using the factory's current set of configuration attributes.</p>
228 *
229 * <p><strong>NOTE</strong> - Depending upon the implementation of
230 * the <code>LogFactory</code> you are using, the <code>Log</code>
231 * instance you are returned may or may not be local to the current
232 * application, and may or may not be returned again on a subsequent
233 * call with the same name argument.</p>
234 *
235 * @param name Logical name of the <code>Log</code> instance to be
236 * returned (the meaning of this name is only known to the underlying
237 * logging implementation that is being wrapped)
238 *
239 * @exception LogConfigurationException if a suitable <code>Log</code>
240 * instance cannot be returned
241 */
242 public abstract Log getInstance(String name)
243 throws LogConfigurationException;
244
245
246 /***
247 * Release any internal references to previously created {@link Log}
248 * instances returned by this factory. This is useful in environments
249 * like servlet containers, which implement application reloading by
250 * throwing away a ClassLoader. Dangling references to objects in that
251 * class loader would prevent garbage collection.
252 */
253 public abstract void release();
254
255
256 /***
257 * Remove any configuration attribute associated with the specified name.
258 * If there is no such attribute, no action is taken.
259 *
260 * @param name Name of the attribute to remove
261 */
262 public abstract void removeAttribute(String name);
263
264
265 /***
266 * Set the configuration attribute with the specified name. Calling
267 * this with a <code>null</code> value is equivalent to calling
268 * <code>removeAttribute(name)</code>.
269 *
270 * @param name Name of the attribute to set
271 * @param value Value of the attribute to set, or <code>null</code>
272 * to remove any setting for this attribute
273 */
274 public abstract void setAttribute(String name, Object value);
275
276
277
278
279
280 /***
281 * The previously constructed <code>LogFactory</code> instances, keyed by
282 * the <code>ClassLoader</code> with which it was created.
283 */
284 protected static Hashtable factories = null;
285
286 /***
287 * Prevously constructed <code>LogFactory</code> instance as in the
288 * <code>factories</code> map, but for the case where
289 * <code>getClassLoader</code> returns <code>null</code>.
290 * This can happen when:
291 * <ul>
292 * <li>using JDK1.1 and the calling code is loaded via the system
293 * classloader (very common)</li>
294 * <li>using JDK1.2+ and the calling code is loaded via the boot
295 * classloader (only likely for embedded systems work).</li>
296 * </ul>
297 * Note that <code>factories</code> is a <i>Hashtable</i> (not a HashMap),
298 * and hashtables don't allow null as a key.
299 */
300 protected static LogFactory nullClassLoaderFactory = null;
301
302 /***
303 * Create the hashtable which will be used to store a map of
304 * (context-classloader -> logfactory-object). Version 1.2+ of Java
305 * supports "weak references", allowing a custom Hashtable class
306 * to be used which uses only weak references to its keys. Using weak
307 * references can fix memory leaks on webapp unload in some cases (though
308 * not all). Version 1.1 of Java does not support weak references, so we
309 * must dynamically determine which we are using. And just for fun, this
310 * code also supports the ability for a system property to specify an
311 * arbitrary Hashtable implementation name.
312 * <p>
313 * Note that the correct way to ensure no memory leaks occur is to ensure
314 * that LogFactory.release(contextClassLoader) is called whenever a
315 * webapp is undeployed.
316 */
317 private static final Hashtable createFactoryStore() {
318 Hashtable result = null;
319 String storeImplementationClass
320 = System.getProperty(HASHTABLE_IMPLEMENTATION_PROPERTY);
321 if (storeImplementationClass == null) {
322 storeImplementationClass = WEAK_HASHTABLE_CLASSNAME;
323 }
324 try {
325 Class implementationClass = Class.forName(storeImplementationClass);
326 result = (Hashtable) implementationClass.newInstance();
327
328 } catch (Throwable t) {
329
330 if (!WEAK_HASHTABLE_CLASSNAME.equals(storeImplementationClass)) {
331
332 if (isDiagnosticsEnabled()) {
333
334 logDiagnostic("[ERROR] LogFactory: Load of custom hashtable failed");
335 } else {
336
337
338 System.err.println("[ERROR] LogFactory: Load of custom hashtable failed");
339 }
340 }
341 }
342 if (result == null) {
343 result = new Hashtable();
344 }
345 return result;
346 }
347
348
349
350
351 /***
352 * <p>Construct (if necessary) and return a <code>LogFactory</code>
353 * instance, using the following ordered lookup procedure to determine
354 * the name of the implementation class to be loaded.</p>
355 * <ul>
356 * <li>The <code>org.apache.commons.logging.LogFactory</code> system
357 * property.</li>
358 * <li>The JDK 1.3 Service Discovery mechanism</li>
359 * <li>Use the properties file <code>commons-logging.properties</code>
360 * file, if found in the class path of this class. The configuration
361 * file is in standard <code>java.util.Properties</code> format and
362 * contains the fully qualified name of the implementation class
363 * with the key being the system property defined above.</li>
364 * <li>Fall back to a default implementation class
365 * (<code>org.apache.commons.logging.impl.LogFactoryImpl</code>).</li>
366 * </ul>
367 *
368 * <p><em>NOTE</em> - If the properties file method of identifying the
369 * <code>LogFactory</code> implementation class is utilized, all of the
370 * properties defined in this file will be set as configuration attributes
371 * on the corresponding <code>LogFactory</code> instance.</p>
372 *
373 * <p><em>NOTE</em> - In a multithreaded environment it is possible
374 * that two different instances will be returned for the same
375 * classloader environment.
376 * </p>
377 *
378 * @exception LogConfigurationException if the implementation class is not
379 * available or cannot be instantiated.
380 */
381 public static LogFactory getFactory() throws LogConfigurationException {
382
383 ClassLoader contextClassLoader = getContextClassLoader();
384
385 if (contextClassLoader == null) {
386
387
388
389 if (isDiagnosticsEnabled()) {
390 logDiagnostic("Context classloader is null.");
391 }
392 }
393
394
395 LogFactory factory = getCachedFactory(contextClassLoader);
396 if (factory != null) {
397 return factory;
398 }
399
400 if (isDiagnosticsEnabled()) {
401 logDiagnostic(
402 "[LOOKUP] LogFactory implementation requested for the first time for context classloader "
403 + objectId(contextClassLoader));
404 logHierarchy("[LOOKUP] ", contextClassLoader);
405 }
406
407
408
409
410
411
412
413
414
415
416
417 Properties props = getConfigurationFile(contextClassLoader, FACTORY_PROPERTIES);
418
419
420
421 ClassLoader baseClassLoader = contextClassLoader;
422 if (props != null) {
423 String useTCCLStr = props.getProperty(TCCL_KEY);
424 if (useTCCLStr != null) {
425
426
427 if (Boolean.valueOf(useTCCLStr).booleanValue() == false) {
428
429
430
431
432
433
434
435 baseClassLoader = thisClassLoader;
436 }
437 }
438 }
439
440
441
442 if (isDiagnosticsEnabled()) {
443 logDiagnostic(
444 "[LOOKUP] Looking for system property [" + FACTORY_PROPERTY
445 + "] to define the LogFactory subclass to use...");
446 }
447
448 try {
449 String factoryClass = System.getProperty(FACTORY_PROPERTY);
450 if (factoryClass != null) {
451 if (isDiagnosticsEnabled()) {
452 logDiagnostic(
453 "[LOOKUP] Creating an instance of LogFactory class '" + factoryClass
454 + "' as specified by system property " + FACTORY_PROPERTY);
455 }
456
457 factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);
458 } else {
459 if (isDiagnosticsEnabled()) {
460 logDiagnostic(
461 "[LOOKUP] No system property [" + FACTORY_PROPERTY
462 + "] defined.");
463 }
464 }
465 } catch (SecurityException e) {
466 if (isDiagnosticsEnabled()) {
467 logDiagnostic(
468 "[LOOKUP] A security exception occurred while trying to create an"
469 + " instance of the custom factory class"
470 + ": [" + e.getMessage().trim()
471 + "]. Trying alternative implementations...");
472 }
473 ;
474 } catch(RuntimeException e) {
475
476
477
478
479
480 if (isDiagnosticsEnabled()) {
481 logDiagnostic(
482 "[LOOKUP] An exception occurred while trying to create an"
483 + " instance of the custom factory class"
484 + ": [" + e.getMessage().trim()
485 + "] as specified by a system property.");
486 }
487 throw e;
488 }
489
490
491
492
493
494
495
496
497 if (factory == null) {
498 if (isDiagnosticsEnabled()) {
499 logDiagnostic(
500 "[LOOKUP] Looking for a resource file of name [" + SERVICE_ID
501 + "] to define the LogFactory subclass to use...");
502 }
503 try {
504 InputStream is = getResourceAsStream(contextClassLoader,
505 SERVICE_ID);
506
507 if( is != null ) {
508
509
510 BufferedReader rd;
511 try {
512 rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
513 } catch (java.io.UnsupportedEncodingException e) {
514 rd = new BufferedReader(new InputStreamReader(is));
515 }
516
517 String factoryClassName = rd.readLine();
518 rd.close();
519
520 if (factoryClassName != null &&
521 ! "".equals(factoryClassName)) {
522 if (isDiagnosticsEnabled()) {
523 logDiagnostic(
524 "[LOOKUP] Creating an instance of LogFactory class " + factoryClassName
525 + " as specified by file '" + SERVICE_ID
526 + "' which was present in the path of the context"
527 + " classloader.");
528 }
529 factory = newFactory(factoryClassName, baseClassLoader, contextClassLoader );
530 }
531 } else {
532
533 if (isDiagnosticsEnabled()) {
534 logDiagnostic(
535 "[LOOKUP] No resource file with name '" + SERVICE_ID
536 + "' found.");
537 }
538 }
539 } catch( Exception ex ) {
540
541
542
543 if (isDiagnosticsEnabled()) {
544 logDiagnostic(
545 "[LOOKUP] A security exception occurred while trying to create an"
546 + " instance of the custom factory class"
547 + ": [" + ex.getMessage().trim()
548 + "]. Trying alternative implementations...");
549 }
550 ;
551 }
552 }
553
554
555
556
557 if (factory == null) {
558 if (props != null) {
559 if (isDiagnosticsEnabled()) {
560 logDiagnostic(
561 "[LOOKUP] Looking in properties file for entry with key '"
562 + FACTORY_PROPERTY
563 + "' to define the LogFactory subclass to use...");
564 }
565 String factoryClass = props.getProperty(FACTORY_PROPERTY);
566 if (factoryClass != null) {
567 if (isDiagnosticsEnabled()) {
568 logDiagnostic(
569 "[LOOKUP] Properties file specifies LogFactory subclass '"
570 + factoryClass + "'");
571 }
572 factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);
573
574
575 } else {
576 if (isDiagnosticsEnabled()) {
577 logDiagnostic(
578 "[LOOKUP] Properties file has no entry specifying LogFactory subclass.");
579 }
580 }
581 } else {
582 if (isDiagnosticsEnabled()) {
583 logDiagnostic(
584 "[LOOKUP] No properties file available to determine"
585 + " LogFactory subclass from..");
586 }
587 }
588 }
589
590
591
592
593 if (factory == null) {
594 if (isDiagnosticsEnabled()) {
595 logDiagnostic(
596 "[LOOKUP] Loading the default LogFactory implementation '" + FACTORY_DEFAULT
597 + "' via the same classloader that loaded this LogFactory"
598 + " class (ie not looking in the context classloader).");
599 }
600
601
602
603
604
605
606
607
608
609
610 factory = newFactory(FACTORY_DEFAULT, thisClassLoader, contextClassLoader);
611 }
612
613 if (factory != null) {
614 /***
615 * Always cache using context class loader.
616 */
617 cacheFactory(contextClassLoader, factory);
618
619 if( props!=null ) {
620 Enumeration names = props.propertyNames();
621 while (names.hasMoreElements()) {
622 String name = (String) names.nextElement();
623 String value = props.getProperty(name);
624 factory.setAttribute(name, value);
625 }
626 }
627 }
628
629 return factory;
630 }
631
632
633 /***
634 * Convenience method to return a named logger, without the application
635 * having to care about factories.
636 *
637 * @param clazz Class from which a log name will be derived
638 *
639 * @exception LogConfigurationException if a suitable <code>Log</code>
640 * instance cannot be returned
641 */
642 public static Log getLog(Class clazz)
643 throws LogConfigurationException {
644
645 return (getFactory().getInstance(clazz));
646
647 }
648
649
650 /***
651 * Convenience method to return a named logger, without the application
652 * having to care about factories.
653 *
654 * @param name Logical name of the <code>Log</code> instance to be
655 * returned (the meaning of this name is only known to the underlying
656 * logging implementation that is being wrapped)
657 *
658 * @exception LogConfigurationException if a suitable <code>Log</code>
659 * instance cannot be returned
660 */
661 public static Log getLog(String name)
662 throws LogConfigurationException {
663
664 return (getFactory().getInstance(name));
665
666 }
667
668
669 /***
670 * Release any internal references to previously created {@link LogFactory}
671 * instances that have been associated with the specified class loader
672 * (if any), after calling the instance method <code>release()</code> on
673 * each of them.
674 *
675 * @param classLoader ClassLoader for which to release the LogFactory
676 */
677 public static void release(ClassLoader classLoader) {
678
679 if (isDiagnosticsEnabled()) {
680 logDiagnostic("Releasing factory for classloader " + objectId(classLoader));
681 }
682 synchronized (factories) {
683 if (classLoader == null) {
684 if (nullClassLoaderFactory != null) {
685 nullClassLoaderFactory.release();
686 nullClassLoaderFactory = null;
687 }
688 } else {
689 LogFactory factory = (LogFactory) factories.get(classLoader);
690 if (factory != null) {
691 factory.release();
692 factories.remove(classLoader);
693 }
694 }
695 }
696
697 }
698
699
700 /***
701 * Release any internal references to previously created {@link LogFactory}
702 * instances, after calling the instance method <code>release()</code> on
703 * each of them. This is useful in environments like servlet containers,
704 * which implement application reloading by throwing away a ClassLoader.
705 * Dangling references to objects in that class loader would prevent
706 * garbage collection.
707 */
708 public static void releaseAll() {
709
710 if (isDiagnosticsEnabled()) {
711 logDiagnostic("Releasing factory for all classloaders.");
712 }
713 synchronized (factories) {
714 Enumeration elements = factories.elements();
715 while (elements.hasMoreElements()) {
716 LogFactory element = (LogFactory) elements.nextElement();
717 element.release();
718 }
719 factories.clear();
720
721 if (nullClassLoaderFactory != null) {
722 nullClassLoaderFactory.release();
723 nullClassLoaderFactory = null;
724 }
725 }
726
727 }
728
729
730
731
732 /***
733 * Safely get access to the classloader for the specified class.
734 * <p>
735 * Theoretically, calling getClassLoader can throw a security exception,
736 * and so should be done under an AccessController in order to provide
737 * maximum flexibility. However in practice people don't appear to use
738 * security policies that forbid getClassLoader calls. So for the moment
739 * all code is written to call this method rather than Class.getClassLoader,
740 * so that we could put AccessController stuff in this method without any
741 * disruption later if we need to.
742 * <p>
743 * Even when using an AccessController, however, this method can still
744 * throw SecurityException. Commons-logging basically relies on the
745 * ability to access classloaders, ie a policy that forbids all
746 * classloader access will also prevent commons-logging from working:
747 * currently this method will throw an exception preventing the entire app
748 * from starting up. Maybe it would be good to detect this situation and
749 * just disable all commons-logging? Not high priority though - as stated
750 * above, security policies that prevent classloader access aren't common.
751 *
752 * @since 1.1
753 */
754 protected static ClassLoader getClassLoader(Class clazz) {
755 try {
756 return clazz.getClassLoader();
757 } catch(SecurityException ex) {
758 if (isDiagnosticsEnabled()) {
759 logDiagnostic(
760 "Unable to get classloader for class '" + clazz
761 + "' due to security restrictions - " + ex.getMessage());
762 }
763 throw ex;
764 }
765 }
766
767 /***
768 * Calls LogFactory.directGetContextClassLoader under the control of an
769 * AccessController class. This means that java code running under a
770 * security manager that forbids access to ClassLoaders will still work
771 * if this class is given appropriate privileges, even when the caller
772 * doesn't have such privileges. Without using an AccessController, the
773 * the entire call stack must have the privilege before the call is
774 * allowed.
775 *
776 * @return the context classloader associated with the current thread,
777 * or null if security doesn't allow it.
778 *
779 * @throws LogConfigurationException if there was some weird error while
780 * attempting to get the context classloader.
781 *
782 * @throws SecurityException if the current java security policy doesn't
783 * allow this class to access the context classloader.
784 */
785 protected static ClassLoader getContextClassLoader()
786 throws LogConfigurationException {
787
788 return (ClassLoader)AccessController.doPrivileged(
789 new PrivilegedAction() {
790 public Object run() {
791 return directGetContextClassLoader();
792 }
793 });
794 }
795
796 /***
797 * Return the thread context class loader if available; otherwise return
798 * null.
799 * <p>
800 * Most/all code should call getContextClassLoader rather than calling
801 * this method directly.
802 * <p>
803 * The thread context class loader is available for JDK 1.2
804 * or later, if certain security conditions are met.
805 * <p>
806 * Note that no internal logging is done within this method because
807 * this method is called every time LogFactory.getLogger() is called,
808 * and we don't want too much output generated here.
809 *
810 * @exception LogConfigurationException if a suitable class loader
811 * cannot be identified.
812 *
813 * @exception SecurityException if the java security policy forbids
814 * access to the context classloader from one of the classes in the
815 * current call stack.
816 * @since 1.1
817 */
818 protected static ClassLoader directGetContextClassLoader()
819 throws LogConfigurationException
820 {
821 ClassLoader classLoader = null;
822
823 try {
824
825 Method method = Thread.class.getMethod("getContextClassLoader",
826 (Class[]) null);
827
828
829 try {
830 classLoader = (ClassLoader)method.invoke(Thread.currentThread(),
831 (Object[]) null);
832 } catch (IllegalAccessException e) {
833 throw new LogConfigurationException
834 ("Unexpected IllegalAccessException", e);
835 } catch (InvocationTargetException e) {
836 /***
837 * InvocationTargetException is thrown by 'invoke' when
838 * the method being invoked (getContextClassLoader) throws
839 * an exception.
840 *
841 * getContextClassLoader() throws SecurityException when
842 * the context class loader isn't an ancestor of the
843 * calling class's class loader, or if security
844 * permissions are restricted.
845 *
846 * In the first case (not related), we want to ignore and
847 * keep going. We cannot help but also ignore the second
848 * with the logic below, but other calls elsewhere (to
849 * obtain a class loader) will trigger this exception where
850 * we can make a distinction.
851 */
852 if (e.getTargetException() instanceof SecurityException) {
853 ;
854 } else {
855
856
857 throw new LogConfigurationException
858 ("Unexpected InvocationTargetException", e.getTargetException());
859 }
860 }
861 } catch (NoSuchMethodException e) {
862
863 classLoader = getClassLoader(LogFactory.class);
864
865
866
867
868
869
870
871
872
873
874
875
876
877 }
878
879
880 return classLoader;
881 }
882
883 /***
884 * Check cached factories (keyed by contextClassLoader)
885 *
886 * @param contextClassLoader is the context classloader associated
887 * with the current thread. This allows separate LogFactory objects
888 * per component within a container, provided each component has
889 * a distinct context classloader set. This parameter may be null
890 * in JDK1.1, and in embedded systems where jcl-using code is
891 * placed in the bootclasspath.
892 *
893 * @return the factory associated with the specified classloader if
894 * one has previously been created, or null if this is the first time
895 * we have seen this particular classloader.
896 */
897 private static LogFactory getCachedFactory(ClassLoader contextClassLoader)
898 {
899 LogFactory factory = null;
900
901 if (contextClassLoader == null) {
902
903
904
905
906 factory = nullClassLoaderFactory;
907 } else {
908 factory = (LogFactory) factories.get(contextClassLoader);
909 }
910
911 return factory;
912 }
913
914 /***
915 * Remember this factory, so later calls to LogFactory.getCachedFactory
916 * can return the previously created object (together with all its
917 * cached Log objects).
918 *
919 * @param classLoader should be the current context classloader. Note that
920 * this can be null under some circumstances; this is ok.
921 *
922 * @param factory should be the factory to cache. This should never be null.
923 */
924 private static void cacheFactory(ClassLoader classLoader, LogFactory factory)
925 {
926
927
928
929 if (factory != null) {
930 if (classLoader == null) {
931 nullClassLoaderFactory = factory;
932 } else {
933 factories.put(classLoader, factory);
934 }
935 }
936 }
937
938 /***
939 * Return a new instance of the specified <code>LogFactory</code>
940 * implementation class, loaded by the specified class loader.
941 * If that fails, try the class loader used to load this
942 * (abstract) LogFactory.
943 * <p>
944 * <h2>ClassLoader conflicts</h2>
945 * Note that there can be problems if the specified ClassLoader is not the
946 * same as the classloader that loaded this class, ie when loading a
947 * concrete LogFactory subclass via a context classloader.
948 * <p>
949 * The problem is the same one that can occur when loading a concrete Log
950 * subclass via a context classloader.
951 * <p>
952 * The problem occurs when code running in the context classloader calls
953 * class X which was loaded via a parent classloader, and class X then calls
954 * LogFactory.getFactory (either directly or via LogFactory.getLog). Because
955 * class X was loaded via the parent, it binds to LogFactory loaded via
956 * the parent. When the code in this method finds some LogFactoryYYYY
957 * class in the child (context) classloader, and there also happens to be a
958 * LogFactory class defined in the child classloader, then LogFactoryYYYY
959 * will be bound to LogFactory@childloader. It cannot be cast to
960 * LogFactory@parentloader, ie this method cannot return the object as
961 * the desired type. Note that it doesn't matter if the LogFactory class
962 * in the child classloader is identical to the LogFactory class in the
963 * parent classloader, they are not compatible.
964 * <p>
965 * The solution taken here is to simply print out an error message when
966 * this occurs then throw an exception. The deployer of the application
967 * must ensure they remove all occurrences of the LogFactory class from
968 * the child classloader in order to resolve the issue. Note that they
969 * do not have to move the custom LogFactory subclass; that is ok as
970 * long as the only LogFactory class it can find to bind to is in the
971 * parent classloader.
972 * <p>
973 * @param factoryClass Fully qualified name of the <code>LogFactory</code>
974 * implementation class
975 * @param classLoader ClassLoader from which to load this class
976 * @param contextClassLoader is the context that this new factory will
977 * manage logging for.
978 *
979 * @exception LogConfigurationException if a suitable instance
980 * cannot be created
981 * @since 1.1
982 */
983 protected static LogFactory newFactory(final String factoryClass,
984 final ClassLoader classLoader,
985 final ClassLoader contextClassLoader)
986 throws LogConfigurationException
987 {
988
989
990
991 Object result = AccessController.doPrivileged(
992 new PrivilegedAction() {
993 public Object run() {
994 return createFactory(factoryClass, classLoader);
995 }
996 });
997
998 if (result instanceof LogConfigurationException) {
999 LogConfigurationException ex = (LogConfigurationException) result;
1000 if (isDiagnosticsEnabled()) {
1001 logDiagnostic(
1002 "An error occurred while loading the factory class:"
1003 + ex.getMessage());
1004 }
1005 throw ex;
1006 }
1007 if (isDiagnosticsEnabled()) {
1008 logDiagnostic(
1009 "Created object " + objectId(result)
1010 + " to manage classloader " + objectId(contextClassLoader));
1011 }
1012 return (LogFactory)result;
1013 }
1014
1015 /***
1016 * Method provided for backwards compatibility; see newFactory version that
1017 * takes 3 parameters.
1018 * <p>
1019 * This method would only ever be called in some rather odd situation.
1020 * Note that this method is static, so overriding in a subclass doesn't
1021 * have any effect unless this method is called from a method in that
1022 * subclass. However this method only makes sense to use from the
1023 * getFactory method, and as that is almost always invoked via
1024 * LogFactory.getFactory, any custom definition in a subclass would be
1025 * pointless. Only a class with a custom getFactory method, then invoked
1026 * directly via CustomFactoryImpl.getFactory or similar would ever call
1027 * this. Anyway, it's here just in case, though the "managed class loader"
1028 * value output to the diagnostics will not report the correct value.
1029 */
1030 protected static LogFactory newFactory(final String factoryClass,
1031 final ClassLoader classLoader) {
1032 return newFactory(factoryClass, classLoader, null);
1033 }
1034
1035 /***
1036 * Implements the operations described in the javadoc for newFactory.
1037 *
1038 * @param factoryClass
1039 *
1040 * @param classLoader used to load the specified factory class. This is
1041 * expected to be either the TCCL or the classloader which loaded this
1042 * class. Note that the classloader which loaded this class might be
1043 * "null" (ie the bootloader) for embedded systems.
1044 *
1045 * @return either a LogFactory object or a LogConfigurationException object.
1046 * @since 1.1
1047 */
1048 protected static Object createFactory(String factoryClass, ClassLoader classLoader) {
1049
1050
1051
1052 Class logFactoryClass = null;
1053 try {
1054 if (classLoader != null) {
1055 try {
1056
1057
1058
1059
1060 logFactoryClass = classLoader.loadClass(factoryClass);
1061 if (LogFactory.class.isAssignableFrom(logFactoryClass)) {
1062 if (isDiagnosticsEnabled()) {
1063 logDiagnostic(
1064 "Loaded class " + logFactoryClass.getName()
1065 + " from classloader " + objectId(classLoader));
1066 }
1067 } else {
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079 if (isDiagnosticsEnabled()) {
1080 logDiagnostic(
1081 "Factory class " + logFactoryClass.getName()
1082 + " loaded from classloader " + objectId(logFactoryClass.getClassLoader())
1083 + " does not extend '" + LogFactory.class.getName()
1084 + "' as loaded by this classloader.");
1085 logHierarchy("[BAD CL TREE] ", classLoader);
1086 }
1087 }
1088
1089 return (LogFactory) logFactoryClass.newInstance();
1090
1091 } catch (ClassNotFoundException ex) {
1092 if (classLoader == thisClassLoader) {
1093
1094 if (isDiagnosticsEnabled()) {
1095 logDiagnostic(
1096 "Unable to locate any class called '" + factoryClass
1097 + "' via classloader " + objectId(classLoader));
1098 }
1099 throw ex;
1100 }
1101
1102 } catch (NoClassDefFoundError e) {
1103 if (classLoader == thisClassLoader) {
1104
1105 if (isDiagnosticsEnabled()) {
1106 logDiagnostic(
1107 "Class '" + factoryClass + "' cannot be loaded"
1108 + " via classloader " + objectId(classLoader)
1109 + " - it depends on some other class that cannot"
1110 + " be found.");
1111 }
1112 throw e;
1113 }
1114
1115 } catch(ClassCastException e) {
1116 if (classLoader == thisClassLoader) {
1117
1118
1119
1120
1121
1122 final boolean implementsLogFactory = implementsLogFactory(logFactoryClass);
1123
1124
1125
1126
1127
1128
1129 String msg =
1130 "The application has specified that a custom LogFactory implementation should be used but " +
1131 "Class '" + factoryClass + "' cannot be converted to '"
1132 + LogFactory.class.getName() + "'. ";
1133 if (implementsLogFactory) {
1134 msg = msg + "The conflict is caused by the presence of multiple LogFactory classes in incompatible classloaders. " +
1135 "Background can be found in http://jakarta.apache.org/commons/logging/tech.html. " +
1136 "If you have not explicitly specified a custom LogFactory then it is likely that " +
1137 "the container has set one without your knowledge. " +
1138 "In this case, consider using the commons-logging-adapters.jar file or " +
1139 "specifying the standard LogFactory from the command line. ";
1140 } else {
1141 msg = msg + "Please check the custom implementation. ";
1142 }
1143 msg = msg + "Help can be found @http://jakarta.apache.org/commons/logging/troubleshooting.html.";
1144
1145 if (isDiagnosticsEnabled()) {
1146 logDiagnostic(msg);
1147 }
1148
1149 ClassCastException ex = new ClassCastException(msg);
1150 throw ex;
1151 }
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165 }
1166 }
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184 if (isDiagnosticsEnabled()) {
1185 logDiagnostic(
1186 "Unable to load factory class via classloader "
1187 + objectId(classLoader)
1188 + " - trying the classloader associated with this LogFactory.");
1189 }
1190 logFactoryClass = Class.forName(factoryClass);
1191 return (LogFactory) logFactoryClass.newInstance();
1192 } catch (Exception e) {
1193
1194 if (isDiagnosticsEnabled()) {
1195 logDiagnostic("Unable to create LogFactory instance.");
1196 }
1197 if (logFactoryClass != null
1198 && !LogFactory.class.isAssignableFrom(logFactoryClass)) {
1199
1200 return new LogConfigurationException(
1201 "The chosen LogFactory implementation does not extend LogFactory."
1202 + " Please check your configuration.",
1203 e);
1204 }
1205 return new LogConfigurationException(e);
1206 }
1207 }
1208
1209 /***
1210 * Determines whether the given class actually implements <code>LogFactory</code>.
1211 * Diagnostic information is also logged.
1212 * <p>
1213 * <strong>Usage:</strong> to diagnose whether a classloader conflict is the cause
1214 * of incompatibility. The test used is whether the class is assignable from
1215 * the <code>LogFactory</code> class loaded by the class's classloader.
1216 * @param logFactoryClass <code>Class</code> which may implement <code>LogFactory</code>
1217 * @return true if the <code>logFactoryClass</code> does extend
1218 * <code>LogFactory</code> when that class is loaded via the same
1219 * classloader that loaded the <code>logFactoryClass</code>.
1220 */
1221 private static boolean implementsLogFactory(Class logFactoryClass) {
1222 boolean implementsLogFactory = false;
1223 if (logFactoryClass != null) {
1224 try {
1225 ClassLoader logFactoryClassLoader = logFactoryClass.getClassLoader();
1226 if (logFactoryClassLoader == null) {
1227 logDiagnostic("[CUSTOM LOG FACTORY] was loaded by the boot classloader");
1228 } else {
1229 logHierarchy("[CUSTOM LOG FACTORY] ", logFactoryClassLoader);
1230 Class factoryFromCustomLoader
1231 = Class.forName("org.apache.commons.logging.LogFactory", false, logFactoryClassLoader);
1232 implementsLogFactory = factoryFromCustomLoader.isAssignableFrom(logFactoryClass);
1233 if (implementsLogFactory) {
1234 logDiagnostic("[CUSTOM LOG FACTORY] " + logFactoryClass.getName()
1235 + " implements LogFactory but was loaded by an incompatible classloader.");
1236 } else {
1237 logDiagnostic("[CUSTOM LOG FACTORY] " + logFactoryClass.getName()
1238 + " does not implement LogFactory.");
1239 }
1240 }
1241 } catch (SecurityException e) {
1242
1243
1244
1245
1246
1247 logDiagnostic("[CUSTOM LOG FACTORY] SecurityException thrown whilst trying to determine whether " +
1248 "the compatibility was caused by a classloader conflict: "
1249 + e.getMessage());
1250 } catch (LinkageError e) {
1251
1252
1253
1254
1255
1256
1257 logDiagnostic("[CUSTOM LOG FACTORY] LinkageError thrown whilst trying to determine whether " +
1258 "the compatibility was caused by a classloader conflict: "
1259 + e.getMessage());
1260 } catch (ClassNotFoundException e) {
1261
1262
1263
1264
1265
1266
1267
1268 logDiagnostic("[CUSTOM LOG FACTORY] LogFactory class cannot be loaded by classloader which loaded the " +
1269 "custom LogFactory implementation. Is the custom factory in the right classloader?");
1270 }
1271 }
1272 return implementsLogFactory;
1273 }
1274
1275 /***
1276 * Applets may run in an environment where accessing resources of a loader is
1277 * a secure operation, but where the commons-logging library has explicitly
1278 * been granted permission for that operation. In this case, we need to
1279 * run the operation using an AccessController.
1280 */
1281 private static InputStream getResourceAsStream(final ClassLoader loader,
1282 final String name)
1283 {
1284 return (InputStream)AccessController.doPrivileged(
1285 new PrivilegedAction() {
1286 public Object run() {
1287 if (loader != null) {
1288 return loader.getResourceAsStream(name);
1289 } else {
1290 return ClassLoader.getSystemResourceAsStream(name);
1291 }
1292 }
1293 });
1294 }
1295
1296 /***
1297 * Given a filename, return an enumeration of URLs pointing to
1298 * all the occurrences of that filename in the classpath.
1299 * <p>
1300 * This is just like ClassLoader.getResources except that the
1301 * operation is done under an AccessController so that this method will
1302 * succeed when this jarfile is privileged but the caller is not.
1303 * This method must therefore remain private to avoid security issues.
1304 * <p>
1305 * If no instances are found, an Enumeration is returned whose
1306 * hasMoreElements method returns false (ie an "empty" enumeration).
1307 * If resources could not be listed for some reason, null is returned.
1308 */
1309 private static Enumeration getResources(final ClassLoader loader,
1310 final String name)
1311 {
1312 PrivilegedAction action =
1313 new PrivilegedAction() {
1314 public Object run() {
1315 try {
1316 if (loader != null) {
1317 return loader.getResources(name);
1318 } else {
1319 return ClassLoader.getSystemResources(name);
1320 }
1321 } catch(IOException e) {
1322 if (isDiagnosticsEnabled()) {
1323 logDiagnostic(
1324 "Exception while trying to find configuration file "
1325 + name + ":" + e.getMessage());
1326 }
1327 return null;
1328 } catch(NoSuchMethodError e) {
1329
1330
1331
1332 return null;
1333 }
1334 }
1335 };
1336 Object result = AccessController.doPrivileged(action);
1337 return (Enumeration) result;
1338 }
1339
1340 /***
1341 * Given a URL that refers to a .properties file, load that file.
1342 * This is done under an AccessController so that this method will
1343 * succeed when this jarfile is privileged but the caller is not.
1344 * This method must therefore remain private to avoid security issues.
1345 * <p>
1346 * Null is returned if the URL cannot be opened.
1347 */
1348 private static Properties getProperties(final URL url) {
1349 PrivilegedAction action =
1350 new PrivilegedAction() {
1351 public Object run() {
1352 try {
1353 InputStream stream = url.openStream();
1354 if (stream != null) {
1355 Properties props = new Properties();
1356 props.load(stream);
1357 stream.close();
1358 return props;
1359 }
1360 } catch(IOException e) {
1361 if (isDiagnosticsEnabled()) {
1362 logDiagnostic("Unable to read URL " + url);
1363 }
1364 }
1365
1366 return null;
1367 }
1368 };
1369 return (Properties) AccessController.doPrivileged(action);
1370 }
1371
1372 /***
1373 * Locate a user-provided configuration file.
1374 * <p>
1375 * The classpath of the specified classLoader (usually the context classloader)
1376 * is searched for properties files of the specified name. If none is found,
1377 * null is returned. If more than one is found, then the file with the greatest
1378 * value for its PRIORITY property is returned. If multiple files have the
1379 * same PRIORITY value then the first in the classpath is returned.
1380 * <p>
1381 * This differs from the 1.0.x releases; those always use the first one found.
1382 * However as the priority is a new field, this change is backwards compatible.
1383 * <p>
1384 * The purpose of the priority field is to allow a webserver administrator to
1385 * override logging settings in all webapps by placing a commons-logging.properties
1386 * file in a shared classpath location with a priority > 0; this overrides any
1387 * commons-logging.properties files without priorities which are in the
1388 * webapps. Webapps can also use explicit priorities to override a configuration
1389 * file in the shared classpath if needed.
1390 */
1391 private static final Properties getConfigurationFile(
1392 ClassLoader classLoader, String fileName) {
1393
1394 Properties props = null;
1395 double priority = 0.0;
1396 URL propsUrl = null;
1397 try {
1398 Enumeration urls = getResources(classLoader, fileName);
1399
1400 if (urls == null) {
1401 return null;
1402 }
1403
1404 while (urls.hasMoreElements()) {
1405 URL url = (URL) urls.nextElement();
1406
1407 Properties newProps = getProperties(url);
1408 if (newProps != null) {
1409 if (props == null) {
1410 propsUrl = url;
1411 props = newProps;
1412 String priorityStr = props.getProperty(PRIORITY_KEY);
1413 priority = 0.0;
1414 if (priorityStr != null) {
1415 priority = Double.parseDouble(priorityStr);
1416 }
1417
1418 if (isDiagnosticsEnabled()) {
1419 logDiagnostic(
1420 "[LOOKUP] Properties file found at '" + url + "'"
1421 + " with priority " + priority);
1422 }
1423 } else {
1424 String newPriorityStr = newProps.getProperty(PRIORITY_KEY);
1425 double newPriority = 0.0;
1426 if (newPriorityStr != null) {
1427 newPriority = Double.parseDouble(newPriorityStr);
1428 }
1429
1430 if (newPriority > priority) {
1431 if (isDiagnosticsEnabled()) {
1432 logDiagnostic(
1433 "[LOOKUP] Properties file at '" + url + "'"
1434 + " with priority " + newPriority
1435 + " overrides file at '" + propsUrl + "'"
1436 + " with priority " + priority);
1437 }
1438
1439 propsUrl = url;
1440 props = newProps;
1441 priority = newPriority;
1442 } else {
1443 if (isDiagnosticsEnabled()) {
1444 logDiagnostic(
1445 "[LOOKUP] Properties file at '" + url + "'"
1446 + " with priority " + newPriority
1447 + " does not override file at '" + propsUrl + "'"
1448 + " with priority " + priority);
1449 }
1450 }
1451 }
1452
1453 }
1454 }
1455 } catch (SecurityException e) {
1456 if (isDiagnosticsEnabled()) {
1457 logDiagnostic("SecurityException thrown while trying to find/read config files.");
1458 }
1459 }
1460
1461 if (isDiagnosticsEnabled()) {
1462 if (props == null) {
1463 logDiagnostic(
1464 "[LOOKUP] No properties file of name '" + fileName
1465 + "' found.");
1466 } else {
1467 logDiagnostic(
1468 "[LOOKUP] Properties file of name '" + fileName
1469 + "' found at '" + propsUrl + '"');
1470 }
1471 }
1472
1473 return props;
1474 }
1475
1476 /***
1477 * Determines whether the user wants internal diagnostic output. If so,
1478 * returns an appropriate writer object. Users can enable diagnostic
1479 * output by setting the system property named {@link #DIAGNOSTICS_DEST_PROPERTY} to
1480 * a filename, or the special values STDOUT or STDERR.
1481 */
1482 private static void initDiagnostics() {
1483 String dest;
1484 try {
1485 dest = System.getProperty(DIAGNOSTICS_DEST_PROPERTY);
1486 if (dest == null) {
1487 return;
1488 }
1489 } catch(SecurityException ex) {
1490
1491
1492 return;
1493 }
1494
1495 if (dest.equals("STDOUT")) {
1496 diagnosticsStream = System.out;
1497 } else if (dest.equals("STDERR")) {
1498 diagnosticsStream = System.err;
1499 } else {
1500 try {
1501
1502 FileOutputStream fos = new FileOutputStream(dest, true);
1503 diagnosticsStream = new PrintStream(fos);
1504 } catch(IOException ex) {
1505
1506 return;
1507 }
1508 }
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519 String classLoaderName;
1520 try {
1521 ClassLoader classLoader = thisClassLoader;
1522 if (thisClassLoader == null) {
1523 classLoaderName = "BOOTLOADER";
1524 } else {
1525 classLoaderName = objectId(classLoader);
1526 }
1527 } catch(SecurityException e) {
1528 classLoaderName = "UNKNOWN";
1529 }
1530 diagnosticPrefix = "[LogFactory from " + classLoaderName + "] ";
1531 }
1532
1533 /***
1534 * Indicates true if the user has enabled internal logging.
1535 * <p>
1536 * By the way, sorry for the incorrect grammar, but calling this method
1537 * areDiagnosticsEnabled just isn't java beans style.
1538 *
1539 * @return true if calls to logDiagnostic will have any effect.
1540 * @since 1.1
1541 */
1542 protected static boolean isDiagnosticsEnabled() {
1543 return diagnosticsStream != null;
1544 }
1545
1546 /***
1547 * Write the specified message to the internal logging destination.
1548 * <p>
1549 * Note that this method is private; concrete subclasses of this class
1550 * should not call it because the diagnosticPrefix string this
1551 * method puts in front of all its messages is LogFactory@....,
1552 * while subclasses should put SomeSubClass@...
1553 * <p>
1554 * Subclasses should instead compute their own prefix, then call
1555 * logRawDiagnostic. Note that calling isDiagnosticsEnabled is
1556 * fine for subclasses.
1557 * <p>
1558 * Note that it is safe to call this method before initDiagnostics
1559 * is called; any output will just be ignored (as isDiagnosticsEnabled
1560 * will return false).
1561 *
1562 * @param msg is the diagnostic message to be output.
1563 */
1564 private static final void logDiagnostic(String msg) {
1565 if (diagnosticsStream != null) {
1566 diagnosticsStream.print(diagnosticPrefix);
1567 diagnosticsStream.println(msg);
1568 diagnosticsStream.flush();
1569 }
1570 }
1571
1572 /***
1573 * Write the specified message to the internal logging destination.
1574 *
1575 * @param msg is the diagnostic message to be output.
1576 * @since 1.1
1577 */
1578 protected static final void logRawDiagnostic(String msg) {
1579 if (diagnosticsStream != null) {
1580 diagnosticsStream.println(msg);
1581 diagnosticsStream.flush();
1582 }
1583 }
1584
1585 /***
1586 * Generate useful diagnostics regarding the classloader tree for
1587 * the specified class.
1588 * <p>
1589 * As an example, if the specified class was loaded via a webapp's
1590 * classloader, then you may get the following output:
1591 * <pre>
1592 * Class com.acme.Foo was loaded via classloader 11111
1593 * ClassLoader tree: 11111 -> 22222 (SYSTEM) -> 33333 -> BOOT
1594 * </pre>
1595 * <p>
1596 * This method returns immediately if isDiagnosticsEnabled()
1597 * returns false.
1598 *
1599 * @param clazz is the class whose classloader + tree are to be
1600 * output.
1601 */
1602 private static void logClassLoaderEnvironment(Class clazz) {
1603 if (!isDiagnosticsEnabled()) {
1604 return;
1605 }
1606
1607 try {
1608 logDiagnostic("[ENV] Extension directories (java.ext.dir): " + System.getProperty("java.ext.dir"));
1609 logDiagnostic("[ENV] Application classpath (java.class.path): " + System.getProperty("java.class.path"));
1610 } catch(SecurityException ex) {
1611 logDiagnostic("[ENV] Security setting prevent interrogation of system classpaths.");
1612 }
1613
1614 String className = clazz.getName();
1615 ClassLoader classLoader;
1616
1617 try {
1618 classLoader = getClassLoader(clazz);
1619 } catch(SecurityException ex) {
1620
1621 logDiagnostic(
1622 "[ENV] Security forbids determining the classloader for " + className);
1623 return;
1624 }
1625
1626 logDiagnostic(
1627 "[ENV] Class " + className + " was loaded via classloader "
1628 + objectId(classLoader));
1629 logHierarchy("[ENV] Ancestry of classloader which loaded " + className + " is ", classLoader);
1630 }
1631
1632 /***
1633 * Logs diagnostic messages about the given classloader
1634 * and it's hierarchy. The prefix is prepended to the message
1635 * and is intended to make it easier to understand the logs.
1636 * @param prefix
1637 * @param classLoader
1638 */
1639 private static void logHierarchy(String prefix, ClassLoader classLoader) {
1640 if (!isDiagnosticsEnabled()) {
1641 return;
1642 }
1643 ClassLoader systemClassLoader;
1644 if (classLoader != null) {
1645 final String classLoaderString = classLoader.toString();
1646 logDiagnostic(prefix + objectId(classLoader) + " == '" + classLoaderString + "'");
1647 }
1648
1649 try {
1650 systemClassLoader = ClassLoader.getSystemClassLoader();
1651 } catch(SecurityException ex) {
1652 logDiagnostic(
1653 prefix + "Security forbids determining the system classloader.");
1654 return;
1655 }
1656 if (classLoader != null) {
1657 StringBuffer buf = new StringBuffer(prefix + "ClassLoader tree:");
1658 for(;;) {
1659 buf.append(objectId(classLoader));
1660 if (classLoader == systemClassLoader) {
1661 buf.append(" (SYSTEM) ");
1662 }
1663
1664 try {
1665 classLoader = classLoader.getParent();
1666 } catch(SecurityException ex) {
1667 buf.append(" --> SECRET");
1668 break;
1669 }
1670
1671 buf.append(" --> ");
1672 if (classLoader == null) {
1673 buf.append("BOOT");
1674 break;
1675 }
1676 }
1677 logDiagnostic(buf.toString());
1678 }
1679 }
1680
1681 /***
1682 * Returns a string that uniquely identifies the specified object, including
1683 * its class.
1684 * <p>
1685 * The returned string is of form "classname@hashcode", ie is the same as
1686 * the return value of the Object.toString() method, but works even when
1687 * the specified object's class has overidden the toString method.
1688 *
1689 * @param o may be null.
1690 * @return a string of form classname@hashcode, or "null" if param o is null.
1691 * @since 1.1
1692 */
1693 public static String objectId(Object o) {
1694 if (o == null) {
1695 return "null";
1696 } else {
1697 return o.getClass().getName() + "@" + System.identityHashCode(o);
1698 }
1699 }
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720 static {
1721
1722 thisClassLoader = getClassLoader(LogFactory.class);
1723 initDiagnostics();
1724 logClassLoaderEnvironment(LogFactory.class);
1725 factories = createFactoryStore();
1726 if (isDiagnosticsEnabled()) {
1727 logDiagnostic("BOOTSTRAP COMPLETED");
1728 }
1729 }
1730 }