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