0001: /*
0002: * Copyright 2001-2004 The Apache Software Foundation.
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016:
0017: package org.apache.commons.logging.impl;
0018:
0019: import java.lang.reflect.Constructor;
0020: import java.lang.reflect.InvocationTargetException;
0021: import java.lang.reflect.Method;
0022: import java.net.URL;
0023: import java.util.Enumeration;
0024: import java.util.Hashtable;
0025: import java.util.Vector;
0026:
0027: import org.apache.commons.logging.Log;
0028: import org.apache.commons.logging.LogConfigurationException;
0029: import org.apache.commons.logging.LogFactory;
0030:
0031: /**
0032: * <p>Concrete subclass of {@link LogFactory} that implements the
0033: * following algorithm to dynamically select a logging implementation
0034: * class to instantiate a wrapper for.</p>
0035: * <ul>
0036: * <li>Use a factory configuration attribute named
0037: * <code>org.apache.commons.logging.Log</code> to identify the
0038: * requested implementation class.</li>
0039: * <li>Use the <code>org.apache.commons.logging.Log</code> system property
0040: * to identify the requested implementation class.</li>
0041: * <li>If <em>Log4J</em> is available, return an instance of
0042: * <code>org.apache.commons.logging.impl.Log4JLogger</code>.</li>
0043: * <li>If <em>JDK 1.4 or later</em> is available, return an instance of
0044: * <code>org.apache.commons.logging.impl.Jdk14Logger</code>.</li>
0045: * <li>Otherwise, return an instance of
0046: * <code>org.apache.commons.logging.impl.SimpleLog</code>.</li>
0047: * </ul>
0048: *
0049: * <p>If the selected {@link Log} implementation class has a
0050: * <code>setLogFactory()</code> method that accepts a {@link LogFactory}
0051: * parameter, this method will be called on each newly created instance
0052: * to identify the associated factory. This makes factory configuration
0053: * attributes available to the Log instance, if it so desires.</p>
0054: *
0055: * <p>This factory will remember previously created <code>Log</code> instances
0056: * for the same name, and will return them on repeated requests to the
0057: * <code>getInstance()</code> method.</p>
0058: *
0059: * @author Rod Waldhoff
0060: * @author Craig R. McClanahan
0061: * @author Richard A. Sitze
0062: * @author Brian Stansberry
0063: * @version $Revision: 399224 $ $Date: 2006-05-03 10:25:54 +0100 (Wed, 03 May 2006) $
0064: */
0065:
0066: public class LogFactoryImpl extends LogFactory {
0067:
0068: /** Log4JLogger class name */
0069: private static final String LOGGING_IMPL_LOG4J_LOGGER = "org.apache.commons.logging.impl.Log4JLogger";
0070: /** Jdk14Logger class name */
0071: private static final String LOGGING_IMPL_JDK14_LOGGER = "org.apache.commons.logging.impl.Jdk14Logger";
0072: /** Jdk13LumberjackLogger class name */
0073: private static final String LOGGING_IMPL_LUMBERJACK_LOGGER = "org.apache.commons.logging.impl.Jdk13LumberjackLogger";
0074: /** SimpleLog class name */
0075: private static final String LOGGING_IMPL_SIMPLE_LOGGER = "org.apache.commons.logging.impl.SimpleLog";
0076:
0077: private static final String PKG_IMPL = "org.apache.commons.logging.impl.";
0078: private static final int PKG_LEN = PKG_IMPL.length();
0079:
0080: // ----------------------------------------------------------- Constructors
0081:
0082: /**
0083: * Public no-arguments constructor required by the lookup mechanism.
0084: */
0085: public LogFactoryImpl() {
0086: super ();
0087: initDiagnostics(); // method on this object
0088: if (isDiagnosticsEnabled()) {
0089: logDiagnostic("Instance created.");
0090: }
0091: }
0092:
0093: // ----------------------------------------------------- Manifest Constants
0094:
0095: /**
0096: * The name (<code>org.apache.commons.logging.Log</code>) of the system
0097: * property identifying our {@link Log} implementation class.
0098: */
0099: public static final String LOG_PROPERTY = "org.apache.commons.logging.Log";
0100:
0101: /**
0102: * The deprecated system property used for backwards compatibility with
0103: * old versions of JCL.
0104: */
0105: protected static final String LOG_PROPERTY_OLD = "org.apache.commons.logging.log";
0106:
0107: /**
0108: * The name (<code>org.apache.commons.logging.Log.allowFlawedContext</code>)
0109: * of the system property which can be set true/false to
0110: * determine system behaviour when a bad context-classloader is encountered.
0111: * When set to false, a LogConfigurationException is thrown if
0112: * LogFactoryImpl is loaded via a child classloader of the TCCL (this
0113: * should never happen in sane systems).
0114: *
0115: * Default behaviour: true (tolerates bad context classloaders)
0116: *
0117: * See also method setAttribute.
0118: */
0119: public static final String ALLOW_FLAWED_CONTEXT_PROPERTY = "org.apache.commons.logging.Log.allowFlawedContext";
0120:
0121: /**
0122: * The name (<code>org.apache.commons.logging.Log.allowFlawedDiscovery</code>)
0123: * of the system property which can be set true/false to
0124: * determine system behaviour when a bad logging adapter class is
0125: * encountered during logging discovery. When set to false, an
0126: * exception will be thrown and the app will fail to start. When set
0127: * to true, discovery will continue (though the user might end up
0128: * with a different logging implementation than they expected).
0129: *
0130: * Default behaviour: true (tolerates bad logging adapters)
0131: *
0132: * See also method setAttribute.
0133: */
0134: public static final String ALLOW_FLAWED_DISCOVERY_PROPERTY = "org.apache.commons.logging.Log.allowFlawedDiscovery";
0135:
0136: /**
0137: * The name (<code>org.apache.commons.logging.Log.allowFlawedHierarchy</code>)
0138: * of the system property which can be set true/false to
0139: * determine system behaviour when a logging adapter class is
0140: * encountered which has bound to the wrong Log class implementation.
0141: * When set to false, an exception will be thrown and the app will fail
0142: * to start. When set to true, discovery will continue (though the user
0143: * might end up with a different logging implementation than they expected).
0144: *
0145: * Default behaviour: true (tolerates bad Log class hierarchy)
0146: *
0147: * See also method setAttribute.
0148: */
0149: public static final String ALLOW_FLAWED_HIERARCHY_PROPERTY = "org.apache.commons.logging.Log.allowFlawedHierarchy";
0150:
0151: /**
0152: * The names of classes that will be tried (in order) as logging
0153: * adapters. Each class is expected to implement the Log interface,
0154: * and to throw NoClassDefFound or ExceptionInInitializerError when
0155: * loaded if the underlying logging library is not available. Any
0156: * other error indicates that the underlying logging library is available
0157: * but broken/unusable for some reason.
0158: */
0159: private static final String[] classesToDiscover = {
0160: LOGGING_IMPL_LOG4J_LOGGER,
0161: "org.apache.commons.logging.impl.Jdk14Logger",
0162: "org.apache.commons.logging.impl.Jdk13LumberjackLogger",
0163: "org.apache.commons.logging.impl.SimpleLog" };
0164:
0165: // ----------------------------------------------------- Instance Variables
0166:
0167: /**
0168: * Determines whether logging classes should be loaded using the thread-context
0169: * classloader, or via the classloader that loaded this LogFactoryImpl class.
0170: */
0171: private boolean useTCCL = true;
0172:
0173: /**
0174: * The string prefixed to every message output by the logDiagnostic method.
0175: */
0176: private String diagnosticPrefix;
0177:
0178: /**
0179: * Configuration attributes.
0180: */
0181: protected Hashtable attributes = new Hashtable();
0182:
0183: /**
0184: * The {@link org.apache.commons.logging.Log} instances that have
0185: * already been created, keyed by logger name.
0186: */
0187: protected Hashtable instances = new Hashtable();
0188:
0189: /**
0190: * Name of the class implementing the Log interface.
0191: */
0192: private String logClassName;
0193:
0194: /**
0195: * The one-argument constructor of the
0196: * {@link org.apache.commons.logging.Log}
0197: * implementation class that will be used to create new instances.
0198: * This value is initialized by <code>getLogConstructor()</code>,
0199: * and then returned repeatedly.
0200: */
0201: protected Constructor logConstructor = null;
0202:
0203: /**
0204: * The signature of the Constructor to be used.
0205: */
0206: protected Class logConstructorSignature[] = { java.lang.String.class };
0207:
0208: /**
0209: * The one-argument <code>setLogFactory</code> method of the selected
0210: * {@link org.apache.commons.logging.Log} method, if it exists.
0211: */
0212: protected Method logMethod = null;
0213:
0214: /**
0215: * The signature of the <code>setLogFactory</code> method to be used.
0216: */
0217: protected Class logMethodSignature[] = { LogFactory.class };
0218:
0219: /**
0220: * See getBaseClassLoader and initConfiguration.
0221: */
0222: private boolean allowFlawedContext;
0223:
0224: /**
0225: * See handleFlawedDiscovery and initConfiguration.
0226: */
0227: private boolean allowFlawedDiscovery;
0228:
0229: /**
0230: * See handleFlawedHierarchy and initConfiguration.
0231: */
0232: private boolean allowFlawedHierarchy;
0233:
0234: // --------------------------------------------------------- Public Methods
0235:
0236: /**
0237: * Return the configuration attribute with the specified name (if any),
0238: * or <code>null</code> if there is no such attribute.
0239: *
0240: * @param name Name of the attribute to return
0241: */
0242: public Object getAttribute(String name) {
0243:
0244: return (attributes.get(name));
0245:
0246: }
0247:
0248: /**
0249: * Return an array containing the names of all currently defined
0250: * configuration attributes. If there are no such attributes, a zero
0251: * length array is returned.
0252: */
0253: public String[] getAttributeNames() {
0254:
0255: Vector names = new Vector();
0256: Enumeration keys = attributes.keys();
0257: while (keys.hasMoreElements()) {
0258: names.addElement((String) keys.nextElement());
0259: }
0260: String results[] = new String[names.size()];
0261: for (int i = 0; i < results.length; i++) {
0262: results[i] = (String) names.elementAt(i);
0263: }
0264: return (results);
0265:
0266: }
0267:
0268: /**
0269: * Convenience method to derive a name from the specified class and
0270: * call <code>getInstance(String)</code> with it.
0271: *
0272: * @param clazz Class for which a suitable Log name will be derived
0273: *
0274: * @exception LogConfigurationException if a suitable <code>Log</code>
0275: * instance cannot be returned
0276: */
0277: public Log getInstance(Class clazz)
0278: throws LogConfigurationException {
0279:
0280: return (getInstance(clazz.getName()));
0281:
0282: }
0283:
0284: /**
0285: * <p>Construct (if necessary) and return a <code>Log</code> instance,
0286: * using the factory's current set of configuration attributes.</p>
0287: *
0288: * <p><strong>NOTE</strong> - Depending upon the implementation of
0289: * the <code>LogFactory</code> you are using, the <code>Log</code>
0290: * instance you are returned may or may not be local to the current
0291: * application, and may or may not be returned again on a subsequent
0292: * call with the same name argument.</p>
0293: *
0294: * @param name Logical name of the <code>Log</code> instance to be
0295: * returned (the meaning of this name is only known to the underlying
0296: * logging implementation that is being wrapped)
0297: *
0298: * @exception LogConfigurationException if a suitable <code>Log</code>
0299: * instance cannot be returned
0300: */
0301: public Log getInstance(String name)
0302: throws LogConfigurationException {
0303:
0304: Log instance = (Log) instances.get(name);
0305: if (instance == null) {
0306: instance = newInstance(name);
0307: instances.put(name, instance);
0308: }
0309: return (instance);
0310:
0311: }
0312:
0313: /**
0314: * Release any internal references to previously created
0315: * {@link org.apache.commons.logging.Log}
0316: * instances returned by this factory. This is useful in environments
0317: * like servlet containers, which implement application reloading by
0318: * throwing away a ClassLoader. Dangling references to objects in that
0319: * class loader would prevent garbage collection.
0320: */
0321: public void release() {
0322:
0323: logDiagnostic("Releasing all known loggers");
0324: instances.clear();
0325: }
0326:
0327: /**
0328: * Remove any configuration attribute associated with the specified name.
0329: * If there is no such attribute, no action is taken.
0330: *
0331: * @param name Name of the attribute to remove
0332: */
0333: public void removeAttribute(String name) {
0334:
0335: attributes.remove(name);
0336:
0337: }
0338:
0339: /**
0340: * Set the configuration attribute with the specified name. Calling
0341: * this with a <code>null</code> value is equivalent to calling
0342: * <code>removeAttribute(name)</code>.
0343: * <p>
0344: * This method can be used to set logging configuration programmatically
0345: * rather than via system properties. It can also be used in code running
0346: * within a container (such as a webapp) to configure behaviour on a
0347: * per-component level instead of globally as system properties would do.
0348: * To use this method instead of a system property, call
0349: * <pre>
0350: * LogFactory.getFactory().setAttribute(...)
0351: * </pre>
0352: * This must be done before the first Log object is created; configuration
0353: * changes after that point will be ignored.
0354: * <p>
0355: * This method is also called automatically if LogFactory detects a
0356: * commons-logging.properties file; every entry in that file is set
0357: * automatically as an attribute here.
0358: *
0359: * @param name Name of the attribute to set
0360: * @param value Value of the attribute to set, or <code>null</code>
0361: * to remove any setting for this attribute
0362: */
0363: public void setAttribute(String name, Object value) {
0364:
0365: if (logConstructor != null) {
0366: logDiagnostic("setAttribute: call too late; configuration already performed.");
0367: }
0368:
0369: if (value == null) {
0370: attributes.remove(name);
0371: } else {
0372: attributes.put(name, value);
0373: }
0374:
0375: if (name.equals(TCCL_KEY)) {
0376: useTCCL = Boolean.valueOf(value.toString()).booleanValue();
0377: }
0378:
0379: }
0380:
0381: // ------------------------------------------------------
0382: // Static Methods
0383: //
0384: // These methods only defined as workarounds for a java 1.2 bug;
0385: // theoretically none of these are needed.
0386: // ------------------------------------------------------
0387:
0388: /**
0389: * Gets the context classloader.
0390: * This method is a workaround for a java 1.2 compiler bug.
0391: * @since 1.1
0392: */
0393: protected static ClassLoader getContextClassLoader()
0394: throws LogConfigurationException {
0395: return LogFactory.getContextClassLoader();
0396: }
0397:
0398: /**
0399: * Workaround for bug in Java1.2; in theory this method is not needed.
0400: * See LogFactory.isDiagnosticsEnabled.
0401: */
0402: protected static boolean isDiagnosticsEnabled() {
0403: return LogFactory.isDiagnosticsEnabled();
0404: }
0405:
0406: /**
0407: * Workaround for bug in Java1.2; in theory this method is not needed.
0408: * See LogFactory.getClassLoader.
0409: * @since 1.1
0410: */
0411: protected static ClassLoader getClassLoader(Class clazz) {
0412: return LogFactory.getClassLoader(clazz);
0413: }
0414:
0415: // ------------------------------------------------------ Protected Methods
0416:
0417: /**
0418: * Calculate and cache a string that uniquely identifies this instance,
0419: * including which classloader the object was loaded from.
0420: * <p>
0421: * This string will later be prefixed to each "internal logging" message
0422: * emitted, so that users can clearly see any unexpected behaviour.
0423: * <p>
0424: * Note that this method does not detect whether internal logging is
0425: * enabled or not, nor where to output stuff if it is; that is all
0426: * handled by the parent LogFactory class. This method just computes
0427: * its own unique prefix for log messages.
0428: */
0429: private void initDiagnostics() {
0430: // It would be nice to include an identifier of the context classloader
0431: // that this LogFactoryImpl object is responsible for. However that
0432: // isn't possible as that information isn't available. It is possible
0433: // to figure this out by looking at the logging from LogFactory to
0434: // see the context & impl ids from when this object was instantiated,
0435: // in order to link the impl id output as this object's prefix back to
0436: // the context it is intended to manage.
0437: // Note that this prefix should be kept consistent with that
0438: // in LogFactory.
0439: Class clazz = this .getClass();
0440: ClassLoader classLoader = getClassLoader(clazz);
0441: String classLoaderName;
0442: try {
0443: if (classLoader == null) {
0444: classLoaderName = "BOOTLOADER";
0445: } else {
0446: classLoaderName = objectId(classLoader);
0447: }
0448: } catch (SecurityException e) {
0449: classLoaderName = "UNKNOWN";
0450: }
0451: diagnosticPrefix = "[LogFactoryImpl@"
0452: + System.identityHashCode(this ) + " from "
0453: + classLoaderName + "] ";
0454: }
0455:
0456: /**
0457: * Output a diagnostic message to a user-specified destination (if the
0458: * user has enabled diagnostic logging).
0459: *
0460: * @param msg diagnostic message
0461: * @since 1.1
0462: */
0463: protected void logDiagnostic(String msg) {
0464: if (isDiagnosticsEnabled()) {
0465: logRawDiagnostic(diagnosticPrefix + msg);
0466: }
0467: }
0468:
0469: /**
0470: * Return the fully qualified Java classname of the {@link Log}
0471: * implementation we will be using.
0472: *
0473: * @deprecated Never invoked by this class; subclasses should not assume
0474: * it will be.
0475: */
0476: protected String getLogClassName() {
0477:
0478: if (logClassName == null) {
0479: discoverLogImplementation(getClass().getName());
0480: }
0481:
0482: return logClassName;
0483: }
0484:
0485: /**
0486: * <p>Return the <code>Constructor</code> that can be called to instantiate
0487: * new {@link org.apache.commons.logging.Log} instances.</p>
0488: *
0489: * <p><strong>IMPLEMENTATION NOTE</strong> - Race conditions caused by
0490: * calling this method from more than one thread are ignored, because
0491: * the same <code>Constructor</code> instance will ultimately be derived
0492: * in all circumstances.</p>
0493: *
0494: * @exception LogConfigurationException if a suitable constructor
0495: * cannot be returned
0496: *
0497: * @deprecated Never invoked by this class; subclasses should not assume
0498: * it will be.
0499: */
0500: protected Constructor getLogConstructor()
0501: throws LogConfigurationException {
0502:
0503: // Return the previously identified Constructor (if any)
0504: if (logConstructor == null) {
0505: discoverLogImplementation(getClass().getName());
0506: }
0507:
0508: return logConstructor;
0509: }
0510:
0511: /**
0512: * Is <em>JDK 1.3 with Lumberjack</em> logging available?
0513: *
0514: * @deprecated Never invoked by this class; subclasses should not assume
0515: * it will be.
0516: */
0517: protected boolean isJdk13LumberjackAvailable() {
0518: return isLogLibraryAvailable("Jdk13Lumberjack",
0519: "org.apache.commons.logging.impl.Jdk13LumberjackLogger");
0520: }
0521:
0522: /**
0523: * <p>Return <code>true</code> if <em>JDK 1.4 or later</em> logging
0524: * is available. Also checks that the <code>Throwable</code> class
0525: * supports <code>getStackTrace()</code>, which is required by
0526: * Jdk14Logger.</p>
0527: *
0528: * @deprecated Never invoked by this class; subclasses should not assume
0529: * it will be.
0530: */
0531: protected boolean isJdk14Available() {
0532: return isLogLibraryAvailable("Jdk14",
0533: "org.apache.commons.logging.impl.Jdk14Logger");
0534: }
0535:
0536: /**
0537: * Is a <em>Log4J</em> implementation available?
0538: *
0539: * @deprecated Never invoked by this class; subclasses should not assume
0540: * it will be.
0541: */
0542: protected boolean isLog4JAvailable() {
0543: return isLogLibraryAvailable("Log4J", LOGGING_IMPL_LOG4J_LOGGER);
0544: }
0545:
0546: /**
0547: * Create and return a new {@link org.apache.commons.logging.Log}
0548: * instance for the specified name.
0549: *
0550: * @param name Name of the new logger
0551: *
0552: * @exception LogConfigurationException if a new instance cannot
0553: * be created
0554: */
0555: protected Log newInstance(String name)
0556: throws LogConfigurationException {
0557:
0558: Log instance = null;
0559: try {
0560: if (logConstructor == null) {
0561: instance = discoverLogImplementation(name);
0562: } else {
0563: Object params[] = { name };
0564: instance = (Log) logConstructor.newInstance(params);
0565: }
0566:
0567: if (logMethod != null) {
0568: Object params[] = { this };
0569: logMethod.invoke(instance, params);
0570: }
0571:
0572: return (instance);
0573:
0574: } catch (LogConfigurationException lce) {
0575:
0576: // this type of exception means there was a problem in discovery
0577: // and we've already output diagnostics about the issue, etc.;
0578: // just pass it on
0579: throw (LogConfigurationException) lce;
0580:
0581: } catch (InvocationTargetException e) {
0582: // A problem occurred invoking the Constructor or Method
0583: // previously discovered
0584: Throwable c = e.getTargetException();
0585: if (c != null) {
0586: throw new LogConfigurationException(c);
0587: } else {
0588: throw new LogConfigurationException(e);
0589: }
0590: } catch (Throwable t) {
0591: // A problem occurred invoking the Constructor or Method
0592: // previously discovered
0593: throw new LogConfigurationException(t);
0594: }
0595: }
0596:
0597: // ------------------------------------------------------ Private Methods
0598:
0599: /**
0600: * Utility method to check whether a particular logging library is
0601: * present and available for use. Note that this does <i>not</i>
0602: * affect the future behaviour of this class.
0603: */
0604: private boolean isLogLibraryAvailable(String name, String classname) {
0605: if (isDiagnosticsEnabled()) {
0606: logDiagnostic("Checking for '" + name + "'.");
0607: }
0608: try {
0609: Log log = createLogFromClass(classname, this .getClass()
0610: .getName(), // dummy category
0611: false);
0612:
0613: if (log == null) {
0614: if (isDiagnosticsEnabled()) {
0615: logDiagnostic("Did not find '" + name + "'.");
0616: }
0617: return false;
0618: } else {
0619: if (isDiagnosticsEnabled()) {
0620: logDiagnostic("Found '" + name + "'.");
0621: }
0622: return true;
0623: }
0624: } catch (LogConfigurationException e) {
0625: if (isDiagnosticsEnabled()) {
0626: logDiagnostic("Logging system '" + name
0627: + "' is available but not useable.");
0628: }
0629: return false;
0630: }
0631: }
0632:
0633: /**
0634: * Attempt to find an attribute (see method setAttribute) or a
0635: * system property with the provided name and return its value.
0636: * <p>
0637: * The attributes associated with this object are checked before
0638: * system properties in case someone has explicitly called setAttribute,
0639: * or a configuration property has been set in a commons-logging.properties
0640: * file.
0641: *
0642: * @return the value associated with the property, or null.
0643: */
0644: private String getConfigurationValue(String property) {
0645: if (isDiagnosticsEnabled()) {
0646: logDiagnostic("[ENV] Trying to get configuration for item "
0647: + property);
0648: }
0649:
0650: Object valueObj = getAttribute(property);
0651: if (valueObj != null) {
0652: if (isDiagnosticsEnabled()) {
0653: logDiagnostic("[ENV] Found LogFactory attribute ["
0654: + valueObj + "] for " + property);
0655: }
0656: return valueObj.toString();
0657: }
0658:
0659: if (isDiagnosticsEnabled()) {
0660: logDiagnostic("[ENV] No LogFactory attribute found for "
0661: + property);
0662: }
0663:
0664: try {
0665: String value = System.getProperty(property);
0666: if (value != null) {
0667: if (isDiagnosticsEnabled()) {
0668: logDiagnostic("[ENV] Found system property ["
0669: + value + "] for " + property);
0670: }
0671: return value;
0672: }
0673:
0674: if (isDiagnosticsEnabled()) {
0675: logDiagnostic("[ENV] No system property found for property "
0676: + property);
0677: }
0678: } catch (SecurityException e) {
0679: if (isDiagnosticsEnabled()) {
0680: logDiagnostic("[ENV] Security prevented reading system property "
0681: + property);
0682: }
0683: }
0684:
0685: if (isDiagnosticsEnabled()) {
0686: logDiagnostic("[ENV] No configuration defined for item "
0687: + property);
0688: }
0689:
0690: return null;
0691: }
0692:
0693: /**
0694: * Get the setting for the user-configurable behaviour specified by key.
0695: * If nothing has explicitly been set, then return dflt.
0696: */
0697: private boolean getBooleanConfiguration(String key, boolean dflt) {
0698: String val = getConfigurationValue(key);
0699: if (val == null)
0700: return dflt;
0701: return Boolean.valueOf(val).booleanValue();
0702: }
0703:
0704: /**
0705: * Initialize a number of variables that control the behaviour of this
0706: * class and that can be tweaked by the user. This is done when the first
0707: * logger is created, not in the constructor of this class, because we
0708: * need to give the user a chance to call method setAttribute in order to
0709: * configure this object.
0710: */
0711: private void initConfiguration() {
0712: allowFlawedContext = getBooleanConfiguration(
0713: ALLOW_FLAWED_CONTEXT_PROPERTY, true);
0714: allowFlawedDiscovery = getBooleanConfiguration(
0715: ALLOW_FLAWED_DISCOVERY_PROPERTY, true);
0716: allowFlawedHierarchy = getBooleanConfiguration(
0717: ALLOW_FLAWED_HIERARCHY_PROPERTY, true);
0718: }
0719:
0720: /**
0721: * Attempts to create a Log instance for the given category name.
0722: * Follows the discovery process described in the class javadoc.
0723: *
0724: * @param logCategory the name of the log category
0725: *
0726: * @throws LogConfigurationException if an error in discovery occurs,
0727: * or if no adapter at all can be instantiated
0728: */
0729: private Log discoverLogImplementation(String logCategory)
0730: throws LogConfigurationException {
0731: if (isDiagnosticsEnabled()) {
0732: logDiagnostic("Discovering a Log implementation...");
0733: }
0734:
0735: initConfiguration();
0736:
0737: Log result = null;
0738:
0739: // See if the user specified the Log implementation to use
0740: String specifiedLogClassName = findUserSpecifiedLogClassName();
0741:
0742: if (specifiedLogClassName != null) {
0743: if (isDiagnosticsEnabled()) {
0744: logDiagnostic("Attempting to load user-specified log class '"
0745: + specifiedLogClassName + "'...");
0746: }
0747:
0748: result = createLogFromClass(specifiedLogClassName,
0749: logCategory, true);
0750: if (result == null) {
0751: StringBuffer messageBuffer = new StringBuffer(
0752: "User-specified log class '");
0753: messageBuffer.append(specifiedLogClassName);
0754: messageBuffer
0755: .append("' cannot be found or is not useable.");
0756:
0757: // Mistyping or misspelling names is a common fault.
0758: // Construct a good error message, if we can
0759: if (specifiedLogClassName != null) {
0760: informUponSimilarName(messageBuffer,
0761: specifiedLogClassName,
0762: LOGGING_IMPL_LOG4J_LOGGER);
0763: informUponSimilarName(messageBuffer,
0764: specifiedLogClassName,
0765: LOGGING_IMPL_JDK14_LOGGER);
0766: informUponSimilarName(messageBuffer,
0767: specifiedLogClassName,
0768: LOGGING_IMPL_LUMBERJACK_LOGGER);
0769: informUponSimilarName(messageBuffer,
0770: specifiedLogClassName,
0771: LOGGING_IMPL_SIMPLE_LOGGER);
0772: }
0773: throw new LogConfigurationException(messageBuffer
0774: .toString());
0775: }
0776:
0777: return result;
0778: }
0779:
0780: // No user specified log; try to discover what's on the classpath
0781: //
0782: // Note that we deliberately loop here over classesToDiscover and
0783: // expect method createLogFromClass to loop over the possible source
0784: // classloaders. The effect is:
0785: // for each discoverable log adapter
0786: // for each possible classloader
0787: // see if it works
0788: //
0789: // It appears reasonable at first glance to do the opposite:
0790: // for each possible classloader
0791: // for each discoverable log adapter
0792: // see if it works
0793: //
0794: // The latter certainly has advantages for user-installable logging
0795: // libraries such as log4j; in a webapp for example this code should
0796: // first check whether the user has provided any of the possible
0797: // logging libraries before looking in the parent classloader.
0798: // Unfortunately, however, Jdk14Logger will always work in jvm>=1.4,
0799: // and SimpleLog will always work in any JVM. So the loop would never
0800: // ever look for logging libraries in the parent classpath. Yet many
0801: // users would expect that putting log4j there would cause it to be
0802: // detected (and this is the historical JCL behaviour). So we go with
0803: // the first approach. A user that has bundled a specific logging lib
0804: // in a webapp should use a commons-logging.properties file or a
0805: // service file in META-INF to force use of that logging lib anyway,
0806: // rather than relying on discovery.
0807:
0808: if (isDiagnosticsEnabled()) {
0809: logDiagnostic("No user-specified Log implementation; performing discovery"
0810: + " using the standard supported logging implementations...");
0811: }
0812: for (int i = 0; (i < classesToDiscover.length)
0813: && (result == null); ++i) {
0814: result = createLogFromClass(classesToDiscover[i],
0815: logCategory, true);
0816: }
0817:
0818: if (result == null) {
0819: throw new LogConfigurationException(
0820: "No suitable Log implementation");
0821: }
0822:
0823: return result;
0824: }
0825:
0826: /**
0827: * Appends message if the given name is similar to the candidate.
0828: * @param messageBuffer <code>StringBuffer</code> the message should be appended to,
0829: * not null
0830: * @param name the (trimmed) name to be test against the candidate, not null
0831: * @param candidate the candidate name (not null)
0832: */
0833: private void informUponSimilarName(
0834: final StringBuffer messageBuffer, final String name,
0835: final String candidate) {
0836: if (name.equals(candidate)) {
0837: // Don't suggest a name that is exactly the same as the one the
0838: // user tried...
0839: return;
0840: }
0841:
0842: // If the user provides a name that is in the right package, and gets
0843: // the first 5 characters of the adapter class right (ignoring case),
0844: // then suggest the candidate adapter class name.
0845: if (name.regionMatches(true, 0, candidate, 0, PKG_LEN + 5)) {
0846: messageBuffer.append(" Did you mean '");
0847: messageBuffer.append(candidate);
0848: messageBuffer.append("'?");
0849: }
0850: }
0851:
0852: /**
0853: * Checks system properties and the attribute map for
0854: * a Log implementation specified by the user under the
0855: * property names {@link #LOG_PROPERTY} or {@link #LOG_PROPERTY_OLD}.
0856: *
0857: * @return classname specified by the user, or <code>null</code>
0858: */
0859: private String findUserSpecifiedLogClassName() {
0860: if (isDiagnosticsEnabled()) {
0861: logDiagnostic("Trying to get log class from attribute '"
0862: + LOG_PROPERTY + "'");
0863: }
0864: String specifiedClass = (String) getAttribute(LOG_PROPERTY);
0865:
0866: if (specifiedClass == null) { // @deprecated
0867: if (isDiagnosticsEnabled()) {
0868: logDiagnostic("Trying to get log class from attribute '"
0869: + LOG_PROPERTY_OLD + "'");
0870: }
0871: specifiedClass = (String) getAttribute(LOG_PROPERTY_OLD);
0872: }
0873:
0874: if (specifiedClass == null) {
0875: if (isDiagnosticsEnabled()) {
0876: logDiagnostic("Trying to get log class from system property '"
0877: + LOG_PROPERTY + "'");
0878: }
0879: try {
0880: specifiedClass = System.getProperty(LOG_PROPERTY);
0881: } catch (SecurityException e) {
0882: if (isDiagnosticsEnabled()) {
0883: logDiagnostic("No access allowed to system property '"
0884: + LOG_PROPERTY + "' - " + e.getMessage());
0885: }
0886: }
0887: }
0888:
0889: if (specifiedClass == null) { // @deprecated
0890: if (isDiagnosticsEnabled()) {
0891: logDiagnostic("Trying to get log class from system property '"
0892: + LOG_PROPERTY_OLD + "'");
0893: }
0894: try {
0895: specifiedClass = System.getProperty(LOG_PROPERTY_OLD);
0896: } catch (SecurityException e) {
0897: if (isDiagnosticsEnabled()) {
0898: logDiagnostic("No access allowed to system property '"
0899: + LOG_PROPERTY_OLD
0900: + "' - "
0901: + e.getMessage());
0902: }
0903: }
0904: }
0905:
0906: // Remove any whitespace; it's never valid in a classname so its
0907: // presence just means a user mistake. As we know what they meant,
0908: // we may as well strip the spaces.
0909: if (specifiedClass != null) {
0910: specifiedClass = specifiedClass.trim();
0911: }
0912:
0913: return specifiedClass;
0914: }
0915:
0916: /**
0917: * Attempts to load the given class, find a suitable constructor,
0918: * and instantiate an instance of Log.
0919: *
0920: * @param logAdapterClassName classname of the Log implementation
0921: *
0922: * @param logCategory argument to pass to the Log implementation's
0923: * constructor
0924: *
0925: * @param affectState <code>true</code> if this object's state should
0926: * be affected by this method call, <code>false</code> otherwise.
0927: *
0928: * @return an instance of the given class, or null if the logging
0929: * library associated with the specified adapter is not available.
0930: *
0931: * @throws LogConfigurationException if there was a serious error with
0932: * configuration and the handleFlawedDiscovery method decided this
0933: * problem was fatal.
0934: */
0935: private Log createLogFromClass(String logAdapterClassName,
0936: String logCategory, boolean affectState)
0937: throws LogConfigurationException {
0938:
0939: if (isDiagnosticsEnabled()) {
0940: logDiagnostic("Attempting to instantiate '"
0941: + logAdapterClassName + "'");
0942: }
0943:
0944: Object[] params = { logCategory };
0945: Log logAdapter = null;
0946: Constructor constructor = null;
0947:
0948: Class logAdapterClass = null;
0949: ClassLoader currentCL = getBaseClassLoader();
0950:
0951: for (;;) {
0952: // Loop through the classloader hierarchy trying to find
0953: // a viable classloader.
0954: logDiagnostic("Trying to load '" + logAdapterClassName
0955: + "' from classloader " + objectId(currentCL));
0956: try {
0957: if (isDiagnosticsEnabled()) {
0958: // Show the location of the first occurrence of the .class file
0959: // in the classpath. This is the location that ClassLoader.loadClass
0960: // will load the class from -- unless the classloader is doing
0961: // something weird.
0962: URL url;
0963: String resourceName = logAdapterClassName.replace(
0964: '.', '/')
0965: + ".class";
0966: if (currentCL != null) {
0967: url = currentCL.getResource(resourceName);
0968: } else {
0969: url = ClassLoader
0970: .getSystemResource(resourceName
0971: + ".class");
0972: }
0973:
0974: if (url == null) {
0975: logDiagnostic("Class '" + logAdapterClassName
0976: + "' [" + resourceName
0977: + "] cannot be found.");
0978: } else {
0979: logDiagnostic("Class '" + logAdapterClassName
0980: + "' was found at '" + url + "'");
0981: }
0982: }
0983:
0984: Class c = null;
0985: try {
0986: c = Class.forName(logAdapterClassName, true,
0987: currentCL);
0988: } catch (ClassNotFoundException originalClassNotFoundException) {
0989: // The current classloader was unable to find the log adapter
0990: // in this or any ancestor classloader. There's no point in
0991: // trying higher up in the hierarchy in this case..
0992: String msg = ""
0993: + originalClassNotFoundException
0994: .getMessage();
0995: logDiagnostic("The log adapter '"
0996: + logAdapterClassName
0997: + "' is not available via classloader "
0998: + objectId(currentCL) + ": " + msg.trim());
0999: try {
1000: // Try the class classloader.
1001: // This may work in cases where the TCCL
1002: // does not contain the code executed or JCL.
1003: // This behaviour indicates that the application
1004: // classloading strategy is not consistent with the
1005: // Java 1.2 classloading guidelines but JCL can
1006: // and so should handle this case.
1007: c = Class.forName(logAdapterClassName);
1008: } catch (ClassNotFoundException secondaryClassNotFoundException) {
1009: // no point continuing: this adapter isn't available
1010: msg = ""
1011: + secondaryClassNotFoundException
1012: .getMessage();
1013: logDiagnostic("The log adapter '"
1014: + logAdapterClassName
1015: + "' is not available via the LogFactoryImpl class classloader: "
1016: + msg.trim());
1017: break;
1018: }
1019: }
1020:
1021: constructor = c.getConstructor(logConstructorSignature);
1022: Object o = constructor.newInstance(params);
1023:
1024: // Note that we do this test after trying to create an instance
1025: // [rather than testing Log.class.isAssignableFrom(c)] so that
1026: // we don't complain about Log hierarchy problems when the
1027: // adapter couldn't be instantiated anyway.
1028: if (o instanceof Log) {
1029: logAdapterClass = c;
1030: logAdapter = (Log) o;
1031: break;
1032: }
1033:
1034: // Oops, we have a potential problem here. An adapter class
1035: // has been found and its underlying lib is present too, but
1036: // there are multiple Log interface classes available making it
1037: // impossible to cast to the type the caller wanted. We
1038: // certainly can't use this logger, but we need to know whether
1039: // to keep on discovering or terminate now.
1040: //
1041: // The handleFlawedHierarchy method will throw
1042: // LogConfigurationException if it regards this problem as
1043: // fatal, and just return if not.
1044: handleFlawedHierarchy(currentCL, c);
1045: } catch (NoClassDefFoundError e) {
1046: // We were able to load the adapter but it had references to
1047: // other classes that could not be found. This simply means that
1048: // the underlying logger library is not present in this or any
1049: // ancestor classloader. There's no point in trying higher up
1050: // in the hierarchy in this case..
1051: String msg = "" + e.getMessage();
1052: logDiagnostic("The log adapter '"
1053: + logAdapterClassName
1054: + "' is missing dependencies when loaded via classloader "
1055: + objectId(currentCL) + ": " + msg.trim());
1056: break;
1057: } catch (ExceptionInInitializerError e) {
1058: // A static initializer block or the initializer code associated
1059: // with a static variable on the log adapter class has thrown
1060: // an exception.
1061: //
1062: // We treat this as meaning the adapter's underlying logging
1063: // library could not be found.
1064: String msg = "" + e.getMessage();
1065: logDiagnostic("The log adapter '"
1066: + logAdapterClassName
1067: + "' is unable to initialize itself when loaded via classloader "
1068: + objectId(currentCL) + ": " + msg.trim());
1069: break;
1070: } catch (LogConfigurationException e) {
1071: // call to handleFlawedHierarchy above must have thrown
1072: // a LogConfigurationException, so just throw it on
1073: throw e;
1074: } catch (Throwable t) {
1075: // handleFlawedDiscovery will determine whether this is a fatal
1076: // problem or not. If it is fatal, then a LogConfigurationException
1077: // will be thrown.
1078: handleFlawedDiscovery(logAdapterClassName, currentCL, t);
1079: }
1080:
1081: if (currentCL == null) {
1082: break;
1083: }
1084:
1085: // try the parent classloader
1086: currentCL = currentCL.getParent();
1087: }
1088:
1089: if ((logAdapter != null) && affectState) {
1090: // We've succeeded, so set instance fields
1091: this .logClassName = logAdapterClassName;
1092: this .logConstructor = constructor;
1093:
1094: // Identify the <code>setLogFactory</code> method (if there is one)
1095: try {
1096: this .logMethod = logAdapterClass.getMethod(
1097: "setLogFactory", logMethodSignature);
1098: logDiagnostic("Found method setLogFactory(LogFactory) in '"
1099: + logAdapterClassName + "'");
1100: } catch (Throwable t) {
1101: this .logMethod = null;
1102: logDiagnostic("[INFO] '" + logAdapterClassName
1103: + "' from classloader " + objectId(currentCL)
1104: + " does not declare optional method "
1105: + "setLogFactory(LogFactory)");
1106: }
1107:
1108: logDiagnostic("Log adapter '" + logAdapterClassName
1109: + "' from classloader "
1110: + objectId(logAdapterClass.getClassLoader())
1111: + " has been selected for use.");
1112: }
1113:
1114: return logAdapter;
1115: }
1116:
1117: /**
1118: * Return the classloader from which we should try to load the logging
1119: * adapter classes.
1120: * <p>
1121: * This method usually returns the context classloader. However if it
1122: * is discovered that the classloader which loaded this class is a child
1123: * of the context classloader <i>and</i> the allowFlawedContext option
1124: * has been set then the classloader which loaded this class is returned
1125: * instead.
1126: * <p>
1127: * The only time when the classloader which loaded this class is a
1128: * descendant (rather than the same as or an ancestor of the context
1129: * classloader) is when an app has created custom classloaders but
1130: * failed to correctly set the context classloader. This is a bug in
1131: * the calling application; however we provide the option for JCL to
1132: * simply generate a warning rather than fail outright.
1133: *
1134: */
1135: private ClassLoader getBaseClassLoader()
1136: throws LogConfigurationException {
1137: ClassLoader this ClassLoader = getClassLoader(LogFactoryImpl.class);
1138:
1139: if (useTCCL == false) {
1140: return this ClassLoader;
1141: }
1142:
1143: ClassLoader contextClassLoader = getContextClassLoader();
1144:
1145: ClassLoader baseClassLoader = getLowestClassLoader(
1146: contextClassLoader, this ClassLoader);
1147:
1148: if (baseClassLoader == null) {
1149: // The two classloaders are not part of a parent child relationship.
1150: // In some classloading setups (e.g. JBoss with its
1151: // UnifiedLoaderRepository) this can still work, so if user hasn't
1152: // forbidden it, just return the contextClassLoader.
1153: if (allowFlawedContext) {
1154: if (isDiagnosticsEnabled()) {
1155: logDiagnostic("[WARNING] the context classloader is not part of a"
1156: + " parent-child relationship with the classloader that"
1157: + " loaded LogFactoryImpl.");
1158: }
1159: // If contextClassLoader were null, getLowestClassLoader() would
1160: // have returned thisClassLoader. The fact we are here means
1161: // contextClassLoader is not null, so we can just return it.
1162: return contextClassLoader;
1163: } else {
1164: throw new LogConfigurationException(
1165: "Bad classloader hierarchy; LogFactoryImpl was loaded via"
1166: + " a classloader that is not related to the current context"
1167: + " classloader.");
1168: }
1169: }
1170:
1171: if (baseClassLoader != contextClassLoader) {
1172: // We really should just use the contextClassLoader as the starting
1173: // point for scanning for log adapter classes. However it is expected
1174: // that there are a number of broken systems out there which create
1175: // custom classloaders but fail to set the context classloader so
1176: // we handle those flawed systems anyway.
1177: if (allowFlawedContext) {
1178: if (isDiagnosticsEnabled()) {
1179: logDiagnostic("Warning: the context classloader is an ancestor of the"
1180: + " classloader that loaded LogFactoryImpl; it should be"
1181: + " the same or a descendant. The application using"
1182: + " commons-logging should ensure the context classloader"
1183: + " is used correctly.");
1184: }
1185: } else {
1186: throw new LogConfigurationException(
1187: "Bad classloader hierarchy; LogFactoryImpl was loaded via"
1188: + " a classloader that is not related to the current context"
1189: + " classloader.");
1190: }
1191: }
1192:
1193: return baseClassLoader;
1194: }
1195:
1196: /**
1197: * Given two related classloaders, return the one which is a child of
1198: * the other.
1199: * <p>
1200: * @param c1 is a classloader (including the null classloader)
1201: * @param c2 is a classloader (including the null classloader)
1202: *
1203: * @return c1 if it has c2 as an ancestor, c2 if it has c1 as an ancestor,
1204: * and null if neither is an ancestor of the other.
1205: */
1206: private ClassLoader getLowestClassLoader(ClassLoader c1,
1207: ClassLoader c2) {
1208: // TODO: use AccessController when dealing with classloaders here
1209:
1210: if (c1 == null)
1211: return c2;
1212:
1213: if (c2 == null)
1214: return c1;
1215:
1216: ClassLoader current;
1217:
1218: // scan c1's ancestors to find c2
1219: current = c1;
1220: while (current != null) {
1221: if (current == c2)
1222: return c1;
1223: current = current.getParent();
1224: }
1225:
1226: // scan c2's ancestors to find c1
1227: current = c2;
1228: while (current != null) {
1229: if (current == c1)
1230: return c2;
1231: current = current.getParent();
1232: }
1233:
1234: return null;
1235: }
1236:
1237: /**
1238: * Generates an internal diagnostic logging of the discovery failure and
1239: * then throws a <code>LogConfigurationException</code> that wraps
1240: * the passed <code>Throwable</code>.
1241: *
1242: * @param logAdapterClassName is the class name of the Log implementation
1243: * that could not be instantiated. Cannot be <code>null</code>.
1244: *
1245: * @param classLoader is the classloader that we were trying to load the
1246: * logAdapterClassName from when the exception occurred.
1247: *
1248: * @param discoveryFlaw is the Throwable created by the classloader
1249: *
1250: * @throws LogConfigurationException ALWAYS
1251: */
1252: private void handleFlawedDiscovery(String logAdapterClassName,
1253: ClassLoader classLoader, Throwable discoveryFlaw) {
1254:
1255: if (isDiagnosticsEnabled()) {
1256: logDiagnostic("Could not instantiate Log '"
1257: + logAdapterClassName + "' -- "
1258: + discoveryFlaw.getClass().getName() + ": "
1259: + discoveryFlaw.getLocalizedMessage());
1260: }
1261:
1262: if (!allowFlawedDiscovery) {
1263: throw new LogConfigurationException(discoveryFlaw);
1264: }
1265: }
1266:
1267: /**
1268: * Report a problem loading the log adapter, then either return
1269: * (if the situation is considered recoverable) or throw a
1270: * LogConfigurationException.
1271: * <p>
1272: * There are two possible reasons why we successfully loaded the
1273: * specified log adapter class then failed to cast it to a Log object:
1274: * <ol>
1275: * <li>the specific class just doesn't implement the Log interface
1276: * (user screwed up), or
1277: * <li> the specified class has bound to a Log class loaded by some other
1278: * classloader; Log@classloaderX cannot be cast to Log@classloaderY.
1279: * </ol>
1280: * <p>
1281: * Here we try to figure out which case has occurred so we can give the
1282: * user some reasonable feedback.
1283: *
1284: * @param badClassLoader is the classloader we loaded the problem class from,
1285: * ie it is equivalent to badClass.getClassLoader().
1286: *
1287: * @param badClass is a Class object with the desired name, but which
1288: * does not implement Log correctly.
1289: *
1290: * @throws LogConfigurationException when the situation
1291: * should not be recovered from.
1292: */
1293: private void handleFlawedHierarchy(ClassLoader badClassLoader,
1294: Class badClass) throws LogConfigurationException {
1295:
1296: boolean implements Log = false;
1297: String logInterfaceName = Log.class.getName();
1298: Class interfaces[] = badClass.getInterfaces();
1299: for (int i = 0; i < interfaces.length; i++) {
1300: if (logInterfaceName.equals(interfaces[i].getName())) {
1301: implements Log = true;
1302: break;
1303: }
1304: }
1305:
1306: if (implements Log) {
1307: // the class does implement an interface called Log, but
1308: // it is in the wrong classloader
1309: if (isDiagnosticsEnabled()) {
1310: try {
1311: ClassLoader logInterfaceClassLoader = getClassLoader(Log.class);
1312: logDiagnostic("Class '"
1313: + badClass.getName()
1314: + "' was found in classloader "
1315: + objectId(badClassLoader)
1316: + ". It is bound to a Log interface which is not"
1317: + " the one loaded from classloader "
1318: + objectId(logInterfaceClassLoader));
1319: } catch (Throwable t) {
1320: logDiagnostic("Error while trying to output diagnostics about"
1321: + " bad class '" + badClass + "'");
1322: }
1323: }
1324:
1325: if (!allowFlawedHierarchy) {
1326: StringBuffer msg = new StringBuffer();
1327: msg.append("Terminating logging for this context ");
1328: msg.append("due to bad log hierarchy. ");
1329: msg.append("You have more than one version of '");
1330: msg.append(Log.class.getName());
1331: msg.append("' visible.");
1332: if (isDiagnosticsEnabled()) {
1333: logDiagnostic(msg.toString());
1334: }
1335: throw new LogConfigurationException(msg.toString());
1336: }
1337:
1338: if (isDiagnosticsEnabled()) {
1339: StringBuffer msg = new StringBuffer();
1340: msg.append("Warning: bad log hierarchy. ");
1341: msg.append("You have more than one version of '");
1342: msg.append(Log.class.getName());
1343: msg.append("' visible.");
1344: logDiagnostic(msg.toString());
1345: }
1346: } else {
1347: // this is just a bad adapter class
1348: if (!allowFlawedDiscovery) {
1349: StringBuffer msg = new StringBuffer();
1350: msg.append("Terminating logging for this context. ");
1351: msg.append("Log class '");
1352: msg.append(badClass.getName());
1353: msg.append("' does not implement the Log interface.");
1354: if (isDiagnosticsEnabled()) {
1355: logDiagnostic(msg.toString());
1356: }
1357:
1358: throw new LogConfigurationException(msg.toString());
1359: }
1360:
1361: if (isDiagnosticsEnabled()) {
1362: StringBuffer msg = new StringBuffer();
1363: msg.append("[WARNING] Log class '");
1364: msg.append(badClass.getName());
1365: msg.append("' does not implement the Log interface.");
1366: logDiagnostic(msg.toString());
1367: }
1368: }
1369: }
1370: }
|