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.impl;
19
20
21 import java.lang.reflect.Constructor;
22 import java.lang.reflect.InvocationTargetException;
23 import java.lang.reflect.Method;
24 import java.net.URL;
25 import java.security.AccessController;
26 import java.security.PrivilegedAction;
27 import java.util.Enumeration;
28 import java.util.Hashtable;
29 import java.util.Vector;
30
31 import org.apache.commons.logging.Log;
32 import org.apache.commons.logging.LogConfigurationException;
33 import org.apache.commons.logging.LogFactory;
34
35
36 /**
37 * <p>Concrete subclass of {@link LogFactory} that implements the
38 * following algorithm to dynamically select a logging implementation
39 * class to instantiate a wrapper for.</p>
40 * <ul>
41 * <li>Use a factory configuration attribute named
42 * <code>org.apache.commons.logging.Log</code> to identify the
43 * requested implementation class.</li>
44 * <li>Use the <code>org.apache.commons.logging.Log</code> system property
45 * to identify the requested implementation class.</li>
46 * <li>If <em>Log4J</em> is available, return an instance of
47 * <code>org.apache.commons.logging.impl.Log4JLogger</code>.</li>
48 * <li>If <em>JDK 1.4 or later</em> is available, return an instance of
49 * <code>org.apache.commons.logging.impl.Jdk14Logger</code>.</li>
50 * <li>Otherwise, return an instance of
51 * <code>org.apache.commons.logging.impl.SimpleLog</code>.</li>
52 * </ul>
53 *
54 * <p>If the selected {@link Log} implementation class has a
55 * <code>setLogFactory()</code> method that accepts a {@link LogFactory}
56 * parameter, this method will be called on each newly created instance
57 * to identify the associated factory. This makes factory configuration
58 * attributes available to the Log instance, if it so desires.</p>
59 *
60 * <p>This factory will remember previously created <code>Log</code> instances
61 * for the same name, and will return them on repeated requests to the
62 * <code>getInstance()</code> method.</p>
63 *
64 * @author Rod Waldhoff
65 * @author Craig R. McClanahan
66 * @author Richard A. Sitze
67 * @author Brian Stansberry
68 * @version $Revision: 581090 $ $Date: 2007-10-02 00:01:06 +0200 (ti, 02 okt 2007) $
69 */
70
71 public class LogFactoryImpl extends LogFactory {
72
73
74 /** Log4JLogger class name */
75 private static final String LOGGING_IMPL_LOG4J_LOGGER = "org.apache.commons.logging.impl.Log4JLogger";
76 /** Jdk14Logger class name */
77 private static final String LOGGING_IMPL_JDK14_LOGGER = "org.apache.commons.logging.impl.Jdk14Logger";
78 /** Jdk13LumberjackLogger class name */
79 private static final String LOGGING_IMPL_LUMBERJACK_LOGGER = "org.apache.commons.logging.impl.Jdk13LumberjackLogger";
80 /** SimpleLog class name */
81 private static final String LOGGING_IMPL_SIMPLE_LOGGER = "org.apache.commons.logging.impl.SimpleLog";
82
83 private static final String PKG_IMPL="org.apache.commons.logging.impl.";
84 private static final int PKG_LEN = PKG_IMPL.length();
85
86 // ----------------------------------------------------------- Constructors
87
88
89
90 /**
91 * Public no-arguments constructor required by the lookup mechanism.
92 */
93 public LogFactoryImpl() {
94 super();
95 initDiagnostics(); // method on this object
96 if (isDiagnosticsEnabled()) {
97 logDiagnostic("Instance created.");
98 }
99 }
100
101
102 // ----------------------------------------------------- Manifest Constants
103
104
105 /**
106 * The name (<code>org.apache.commons.logging.Log</code>) of the system
107 * property identifying our {@link Log} implementation class.
108 */
109 public static final String LOG_PROPERTY =
110 "org.apache.commons.logging.Log";
111
112
113 /**
114 * The deprecated system property used for backwards compatibility with
115 * old versions of JCL.
116 */
117 protected static final String LOG_PROPERTY_OLD =
118 "org.apache.commons.logging.log";
119
120 /**
121 * The name (<code>org.apache.commons.logging.Log.allowFlawedContext</code>)
122 * of the system property which can be set true/false to
123 * determine system behaviour when a bad context-classloader is encountered.
124 * When set to false, a LogConfigurationException is thrown if
125 * LogFactoryImpl is loaded via a child classloader of the TCCL (this
126 * should never happen in sane systems).
127 *
128 * Default behaviour: true (tolerates bad context classloaders)
129 *
130 * See also method setAttribute.
131 */
132 public static final String ALLOW_FLAWED_CONTEXT_PROPERTY =
133 "org.apache.commons.logging.Log.allowFlawedContext";
134
135 /**
136 * The name (<code>org.apache.commons.logging.Log.allowFlawedDiscovery</code>)
137 * of the system property which can be set true/false to
138 * determine system behaviour when a bad logging adapter class is
139 * encountered during logging discovery. When set to false, an
140 * exception will be thrown and the app will fail to start. When set
141 * to true, discovery will continue (though the user might end up
142 * with a different logging implementation than they expected).
143 *
144 * Default behaviour: true (tolerates bad logging adapters)
145 *
146 * See also method setAttribute.
147 */
148 public static final String ALLOW_FLAWED_DISCOVERY_PROPERTY =
149 "org.apache.commons.logging.Log.allowFlawedDiscovery";
150
151 /**
152 * The name (<code>org.apache.commons.logging.Log.allowFlawedHierarchy</code>)
153 * of the system property which can be set true/false to
154 * determine system behaviour when a logging adapter class is
155 * encountered which has bound to the wrong Log class implementation.
156 * When set to false, an exception will be thrown and the app will fail
157 * to start. When set to true, discovery will continue (though the user
158 * might end up with a different logging implementation than they expected).
159 *
160 * Default behaviour: true (tolerates bad Log class hierarchy)
161 *
162 * See also method setAttribute.
163 */
164 public static final String ALLOW_FLAWED_HIERARCHY_PROPERTY =
165 "org.apache.commons.logging.Log.allowFlawedHierarchy";
166
167
168 /**
169 * The names of classes that will be tried (in order) as logging
170 * adapters. Each class is expected to implement the Log interface,
171 * and to throw NoClassDefFound or ExceptionInInitializerError when
172 * loaded if the underlying logging library is not available. Any
173 * other error indicates that the underlying logging library is available
174 * but broken/unusable for some reason.
175 */
176 private static final String[] classesToDiscover = {
177 LOGGING_IMPL_LOG4J_LOGGER,
178 "org.apache.commons.logging.impl.Jdk14Logger",
179 "org.apache.commons.logging.impl.Jdk13LumberjackLogger",
180 "org.apache.commons.logging.impl.SimpleLog"
181 };
182
183
184 // ----------------------------------------------------- Instance Variables
185
186 /**
187 * Determines whether logging classes should be loaded using the thread-context
188 * classloader, or via the classloader that loaded this LogFactoryImpl class.
189 */
190 private boolean useTCCL = true;
191
192 /**
193 * The string prefixed to every message output by the logDiagnostic method.
194 */
195 private String diagnosticPrefix;
196
197
198 /**
199 * Configuration attributes.
200 */
201 protected Hashtable attributes = new Hashtable();
202
203
204 /**
205 * The {@link org.apache.commons.logging.Log} instances that have
206 * already been created, keyed by logger name.
207 */
208 protected Hashtable instances = new Hashtable();
209
210
211 /**
212 * Name of the class implementing the Log interface.
213 */
214 private String logClassName;
215
216
217 /**
218 * The one-argument constructor of the
219 * {@link org.apache.commons.logging.Log}
220 * implementation class that will be used to create new instances.
221 * This value is initialized by <code>getLogConstructor()</code>,
222 * and then returned repeatedly.
223 */
224 protected Constructor logConstructor = null;
225
226
227 /**
228 * The signature of the Constructor to be used.
229 */
230 protected Class logConstructorSignature[] =
231 { java.lang.String.class };
232
233
234 /**
235 * The one-argument <code>setLogFactory</code> method of the selected
236 * {@link org.apache.commons.logging.Log} method, if it exists.
237 */
238 protected Method logMethod = null;
239
240
241 /**
242 * The signature of the <code>setLogFactory</code> method to be used.
243 */
244 protected Class logMethodSignature[] =
245 { LogFactory.class };
246
247 /**
248 * See getBaseClassLoader and initConfiguration.
249 */
250 private boolean allowFlawedContext;
251
252 /**
253 * See handleFlawedDiscovery and initConfiguration.
254 */
255 private boolean allowFlawedDiscovery;
256
257 /**
258 * See handleFlawedHierarchy and initConfiguration.
259 */
260 private boolean allowFlawedHierarchy;
261
262 // --------------------------------------------------------- Public Methods
263
264
265 /**
266 * Return the configuration attribute with the specified name (if any),
267 * or <code>null</code> if there is no such attribute.
268 *
269 * @param name Name of the attribute to return
270 */
271 public Object getAttribute(String name) {
272
273 return (attributes.get(name));
274
275 }
276
277
278 /**
279 * Return an array containing the names of all currently defined
280 * configuration attributes. If there are no such attributes, a zero
281 * length array is returned.
282 */
283 public String[] getAttributeNames() {
284
285 Vector names = new Vector();
286 Enumeration keys = attributes.keys();
287 while (keys.hasMoreElements()) {
288 names.addElement((String) keys.nextElement());
289 }
290 String results[] = new String[names.size()];
291 for (int i = 0; i < results.length; i++) {
292 results[i] = (String) names.elementAt(i);
293 }
294 return (results);
295
296 }
297
298
299 /**
300 * Convenience method to derive a name from the specified class and
301 * call <code>getInstance(String)</code> with it.
302 *
303 * @param clazz Class for which a suitable Log name will be derived
304 *
305 * @exception LogConfigurationException if a suitable <code>Log</code>
306 * instance cannot be returned
307 */
308 public Log getInstance(Class clazz) throws LogConfigurationException {
309
310 return (getInstance(clazz.getName()));
311
312 }
313
314
315 /**
316 * <p>Construct (if necessary) and return a <code>Log</code> instance,
317 * using the factory's current set of configuration attributes.</p>
318 *
319 * <p><strong>NOTE</strong> - Depending upon the implementation of
320 * the <code>LogFactory</code> you are using, the <code>Log</code>
321 * instance you are returned may or may not be local to the current
322 * application, and may or may not be returned again on a subsequent
323 * call with the same name argument.</p>
324 *
325 * @param name Logical name of the <code>Log</code> instance to be
326 * returned (the meaning of this name is only known to the underlying
327 * logging implementation that is being wrapped)
328 *
329 * @exception LogConfigurationException if a suitable <code>Log</code>
330 * instance cannot be returned
331 */
332 public Log getInstance(String name) throws LogConfigurationException {
333
334 Log instance = (Log) instances.get(name);
335 if (instance == null) {
336 instance = newInstance(name);
337 instances.put(name, instance);
338 }
339 return (instance);
340
341 }
342
343
344 /**
345 * Release any internal references to previously created
346 * {@link org.apache.commons.logging.Log}
347 * instances returned by this factory. This is useful in environments
348 * like servlet containers, which implement application reloading by
349 * throwing away a ClassLoader. Dangling references to objects in that
350 * class loader would prevent garbage collection.
351 */
352 public void release() {
353
354 logDiagnostic("Releasing all known loggers");
355 instances.clear();
356 }
357
358
359 /**
360 * Remove any configuration attribute associated with the specified name.
361 * If there is no such attribute, no action is taken.
362 *
363 * @param name Name of the attribute to remove
364 */
365 public void removeAttribute(String name) {
366
367 attributes.remove(name);
368
369 }
370
371
372 /**
373 * Set the configuration attribute with the specified name. Calling
374 * this with a <code>null</code> value is equivalent to calling
375 * <code>removeAttribute(name)</code>.
376 * <p>
377 * This method can be used to set logging configuration programmatically
378 * rather than via system properties. It can also be used in code running
379 * within a container (such as a webapp) to configure behaviour on a
380 * per-component level instead of globally as system properties would do.
381 * To use this method instead of a system property, call
382 * <pre>
383 * LogFactory.getFactory().setAttribute(...)
384 * </pre>
385 * This must be done before the first Log object is created; configuration
386 * changes after that point will be ignored.
387 * <p>
388 * This method is also called automatically if LogFactory detects a
389 * commons-logging.properties file; every entry in that file is set
390 * automatically as an attribute here.
391 *
392 * @param name Name of the attribute to set
393 * @param value Value of the attribute to set, or <code>null</code>
394 * to remove any setting for this attribute
395 */
396 public void setAttribute(String name, Object value) {
397
398 if (logConstructor != null) {
399 logDiagnostic("setAttribute: call too late; configuration already performed.");
400 }
401
402 if (value == null) {
403 attributes.remove(name);
404 } else {
405 attributes.put(name, value);
406 }
407
408 if (name.equals(TCCL_KEY)) {
409 useTCCL = Boolean.valueOf(value.toString()).booleanValue();
410 }
411
412 }
413
414
415 // ------------------------------------------------------
416 // Static Methods
417 //
418 // These methods only defined as workarounds for a java 1.2 bug;
419 // theoretically none of these are needed.
420 // ------------------------------------------------------
421
422 /**
423 * Gets the context classloader.
424 * This method is a workaround for a java 1.2 compiler bug.
425 * @since 1.1
426 */
427 protected static ClassLoader getContextClassLoader() throws LogConfigurationException {
428 return LogFactory.getContextClassLoader();
429 }
430
431
432 /**
433 * Workaround for bug in Java1.2; in theory this method is not needed.
434 * See LogFactory.isDiagnosticsEnabled.
435 */
436 protected static boolean isDiagnosticsEnabled() {
437 return LogFactory.isDiagnosticsEnabled();
438 }
439
440
441 /**
442 * Workaround for bug in Java1.2; in theory this method is not needed.
443 * See LogFactory.getClassLoader.
444 * @since 1.1
445 */
446 protected static ClassLoader getClassLoader(Class clazz) {
447 return LogFactory.getClassLoader(clazz);
448 }
449
450
451 // ------------------------------------------------------ Protected Methods
452
453 /**
454 * Calculate and cache a string that uniquely identifies this instance,
455 * including which classloader the object was loaded from.
456 * <p>
457 * This string will later be prefixed to each "internal logging" message
458 * emitted, so that users can clearly see any unexpected behaviour.
459 * <p>
460 * Note that this method does not detect whether internal logging is
461 * enabled or not, nor where to output stuff if it is; that is all
462 * handled by the parent LogFactory class. This method just computes
463 * its own unique prefix for log messages.
464 */
465 private void initDiagnostics() {
466 // It would be nice to include an identifier of the context classloader
467 // that this LogFactoryImpl object is responsible for. However that
468 // isn't possible as that information isn't available. It is possible
469 // to figure this out by looking at the logging from LogFactory to
470 // see the context & impl ids from when this object was instantiated,
471 // in order to link the impl id output as this object's prefix back to
472 // the context it is intended to manage.
473 // Note that this prefix should be kept consistent with that
474 // in LogFactory.
475 Class clazz = this.getClass();
476 ClassLoader classLoader = getClassLoader(clazz);
477 String classLoaderName;
478 try {
479 if (classLoader == null) {
480 classLoaderName = "BOOTLOADER";
481 } else {
482 classLoaderName = objectId(classLoader);
483 }
484 } catch(SecurityException e) {
485 classLoaderName = "UNKNOWN";
486 }
487 diagnosticPrefix = "[LogFactoryImpl@" + System.identityHashCode(this) + " from " + classLoaderName + "] ";
488 }
489
490
491 /**
492 * Output a diagnostic message to a user-specified destination (if the
493 * user has enabled diagnostic logging).
494 *
495 * @param msg diagnostic message
496 * @since 1.1
497 */
498 protected void logDiagnostic(String msg) {
499 if (isDiagnosticsEnabled()) {
500 logRawDiagnostic(diagnosticPrefix + msg);
501 }
502 }
503
504 /**
505 * Return the fully qualified Java classname of the {@link Log}
506 * implementation we will be using.
507 *
508 * @deprecated Never invoked by this class; subclasses should not assume
509 * it will be.
510 */
511 protected String getLogClassName() {
512
513 if (logClassName == null) {
514 discoverLogImplementation(getClass().getName());
515 }
516
517 return logClassName;
518 }
519
520
521 /**
522 * <p>Return the <code>Constructor</code> that can be called to instantiate
523 * new {@link org.apache.commons.logging.Log} instances.</p>
524 *
525 * <p><strong>IMPLEMENTATION NOTE</strong> - Race conditions caused by
526 * calling this method from more than one thread are ignored, because
527 * the same <code>Constructor</code> instance will ultimately be derived
528 * in all circumstances.</p>
529 *
530 * @exception LogConfigurationException if a suitable constructor
531 * cannot be returned
532 *
533 * @deprecated Never invoked by this class; subclasses should not assume
534 * it will be.
535 */
536 protected Constructor getLogConstructor()
537 throws LogConfigurationException {
538
539 // Return the previously identified Constructor (if any)
540 if (logConstructor == null) {
541 discoverLogImplementation(getClass().getName());
542 }
543
544 return logConstructor;
545 }
546
547
548 /**
549 * Is <em>JDK 1.3 with Lumberjack</em> logging available?
550 *
551 * @deprecated Never invoked by this class; subclasses should not assume
552 * it will be.
553 */
554 protected boolean isJdk13LumberjackAvailable() {
555 return isLogLibraryAvailable(
556 "Jdk13Lumberjack",
557 "org.apache.commons.logging.impl.Jdk13LumberjackLogger");
558 }
559
560
561 /**
562 * <p>Return <code>true</code> if <em>JDK 1.4 or later</em> logging
563 * is available. Also checks that the <code>Throwable</code> class
564 * supports <code>getStackTrace()</code>, which is required by
565 * Jdk14Logger.</p>
566 *
567 * @deprecated Never invoked by this class; subclasses should not assume
568 * it will be.
569 */
570 protected boolean isJdk14Available() {
571 return isLogLibraryAvailable(
572 "Jdk14",
573 "org.apache.commons.logging.impl.Jdk14Logger");
574 }
575
576
577 /**
578 * Is a <em>Log4J</em> implementation available?
579 *
580 * @deprecated Never invoked by this class; subclasses should not assume
581 * it will be.
582 */
583 protected boolean isLog4JAvailable() {
584 return isLogLibraryAvailable(
585 "Log4J",
586 LOGGING_IMPL_LOG4J_LOGGER);
587 }
588
589
590 /**
591 * Create and return a new {@link org.apache.commons.logging.Log}
592 * instance for the specified name.
593 *
594 * @param name Name of the new logger
595 *
596 * @exception LogConfigurationException if a new instance cannot
597 * be created
598 */
599 protected Log newInstance(String name) throws LogConfigurationException {
600
601 Log instance = null;
602 try {
603 if (logConstructor == null) {
604 instance = discoverLogImplementation(name);
605 }
606 else {
607 Object params[] = { name };
608 instance = (Log) logConstructor.newInstance(params);
609 }
610
611 if (logMethod != null) {
612 Object params[] = { this };
613 logMethod.invoke(instance, params);
614 }
615
616 return (instance);
617
618 } catch (LogConfigurationException lce) {
619
620 // this type of exception means there was a problem in discovery
621 // and we've already output diagnostics about the issue, etc.;
622 // just pass it on
623 throw (LogConfigurationException) lce;
624
625 } catch (InvocationTargetException e) {
626 // A problem occurred invoking the Constructor or Method
627 // previously discovered
628 Throwable c = e.getTargetException();
629 if (c != null) {
630 throw new LogConfigurationException(c);
631 } else {
632 throw new LogConfigurationException(e);
633 }
634 } catch (Throwable t) {
635 // A problem occurred invoking the Constructor or Method
636 // previously discovered
637 throw new LogConfigurationException(t);
638 }
639 }
640
641
642 // ------------------------------------------------------ Private Methods
643
644 /**
645 * Calls LogFactory.directGetContextClassLoader under the control of an
646 * AccessController class. This means that java code running under a
647 * security manager that forbids access to ClassLoaders will still work
648 * if this class is given appropriate privileges, even when the caller
649 * doesn't have such privileges. Without using an AccessController, the
650 * the entire call stack must have the privilege before the call is
651 * allowed.
652 *
653 * @return the context classloader associated with the current thread,
654 * or null if security doesn't allow it.
655 *
656 * @throws LogConfigurationException if there was some weird error while
657 * attempting to get the context classloader.
658 *
659 * @throws SecurityException if the current java security policy doesn't
660 * allow this class to access the context classloader.
661 */
662 private static ClassLoader getContextClassLoaderInternal()
663 throws LogConfigurationException {
664 return (ClassLoader)AccessController.doPrivileged(
665 new PrivilegedAction() {
666 public Object run() {
667 return LogFactory.directGetContextClassLoader();
668 }
669 });
670 }
671
672 /**
673 * Read the specified system property, using an AccessController so that
674 * the property can be read if JCL has been granted the appropriate
675 * security rights even if the calling code has not.
676 * <p>
677 * Take care not to expose the value returned by this method to the
678 * calling application in any way; otherwise the calling app can use that
679 * info to access data that should not be available to it.
680 */
681 private static String getSystemProperty(final String key, final String def)
682 throws SecurityException {
683 return (String) AccessController.doPrivileged(
684 new PrivilegedAction() {
685 public Object run() {
686 return System.getProperty(key, def);
687 }
688 });
689 }
690
691 /**
692 * Fetch the parent classloader of a specified classloader.
693 * <p>
694 * If a SecurityException occurs, null is returned.
695 * <p>
696 * Note that this method is non-static merely so logDiagnostic is available.
697 */
698 private ClassLoader getParentClassLoader(final ClassLoader cl) {
699 try {
700 return (ClassLoader)AccessController.doPrivileged(
701 new PrivilegedAction() {
702 public Object run() {
703 return cl.getParent();
704 }
705 });
706 } catch(SecurityException ex) {
707 logDiagnostic("[SECURITY] Unable to obtain parent classloader");
708 return null;
709 }
710
711 }
712
713 /**
714 * Utility method to check whether a particular logging library is
715 * present and available for use. Note that this does <i>not</i>
716 * affect the future behaviour of this class.
717 */
718 private boolean isLogLibraryAvailable(String name, String classname) {
719 if (isDiagnosticsEnabled()) {
720 logDiagnostic("Checking for '" + name + "'.");
721 }
722 try {
723 Log log = createLogFromClass(
724 classname,
725 this.getClass().getName(), // dummy category
726 false);
727
728 if (log == null) {
729 if (isDiagnosticsEnabled()) {
730 logDiagnostic("Did not find '" + name + "'.");
731 }
732 return false;
733 } else {
734 if (isDiagnosticsEnabled()) {
735 logDiagnostic("Found '" + name + "'.");
736 }
737 return true;
738 }
739 } catch(LogConfigurationException e) {
740 if (isDiagnosticsEnabled()) {
741 logDiagnostic("Logging system '" + name + "' is available but not useable.");
742 }
743 return false;
744 }
745 }
746
747 /**
748 * Attempt to find an attribute (see method setAttribute) or a
749 * system property with the provided name and return its value.
750 * <p>
751 * The attributes associated with this object are checked before
752 * system properties in case someone has explicitly called setAttribute,
753 * or a configuration property has been set in a commons-logging.properties
754 * file.
755 *
756 * @return the value associated with the property, or null.
757 */
758 private String getConfigurationValue(String property) {
759 if (isDiagnosticsEnabled()) {
760 logDiagnostic("[ENV] Trying to get configuration for item " + property);
761 }
762
763 Object valueObj = getAttribute(property);
764 if (valueObj != null) {
765 if (isDiagnosticsEnabled()) {
766 logDiagnostic("[ENV] Found LogFactory attribute [" + valueObj + "] for " + property);
767 }
768 return valueObj.toString();
769 }
770
771 if (isDiagnosticsEnabled()) {
772 logDiagnostic("[ENV] No LogFactory attribute found for " + property);
773 }
774
775 try {
776 // warning: minor security hole here, in that we potentially read a system
777 // property that the caller cannot, then output it in readable form as a
778 // diagnostic message. However it's only ever JCL-specific properties
779 // involved here, so the harm is truly trivial.
780 String value = getSystemProperty(property, null);
781 if (value != null) {
782 if (isDiagnosticsEnabled()) {
783 logDiagnostic("[ENV] Found system property [" + value + "] for " + property);
784 }
785 return value;
786 }
787
788 if (isDiagnosticsEnabled()) {
789 logDiagnostic("[ENV] No system property found for property " + property);
790 }
791 } catch (SecurityException e) {
792 if (isDiagnosticsEnabled()) {
793 logDiagnostic("[ENV] Security prevented reading system property " + property);
794 }
795 }
796
797 if (isDiagnosticsEnabled()) {
798 logDiagnostic("[ENV] No configuration defined for item " + property);
799 }
800
801 return null;
802 }
803
804 /**
805 * Get the setting for the user-configurable behaviour specified by key.
806 * If nothing has explicitly been set, then return dflt.
807 */
808 private boolean getBooleanConfiguration(String key, boolean dflt) {
809 String val = getConfigurationValue(key);
810 if (val == null)
811 return dflt;
812 return Boolean.valueOf(val).booleanValue();
813 }
814
815 /**
816 * Initialize a number of variables that control the behaviour of this
817 * class and that can be tweaked by the user. This is done when the first
818 * logger is created, not in the constructor of this class, because we
819 * need to give the user a chance to call method setAttribute in order to
820 * configure this object.
821 */
822 private void initConfiguration() {
823 allowFlawedContext = getBooleanConfiguration(ALLOW_FLAWED_CONTEXT_PROPERTY, true);
824 allowFlawedDiscovery = getBooleanConfiguration(ALLOW_FLAWED_DISCOVERY_PROPERTY, true);
825 allowFlawedHierarchy = getBooleanConfiguration(ALLOW_FLAWED_HIERARCHY_PROPERTY, true);
826 }
827
828
829 /**
830 * Attempts to create a Log instance for the given category name.
831 * Follows the discovery process described in the class javadoc.
832 *
833 * @param logCategory the name of the log category
834 *
835 * @throws LogConfigurationException if an error in discovery occurs,
836 * or if no adapter at all can be instantiated
837 */
838 private Log discoverLogImplementation(String logCategory)
839 throws LogConfigurationException
840 {
841 if (isDiagnosticsEnabled()) {
842 logDiagnostic("Discovering a Log implementation...");
843 }
844
845 initConfiguration();
846
847 Log result = null;
848
849 // See if the user specified the Log implementation to use
850 String specifiedLogClassName = findUserSpecifiedLogClassName();
851
852 if (specifiedLogClassName != null) {
853 if (isDiagnosticsEnabled()) {
854 logDiagnostic("Attempting to load user-specified log class '" +
855 specifiedLogClassName + "'...");
856 }
857
858 result = createLogFromClass(specifiedLogClassName,
859 logCategory,
860 true);
861 if (result == null) {
862 StringBuffer messageBuffer = new StringBuffer("User-specified log class '");
863 messageBuffer.append(specifiedLogClassName);
864 messageBuffer.append("' cannot be found or is not useable.");
865
866 // Mistyping or misspelling names is a common fault.
867 // Construct a good error message, if we can
868 if (specifiedLogClassName != null) {
869 informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_LOG4J_LOGGER);
870 informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_JDK14_LOGGER);
871 informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_LUMBERJACK_LOGGER);
872 informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_SIMPLE_LOGGER);
873 }
874 throw new LogConfigurationException(messageBuffer.toString());
875 }
876
877 return result;
878 }
879
880 // No user specified log; try to discover what's on the classpath
881 //
882 // Note that we deliberately loop here over classesToDiscover and
883 // expect method createLogFromClass to loop over the possible source
884 // classloaders. The effect is:
885 // for each discoverable log adapter
886 // for each possible classloader
887 // see if it works
888 //
889 // It appears reasonable at first glance to do the opposite:
890 // for each possible classloader
891 // for each discoverable log adapter
892 // see if it works
893 //
894 // The latter certainly has advantages for user-installable logging
895 // libraries such as log4j; in a webapp for example this code should
896 // first check whether the user has provided any of the possible
897 // logging libraries before looking in the parent classloader.
898 // Unfortunately, however, Jdk14Logger will always work in jvm>=1.4,
899 // and SimpleLog will always work in any JVM. So the loop would never
900 // ever look for logging libraries in the parent classpath. Yet many
901 // users would expect that putting log4j there would cause it to be
902 // detected (and this is the historical JCL behaviour). So we go with
903 // the first approach. A user that has bundled a specific logging lib
904 // in a webapp should use a commons-logging.properties file or a
905 // service file in META-INF to force use of that logging lib anyway,
906 // rather than relying on discovery.
907
908 if (isDiagnosticsEnabled()) {
909 logDiagnostic(
910 "No user-specified Log implementation; performing discovery" +
911 " using the standard supported logging implementations...");
912 }
913 for(int i=0; (i<classesToDiscover.length) && (result == null); ++i) {
914 result = createLogFromClass(classesToDiscover[i], logCategory, true);
915 }
916
917 if (result == null) {
918 throw new LogConfigurationException
919 ("No suitable Log implementation");
920 }
921
922 return result;
923 }
924
925
926 /**
927 * Appends message if the given name is similar to the candidate.
928 * @param messageBuffer <code>StringBuffer</code> the message should be appended to,
929 * not null
930 * @param name the (trimmed) name to be test against the candidate, not null
931 * @param candidate the candidate name (not null)
932 */
933 private void informUponSimilarName(final StringBuffer messageBuffer, final String name,
934 final String candidate) {
935 if (name.equals(candidate)) {
936 // Don't suggest a name that is exactly the same as the one the
937 // user tried...
938 return;
939 }
940
941 // If the user provides a name that is in the right package, and gets
942 // the first 5 characters of the adapter class right (ignoring case),
943 // then suggest the candidate adapter class name.
944 if (name.regionMatches(true, 0, candidate, 0, PKG_LEN + 5)) {
945 messageBuffer.append(" Did you mean '");
946 messageBuffer.append(candidate);
947 messageBuffer.append("'?");
948 }
949 }
950
951
952 /**
953 * Checks system properties and the attribute map for
954 * a Log implementation specified by the user under the
955 * property names {@link #LOG_PROPERTY} or {@link #LOG_PROPERTY_OLD}.
956 *
957 * @return classname specified by the user, or <code>null</code>
958 */
959 private String findUserSpecifiedLogClassName()
960 {
961 if (isDiagnosticsEnabled()) {
962 logDiagnostic("Trying to get log class from attribute '" + LOG_PROPERTY + "'");
963 }
964 String specifiedClass = (String) getAttribute(LOG_PROPERTY);
965
966 if (specifiedClass == null) { // @deprecated
967 if (isDiagnosticsEnabled()) {
968 logDiagnostic("Trying to get log class from attribute '" +
969 LOG_PROPERTY_OLD + "'");
970 }
971 specifiedClass = (String) getAttribute(LOG_PROPERTY_OLD);
972 }
973
974 if (specifiedClass == null) {
975 if (isDiagnosticsEnabled()) {
976 logDiagnostic("Trying to get log class from system property '" +
977 LOG_PROPERTY + "'");
978 }
979 try {
980 specifiedClass = getSystemProperty(LOG_PROPERTY, null);
981 } catch (SecurityException e) {
982 if (isDiagnosticsEnabled()) {
983 logDiagnostic("No access allowed to system property '" +
984 LOG_PROPERTY + "' - " + e.getMessage());
985 }
986 }
987 }
988
989 if (specifiedClass == null) { // @deprecated
990 if (isDiagnosticsEnabled()) {
991 logDiagnostic("Trying to get log class from system property '" +
992 LOG_PROPERTY_OLD + "'");
993 }
994 try {
995 specifiedClass = getSystemProperty(LOG_PROPERTY_OLD, null);
996 } catch (SecurityException e) {
997 if (isDiagnosticsEnabled()) {
998 logDiagnostic("No access allowed to system property '" +
999 LOG_PROPERTY_OLD + "' - " + e.getMessage());
1000 }
1001 }
1002 }
1003
1004 // Remove any whitespace; it's never valid in a classname so its
1005 // presence just means a user mistake. As we know what they meant,
1006 // we may as well strip the spaces.
1007 if (specifiedClass != null) {
1008 specifiedClass = specifiedClass.trim();
1009 }
1010
1011 return specifiedClass;
1012 }
1013
1014
1015 /**
1016 * Attempts to load the given class, find a suitable constructor,
1017 * and instantiate an instance of Log.
1018 *
1019 * @param logAdapterClassName classname of the Log implementation
1020 *
1021 * @param logCategory argument to pass to the Log implementation's
1022 * constructor
1023 *
1024 * @param affectState <code>true</code> if this object's state should
1025 * be affected by this method call, <code>false</code> otherwise.
1026 *
1027 * @return an instance of the given class, or null if the logging
1028 * library associated with the specified adapter is not available.
1029 *
1030 * @throws LogConfigurationException if there was a serious error with
1031 * configuration and the handleFlawedDiscovery method decided this
1032 * problem was fatal.
1033 */
1034 private Log createLogFromClass(String logAdapterClassName,
1035 String logCategory,
1036 boolean affectState)
1037 throws LogConfigurationException {
1038
1039 if (isDiagnosticsEnabled()) {
1040 logDiagnostic("Attempting to instantiate '" + logAdapterClassName + "'");
1041 }
1042
1043 Object[] params = { logCategory };
1044 Log logAdapter = null;
1045 Constructor constructor = null;
1046
1047 Class logAdapterClass = null;
1048 ClassLoader currentCL = getBaseClassLoader();
1049
1050 for(;;) {
1051 // Loop through the classloader hierarchy trying to find
1052 // a viable classloader.
1053 logDiagnostic(
1054 "Trying to load '"
1055 + logAdapterClassName
1056 + "' from classloader "
1057 + objectId(currentCL));
1058 try {
1059 if (isDiagnosticsEnabled()) {
1060 // Show the location of the first occurrence of the .class file
1061 // in the classpath. This is the location that ClassLoader.loadClass
1062 // will load the class from -- unless the classloader is doing
1063 // something weird.
1064 URL url;
1065 String resourceName = logAdapterClassName.replace('.', '/') + ".class";
1066 if (currentCL != null) {
1067 url = currentCL.getResource(resourceName );
1068 } else {
1069 url = ClassLoader.getSystemResource(resourceName + ".class");
1070 }
1071
1072 if (url == null) {
1073 logDiagnostic("Class '" + logAdapterClassName + "' [" + resourceName + "] cannot be found.");
1074 } else {
1075 logDiagnostic("Class '" + logAdapterClassName + "' was found at '" + url + "'");
1076 }
1077 }
1078
1079 Class c = null;
1080 try {
1081 c = Class.forName(logAdapterClassName, true, currentCL);
1082 } catch (ClassNotFoundException originalClassNotFoundException) {
1083 // The current classloader was unable to find the log adapter
1084 // in this or any ancestor classloader. There's no point in
1085 // trying higher up in the hierarchy in this case..
1086 String msg = "" + originalClassNotFoundException.getMessage();
1087 logDiagnostic(
1088 "The log adapter '"
1089 + logAdapterClassName
1090 + "' is not available via classloader "
1091 + objectId(currentCL)
1092 + ": "
1093 + msg.trim());
1094 try {
1095 // Try the class classloader.
1096 // This may work in cases where the TCCL
1097 // does not contain the code executed or JCL.
1098 // This behaviour indicates that the application
1099 // classloading strategy is not consistent with the
1100 // Java 1.2 classloading guidelines but JCL can
1101 // and so should handle this case.
1102 c = Class.forName(logAdapterClassName);
1103 } catch (ClassNotFoundException secondaryClassNotFoundException) {
1104 // no point continuing: this adapter isn't available
1105 msg = "" + secondaryClassNotFoundException.getMessage();
1106 logDiagnostic(
1107 "The log adapter '"
1108 + logAdapterClassName
1109 + "' is not available via the LogFactoryImpl class classloader: "
1110 + msg.trim());
1111 break;
1112 }
1113 }
1114
1115 constructor = c.getConstructor(logConstructorSignature);
1116 Object o = constructor.newInstance(params);
1117
1118 // Note that we do this test after trying to create an instance
1119 // [rather than testing Log.class.isAssignableFrom(c)] so that
1120 // we don't complain about Log hierarchy problems when the
1121 // adapter couldn't be instantiated anyway.
1122 if (o instanceof Log) {
1123 logAdapterClass = c;
1124 logAdapter = (Log) o;
1125 break;
1126 }
1127
1128 // Oops, we have a potential problem here. An adapter class
1129 // has been found and its underlying lib is present too, but
1130 // there are multiple Log interface classes available making it
1131 // impossible to cast to the type the caller wanted. We
1132 // certainly can't use this logger, but we need to know whether
1133 // to keep on discovering or terminate now.
1134 //
1135 // The handleFlawedHierarchy method will throw
1136 // LogConfigurationException if it regards this problem as
1137 // fatal, and just return if not.
1138 handleFlawedHierarchy(currentCL, c);
1139 } catch (NoClassDefFoundError e) {
1140 // We were able to load the adapter but it had references to
1141 // other classes that could not be found. This simply means that
1142 // the underlying logger library is not present in this or any
1143 // ancestor classloader. There's no point in trying higher up
1144 // in the hierarchy in this case..
1145 String msg = "" + e.getMessage();
1146 logDiagnostic(
1147 "The log adapter '"
1148 + logAdapterClassName
1149 + "' is missing dependencies when loaded via classloader "
1150 + objectId(currentCL)
1151 + ": "
1152 + msg.trim());
1153 break;
1154 } catch (ExceptionInInitializerError e) {
1155 // A static initializer block or the initializer code associated
1156 // with a static variable on the log adapter class has thrown
1157 // an exception.
1158 //
1159 // We treat this as meaning the adapter's underlying logging
1160 // library could not be found.
1161 String msg = "" + e.getMessage();
1162 logDiagnostic(
1163 "The log adapter '"
1164 + logAdapterClassName
1165 + "' is unable to initialize itself when loaded via classloader "
1166 + objectId(currentCL)
1167 + ": "
1168 + msg.trim());
1169 break;
1170 } catch(LogConfigurationException e) {
1171 // call to handleFlawedHierarchy above must have thrown
1172 // a LogConfigurationException, so just throw it on
1173 throw e;
1174 } catch(Throwable t) {
1175 // handleFlawedDiscovery will determine whether this is a fatal
1176 // problem or not. If it is fatal, then a LogConfigurationException
1177 // will be thrown.
1178 handleFlawedDiscovery(logAdapterClassName, currentCL, t);
1179 }
1180
1181 if (currentCL == null) {
1182 break;
1183 }
1184
1185 // try the parent classloader
1186 // currentCL = currentCL.getParent();
1187 currentCL = getParentClassLoader(currentCL);
1188 }
1189
1190 if ((logAdapter != null) && affectState) {
1191 // We've succeeded, so set instance fields
1192 this.logClassName = logAdapterClassName;
1193 this.logConstructor = constructor;
1194
1195 // Identify the <code>setLogFactory</code> method (if there is one)
1196 try {
1197 this.logMethod = logAdapterClass.getMethod("setLogFactory",
1198 logMethodSignature);
1199 logDiagnostic("Found method setLogFactory(LogFactory) in '"
1200 + logAdapterClassName + "'");
1201 } catch (Throwable t) {
1202 this.logMethod = null;
1203 logDiagnostic(
1204 "[INFO] '" + logAdapterClassName
1205 + "' from classloader " + objectId(currentCL)
1206 + " does not declare optional method "
1207 + "setLogFactory(LogFactory)");
1208 }
1209
1210 logDiagnostic(
1211 "Log adapter '" + logAdapterClassName
1212 + "' from classloader " + objectId(logAdapterClass.getClassLoader())
1213 + " has been selected for use.");
1214 }
1215
1216 return logAdapter;
1217 }
1218
1219
1220 /**
1221 * Return the classloader from which we should try to load the logging
1222 * adapter classes.
1223 * <p>
1224 * This method usually returns the context classloader. However if it
1225 * is discovered that the classloader which loaded this class is a child
1226 * of the context classloader <i>and</i> the allowFlawedContext option
1227 * has been set then the classloader which loaded this class is returned
1228 * instead.
1229 * <p>
1230 * The only time when the classloader which loaded this class is a
1231 * descendant (rather than the same as or an ancestor of the context
1232 * classloader) is when an app has created custom classloaders but
1233 * failed to correctly set the context classloader. This is a bug in
1234 * the calling application; however we provide the option for JCL to
1235 * simply generate a warning rather than fail outright.
1236 *
1237 */
1238 private ClassLoader getBaseClassLoader() throws LogConfigurationException {
1239 ClassLoader thisClassLoader = getClassLoader(LogFactoryImpl.class);
1240
1241 if (useTCCL == false) {
1242 return thisClassLoader;
1243 }
1244
1245 ClassLoader contextClassLoader = getContextClassLoaderInternal();
1246
1247 ClassLoader baseClassLoader = getLowestClassLoader(
1248 contextClassLoader, thisClassLoader);
1249
1250 if (baseClassLoader == null) {
1251 // The two classloaders are not part of a parent child relationship.
1252 // In some classloading setups (e.g. JBoss with its
1253 // UnifiedLoaderRepository) this can still work, so if user hasn't
1254 // forbidden it, just return the contextClassLoader.
1255 if (allowFlawedContext) {
1256 if (isDiagnosticsEnabled()) {
1257 logDiagnostic(
1258 "[WARNING] the context classloader is not part of a"
1259 + " parent-child relationship with the classloader that"
1260 + " loaded LogFactoryImpl.");
1261 }
1262 // If contextClassLoader were null, getLowestClassLoader() would
1263 // have returned thisClassLoader. The fact we are here means
1264 // contextClassLoader is not null, so we can just return it.
1265 return contextClassLoader;
1266 }
1267 else {
1268 throw new LogConfigurationException(
1269 "Bad classloader hierarchy; LogFactoryImpl was loaded via"
1270 + " a classloader that is not related to the current context"
1271 + " classloader.");
1272 }
1273 }
1274
1275 if (baseClassLoader != contextClassLoader) {
1276 // We really should just use the contextClassLoader as the starting
1277 // point for scanning for log adapter classes. However it is expected
1278 // that there are a number of broken systems out there which create
1279 // custom classloaders but fail to set the context classloader so
1280 // we handle those flawed systems anyway.
1281 if (allowFlawedContext) {
1282 if (isDiagnosticsEnabled()) {
1283 logDiagnostic(
1284 "Warning: the context classloader is an ancestor of the"
1285 + " classloader that loaded LogFactoryImpl; it should be"
1286 + " the same or a descendant. The application using"
1287 + " commons-logging should ensure the context classloader"
1288 + " is used correctly.");
1289 }
1290 } else {
1291 throw new LogConfigurationException(
1292 "Bad classloader hierarchy; LogFactoryImpl was loaded via"
1293 + " a classloader that is not related to the current context"
1294 + " classloader.");
1295 }
1296 }
1297
1298 return baseClassLoader;
1299 }
1300
1301 /**
1302 * Given two related classloaders, return the one which is a child of
1303 * the other.
1304 * <p>
1305 * @param c1 is a classloader (including the null classloader)
1306 * @param c2 is a classloader (including the null classloader)
1307 *
1308 * @return c1 if it has c2 as an ancestor, c2 if it has c1 as an ancestor,
1309 * and null if neither is an ancestor of the other.
1310 */
1311 private ClassLoader getLowestClassLoader(ClassLoader c1, ClassLoader c2) {
1312 // TODO: use AccessController when dealing with classloaders here
1313
1314 if (c1 == null)
1315 return c2;
1316
1317 if (c2 == null)
1318 return c1;
1319
1320 ClassLoader current;
1321
1322 // scan c1's ancestors to find c2
1323 current = c1;
1324 while (current != null) {
1325 if (current == c2)
1326 return c1;
1327 current = current.getParent();
1328 }
1329
1330 // scan c2's ancestors to find c1
1331 current = c2;
1332 while (current != null) {
1333 if (current == c1)
1334 return c2;
1335 current = current.getParent();
1336 }
1337
1338 return null;
1339 }
1340
1341 /**
1342 * Generates an internal diagnostic logging of the discovery failure and
1343 * then throws a <code>LogConfigurationException</code> that wraps
1344 * the passed <code>Throwable</code>.
1345 *
1346 * @param logAdapterClassName is the class name of the Log implementation
1347 * that could not be instantiated. Cannot be <code>null</code>.
1348 *
1349 * @param classLoader is the classloader that we were trying to load the
1350 * logAdapterClassName from when the exception occurred.
1351 *
1352 * @param discoveryFlaw is the Throwable created by the classloader
1353 *
1354 * @throws LogConfigurationException ALWAYS
1355 */
1356 private void handleFlawedDiscovery(String logAdapterClassName,
1357 ClassLoader classLoader,
1358 Throwable discoveryFlaw) {
1359
1360 if (isDiagnosticsEnabled()) {
1361 logDiagnostic("Could not instantiate Log '"
1362 + logAdapterClassName + "' -- "
1363 + discoveryFlaw.getClass().getName() + ": "
1364 + discoveryFlaw.getLocalizedMessage());
1365
1366 if (discoveryFlaw instanceof InvocationTargetException ) {
1367 // Ok, the lib is there but while trying to create a real underlying
1368 // logger something failed in the underlying lib; display info about
1369 // that if possible.
1370 InvocationTargetException ite = (InvocationTargetException)discoveryFlaw;
1371 Throwable cause = ite.getTargetException();
1372 if (cause != null) {
1373 logDiagnostic("... InvocationTargetException: " +
1374 cause.getClass().getName() + ": " +
1375 cause.getLocalizedMessage());
1376
1377 if (cause instanceof ExceptionInInitializerError) {
1378 ExceptionInInitializerError eiie = (ExceptionInInitializerError)cause;
1379 Throwable cause2 = eiie.getException();
1380 if (cause2 != null) {
1381 logDiagnostic("... ExceptionInInitializerError: " +
1382 cause2.getClass().getName() + ": " +
1383 cause2.getLocalizedMessage());
1384 }
1385 }
1386 }
1387 }
1388 }
1389
1390 if (!allowFlawedDiscovery) {
1391 throw new LogConfigurationException(discoveryFlaw);
1392 }
1393 }
1394
1395
1396 /**
1397 * Report a problem loading the log adapter, then either return
1398 * (if the situation is considered recoverable) or throw a
1399 * LogConfigurationException.
1400 * <p>
1401 * There are two possible reasons why we successfully loaded the
1402 * specified log adapter class then failed to cast it to a Log object:
1403 * <ol>
1404 * <li>the specific class just doesn't implement the Log interface
1405 * (user screwed up), or
1406 * <li> the specified class has bound to a Log class loaded by some other
1407 * classloader; Log@classloaderX cannot be cast to Log@classloaderY.
1408 * </ol>
1409 * <p>
1410 * Here we try to figure out which case has occurred so we can give the
1411 * user some reasonable feedback.
1412 *
1413 * @param badClassLoader is the classloader we loaded the problem class from,
1414 * ie it is equivalent to badClass.getClassLoader().
1415 *
1416 * @param badClass is a Class object with the desired name, but which
1417 * does not implement Log correctly.
1418 *
1419 * @throws LogConfigurationException when the situation
1420 * should not be recovered from.
1421 */
1422 private void handleFlawedHierarchy(ClassLoader badClassLoader, Class badClass)
1423 throws LogConfigurationException {
1424
1425 boolean implementsLog = false;
1426 String logInterfaceName = Log.class.getName();
1427 Class interfaces[] = badClass.getInterfaces();
1428 for (int i = 0; i < interfaces.length; i++) {
1429 if (logInterfaceName.equals(interfaces[i].getName())) {
1430 implementsLog = true;
1431 break;
1432 }
1433 }
1434
1435 if (implementsLog) {
1436 // the class does implement an interface called Log, but
1437 // it is in the wrong classloader
1438 if (isDiagnosticsEnabled()) {
1439 try {
1440 ClassLoader logInterfaceClassLoader = getClassLoader(Log.class);
1441 logDiagnostic(
1442 "Class '" + badClass.getName()
1443 + "' was found in classloader "
1444 + objectId(badClassLoader)
1445 + ". It is bound to a Log interface which is not"
1446 + " the one loaded from classloader "
1447 + objectId(logInterfaceClassLoader));
1448 } catch (Throwable t) {
1449 logDiagnostic(
1450 "Error while trying to output diagnostics about"
1451 + " bad class '" + badClass + "'");
1452 }
1453 }
1454
1455 if (!allowFlawedHierarchy) {
1456 StringBuffer msg = new StringBuffer();
1457 msg.append("Terminating logging for this context ");
1458 msg.append("due to bad log hierarchy. ");
1459 msg.append("You have more than one version of '");
1460 msg.append(Log.class.getName());
1461 msg.append("' visible.");
1462 if (isDiagnosticsEnabled()) {
1463 logDiagnostic(msg.toString());
1464 }
1465 throw new LogConfigurationException(msg.toString());
1466 }
1467
1468 if (isDiagnosticsEnabled()) {
1469 StringBuffer msg = new StringBuffer();
1470 msg.append("Warning: bad log hierarchy. ");
1471 msg.append("You have more than one version of '");
1472 msg.append(Log.class.getName());
1473 msg.append("' visible.");
1474 logDiagnostic(msg.toString());
1475 }
1476 } else {
1477 // this is just a bad adapter class
1478 if (!allowFlawedDiscovery) {
1479 StringBuffer msg = new StringBuffer();
1480 msg.append("Terminating logging for this context. ");
1481 msg.append("Log class '");
1482 msg.append(badClass.getName());
1483 msg.append("' does not implement the Log interface.");
1484 if (isDiagnosticsEnabled()) {
1485 logDiagnostic(msg.toString());
1486 }
1487
1488 throw new LogConfigurationException(msg.toString());
1489 }
1490
1491 if (isDiagnosticsEnabled()) {
1492 StringBuffer msg = new StringBuffer();
1493 msg.append("[WARNING] Log class '");
1494 msg.append(badClass.getName());
1495 msg.append("' does not implement the Log interface.");
1496 logDiagnostic(msg.toString());
1497 }
1498 }
1499 }
1500 }