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