From 0b4d31d127bdcd045f399074856e2460ddb128b0 Mon Sep 17 00:00:00 2001 From: Gary Gregory Date: Wed, 18 Oct 2023 17:04:00 -0400 Subject: [PATCH] Sort members --- .../logging/LogConfigurationException.java | 24 +- .../apache/commons/logging/LogFactory.java | 1726 ++++++++--------- .../org/apache/commons/logging/LogSource.java | 112 +- .../commons/logging/impl/AvalonLogger.java | 122 +- .../logging/impl/Jdk13LumberjackLogger.java | 118 +- .../commons/logging/impl/Jdk14Logger.java | 68 +- .../commons/logging/impl/Log4JLogger.java | 196 +- .../commons/logging/impl/LogFactoryImpl.java | 1418 +++++++------- .../commons/logging/impl/LogKitLogger.java | 194 +- .../apache/commons/logging/impl/NoOpLog.java | 60 +- .../commons/logging/impl/SimpleLog.java | 874 ++++----- .../commons/logging/impl/WeakHashtable.java | 378 ++-- .../commons/logging/AltHashtableTestCase.java | 30 +- .../logging/BadHashtablePropertyTestCase.java | 8 +- .../logging/BasicOperationsTestCase.java | 72 +- .../logging/GarbageCollectionHelper.java | 46 +- .../apache/commons/logging/LoadTestCase.java | 94 +- .../commons/logging/PathableClassLoader.java | 308 +-- .../logging/impl/WeakHashtableTestCase.java | 90 +- .../jdk14/CustomConfigAPITestCase.java | 8 +- .../jdk14/CustomConfigFullTestCase.java | 10 +- .../logging/jdk14/CustomConfigTestCase.java | 428 ++-- .../logging/jdk14/DefaultConfigTestCase.java | 138 +- .../commons/logging/jdk14/TestHandler.java | 8 +- .../commons/logging/log4j/StandardTests.java | 188 +- .../logging/log4j/log4j12/TestAppender.java | 12 +- .../logging/logkit/StandardTestCase.java | 96 +- .../commons/logging/noop/NoOpLogTestCase.java | 58 +- .../logging/pathable/ChildFirstTestCase.java | 82 +- .../logging/pathable/GeneralTestCase.java | 94 +- .../logging/pathable/ParentFirstTestCase.java | 82 +- .../logging/security/MockSecurityManager.java | 26 +- .../security/SecurityAllowedTestCase.java | 4 +- .../security/SecurityForbiddenTestCase.java | 42 +- .../logging/simple/CustomConfigTestCase.java | 166 +- .../simple/DateTimeCustomConfigTestCase.java | 44 +- .../logging/simple/DecoratedSimpleLog.java | 90 +- .../logging/simple/DefaultConfigTestCase.java | 162 +- .../commons/logging/simple/LogRecord.java | 8 +- .../commons/logging/tccl/custom/MyLog.java | 34 +- 40 files changed, 3859 insertions(+), 3859 deletions(-) diff --git a/src/main/java/org/apache/commons/logging/LogConfigurationException.java b/src/main/java/org/apache/commons/logging/LogConfigurationException.java index 438f786..c2fa099 100644 --- a/src/main/java/org/apache/commons/logging/LogConfigurationException.java +++ b/src/main/java/org/apache/commons/logging/LogConfigurationException.java @@ -27,6 +27,11 @@ public class LogConfigurationException extends RuntimeException { /** Serializable version identifier. */ private static final long serialVersionUID = 8486587136871052495L; + /** + * The underlying cause of this exception. + */ + protected Throwable cause; + /** * Construct a new exception with {@code null} as its detail message. */ @@ -42,16 +47,6 @@ public class LogConfigurationException extends RuntimeException { super(message); } - /** - * Construct a new exception with the specified cause and a derived - * detail message. - * - * @param cause The underlying cause - */ - public LogConfigurationException(final Throwable cause) { - this(cause == null ? null : cause.toString(), cause); - } - /** * Construct a new exception with the specified detail message and cause. * @@ -64,9 +59,14 @@ public class LogConfigurationException extends RuntimeException { } /** - * The underlying cause of this exception. + * Construct a new exception with the specified cause and a derived + * detail message. + * + * @param cause The underlying cause */ - protected Throwable cause; + public LogConfigurationException(final Throwable cause) { + this(cause == null ? null : cause.toString(), cause); + } /** * Return the underlying cause of this exception (if any). diff --git a/src/main/java/org/apache/commons/logging/LogFactory.java b/src/main/java/org/apache/commons/logging/LogFactory.java index f0a71dd..9773a88 100644 --- a/src/main/java/org/apache/commons/logging/LogFactory.java +++ b/src/main/java/org/apache/commons/logging/LogFactory.java @@ -200,90 +200,14 @@ public abstract class LogFactory { // ----------------------------------------------------------- Constructors - /** - * Protected constructor that is not available for public use. - */ - protected LogFactory() { - } - - // --------------------------------------------------------- Public Methods - - /** - * Return the configuration attribute with the specified name (if any), - * or {@code null} if there is no such attribute. - * - * @param name Name of the attribute to return - * @return the configuration attribute with the specified name. - */ - public abstract Object getAttribute(String name); - - /** - * Gets an array containing the names of all currently defined configuration attributes. If there are no such attributes, a zero length array is returned. - * - * @return an array containing the names of all currently defined configuration attributes - */ - public abstract String[] getAttributeNames(); - - /** - * Convenience method to derive a name from the specified class and call {@code getInstance(String)} with it. - * - * @param clazz Class for which a suitable Log name will be derived - * @return a name from the specified class. - * @throws LogConfigurationException if a suitable {@code Log} instance cannot be returned - */ - public abstract Log getInstance(Class clazz) throws LogConfigurationException; - - /** - * Construct (if necessary) and return a {@code Log} instance, using the factory's current set of configuration attributes. - *

- * NOTE - Depending upon the implementation of the {@code LogFactory} you are using, the {@code Log} instance you are returned may or may - * not be local to the current application, and may or may not be returned again on a subsequent call with the same name argument. - *

- * - * @param name Logical name of the {@code Log} instance to be returned (the meaning of this name is only known to the underlying logging implementation that - * is being wrapped) - * @return a {@code Log} instance. - * @throws LogConfigurationException if a suitable {@code Log} instance cannot be returned - */ - public abstract Log getInstance(String name) - throws LogConfigurationException; - - /** - * Release any internal references to previously created {@link Log} - * instances returned by this factory. This is useful in environments - * like servlet containers, which implement application reloading by - * throwing away a ClassLoader. Dangling references to objects in that - * class loader would prevent garbage collection. - */ - public abstract void release(); - - /** - * Remove any configuration attribute associated with the specified name. - * If there is no such attribute, no action is taken. - * - * @param name Name of the attribute to remove - */ - public abstract void removeAttribute(String name); - - /** - * Set the configuration attribute with the specified name. Calling - * this with a {@code null} value is equivalent to calling - * {@code removeAttribute(name)}. - * - * @param name Name of the attribute to set - * @param value Value of the attribute to set, or {@code null} - * to remove any setting for this attribute - */ - public abstract void setAttribute(String name, Object value); - - // ------------------------------------------------------- Static Variables - /** * The previously constructed {@code LogFactory} instances, keyed by * the {@code ClassLoader} with which it was created. */ protected static Hashtable factories; + // --------------------------------------------------------- Public Methods + /** * Previously constructed {@code LogFactory} instance as in the * {@code factories} map, but for the case where @@ -302,6 +226,223 @@ public abstract class LogFactory { @Deprecated protected static volatile LogFactory nullClassLoaderFactory; + static { + // note: it's safe to call methods before initDiagnostics (though + // diagnostic output gets discarded). + ClassLoader thisClassLoader = getClassLoader(LogFactory.class); + thisClassLoaderRef = new WeakReference(thisClassLoader); + // In order to avoid confusion where multiple instances of JCL are + // being used via different classloaders within the same app, we + // ensure each logged message has a prefix of form + // [LogFactory from classloader OID] + // + // Note that this prefix should be kept consistent with that + // in LogFactoryImpl. However here we don't need to output info + // about the actual *instance* of LogFactory, as all methods that + // output diagnostics from this class are static. + String classLoaderName; + try { + if (thisClassLoader == null) { + classLoaderName = "BOOTLOADER"; + } else { + classLoaderName = objectId(thisClassLoader); + } + } catch (final SecurityException e) { + classLoaderName = "UNKNOWN"; + } + diagnosticPrefix = "[LogFactory from " + classLoaderName + "] "; + DIAGNOSTICS_STREAM = initDiagnostics(); + logClassLoaderEnvironment(LogFactory.class); + factories = createFactoryStore(); + if (isDiagnosticsEnabled()) { + logDiagnostic("BOOTSTRAP COMPLETED"); + } + } + + /** + * Remember this factory, so later calls to LogFactory.getCachedFactory + * can return the previously created object (together with all its + * cached Log objects). + * + * @param classLoader should be the current context classloader. Note that + * this can be null under some circumstances; this is ok. + * @param factory should be the factory to cache. This should never be null. + */ + private static void cacheFactory(final ClassLoader classLoader, final LogFactory factory) { + // Ideally we would assert(factory != null) here. However reporting + // errors from within a logging implementation is a little tricky! + + if (factory != null) { + if (classLoader == null) { + nullClassLoaderFactory = factory; + } else { + factories.put(classLoader, factory); + } + } + } + + /** + * Implements the operations described in the javadoc for newFactory. + * + * @param factoryClass Factory class. + * @param classLoader used to load the specified factory class. This is expected to be either the TCCL or the classloader which loaded this class. Note + * that the classloader which loaded this class might be "null" (ie the bootloader) for embedded systems. + * @return either a LogFactory object or a LogConfigurationException object. + * @since 1.1 + */ + protected static Object createFactory(final String factoryClass, final ClassLoader classLoader) { + // This will be used to diagnose bad configurations + // and allow a useful message to be sent to the user + Class logFactoryClass = null; + try { + if (classLoader != null) { + try { + // First the given class loader param (thread class loader) + + // Warning: must typecast here & allow exception + // to be generated/caught & recast properly. + logFactoryClass = classLoader.loadClass(factoryClass); + if (LogFactory.class.isAssignableFrom(logFactoryClass)) { + if (isDiagnosticsEnabled()) { + logDiagnostic("Loaded class " + logFactoryClass.getName() + + " from classloader " + objectId(classLoader)); + } + } else { + // + // This indicates a problem with the ClassLoader tree. + // An incompatible ClassLoader was used to load the + // implementation. + // As the same classes + // must be available in multiple class loaders, + // it is very likely that multiple JCL jars are present. + // The most likely fix for this + // problem is to remove the extra JCL jars from the + // ClassLoader hierarchy. + // + if (isDiagnosticsEnabled()) { + logDiagnostic("Factory class " + logFactoryClass.getName() + + " loaded from classloader " + objectId(logFactoryClass.getClassLoader()) + + " does not extend '" + LogFactory.class.getName() + + "' as loaded by this classloader."); + logHierarchy("[BAD CL TREE] ", classLoader); + } + } + + return (LogFactory) logFactoryClass.getConstructor().newInstance(); + + } catch (final ClassNotFoundException ex) { + if (classLoader == thisClassLoaderRef.get()) { + // Nothing more to try, onwards. + if (isDiagnosticsEnabled()) { + logDiagnostic("Unable to locate any class called '" + factoryClass + + "' via classloader " + objectId(classLoader)); + } + throw ex; + } + // ignore exception, continue + } catch (final NoClassDefFoundError e) { + if (classLoader == thisClassLoaderRef.get()) { + // Nothing more to try, onwards. + if (isDiagnosticsEnabled()) { + logDiagnostic("Class '" + factoryClass + "' cannot be loaded" + + " via classloader " + objectId(classLoader) + + " - it depends on some other class that cannot be found."); + } + throw e; + } + // ignore exception, continue + } catch (final ClassCastException e) { + if (classLoader == thisClassLoaderRef.get()) { + // There's no point in falling through to the code below that + // tries again with thisClassLoaderRef, because we've just tried + // loading with that loader (not the TCCL). Just throw an + // appropriate exception here. + + final boolean implementsLogFactory = implementsLogFactory(logFactoryClass); + + // + // Construct a good message: users may not actual expect that a custom implementation + // has been specified. Several well known containers use this mechanism to adapt JCL + // to their native logging system. + // + final StringBuilder msg = new StringBuilder(); + msg.append("The application has specified that a custom LogFactory implementation "); + msg.append("should be used but Class '"); + msg.append(factoryClass); + msg.append("' cannot be converted to '"); + msg.append(LogFactory.class.getName()); + msg.append("'. "); + if (implementsLogFactory) { + msg.append("The conflict is caused by the presence of multiple LogFactory classes "); + msg.append("in incompatible classloaders. "); + msg.append("Background can be found in http://commons.apache.org/logging/tech.html. "); + msg.append("If you have not explicitly specified a custom LogFactory then it is likely "); + msg.append("that the container has set one without your knowledge. "); + msg.append("In this case, consider using the commons-logging-adapters.jar file or "); + msg.append("specifying the standard LogFactory from the command line. "); + } else { + msg.append("Please check the custom implementation. "); + } + msg.append("Help can be found @http://commons.apache.org/logging/troubleshooting.html."); + + if (isDiagnosticsEnabled()) { + logDiagnostic(msg.toString()); + } + + throw new ClassCastException(msg.toString()); + } + + // Ignore exception, continue. Presumably the classloader was the + // TCCL; the code below will try to load the class via thisClassLoaderRef. + // This will handle the case where the original calling class is in + // a shared classpath but the TCCL has a copy of LogFactory and the + // specified LogFactory implementation; we will fall back to using the + // LogFactory implementation from the same classloader as this class. + // + // Issue: this doesn't handle the reverse case, where this LogFactory + // is in the webapp, and the specified LogFactory implementation is + // in a shared classpath. In that case: + // (a) the class really does implement LogFactory (bad log msg above) + // (b) the fallback code will result in exactly the same problem. + } + } + + /* At this point, either classLoader == null, OR + * classLoader was unable to load factoryClass. + * + * In either case, we call Class.forName, which is equivalent + * to LogFactory.class.getClassLoader().load(name), ie we ignore + * the classloader parameter the caller passed, and fall back + * to trying the classloader associated with this class. See the + * javadoc for the newFactory method for more info on the + * consequences of this. + * + * Notes: + * * LogFactory.class.getClassLoader() may return 'null' + * if LogFactory is loaded by the bootstrap classloader. + */ + // Warning: must typecast here & allow exception + // to be generated/caught & recast properly. + if (isDiagnosticsEnabled()) { + logDiagnostic("Unable to load factory class via classloader " + objectId(classLoader) + + " - trying the classloader associated with this LogFactory."); + } + logFactoryClass = Class.forName(factoryClass); + return (LogFactory) logFactoryClass.newInstance(); + } catch (final Exception e) { + // Check to see if we've got a bad configuration + if (isDiagnosticsEnabled()) { + logDiagnostic("Unable to create LogFactory instance."); + } + if (logFactoryClass != null && !LogFactory.class.isAssignableFrom(logFactoryClass)) { + return new LogConfigurationException( + "The chosen LogFactory implementation does not extend LogFactory." + + " Please check your configuration.", e); + } + return new LogConfigurationException(e); + } + } + /** * Create the hashtable which will be used to store a map of * (context-classloader -> logfactory-object). Version 1.2+ of Java @@ -356,36 +497,260 @@ public abstract class LogFactory { return result; } - // --------------------------------------------------------- Static Methods + /** + * Return the thread context class loader if available; otherwise return null. + *

+ * Most/all code should call getContextClassLoaderInternal rather than + * calling this method directly. + *

+ * The thread context class loader is available for JDK 1.2 + * or later, if certain security conditions are met. + *

+ * Note that no internal logging is done within this method because + * this method is called every time LogFactory.getLogger() is called, + * and we don't want too much output generated here. + * + * @throws LogConfigurationException if a suitable class loader + * cannot be identified. + * @return the thread's context classloader or {@code null} if the java security + * policy forbids access to the context classloader from one of the classes + * in the current call stack. + * @since 1.1 + */ + protected static ClassLoader directGetContextClassLoader() throws LogConfigurationException { + ClassLoader classLoader = null; - /** Utility method to safely trim a string. */ - private static String trim(final String src) { - if (src == null) { - return null; + try { + classLoader = Thread.currentThread().getContextClassLoader(); + } catch (final SecurityException ex) { + /** + * getContextClassLoader() throws SecurityException when + * the context class loader isn't an ancestor of the + * calling class's class loader, or if security + * permissions are restricted. + * + * We ignore this exception to be consistent with the previous + * behavior (e.g. 1.1.3 and earlier). + */ + // ignore } - return src.trim(); + + // Return the selected class loader + return classLoader; } /** - * Checks whether the supplied Throwable is one that needs to be - * re-thrown and ignores all others. + * Check cached factories (keyed by contextClassLoader) * - * The following errors are re-thrown: - *

+ * @param contextClassLoader is the context classloader associated + * with the current thread. This allows separate LogFactory objects + * per component within a container, provided each component has + * a distinct context classloader set. This parameter may be null + * in JDK1.1, and in embedded systems where jcl-using code is + * placed in the bootclasspath. * - * @param t the Throwable to check + * @return the factory associated with the specified classloader if + * one has previously been created, or null if this is the first time + * we have seen this particular classloader. */ - protected static void handleThrowable(final Throwable t) { - if (t instanceof ThreadDeath) { - throw (ThreadDeath) t; + private static LogFactory getCachedFactory(final ClassLoader contextClassLoader) { + if (contextClassLoader == null) { + // We have to handle this specially, as factories is a Hashtable + // and those don't accept null as a key value. + // + // nb: nullClassLoaderFactory might be null. That's ok. + return nullClassLoaderFactory; } - if (t instanceof VirtualMachineError) { - throw (VirtualMachineError) t; + return (LogFactory) factories.get(contextClassLoader); + } + + // ------------------------------------------------------- Static Variables + + /** + * Safely get access to the classloader for the specified class. + *

+ * Theoretically, calling getClassLoader can throw a security exception, + * and so should be done under an AccessController in order to provide + * maximum flexibility. However in practice people don't appear to use + * security policies that forbid getClassLoader calls. So for the moment + * all code is written to call this method rather than Class.getClassLoader, + * so that we could put AccessController stuff in this method without any + * disruption later if we need to. + *

+ *

+ * Even when using an AccessController, however, this method can still + * throw SecurityException. Commons-logging basically relies on the + * ability to access classloaders, ie a policy that forbids all + * classloader access will also prevent commons-logging from working: + * currently this method will throw an exception preventing the entire app + * from starting up. Maybe it would be good to detect this situation and + * just disable all commons-logging? Not high priority though - as stated + * above, security policies that prevent classloader access aren't common. + *

+ *

+ * Note that returning an object fetched via an AccessController would + * technically be a security flaw anyway; untrusted code that has access + * to a trusted JCL library could use it to fetch the classloader for + * a class even when forbidden to do so directly. + *

+ * + * @param clazz Class. + * @return a ClassLoader. + * + * @since 1.1 + */ + protected static ClassLoader getClassLoader(final Class clazz) { + try { + return clazz.getClassLoader(); + } catch (final SecurityException ex) { + if (isDiagnosticsEnabled()) { + logDiagnostic("Unable to get classloader for class '" + clazz + + "' due to security restrictions - " + ex.getMessage()); + } + throw ex; } - // All other instances of Throwable will be silently ignored + } + + /** + * Locate a user-provided configuration file. + *

+ * The classpath of the specified classLoader (usually the context classloader) + * is searched for properties files of the specified name. If none is found, + * null is returned. If more than one is found, then the file with the greatest + * value for its PRIORITY property is returned. If multiple files have the + * same PRIORITY value then the first in the classpath is returned. + *

+ * This differs from the 1.0.x releases; those always use the first one found. + * However as the priority is a new field, this change is backwards compatible. + *

+ * The purpose of the priority field is to allow a webserver administrator to + * override logging settings in all webapps by placing a commons-logging.properties + * file in a shared classpath location with a priority > 0; this overrides any + * commons-logging.properties files without priorities which are in the + * webapps. Webapps can also use explicit priorities to override a configuration + * file in the shared classpath if needed. + */ + private static final Properties getConfigurationFile(final ClassLoader classLoader, final String fileName) { + Properties props = null; + double priority = 0.0; + URL propsUrl = null; + try { + final Enumeration urls = getResources(classLoader, fileName); + + if (urls == null) { + return null; + } + + while (urls.hasMoreElements()) { + final URL url = (URL) urls.nextElement(); + + final Properties newProps = getProperties(url); + if (newProps != null) { + if (props == null) { + propsUrl = url; + props = newProps; + final String priorityStr = props.getProperty(PRIORITY_KEY); + priority = 0.0; + if (priorityStr != null) { + priority = Double.parseDouble(priorityStr); + } + + if (isDiagnosticsEnabled()) { + logDiagnostic("[LOOKUP] Properties file found at '" + url + "'" + + " with priority " + priority); + } + } else { + final String newPriorityStr = newProps.getProperty(PRIORITY_KEY); + double newPriority = 0.0; + if (newPriorityStr != null) { + newPriority = Double.parseDouble(newPriorityStr); + } + + if (newPriority > priority) { + if (isDiagnosticsEnabled()) { + logDiagnostic("[LOOKUP] Properties file at '" + url + "'" + + " with priority " + newPriority + + " overrides file at '" + propsUrl + "'" + + " with priority " + priority); + } + + propsUrl = url; + props = newProps; + priority = newPriority; + } else { + if (isDiagnosticsEnabled()) { + logDiagnostic("[LOOKUP] Properties file at '" + url + "'" + + " with priority " + newPriority + + " does not override file at '" + propsUrl + "'" + + " with priority " + priority); + } + } + } + + } + } + } catch (final SecurityException e) { + if (isDiagnosticsEnabled()) { + logDiagnostic("SecurityException thrown while trying to find/read config files."); + } + } + + if (isDiagnosticsEnabled()) { + if (props == null) { + logDiagnostic("[LOOKUP] No properties file of name '" + fileName + "' found."); + } else { + logDiagnostic("[LOOKUP] Properties file of name '" + fileName + "' found at '" + propsUrl + '"'); + } + } + + return props; + } + + /** + * Returns the current context classloader. + *

+ * In versions prior to 1.1, this method did not use an AccessController. + * In version 1.1, an AccessController wrapper was incorrectly added to + * this method, causing a minor security flaw. + *

+ * In version 1.1.1 this change was reverted; this method no longer uses + * an AccessController. User code wishing to obtain the context classloader + * must invoke this method via AccessController.doPrivileged if it needs + * support for that. + * + * @return the context classloader associated with the current thread, + * or null if security doesn't allow it. + * @throws LogConfigurationException if there was some weird error while + * attempting to get the context classloader. + */ + protected static ClassLoader getContextClassLoader() throws LogConfigurationException { + return directGetContextClassLoader(); + } + + // --------------------------------------------------------- Static Methods + + /** + * Calls LogFactory.directGetContextClassLoader under the control of an + * AccessController class. This means that java code running under a + * security manager that forbids access to ClassLoaders will still work + * if this class is given appropriate privileges, even when the caller + * doesn't have such privileges. Without using an AccessController, the + * the entire call stack must have the privilege before the call is + * allowed. + * + * @return the context classloader associated with the current thread, + * or null if security doesn't allow it. + * @throws LogConfigurationException if there was some weird error while + * attempting to get the context classloader. + */ + private static ClassLoader getContextClassLoaderInternal() throws LogConfigurationException { + return (ClassLoader)AccessController.doPrivileged( + new PrivilegedAction() { + @Override + public Object run() { + return directGetContextClassLoader(); + } + }); } /** @@ -647,491 +1012,158 @@ public abstract class LogFactory { } /** - * Release any internal references to previously created {@link LogFactory} - * instances that have been associated with the specified class loader - * (if any), after calling the instance method {@code release()} on - * each of them. - * - * @param classLoader ClassLoader for which to release the LogFactory + * Given a URL that refers to a .properties file, load that file. + * This is done under an AccessController so that this method will + * succeed when this jarfile is privileged but the caller is not. + * This method must therefore remain private to avoid security issues. + *

+ * {@code Null} is returned if the URL cannot be opened. */ - public static void release(final ClassLoader classLoader) { - if (isDiagnosticsEnabled()) { - logDiagnostic("Releasing factory for classloader " + objectId(classLoader)); - } - // factories is not final and could be replaced in this block. - final Hashtable factories = LogFactory.factories; - synchronized (factories) { - if (classLoader == null) { - if (nullClassLoaderFactory != null) { - nullClassLoaderFactory.release(); - nullClassLoaderFactory = null; + private static Properties getProperties(final URL url) { + final PrivilegedAction action = + new PrivilegedAction() { + @Override + public Object run() { + InputStream stream = null; + try { + // We must ensure that useCaches is set to false, as the + // default behavior of java is to cache file handles, and + // this "locks" files, preventing hot-redeploy on windows. + final URLConnection connection = url.openConnection(); + connection.setUseCaches(false); + stream = connection.getInputStream(); + if (stream != null) { + final Properties props = new Properties(); + props.load(stream); + stream.close(); + stream = null; + return props; + } + } catch (final IOException e) { + if (isDiagnosticsEnabled()) { + logDiagnostic("Unable to read URL " + url); + } + } finally { + if (stream != null) { + try { + stream.close(); + } catch (final IOException e) { + // ignore exception; this should not happen + if (isDiagnosticsEnabled()) { + logDiagnostic("Unable to close stream for URL " + url); + } + } + } + } + + return null; } - } else { - final LogFactory factory = (LogFactory) factories.get(classLoader); - if (factory != null) { - factory.release(); - factories.remove(classLoader); - } - } - } + }; + return (Properties) AccessController.doPrivileged(action); } /** - * Release any internal references to previously created {@link LogFactory} - * instances, after calling the instance method {@code release()} on - * each of them. This is useful in environments like servlet containers, - * which implement application reloading by throwing away a ClassLoader. - * Dangling references to objects in that class loader would prevent - * garbage collection. + * Applets may run in an environment where accessing resources of a loader is + * a secure operation, but where the commons-logging library has explicitly + * been granted permission for that operation. In this case, we need to + * run the operation using an AccessController. */ - public static void releaseAll() { - if (isDiagnosticsEnabled()) { - logDiagnostic("Releasing factory for all classloaders."); - } - // factories is not final and could be replaced in this block. - final Hashtable factories = LogFactory.factories; - synchronized (factories) { - final Enumeration elements = factories.elements(); - while (elements.hasMoreElements()) { - final LogFactory element = (LogFactory) elements.nextElement(); - element.release(); - } - factories.clear(); + private static InputStream getResourceAsStream(final ClassLoader loader, final String name) { + return (InputStream)AccessController.doPrivileged( + new PrivilegedAction() { + @Override + public Object run() { + if (loader != null) { + return loader.getResourceAsStream(name); + } + return ClassLoader.getSystemResourceAsStream(name); + } + }); + } - if (nullClassLoaderFactory != null) { - nullClassLoaderFactory.release(); - nullClassLoaderFactory = null; - } - } + /** + * Given a filename, return an enumeration of URLs pointing to + * all the occurrences of that filename in the classpath. + *

+ * This is just like ClassLoader.getResources except that the + * operation is done under an AccessController so that this method will + * succeed when this jarfile is privileged but the caller is not. + * This method must therefore remain private to avoid security issues. + *

+ * If no instances are found, an Enumeration is returned whose + * hasMoreElements method returns false (ie an "empty" enumeration). + * If resources could not be listed for some reason, null is returned. + */ + private static Enumeration getResources(final ClassLoader loader, final String name) { + final PrivilegedAction action = + new PrivilegedAction() { + @Override + public Object run() { + try { + if (loader != null) { + return loader.getResources(name); + } + return ClassLoader.getSystemResources(name); + } catch (final IOException e) { + if (isDiagnosticsEnabled()) { + logDiagnostic("Exception while trying to find configuration file " + + name + ":" + e.getMessage()); + } + return null; + } catch (final NoSuchMethodError e) { + // we must be running on a 1.1 JVM which doesn't support + // ClassLoader.getSystemResources; just return null in + // this case. + return null; + } + } + }; + final Object result = AccessController.doPrivileged(action); + return (Enumeration) result; } // ------------------------------------------------------ Protected Methods /** - * Safely get access to the classloader for the specified class. + * Read the specified system property, using an AccessController so that + * the property can be read if JCL has been granted the appropriate + * security rights even if the calling code has not. *

- * Theoretically, calling getClassLoader can throw a security exception, - * and so should be done under an AccessController in order to provide - * maximum flexibility. However in practice people don't appear to use - * security policies that forbid getClassLoader calls. So for the moment - * all code is written to call this method rather than Class.getClassLoader, - * so that we could put AccessController stuff in this method without any - * disruption later if we need to. - *

- *

- * Even when using an AccessController, however, this method can still - * throw SecurityException. Commons-logging basically relies on the - * ability to access classloaders, ie a policy that forbids all - * classloader access will also prevent commons-logging from working: - * currently this method will throw an exception preventing the entire app - * from starting up. Maybe it would be good to detect this situation and - * just disable all commons-logging? Not high priority though - as stated - * above, security policies that prevent classloader access aren't common. - *

- *

- * Note that returning an object fetched via an AccessController would - * technically be a security flaw anyway; untrusted code that has access - * to a trusted JCL library could use it to fetch the classloader for - * a class even when forbidden to do so directly. - *

- * - * @param clazz Class. - * @return a ClassLoader. - * - * @since 1.1 + * Take care not to expose the value returned by this method to the + * calling application in any way; otherwise the calling app can use that + * info to access data that should not be available to it. */ - protected static ClassLoader getClassLoader(final Class clazz) { - try { - return clazz.getClassLoader(); - } catch (final SecurityException ex) { - if (isDiagnosticsEnabled()) { - logDiagnostic("Unable to get classloader for class '" + clazz + - "' due to security restrictions - " + ex.getMessage()); - } - throw ex; - } - } - - /** - * Returns the current context classloader. - *

- * In versions prior to 1.1, this method did not use an AccessController. - * In version 1.1, an AccessController wrapper was incorrectly added to - * this method, causing a minor security flaw. - *

- * In version 1.1.1 this change was reverted; this method no longer uses - * an AccessController. User code wishing to obtain the context classloader - * must invoke this method via AccessController.doPrivileged if it needs - * support for that. - * - * @return the context classloader associated with the current thread, - * or null if security doesn't allow it. - * @throws LogConfigurationException if there was some weird error while - * attempting to get the context classloader. - */ - protected static ClassLoader getContextClassLoader() throws LogConfigurationException { - return directGetContextClassLoader(); - } - - /** - * Calls LogFactory.directGetContextClassLoader under the control of an - * AccessController class. This means that java code running under a - * security manager that forbids access to ClassLoaders will still work - * if this class is given appropriate privileges, even when the caller - * doesn't have such privileges. Without using an AccessController, the - * the entire call stack must have the privilege before the call is - * allowed. - * - * @return the context classloader associated with the current thread, - * or null if security doesn't allow it. - * @throws LogConfigurationException if there was some weird error while - * attempting to get the context classloader. - */ - private static ClassLoader getContextClassLoaderInternal() throws LogConfigurationException { - return (ClassLoader)AccessController.doPrivileged( - new PrivilegedAction() { - @Override - public Object run() { - return directGetContextClassLoader(); - } - }); - } - - /** - * Return the thread context class loader if available; otherwise return null. - *

- * Most/all code should call getContextClassLoaderInternal rather than - * calling this method directly. - *

- * The thread context class loader is available for JDK 1.2 - * or later, if certain security conditions are met. - *

- * Note that no internal logging is done within this method because - * this method is called every time LogFactory.getLogger() is called, - * and we don't want too much output generated here. - * - * @throws LogConfigurationException if a suitable class loader - * cannot be identified. - * @return the thread's context classloader or {@code null} if the java security - * policy forbids access to the context classloader from one of the classes - * in the current call stack. - * @since 1.1 - */ - protected static ClassLoader directGetContextClassLoader() throws LogConfigurationException { - ClassLoader classLoader = null; - - try { - classLoader = Thread.currentThread().getContextClassLoader(); - } catch (final SecurityException ex) { - /** - * getContextClassLoader() throws SecurityException when - * the context class loader isn't an ancestor of the - * calling class's class loader, or if security - * permissions are restricted. - * - * We ignore this exception to be consistent with the previous - * behavior (e.g. 1.1.3 and earlier). - */ - // ignore - } - - // Return the selected class loader - return classLoader; - } - - /** - * Check cached factories (keyed by contextClassLoader) - * - * @param contextClassLoader is the context classloader associated - * with the current thread. This allows separate LogFactory objects - * per component within a container, provided each component has - * a distinct context classloader set. This parameter may be null - * in JDK1.1, and in embedded systems where jcl-using code is - * placed in the bootclasspath. - * - * @return the factory associated with the specified classloader if - * one has previously been created, or null if this is the first time - * we have seen this particular classloader. - */ - private static LogFactory getCachedFactory(final ClassLoader contextClassLoader) { - if (contextClassLoader == null) { - // We have to handle this specially, as factories is a Hashtable - // and those don't accept null as a key value. - // - // nb: nullClassLoaderFactory might be null. That's ok. - return nullClassLoaderFactory; - } - return (LogFactory) factories.get(contextClassLoader); - } - - /** - * Remember this factory, so later calls to LogFactory.getCachedFactory - * can return the previously created object (together with all its - * cached Log objects). - * - * @param classLoader should be the current context classloader. Note that - * this can be null under some circumstances; this is ok. - * @param factory should be the factory to cache. This should never be null. - */ - private static void cacheFactory(final ClassLoader classLoader, final LogFactory factory) { - // Ideally we would assert(factory != null) here. However reporting - // errors from within a logging implementation is a little tricky! - - if (factory != null) { - if (classLoader == null) { - nullClassLoaderFactory = factory; - } else { - factories.put(classLoader, factory); - } - } - } - - /** - * Return a new instance of the specified {@code LogFactory} implementation class, loaded by the specified class loader. If that fails, try the class loader - * used to load this (abstract) LogFactory. - *

ClassLoader conflicts

- *

- * Note that there can be problems if the specified ClassLoader is not the same as the classloader that loaded this class, ie when loading a concrete - * LogFactory subclass via a context classloader. - *

- *

- * The problem is the same one that can occur when loading a concrete Log subclass via a context classloader. - *

- *

- * The problem occurs when code running in the context classloader calls class X which was loaded via a parent classloader, and class X then calls - * LogFactory.getFactory (either directly or via LogFactory.getLog). Because class X was loaded via the parent, it binds to LogFactory loaded via the - * parent. When the code in this method finds some LogFactoryYYYY class in the child (context) classloader, and there also happens to be a LogFactory class - * defined in the child classloader, then LogFactoryYYYY will be bound to LogFactory@childloader. It cannot be cast to LogFactory@parentloader, ie this - * method cannot return the object as the desired type. Note that it doesn't matter if the LogFactory class in the child classloader is identical to the - * LogFactory class in the parent classloader, they are not compatible. - *

- *

- * The solution taken here is to simply print out an error message when this occurs then throw an exception. The deployer of the application must ensure - * they remove all occurrences of the LogFactory class from the child classloader in order to resolve the issue. Note that they do not have to move the - * custom LogFactory subclass; that is ok as long as the only LogFactory class it can find to bind to is in the parent classloader. - *

- * - * @param factoryClass Fully qualified name of the {@code LogFactory} implementation class - * @param classLoader ClassLoader from which to load this class - * @param contextClassLoader is the context that this new factory will manage logging for. - * @return a new instance of the specified {@code LogFactory}. - * @throws LogConfigurationException if a suitable instance cannot be created - * @since 1.1 - */ - protected static LogFactory newFactory(final String factoryClass, - final ClassLoader classLoader, - final ClassLoader contextClassLoader) - throws LogConfigurationException { - // Note that any unchecked exceptions thrown by the createFactory - // method will propagate out of this method; in particular a - // ClassCastException can be thrown. - final Object result = AccessController.doPrivileged( - new PrivilegedAction() { - @Override - public Object run() { - return createFactory(factoryClass, classLoader); - } - }); - - if (result instanceof LogConfigurationException) { - final LogConfigurationException ex = (LogConfigurationException) result; - if (isDiagnosticsEnabled()) { - logDiagnostic("An error occurred while loading the factory class:" + ex.getMessage()); - } - throw ex; - } - if (isDiagnosticsEnabled()) { - logDiagnostic("Created object " + objectId(result) + " to manage classloader " + - objectId(contextClassLoader)); - } - return (LogFactory)result; - } - - /** - * Method provided for backwards compatibility; see newFactory version that - * takes 3 parameters. - *

- * This method would only ever be called in some rather odd situation. - * Note that this method is static, so overriding in a subclass doesn't - * have any effect unless this method is called from a method in that - * subclass. However this method only makes sense to use from the - * getFactory method, and as that is almost always invoked via - * LogFactory.getFactory, any custom definition in a subclass would be - * pointless. Only a class with a custom getFactory method, then invoked - * directly via CustomFactoryImpl.getFactory or similar would ever call - * this. Anyway, it's here just in case, though the "managed class loader" - * value output to the diagnostics will not report the correct value. - *

- * - * @param factoryClass factory class. - * @param classLoader class loader. - * @return a LogFactory. - */ - protected static LogFactory newFactory(final String factoryClass, - final ClassLoader classLoader) { - return newFactory(factoryClass, classLoader, null); - } - - /** - * Implements the operations described in the javadoc for newFactory. - * - * @param factoryClass Factory class. - * @param classLoader used to load the specified factory class. This is expected to be either the TCCL or the classloader which loaded this class. Note - * that the classloader which loaded this class might be "null" (ie the bootloader) for embedded systems. - * @return either a LogFactory object or a LogConfigurationException object. - * @since 1.1 - */ - protected static Object createFactory(final String factoryClass, final ClassLoader classLoader) { - // This will be used to diagnose bad configurations - // and allow a useful message to be sent to the user - Class logFactoryClass = null; - try { - if (classLoader != null) { - try { - // First the given class loader param (thread class loader) - - // Warning: must typecast here & allow exception - // to be generated/caught & recast properly. - logFactoryClass = classLoader.loadClass(factoryClass); - if (LogFactory.class.isAssignableFrom(logFactoryClass)) { - if (isDiagnosticsEnabled()) { - logDiagnostic("Loaded class " + logFactoryClass.getName() + - " from classloader " + objectId(classLoader)); - } - } else { - // - // This indicates a problem with the ClassLoader tree. - // An incompatible ClassLoader was used to load the - // implementation. - // As the same classes - // must be available in multiple class loaders, - // it is very likely that multiple JCL jars are present. - // The most likely fix for this - // problem is to remove the extra JCL jars from the - // ClassLoader hierarchy. - // - if (isDiagnosticsEnabled()) { - logDiagnostic("Factory class " + logFactoryClass.getName() + - " loaded from classloader " + objectId(logFactoryClass.getClassLoader()) + - " does not extend '" + LogFactory.class.getName() + - "' as loaded by this classloader."); - logHierarchy("[BAD CL TREE] ", classLoader); - } + private static String getSystemProperty(final String key, final String def) + throws SecurityException { + return (String) AccessController.doPrivileged( + new PrivilegedAction() { + @Override + public Object run() { + return System.getProperty(key, def); } + }); + } - return (LogFactory) logFactoryClass.getConstructor().newInstance(); - - } catch (final ClassNotFoundException ex) { - if (classLoader == thisClassLoaderRef.get()) { - // Nothing more to try, onwards. - if (isDiagnosticsEnabled()) { - logDiagnostic("Unable to locate any class called '" + factoryClass + - "' via classloader " + objectId(classLoader)); - } - throw ex; - } - // ignore exception, continue - } catch (final NoClassDefFoundError e) { - if (classLoader == thisClassLoaderRef.get()) { - // Nothing more to try, onwards. - if (isDiagnosticsEnabled()) { - logDiagnostic("Class '" + factoryClass + "' cannot be loaded" + - " via classloader " + objectId(classLoader) + - " - it depends on some other class that cannot be found."); - } - throw e; - } - // ignore exception, continue - } catch (final ClassCastException e) { - if (classLoader == thisClassLoaderRef.get()) { - // There's no point in falling through to the code below that - // tries again with thisClassLoaderRef, because we've just tried - // loading with that loader (not the TCCL). Just throw an - // appropriate exception here. - - final boolean implementsLogFactory = implementsLogFactory(logFactoryClass); - - // - // Construct a good message: users may not actual expect that a custom implementation - // has been specified. Several well known containers use this mechanism to adapt JCL - // to their native logging system. - // - final StringBuilder msg = new StringBuilder(); - msg.append("The application has specified that a custom LogFactory implementation "); - msg.append("should be used but Class '"); - msg.append(factoryClass); - msg.append("' cannot be converted to '"); - msg.append(LogFactory.class.getName()); - msg.append("'. "); - if (implementsLogFactory) { - msg.append("The conflict is caused by the presence of multiple LogFactory classes "); - msg.append("in incompatible classloaders. "); - msg.append("Background can be found in http://commons.apache.org/logging/tech.html. "); - msg.append("If you have not explicitly specified a custom LogFactory then it is likely "); - msg.append("that the container has set one without your knowledge. "); - msg.append("In this case, consider using the commons-logging-adapters.jar file or "); - msg.append("specifying the standard LogFactory from the command line. "); - } else { - msg.append("Please check the custom implementation. "); - } - msg.append("Help can be found @http://commons.apache.org/logging/troubleshooting.html."); - - if (isDiagnosticsEnabled()) { - logDiagnostic(msg.toString()); - } - - throw new ClassCastException(msg.toString()); - } - - // Ignore exception, continue. Presumably the classloader was the - // TCCL; the code below will try to load the class via thisClassLoaderRef. - // This will handle the case where the original calling class is in - // a shared classpath but the TCCL has a copy of LogFactory and the - // specified LogFactory implementation; we will fall back to using the - // LogFactory implementation from the same classloader as this class. - // - // Issue: this doesn't handle the reverse case, where this LogFactory - // is in the webapp, and the specified LogFactory implementation is - // in a shared classpath. In that case: - // (a) the class really does implement LogFactory (bad log msg above) - // (b) the fallback code will result in exactly the same problem. - } - } - - /* At this point, either classLoader == null, OR - * classLoader was unable to load factoryClass. - * - * In either case, we call Class.forName, which is equivalent - * to LogFactory.class.getClassLoader().load(name), ie we ignore - * the classloader parameter the caller passed, and fall back - * to trying the classloader associated with this class. See the - * javadoc for the newFactory method for more info on the - * consequences of this. - * - * Notes: - * * LogFactory.class.getClassLoader() may return 'null' - * if LogFactory is loaded by the bootstrap classloader. - */ - // Warning: must typecast here & allow exception - // to be generated/caught & recast properly. - if (isDiagnosticsEnabled()) { - logDiagnostic("Unable to load factory class via classloader " + objectId(classLoader) + - " - trying the classloader associated with this LogFactory."); - } - logFactoryClass = Class.forName(factoryClass); - return (LogFactory) logFactoryClass.newInstance(); - } catch (final Exception e) { - // Check to see if we've got a bad configuration - if (isDiagnosticsEnabled()) { - logDiagnostic("Unable to create LogFactory instance."); - } - if (logFactoryClass != null && !LogFactory.class.isAssignableFrom(logFactoryClass)) { - return new LogConfigurationException( - "The chosen LogFactory implementation does not extend LogFactory." + - " Please check your configuration.", e); - } - return new LogConfigurationException(e); + /** + * Checks whether the supplied Throwable is one that needs to be + * re-thrown and ignores all others. + * + * The following errors are re-thrown: + * + * + * @param t the Throwable to check + */ + protected static void handleThrowable(final Throwable t) { + if (t instanceof ThreadDeath) { + throw (ThreadDeath) t; } + if (t instanceof VirtualMachineError) { + throw (VirtualMachineError) t; + } + // All other instances of Throwable will be silently ignored } /** @@ -1198,232 +1230,6 @@ public abstract class LogFactory { return implementsLogFactory; } - /** - * Applets may run in an environment where accessing resources of a loader is - * a secure operation, but where the commons-logging library has explicitly - * been granted permission for that operation. In this case, we need to - * run the operation using an AccessController. - */ - private static InputStream getResourceAsStream(final ClassLoader loader, final String name) { - return (InputStream)AccessController.doPrivileged( - new PrivilegedAction() { - @Override - public Object run() { - if (loader != null) { - return loader.getResourceAsStream(name); - } - return ClassLoader.getSystemResourceAsStream(name); - } - }); - } - - /** - * Given a filename, return an enumeration of URLs pointing to - * all the occurrences of that filename in the classpath. - *

- * This is just like ClassLoader.getResources except that the - * operation is done under an AccessController so that this method will - * succeed when this jarfile is privileged but the caller is not. - * This method must therefore remain private to avoid security issues. - *

- * If no instances are found, an Enumeration is returned whose - * hasMoreElements method returns false (ie an "empty" enumeration). - * If resources could not be listed for some reason, null is returned. - */ - private static Enumeration getResources(final ClassLoader loader, final String name) { - final PrivilegedAction action = - new PrivilegedAction() { - @Override - public Object run() { - try { - if (loader != null) { - return loader.getResources(name); - } - return ClassLoader.getSystemResources(name); - } catch (final IOException e) { - if (isDiagnosticsEnabled()) { - logDiagnostic("Exception while trying to find configuration file " + - name + ":" + e.getMessage()); - } - return null; - } catch (final NoSuchMethodError e) { - // we must be running on a 1.1 JVM which doesn't support - // ClassLoader.getSystemResources; just return null in - // this case. - return null; - } - } - }; - final Object result = AccessController.doPrivileged(action); - return (Enumeration) result; - } - - /** - * Given a URL that refers to a .properties file, load that file. - * This is done under an AccessController so that this method will - * succeed when this jarfile is privileged but the caller is not. - * This method must therefore remain private to avoid security issues. - *

- * {@code Null} is returned if the URL cannot be opened. - */ - private static Properties getProperties(final URL url) { - final PrivilegedAction action = - new PrivilegedAction() { - @Override - public Object run() { - InputStream stream = null; - try { - // We must ensure that useCaches is set to false, as the - // default behavior of java is to cache file handles, and - // this "locks" files, preventing hot-redeploy on windows. - final URLConnection connection = url.openConnection(); - connection.setUseCaches(false); - stream = connection.getInputStream(); - if (stream != null) { - final Properties props = new Properties(); - props.load(stream); - stream.close(); - stream = null; - return props; - } - } catch (final IOException e) { - if (isDiagnosticsEnabled()) { - logDiagnostic("Unable to read URL " + url); - } - } finally { - if (stream != null) { - try { - stream.close(); - } catch (final IOException e) { - // ignore exception; this should not happen - if (isDiagnosticsEnabled()) { - logDiagnostic("Unable to close stream for URL " + url); - } - } - } - } - - return null; - } - }; - return (Properties) AccessController.doPrivileged(action); - } - - /** - * Locate a user-provided configuration file. - *

- * The classpath of the specified classLoader (usually the context classloader) - * is searched for properties files of the specified name. If none is found, - * null is returned. If more than one is found, then the file with the greatest - * value for its PRIORITY property is returned. If multiple files have the - * same PRIORITY value then the first in the classpath is returned. - *

- * This differs from the 1.0.x releases; those always use the first one found. - * However as the priority is a new field, this change is backwards compatible. - *

- * The purpose of the priority field is to allow a webserver administrator to - * override logging settings in all webapps by placing a commons-logging.properties - * file in a shared classpath location with a priority > 0; this overrides any - * commons-logging.properties files without priorities which are in the - * webapps. Webapps can also use explicit priorities to override a configuration - * file in the shared classpath if needed. - */ - private static final Properties getConfigurationFile(final ClassLoader classLoader, final String fileName) { - Properties props = null; - double priority = 0.0; - URL propsUrl = null; - try { - final Enumeration urls = getResources(classLoader, fileName); - - if (urls == null) { - return null; - } - - while (urls.hasMoreElements()) { - final URL url = (URL) urls.nextElement(); - - final Properties newProps = getProperties(url); - if (newProps != null) { - if (props == null) { - propsUrl = url; - props = newProps; - final String priorityStr = props.getProperty(PRIORITY_KEY); - priority = 0.0; - if (priorityStr != null) { - priority = Double.parseDouble(priorityStr); - } - - if (isDiagnosticsEnabled()) { - logDiagnostic("[LOOKUP] Properties file found at '" + url + "'" + - " with priority " + priority); - } - } else { - final String newPriorityStr = newProps.getProperty(PRIORITY_KEY); - double newPriority = 0.0; - if (newPriorityStr != null) { - newPriority = Double.parseDouble(newPriorityStr); - } - - if (newPriority > priority) { - if (isDiagnosticsEnabled()) { - logDiagnostic("[LOOKUP] Properties file at '" + url + "'" + - " with priority " + newPriority + - " overrides file at '" + propsUrl + "'" + - " with priority " + priority); - } - - propsUrl = url; - props = newProps; - priority = newPriority; - } else { - if (isDiagnosticsEnabled()) { - logDiagnostic("[LOOKUP] Properties file at '" + url + "'" + - " with priority " + newPriority + - " does not override file at '" + propsUrl + "'" + - " with priority " + priority); - } - } - } - - } - } - } catch (final SecurityException e) { - if (isDiagnosticsEnabled()) { - logDiagnostic("SecurityException thrown while trying to find/read config files."); - } - } - - if (isDiagnosticsEnabled()) { - if (props == null) { - logDiagnostic("[LOOKUP] No properties file of name '" + fileName + "' found."); - } else { - logDiagnostic("[LOOKUP] Properties file of name '" + fileName + "' found at '" + propsUrl + '"'); - } - } - - return props; - } - - /** - * Read the specified system property, using an AccessController so that - * the property can be read if JCL has been granted the appropriate - * security rights even if the calling code has not. - *

- * Take care not to expose the value returned by this method to the - * calling application in any way; otherwise the calling app can use that - * info to access data that should not be available to it. - */ - private static String getSystemProperty(final String key, final String def) - throws SecurityException { - return (String) AccessController.doPrivileged( - new PrivilegedAction() { - @Override - public Object run() { - return System.getProperty(key, def); - } - }); - } - /** * Determines whether the user wants internal diagnostic output. If so, * returns an appropriate writer object. Users can enable diagnostic @@ -1472,45 +1278,6 @@ public abstract class LogFactory { return DIAGNOSTICS_STREAM != null; } - /** - * Write the specified message to the internal logging destination. - *

- * Note that this method is private; concrete subclasses of this class - * should not call it because the diagnosticPrefix string this - * method puts in front of all its messages is LogFactory@...., - * while subclasses should put SomeSubClass@... - *

- * Subclasses should instead compute their own prefix, then call - * logRawDiagnostic. Note that calling isDiagnosticsEnabled is - * fine for subclasses. - *

- * Note that it is safe to call this method before initDiagnostics - * is called; any output will just be ignored (as isDiagnosticsEnabled - * will return false). - * - * @param msg is the diagnostic message to be output. - */ - private static final void logDiagnostic(final String msg) { - if (DIAGNOSTICS_STREAM != null) { - DIAGNOSTICS_STREAM.print(diagnosticPrefix); - DIAGNOSTICS_STREAM.println(msg); - DIAGNOSTICS_STREAM.flush(); - } - } - - /** - * Write the specified message to the internal logging destination. - * - * @param msg is the diagnostic message to be output. - * @since 1.1 - */ - protected static final void logRawDiagnostic(final String msg) { - if (DIAGNOSTICS_STREAM != null) { - DIAGNOSTICS_STREAM.println(msg); - DIAGNOSTICS_STREAM.flush(); - } - } - /** * Generate useful diagnostics regarding the classloader tree for * the specified class. @@ -1558,6 +1325,32 @@ public abstract class LogFactory { logHierarchy("[ENV] Ancestry of classloader which loaded " + className + " is ", classLoader); } + /** + * Write the specified message to the internal logging destination. + *

+ * Note that this method is private; concrete subclasses of this class + * should not call it because the diagnosticPrefix string this + * method puts in front of all its messages is LogFactory@...., + * while subclasses should put SomeSubClass@... + *

+ * Subclasses should instead compute their own prefix, then call + * logRawDiagnostic. Note that calling isDiagnosticsEnabled is + * fine for subclasses. + *

+ * Note that it is safe to call this method before initDiagnostics + * is called; any output will just be ignored (as isDiagnosticsEnabled + * will return false). + * + * @param msg is the diagnostic message to be output. + */ + private static final void logDiagnostic(final String msg) { + if (DIAGNOSTICS_STREAM != null) { + DIAGNOSTICS_STREAM.print(diagnosticPrefix); + DIAGNOSTICS_STREAM.println(msg); + DIAGNOSTICS_STREAM.flush(); + } + } + /** * Logs diagnostic messages about the given classloader * and it's hierarchy. The prefix is prepended to the message @@ -1606,6 +1399,105 @@ public abstract class LogFactory { } } + /** + * Write the specified message to the internal logging destination. + * + * @param msg is the diagnostic message to be output. + * @since 1.1 + */ + protected static final void logRawDiagnostic(final String msg) { + if (DIAGNOSTICS_STREAM != null) { + DIAGNOSTICS_STREAM.println(msg); + DIAGNOSTICS_STREAM.flush(); + } + } + + /** + * Method provided for backwards compatibility; see newFactory version that + * takes 3 parameters. + *

+ * This method would only ever be called in some rather odd situation. + * Note that this method is static, so overriding in a subclass doesn't + * have any effect unless this method is called from a method in that + * subclass. However this method only makes sense to use from the + * getFactory method, and as that is almost always invoked via + * LogFactory.getFactory, any custom definition in a subclass would be + * pointless. Only a class with a custom getFactory method, then invoked + * directly via CustomFactoryImpl.getFactory or similar would ever call + * this. Anyway, it's here just in case, though the "managed class loader" + * value output to the diagnostics will not report the correct value. + *

+ * + * @param factoryClass factory class. + * @param classLoader class loader. + * @return a LogFactory. + */ + protected static LogFactory newFactory(final String factoryClass, + final ClassLoader classLoader) { + return newFactory(factoryClass, classLoader, null); + } + + /** + * Return a new instance of the specified {@code LogFactory} implementation class, loaded by the specified class loader. If that fails, try the class loader + * used to load this (abstract) LogFactory. + *

ClassLoader conflicts

+ *

+ * Note that there can be problems if the specified ClassLoader is not the same as the classloader that loaded this class, ie when loading a concrete + * LogFactory subclass via a context classloader. + *

+ *

+ * The problem is the same one that can occur when loading a concrete Log subclass via a context classloader. + *

+ *

+ * The problem occurs when code running in the context classloader calls class X which was loaded via a parent classloader, and class X then calls + * LogFactory.getFactory (either directly or via LogFactory.getLog). Because class X was loaded via the parent, it binds to LogFactory loaded via the + * parent. When the code in this method finds some LogFactoryYYYY class in the child (context) classloader, and there also happens to be a LogFactory class + * defined in the child classloader, then LogFactoryYYYY will be bound to LogFactory@childloader. It cannot be cast to LogFactory@parentloader, ie this + * method cannot return the object as the desired type. Note that it doesn't matter if the LogFactory class in the child classloader is identical to the + * LogFactory class in the parent classloader, they are not compatible. + *

+ *

+ * The solution taken here is to simply print out an error message when this occurs then throw an exception. The deployer of the application must ensure + * they remove all occurrences of the LogFactory class from the child classloader in order to resolve the issue. Note that they do not have to move the + * custom LogFactory subclass; that is ok as long as the only LogFactory class it can find to bind to is in the parent classloader. + *

+ * + * @param factoryClass Fully qualified name of the {@code LogFactory} implementation class + * @param classLoader ClassLoader from which to load this class + * @param contextClassLoader is the context that this new factory will manage logging for. + * @return a new instance of the specified {@code LogFactory}. + * @throws LogConfigurationException if a suitable instance cannot be created + * @since 1.1 + */ + protected static LogFactory newFactory(final String factoryClass, + final ClassLoader classLoader, + final ClassLoader contextClassLoader) + throws LogConfigurationException { + // Note that any unchecked exceptions thrown by the createFactory + // method will propagate out of this method; in particular a + // ClassCastException can be thrown. + final Object result = AccessController.doPrivileged( + new PrivilegedAction() { + @Override + public Object run() { + return createFactory(factoryClass, classLoader); + } + }); + + if (result instanceof LogConfigurationException) { + final LogConfigurationException ex = (LogConfigurationException) result; + if (isDiagnosticsEnabled()) { + logDiagnostic("An error occurred while loading the factory class:" + ex.getMessage()); + } + throw ex; + } + if (isDiagnosticsEnabled()) { + logDiagnostic("Created object " + objectId(result) + " to manage classloader " + + objectId(contextClassLoader)); + } + return (LogFactory)result; + } + /** * Returns a string that uniquely identifies the specified object, including * its class. @@ -1625,6 +1517,136 @@ public abstract class LogFactory { return o.getClass().getName() + "@" + System.identityHashCode(o); } + /** + * Release any internal references to previously created {@link LogFactory} + * instances that have been associated with the specified class loader + * (if any), after calling the instance method {@code release()} on + * each of them. + * + * @param classLoader ClassLoader for which to release the LogFactory + */ + public static void release(final ClassLoader classLoader) { + if (isDiagnosticsEnabled()) { + logDiagnostic("Releasing factory for classloader " + objectId(classLoader)); + } + // factories is not final and could be replaced in this block. + final Hashtable factories = LogFactory.factories; + synchronized (factories) { + if (classLoader == null) { + if (nullClassLoaderFactory != null) { + nullClassLoaderFactory.release(); + nullClassLoaderFactory = null; + } + } else { + final LogFactory factory = (LogFactory) factories.get(classLoader); + if (factory != null) { + factory.release(); + factories.remove(classLoader); + } + } + } + } + + /** + * Release any internal references to previously created {@link LogFactory} + * instances, after calling the instance method {@code release()} on + * each of them. This is useful in environments like servlet containers, + * which implement application reloading by throwing away a ClassLoader. + * Dangling references to objects in that class loader would prevent + * garbage collection. + */ + public static void releaseAll() { + if (isDiagnosticsEnabled()) { + logDiagnostic("Releasing factory for all classloaders."); + } + // factories is not final and could be replaced in this block. + final Hashtable factories = LogFactory.factories; + synchronized (factories) { + final Enumeration elements = factories.elements(); + while (elements.hasMoreElements()) { + final LogFactory element = (LogFactory) elements.nextElement(); + element.release(); + } + factories.clear(); + + if (nullClassLoaderFactory != null) { + nullClassLoaderFactory.release(); + nullClassLoaderFactory = null; + } + } + } + + /** Utility method to safely trim a string. */ + private static String trim(final String src) { + if (src == null) { + return null; + } + return src.trim(); + } + + /** + * Protected constructor that is not available for public use. + */ + protected LogFactory() { + } + + /** + * Return the configuration attribute with the specified name (if any), + * or {@code null} if there is no such attribute. + * + * @param name Name of the attribute to return + * @return the configuration attribute with the specified name. + */ + public abstract Object getAttribute(String name); + + /** + * Gets an array containing the names of all currently defined configuration attributes. If there are no such attributes, a zero length array is returned. + * + * @return an array containing the names of all currently defined configuration attributes + */ + public abstract String[] getAttributeNames(); + + /** + * Convenience method to derive a name from the specified class and call {@code getInstance(String)} with it. + * + * @param clazz Class for which a suitable Log name will be derived + * @return a name from the specified class. + * @throws LogConfigurationException if a suitable {@code Log} instance cannot be returned + */ + public abstract Log getInstance(Class clazz) throws LogConfigurationException; + + /** + * Construct (if necessary) and return a {@code Log} instance, using the factory's current set of configuration attributes. + *

+ * NOTE - Depending upon the implementation of the {@code LogFactory} you are using, the {@code Log} instance you are returned may or may + * not be local to the current application, and may or may not be returned again on a subsequent call with the same name argument. + *

+ * + * @param name Logical name of the {@code Log} instance to be returned (the meaning of this name is only known to the underlying logging implementation that + * is being wrapped) + * @return a {@code Log} instance. + * @throws LogConfigurationException if a suitable {@code Log} instance cannot be returned + */ + public abstract Log getInstance(String name) + throws LogConfigurationException; + + /** + * Release any internal references to previously created {@link Log} + * instances returned by this factory. This is useful in environments + * like servlet containers, which implement application reloading by + * throwing away a ClassLoader. Dangling references to objects in that + * class loader would prevent garbage collection. + */ + public abstract void release(); + + /** + * Remove any configuration attribute associated with the specified name. + * If there is no such attribute, no action is taken. + * + * @param name Name of the attribute to remove + */ + public abstract void removeAttribute(String name); + // ---------------------------------------------------------------------- // Static initializer block to perform initialization at class load time. // @@ -1644,36 +1666,14 @@ public abstract class LogFactory { // of the class file. // ---------------------------------------------------------------------- - static { - // note: it's safe to call methods before initDiagnostics (though - // diagnostic output gets discarded). - ClassLoader thisClassLoader = getClassLoader(LogFactory.class); - thisClassLoaderRef = new WeakReference(thisClassLoader); - // In order to avoid confusion where multiple instances of JCL are - // being used via different classloaders within the same app, we - // ensure each logged message has a prefix of form - // [LogFactory from classloader OID] - // - // Note that this prefix should be kept consistent with that - // in LogFactoryImpl. However here we don't need to output info - // about the actual *instance* of LogFactory, as all methods that - // output diagnostics from this class are static. - String classLoaderName; - try { - if (thisClassLoader == null) { - classLoaderName = "BOOTLOADER"; - } else { - classLoaderName = objectId(thisClassLoader); - } - } catch (final SecurityException e) { - classLoaderName = "UNKNOWN"; - } - diagnosticPrefix = "[LogFactory from " + classLoaderName + "] "; - DIAGNOSTICS_STREAM = initDiagnostics(); - logClassLoaderEnvironment(LogFactory.class); - factories = createFactoryStore(); - if (isDiagnosticsEnabled()) { - logDiagnostic("BOOTSTRAP COMPLETED"); - } - } + /** + * Set the configuration attribute with the specified name. Calling + * this with a {@code null} value is equivalent to calling + * {@code removeAttribute(name)}. + * + * @param name Name of the attribute to set + * @param value Value of the attribute to set, or {@code null} + * to remove any setting for this attribute + */ + public abstract void setAttribute(String name, Object value); } diff --git a/src/main/java/org/apache/commons/logging/LogSource.java b/src/main/java/org/apache/commons/logging/LogSource.java index 5517bdb..4bbb0b8 100644 --- a/src/main/java/org/apache/commons/logging/LogSource.java +++ b/src/main/java/org/apache/commons/logging/LogSource.java @@ -134,55 +134,18 @@ public class LogSource { // ------------------------------------------------------------ Constructor - /** Don't allow others to create instances. */ - private LogSource() { + /** + * Get a {@code Log} instance by class. + * + * @param clazz a Class. + * @return a {@code Log} instance. + */ + static public Log getInstance(final Class clazz) { + return getInstance(clazz.getName()); } // ---------------------------------------------------------- Class Methods - /** - * Set the log implementation/log implementation factory by the name of the class. The given class must implement {@link Log}, and provide a constructor - * that takes a single {@link String} argument (containing the name of the log). - * - * @param className class name. - * @throws LinkageError if there is missing dependency. - * @throws NoSuchMethodException if a matching method is not found. - * @throws SecurityException If a security manager, s, is present and the caller's class loader is not the same as or an ancestor of the class - * loader for the current class and invocation of {@link SecurityManager#checkPackageAccess s.checkPackageAccess()} denies - * access to the package of this class. - * @throws ClassNotFoundException if the class cannot be located - */ - static public void setLogImplementation(final String className) - throws LinkageError, NoSuchMethodException, SecurityException, ClassNotFoundException { - try { - final Class logclass = Class.forName(className); - final Class[] argtypes = new Class[1]; - argtypes[0] = "".getClass(); - logImplctor = logclass.getConstructor(argtypes); - } catch (final Throwable t) { - logImplctor = null; - } - } - - /** - * Set the log implementation/log implementation factory by class. The given class must implement {@link Log}, and provide a constructor that takes a single - * {@link String} argument (containing the name of the log). - * - * @param logclass class. - * @throws LinkageError if there is missing dependency. - * @throws ExceptionInInitializerError unexpected exception has occurred in a static initializer. - * @throws NoSuchMethodException if a matching method is not found. - * @throws SecurityException If a security manager, s, is present and the caller's class loader is not the same as or an ancestor of the - * class loader for the current class and invocation of {@link SecurityManager#checkPackageAccess - * s.checkPackageAccess()} denies access to the package of this class. - */ - static public void setLogImplementation(final Class logclass) - throws LinkageError, ExceptionInInitializerError, NoSuchMethodException, SecurityException { - final Class[] argtypes = new Class[1]; - argtypes[0] = "".getClass(); - logImplctor = logclass.getConstructor(argtypes); - } - /** * Get a {@code Log} instance by class name. * @@ -194,13 +157,14 @@ public class LogSource { } /** - * Get a {@code Log} instance by class. + * Returns a {@link String} array containing the names of + * all logs known to me. * - * @param clazz a Class. - * @return a {@code Log} instance. + * @return a {@link String} array containing the names of + * all logs known to me. */ - static public Log getInstance(final Class clazz) { - return getInstance(clazz.getName()); + static public String[] getLogNames() { + return (String[]) logs.keySet().toArray(EMPTY_STRING_ARRAY); } /** @@ -236,13 +200,49 @@ public class LogSource { } /** - * Returns a {@link String} array containing the names of - * all logs known to me. + * Set the log implementation/log implementation factory by class. The given class must implement {@link Log}, and provide a constructor that takes a single + * {@link String} argument (containing the name of the log). * - * @return a {@link String} array containing the names of - * all logs known to me. + * @param logclass class. + * @throws LinkageError if there is missing dependency. + * @throws ExceptionInInitializerError unexpected exception has occurred in a static initializer. + * @throws NoSuchMethodException if a matching method is not found. + * @throws SecurityException If a security manager, s, is present and the caller's class loader is not the same as or an ancestor of the + * class loader for the current class and invocation of {@link SecurityManager#checkPackageAccess + * s.checkPackageAccess()} denies access to the package of this class. */ - static public String[] getLogNames() { - return (String[]) logs.keySet().toArray(EMPTY_STRING_ARRAY); + static public void setLogImplementation(final Class logclass) + throws LinkageError, ExceptionInInitializerError, NoSuchMethodException, SecurityException { + final Class[] argtypes = new Class[1]; + argtypes[0] = "".getClass(); + logImplctor = logclass.getConstructor(argtypes); + } + + /** + * Set the log implementation/log implementation factory by the name of the class. The given class must implement {@link Log}, and provide a constructor + * that takes a single {@link String} argument (containing the name of the log). + * + * @param className class name. + * @throws LinkageError if there is missing dependency. + * @throws NoSuchMethodException if a matching method is not found. + * @throws SecurityException If a security manager, s, is present and the caller's class loader is not the same as or an ancestor of the class + * loader for the current class and invocation of {@link SecurityManager#checkPackageAccess s.checkPackageAccess()} denies + * access to the package of this class. + * @throws ClassNotFoundException if the class cannot be located + */ + static public void setLogImplementation(final String className) + throws LinkageError, NoSuchMethodException, SecurityException, ClassNotFoundException { + try { + final Class logclass = Class.forName(className); + final Class[] argtypes = new Class[1]; + argtypes[0] = "".getClass(); + logImplctor = logclass.getConstructor(argtypes); + } catch (final Throwable t) { + logImplctor = null; + } + } + + /** Don't allow others to create instances. */ + private LogSource() { } } diff --git a/src/main/java/org/apache/commons/logging/impl/AvalonLogger.java b/src/main/java/org/apache/commons/logging/impl/AvalonLogger.java index fc51fd6..13c2070 100644 --- a/src/main/java/org/apache/commons/logging/impl/AvalonLogger.java +++ b/src/main/java/org/apache/commons/logging/impl/AvalonLogger.java @@ -57,6 +57,16 @@ public class AvalonLogger implements Log { /** Ancestral Avalon logger. */ private static volatile Logger defaultLogger; + /** + * Sets the ancestral Avalon logger from which the delegating loggers will descend. + * + * @param logger the default avalon logger, + * in case there is no logger instance supplied in constructor + */ + public static void setDefaultLogger(final Logger logger) { + defaultLogger = logger; + } + /** Avalon logger used to perform log. */ private final transient Logger logger; @@ -82,22 +92,16 @@ public class AvalonLogger implements Log { } /** - * Gets the Avalon logger implementation used to perform logging. + * Logs a message with {@code org.apache.avalon.framework.logger.Logger.debug}. * - * @return avalon logger implementation + * @param message to log. + * @see org.apache.commons.logging.Log#debug(Object) */ - public Logger getLogger() { - return logger; - } - - /** - * Sets the ancestral Avalon logger from which the delegating loggers will descend. - * - * @param logger the default avalon logger, - * in case there is no logger instance supplied in constructor - */ - public static void setDefaultLogger(final Logger logger) { - defaultLogger = logger; + @Override + public void debug(final Object message) { + if (getLogger().isDebugEnabled()) { + getLogger().debug(String.valueOf(message)); + } } /** @@ -115,15 +119,15 @@ public class AvalonLogger implements Log { } /** - * Logs a message with {@code org.apache.avalon.framework.logger.Logger.debug}. + * Logs a message with {@code org.apache.avalon.framework.logger.Logger.error}. * - * @param message to log. - * @see org.apache.commons.logging.Log#debug(Object) + * @param message to log + * @see org.apache.commons.logging.Log#error(Object) */ @Override - public void debug(final Object message) { - if (getLogger().isDebugEnabled()) { - getLogger().debug(String.valueOf(message)); + public void error(final Object message) { + if (getLogger().isErrorEnabled()) { + getLogger().error(String.valueOf(message)); } } @@ -142,15 +146,15 @@ public class AvalonLogger implements Log { } /** - * Logs a message with {@code org.apache.avalon.framework.logger.Logger.error}. + * Logs a message with {@code org.apache.avalon.framework.logger.Logger.fatalError}. * * @param message to log - * @see org.apache.commons.logging.Log#error(Object) + * @see org.apache.commons.logging.Log#fatal(Object) */ @Override - public void error(final Object message) { - if (getLogger().isErrorEnabled()) { - getLogger().error(String.valueOf(message)); + public void fatal(final Object message) { + if (getLogger().isFatalErrorEnabled()) { + getLogger().fatalError(String.valueOf(message)); } } @@ -169,15 +173,24 @@ public class AvalonLogger implements Log { } /** - * Logs a message with {@code org.apache.avalon.framework.logger.Logger.fatalError}. + * Gets the Avalon logger implementation used to perform logging. + * + * @return avalon logger implementation + */ + public Logger getLogger() { + return logger; + } + + /** + * Logs a message with {@code org.apache.avalon.framework.logger.Logger.info}. * * @param message to log - * @see org.apache.commons.logging.Log#fatal(Object) + * @see org.apache.commons.logging.Log#info(Object) */ @Override - public void fatal(final Object message) { - if (getLogger().isFatalErrorEnabled()) { - getLogger().fatalError(String.valueOf(message)); + public void info(final Object message) { + if (getLogger().isInfoEnabled()) { + getLogger().info(String.valueOf(message)); } } @@ -195,19 +208,6 @@ public class AvalonLogger implements Log { } } - /** - * Logs a message with {@code org.apache.avalon.framework.logger.Logger.info}. - * - * @param message to log - * @see org.apache.commons.logging.Log#info(Object) - */ - @Override - public void info(final Object message) { - if (getLogger().isInfoEnabled()) { - getLogger().info(String.valueOf(message)); - } - } - /** * Is logging to {@code org.apache.avalon.framework.logger.Logger.debug} enabled? * @see org.apache.commons.logging.Log#isDebugEnabled() @@ -262,6 +262,19 @@ public class AvalonLogger implements Log { return getLogger().isWarnEnabled(); } + /** + * Logs a message with {@code org.apache.avalon.framework.logger.Logger.debug}. + * + * @param message to log + * @see org.apache.commons.logging.Log#trace(Object) + */ + @Override + public void trace(final Object message) { + if (getLogger().isDebugEnabled()) { + getLogger().debug(String.valueOf(message)); + } + } + /** * Logs a message with {@code org.apache.avalon.framework.logger.Logger.debug}. * @@ -277,15 +290,15 @@ public class AvalonLogger implements Log { } /** - * Logs a message with {@code org.apache.avalon.framework.logger.Logger.debug}. + * Logs a message with {@code org.apache.avalon.framework.logger.Logger.warn}. * * @param message to log - * @see org.apache.commons.logging.Log#trace(Object) + * @see org.apache.commons.logging.Log#warn(Object) */ @Override - public void trace(final Object message) { - if (getLogger().isDebugEnabled()) { - getLogger().debug(String.valueOf(message)); + public void warn(final Object message) { + if (getLogger().isWarnEnabled()) { + getLogger().warn(String.valueOf(message)); } } @@ -302,17 +315,4 @@ public class AvalonLogger implements Log { getLogger().warn(String.valueOf(message), t); } } - - /** - * Logs a message with {@code org.apache.avalon.framework.logger.Logger.warn}. - * - * @param message to log - * @see org.apache.commons.logging.Log#warn(Object) - */ - @Override - public void warn(final Object message) { - if (getLogger().isWarnEnabled()) { - getLogger().warn(String.valueOf(message)); - } - } } diff --git a/src/main/java/org/apache/commons/logging/impl/Jdk13LumberjackLogger.java b/src/main/java/org/apache/commons/logging/impl/Jdk13LumberjackLogger.java index dd6a3d8..1abf6fb 100644 --- a/src/main/java/org/apache/commons/logging/impl/Jdk13LumberjackLogger.java +++ b/src/main/java/org/apache/commons/logging/impl/Jdk13LumberjackLogger.java @@ -43,11 +43,19 @@ public class Jdk13LumberjackLogger implements Log, Serializable { // ----------------------------------------------------- Instance Variables + /** + * This member variable simply ensures that any attempt to initialize + * this class in a pre-1.4 JVM will result in an ExceptionInInitializerError. + * It must not be private, as an optimising compiler could detect that it + * is not used and optimise it away. + */ + protected static final Level dummyLevel = Level.FINE; + /** * The underlying Logger implementation we are using. */ protected transient Logger logger; - + /** * Name. */ @@ -58,18 +66,10 @@ public class Jdk13LumberjackLogger implements Log, Serializable { /** Source method name. */ private String sourceMethodName = "unknown"; - + /** Class and method found flag. */ private boolean classAndMethodFound; - /** - * This member variable simply ensures that any attempt to initialize - * this class in a pre-1.4 JVM will result in an ExceptionInInitializerError. - * It must not be private, as an optimising compiler could detect that it - * is not used and optimise it away. - */ - protected static final Level dummyLevel = Level.FINE; - // ----------------------------------------------------------- Constructors /** @@ -84,55 +84,6 @@ public class Jdk13LumberjackLogger implements Log, Serializable { // --------------------------------------------------------- Public Methods - private void log( final Level level, final String msg, final Throwable ex ) { - if ( getLogger().isLoggable(level) ) { - final LogRecord record = new LogRecord(level, msg); - if ( !classAndMethodFound ) { - getClassAndMethod(); - } - record.setSourceClassName(sourceClassName); - record.setSourceMethodName(sourceMethodName); - if ( ex != null ) { - record.setThrown(ex); - } - getLogger().log(record); - } - } - - /** - * Gets the class and method by looking at the stack trace for the - * first entry that is not this class. - */ - private void getClassAndMethod() { - try { - final Throwable throwable = new Throwable(); - throwable.fillInStackTrace(); - final StringWriter stringWriter = new StringWriter(); - final PrintWriter printWriter = new PrintWriter( stringWriter ); - throwable.printStackTrace( printWriter ); - final String traceString = stringWriter.getBuffer().toString(); - final StringTokenizer tokenizer = - new StringTokenizer( traceString, "\n" ); - tokenizer.nextToken(); - String line = tokenizer.nextToken(); - while (!line.contains(this.getClass().getName())) { - line = tokenizer.nextToken(); - } - while (line.contains(this.getClass().getName())) { - line = tokenizer.nextToken(); - } - final int start = line.indexOf( "at " ) + 3; - final int end = line.indexOf( '(' ); - final String temp = line.substring( start, end ); - final int lastPeriod = temp.lastIndexOf( '.' ); - sourceClassName = temp.substring( 0, lastPeriod ); - sourceMethodName = temp.substring( lastPeriod + 1 ); - } catch ( final Exception ex ) { - // ignore - leave class and methodname unknown - } - classAndMethodFound = true; - } - /** * Logs a message with {@code java.util.logging.Level.FINE}. * @@ -202,6 +153,40 @@ public class Jdk13LumberjackLogger implements Log, Serializable { log(Level.SEVERE, String.valueOf(message), exception); } + /** + * Gets the class and method by looking at the stack trace for the + * first entry that is not this class. + */ + private void getClassAndMethod() { + try { + final Throwable throwable = new Throwable(); + throwable.fillInStackTrace(); + final StringWriter stringWriter = new StringWriter(); + final PrintWriter printWriter = new PrintWriter( stringWriter ); + throwable.printStackTrace( printWriter ); + final String traceString = stringWriter.getBuffer().toString(); + final StringTokenizer tokenizer = + new StringTokenizer( traceString, "\n" ); + tokenizer.nextToken(); + String line = tokenizer.nextToken(); + while (!line.contains(this.getClass().getName())) { + line = tokenizer.nextToken(); + } + while (line.contains(this.getClass().getName())) { + line = tokenizer.nextToken(); + } + final int start = line.indexOf( "at " ) + 3; + final int end = line.indexOf( '(' ); + final String temp = line.substring( start, end ); + final int lastPeriod = temp.lastIndexOf( '.' ); + sourceClassName = temp.substring( 0, lastPeriod ); + sourceMethodName = temp.substring( lastPeriod + 1 ); + } catch ( final Exception ex ) { + // ignore - leave class and methodname unknown + } + classAndMethodFound = true; + } + /** * Return the native Logger instance we are using. * @@ -285,6 +270,21 @@ public class Jdk13LumberjackLogger implements Log, Serializable { return getLogger().isLoggable(Level.WARNING); } + private void log( final Level level, final String msg, final Throwable ex ) { + if ( getLogger().isLoggable(level) ) { + final LogRecord record = new LogRecord(level, msg); + if ( !classAndMethodFound ) { + getClassAndMethod(); + } + record.setSourceClassName(sourceClassName); + record.setSourceMethodName(sourceMethodName); + if ( ex != null ) { + record.setThrown(ex); + } + getLogger().log(record); + } + } + /** * Logs a message with {@code java.util.logging.Level.FINEST}. * diff --git a/src/main/java/org/apache/commons/logging/impl/Jdk14Logger.java b/src/main/java/org/apache/commons/logging/impl/Jdk14Logger.java index 58e5ce6..d3f349d 100644 --- a/src/main/java/org/apache/commons/logging/impl/Jdk14Logger.java +++ b/src/main/java/org/apache/commons/logging/impl/Jdk14Logger.java @@ -43,16 +43,6 @@ public class Jdk14Logger implements Log, Serializable { // ----------------------------------------------------------- Constructors - /** - * Construct a named instance of this Logger. - * - * @param name Name of the logger to be constructed - */ - public Jdk14Logger(final String name) { - this.name = name; - logger = getLogger(); - } - /** * The underlying Logger implementation we are using. */ @@ -64,31 +54,13 @@ public class Jdk14Logger implements Log, Serializable { protected String name; /** - * Logs a message at the given level. - * @param level The level. - * @param msg The message. - * @param ex The exception. + * Construct a named instance of this Logger. + * + * @param name Name of the logger to be constructed */ - protected void log(final Level level, final String msg, final Throwable ex) { - final Logger logger = getLogger(); - if (logger.isLoggable(level)) { - // Hack (?) to get the stack trace. - final Throwable dummyException = new Throwable(); - final StackTraceElement[] locations = dummyException.getStackTrace(); - // LOGGING-132: use the provided logger name instead of the class name - final String cname = name; - String method = "unknown"; - // Caller will be the third element - if (locations != null && locations.length > 2) { - final StackTraceElement caller = locations[2]; - method = caller.getMethodName(); - } - if (ex == null) { - logger.logp(level, cname, method, msg); - } else { - logger.logp(level, cname, method, msg, ex); - } - } + public Jdk14Logger(final String name) { + this.name = name; + logger = getLogger(); } /** @@ -243,6 +215,34 @@ public class Jdk14Logger implements Log, Serializable { return getLogger().isLoggable(Level.WARNING); } + /** + * Logs a message at the given level. + * @param level The level. + * @param msg The message. + * @param ex The exception. + */ + protected void log(final Level level, final String msg, final Throwable ex) { + final Logger logger = getLogger(); + if (logger.isLoggable(level)) { + // Hack (?) to get the stack trace. + final Throwable dummyException = new Throwable(); + final StackTraceElement[] locations = dummyException.getStackTrace(); + // LOGGING-132: use the provided logger name instead of the class name + final String cname = name; + String method = "unknown"; + // Caller will be the third element + if (locations != null && locations.length > 2) { + final StackTraceElement caller = locations[2]; + method = caller.getMethodName(); + } + if (ex == null) { + logger.logp(level, cname, method, msg); + } else { + logger.logp(level, cname, method, msg, ex); + } + } + } + /** * Logs a message with {@code java.util.logging.Level.FINEST}. * diff --git a/src/main/java/org/apache/commons/logging/impl/Log4JLogger.java b/src/main/java/org/apache/commons/logging/impl/Log4JLogger.java index 20b9446..abd896b 100644 --- a/src/main/java/org/apache/commons/logging/impl/Log4JLogger.java +++ b/src/main/java/org/apache/commons/logging/impl/Log4JLogger.java @@ -48,28 +48,8 @@ public class Log4JLogger implements Log, Serializable { /** The fully qualified name of the Log4JLogger class. */ private static final String FQCN = Log4JLogger.class.getName(); - /** Log to this logger */ - private transient volatile Logger logger; - - /** Logger name */ - private final String name; - private static final Priority traceLevel; - // ------------------------------------------------------------ - // Static Initializer. - // - // Note that this must come after the static variable declarations - // otherwise initializer expressions associated with those variables - // will override any settings done here. - // - // Verify that log4j is available, and that it is version 1.2. - // If an ExceptionInInitializerError is generated, then LogFactoryImpl - // will treat that as meaning that the appropriate underlying logging - // library is just not present - if discovery is in progress then - // discovery will continue. - // ------------------------------------------------------------ - static { if (!Priority.class.isAssignableFrom(Level.class)) { // nope, this is log4j 1.3, so force an ExceptionInInitializerError @@ -90,6 +70,26 @@ public class Log4JLogger implements Log, Serializable { traceLevel = _traceLevel; } + /** Log to this logger */ + private transient volatile Logger logger; + + // ------------------------------------------------------------ + // Static Initializer. + // + // Note that this must come after the static variable declarations + // otherwise initializer expressions associated with those variables + // will override any settings done here. + // + // Verify that log4j is available, and that it is version 1.2. + // If an ExceptionInInitializerError is generated, then LogFactoryImpl + // will treat that as meaning that the appropriate underlying logging + // library is just not present - if discovery is in progress then + // discovery will continue. + // ------------------------------------------------------------ + + /** Logger name */ + private final String name; + /** * Constructs a new instance. */ @@ -97,16 +97,6 @@ public class Log4JLogger implements Log, Serializable { name = null; } - /** - * Base constructor. - * - * @param name name. - */ - public Log4JLogger(final String name) { - this.name = name; - this.logger = getLogger(); - } - /** * For use with a log4j factory. * @@ -122,30 +112,13 @@ public class Log4JLogger implements Log, Serializable { } /** - * Logs a message with {@code org.apache.log4j.Priority.TRACE}. - * When using a log4j version that does not support the {@code TRACE} - * level, the message will be logged at the {@code DEBUG} level. + * Base constructor. * - * @param message to log - * @see org.apache.commons.logging.Log#trace(Object) + * @param name name. */ - @Override - public void trace(final Object message) { - getLogger().log(FQCN, traceLevel, message, null); - } - - /** - * Logs a message with {@code org.apache.log4j.Priority.TRACE}. - * When using a log4j version that does not support the {@code TRACE} - * level, the message will be logged at the {@code DEBUG} level. - * - * @param message to log - * @param t log this cause - * @see org.apache.commons.logging.Log#trace(Object, Throwable) - */ - @Override - public void trace(final Object message, final Throwable t) { - getLogger().log(FQCN, traceLevel, message, t); + public Log4JLogger(final String name) { + this.name = name; + this.logger = getLogger(); } /** @@ -171,52 +144,6 @@ public class Log4JLogger implements Log, Serializable { getLogger().log(FQCN, Level.DEBUG, message, t); } - /** - * Logs a message with {@code org.apache.log4j.Priority.INFO}. - * - * @param message to log - * @see org.apache.commons.logging.Log#info(Object) - */ - @Override - public void info(final Object message) { - getLogger().log(FQCN, Level.INFO, message, null); - } - - /** - * Logs a message with {@code org.apache.log4j.Priority.INFO}. - * - * @param message to log - * @param t log this cause - * @see org.apache.commons.logging.Log#info(Object, Throwable) - */ - @Override - public void info(final Object message, final Throwable t) { - getLogger().log(FQCN, Level.INFO, message, t); - } - - /** - * Logs a message with {@code org.apache.log4j.Priority.WARN}. - * - * @param message to log - * @see org.apache.commons.logging.Log#warn(Object) - */ - @Override - public void warn(final Object message) { - getLogger().log(FQCN, Level.WARN, message, null); - } - - /** - * Logs a message with {@code org.apache.log4j.Priority.WARN}. - * - * @param message to log - * @param t log this cause - * @see org.apache.commons.logging.Log#warn(Object, Throwable) - */ - @Override - public void warn(final Object message, final Throwable t) { - getLogger().log(FQCN, Level.WARN, message, t); - } - /** * Logs a message with {@code org.apache.log4j.Priority.ERROR}. * @@ -281,6 +208,29 @@ public class Log4JLogger implements Log, Serializable { return result; } + /** + * Logs a message with {@code org.apache.log4j.Priority.INFO}. + * + * @param message to log + * @see org.apache.commons.logging.Log#info(Object) + */ + @Override + public void info(final Object message) { + getLogger().log(FQCN, Level.INFO, message, null); + } + + /** + * Logs a message with {@code org.apache.log4j.Priority.INFO}. + * + * @param message to log + * @param t log this cause + * @see org.apache.commons.logging.Log#info(Object, Throwable) + */ + @Override + public void info(final Object message, final Throwable t) { + getLogger().log(FQCN, Level.INFO, message, t); + } + /** * Tests whether the Log4j Logger used is enabled for {@code DEBUG} priority. */ @@ -330,4 +280,54 @@ public class Log4JLogger implements Log, Serializable { public boolean isWarnEnabled() { return getLogger().isEnabledFor(Level.WARN); } + + /** + * Logs a message with {@code org.apache.log4j.Priority.TRACE}. + * When using a log4j version that does not support the {@code TRACE} + * level, the message will be logged at the {@code DEBUG} level. + * + * @param message to log + * @see org.apache.commons.logging.Log#trace(Object) + */ + @Override + public void trace(final Object message) { + getLogger().log(FQCN, traceLevel, message, null); + } + + /** + * Logs a message with {@code org.apache.log4j.Priority.TRACE}. + * When using a log4j version that does not support the {@code TRACE} + * level, the message will be logged at the {@code DEBUG} level. + * + * @param message to log + * @param t log this cause + * @see org.apache.commons.logging.Log#trace(Object, Throwable) + */ + @Override + public void trace(final Object message, final Throwable t) { + getLogger().log(FQCN, traceLevel, message, t); + } + + /** + * Logs a message with {@code org.apache.log4j.Priority.WARN}. + * + * @param message to log + * @see org.apache.commons.logging.Log#warn(Object) + */ + @Override + public void warn(final Object message) { + getLogger().log(FQCN, Level.WARN, message, null); + } + + /** + * Logs a message with {@code org.apache.log4j.Priority.WARN}. + * + * @param message to log + * @param t log this cause + * @see org.apache.commons.logging.Log#warn(Object, Throwable) + */ + @Override + public void warn(final Object message, final Throwable t) { + getLogger().log(FQCN, Level.WARN, message, t); + } } diff --git a/src/main/java/org/apache/commons/logging/impl/LogFactoryImpl.java b/src/main/java/org/apache/commons/logging/impl/LogFactoryImpl.java index 2a12bc8..cb1ee5f 100644 --- a/src/main/java/org/apache/commons/logging/impl/LogFactoryImpl.java +++ b/src/main/java/org/apache/commons/logging/impl/LogFactoryImpl.java @@ -82,24 +82,14 @@ public class LogFactoryImpl extends LogFactory { // ----------------------------------------------------------- Constructors - /** - * Public no-arguments constructor required by the lookup mechanism. - */ - public LogFactoryImpl() { - initDiagnostics(); // method on this object - if (isDiagnosticsEnabled()) { - logDiagnostic("Instance created."); - } - } - - // ----------------------------------------------------- Manifest Constants - /** * The name ({@code org.apache.commons.logging.Log}) of the system * property identifying our {@link Log} implementation class. */ public static final String LOG_PROPERTY = "org.apache.commons.logging.Log"; + // ----------------------------------------------------- Manifest Constants + /** * The deprecated system property used for backwards compatibility with * old versions of JCL. @@ -168,8 +158,79 @@ public class LogFactoryImpl extends LogFactory { "org.apache.commons.logging.impl.SimpleLog" }; + /** + * Workaround for bug in Java1.2; in theory this method is not needed. {@link LogFactory#getClassLoader(Class)}. + * + * @param clazz See {@link LogFactory#getClassLoader(Class)}. + * @return See {@link LogFactory#getClassLoader(Class)}. + * @since 1.1 + */ + protected static ClassLoader getClassLoader(final Class clazz) { + return LogFactory.getClassLoader(clazz); + } + // ----------------------------------------------------- Instance Variables + /** + * Gets the context ClassLoader. + * This method is a workaround for a java 1.2 compiler bug. + * + * @return the context ClassLoader + * @since 1.1 + */ + protected static ClassLoader getContextClassLoader() throws LogConfigurationException { + return LogFactory.getContextClassLoader(); + } + + /** + * Calls LogFactory.directGetContextClassLoader under the control of an + * AccessController class. This means that java code running under a + * security manager that forbids access to ClassLoaders will still work + * if this class is given appropriate privileges, even when the caller + * doesn't have such privileges. Without using an AccessController, the + * the entire call stack must have the privilege before the call is + * allowed. + * + * @return the context classloader associated with the current thread, + * or null if security doesn't allow it. + * + * @throws LogConfigurationException if there was some weird error while + * attempting to get the context classloader. + * + * @throws SecurityException if the current java security policy doesn't + * allow this class to access the context classloader. + */ + private static ClassLoader getContextClassLoaderInternal() + throws LogConfigurationException { + return (ClassLoader)AccessController.doPrivileged( + (PrivilegedAction) LogFactory::directGetContextClassLoader); + } + + /** + * Read the specified system property, using an AccessController so that + * the property can be read if JCL has been granted the appropriate + * security rights even if the calling code has not. + *

+ * Take care not to expose the value returned by this method to the + * calling application in any way; otherwise the calling app can use that + * info to access data that should not be available to it. + */ + private static String getSystemProperty(final String key, final String def) + throws SecurityException { + return (String) AccessController.doPrivileged( + (PrivilegedAction) () -> System.getProperty(key, def)); + } + + /** + * Workaround for bug in Java1.2; in theory this method is not needed. + * + * @return Same as {@link LogFactory#isDiagnosticsEnabled()}. + * @see LogFactory#isDiagnosticsEnabled() + */ + protected static boolean isDiagnosticsEnabled() { + return LogFactory.isDiagnosticsEnabled(); + } + /** * Determines whether logging classes should be loaded using the thread-context * classloader, or via the classloader that loaded this LogFactoryImpl class. @@ -217,6 +278,8 @@ public class LogFactoryImpl extends LogFactory { */ protected Method logMethod; + // --------------------------------------------------------- Public Methods + /** * The signature of the {@code setLogFactory} method to be used. */ @@ -237,709 +300,16 @@ public class LogFactoryImpl extends LogFactory { */ private boolean allowFlawedHierarchy; - // --------------------------------------------------------- Public Methods - /** - * Return the configuration attribute with the specified name (if any), - * or {@code null} if there is no such attribute. - * - * @param name Name of the attribute to return + * Public no-arguments constructor required by the lookup mechanism. */ - @Override - public Object getAttribute(final String name) { - return attributes.get(name); - } - - /** - * Return an array containing the names of all currently defined - * configuration attributes. If there are no such attributes, a zero - * length array is returned. - */ - @Override - public String[] getAttributeNames() { - return (String[]) attributes.keySet().toArray(EMPTY_STRING_ARRAY); - } - - /** - * Convenience method to derive a name from the specified class and - * call {@code getInstance(String)} with it. - * - * @param clazz Class for which a suitable Log name will be derived - * - * @throws LogConfigurationException if a suitable {@code Log} - * instance cannot be returned - */ - @Override - public Log getInstance(final Class clazz) throws LogConfigurationException { - return getInstance(clazz.getName()); - } - - /** - *

Construct (if necessary) and return a {@code Log} instance, - * using the factory's current set of configuration attributes.

- * - *

NOTE - Depending upon the implementation of - * the {@code LogFactory} you are using, the {@code Log} - * instance you are returned may or may not be local to the current - * application, and may or may not be returned again on a subsequent - * call with the same name argument.

- * - * @param name Logical name of the {@code Log} instance to be - * returned (the meaning of this name is only known to the underlying - * logging implementation that is being wrapped) - * - * @throws LogConfigurationException if a suitable {@code Log} - * instance cannot be returned - */ - @Override - public Log getInstance(final String name) throws LogConfigurationException { - Log instance = (Log) instances.get(name); - if (instance == null) { - instance = newInstance(name); - instances.put(name, instance); - } - return instance; - } - - /** - * Release any internal references to previously created - * {@link org.apache.commons.logging.Log} - * instances returned by this factory. This is useful in environments - * like servlet containers, which implement application reloading by - * throwing away a ClassLoader. Dangling references to objects in that - * class loader would prevent garbage collection. - */ - @Override - public void release() { - - logDiagnostic("Releasing all known loggers"); - instances.clear(); - } - - /** - * Remove any configuration attribute associated with the specified name. - * If there is no such attribute, no action is taken. - * - * @param name Name of the attribute to remove - */ - @Override - public void removeAttribute(final String name) { - attributes.remove(name); - } - - /** - * Set the configuration attribute with the specified name. Calling - * this with a {@code null} value is equivalent to calling - * {@code removeAttribute(name)}. - *

- * This method can be used to set logging configuration programmatically - * rather than via system properties. It can also be used in code running - * within a container (such as a webapp) to configure behavior on a - * per-component level instead of globally as system properties would do. - * To use this method instead of a system property, call - *

-     * LogFactory.getFactory().setAttribute(...)
-     * 
- * This must be done before the first Log object is created; configuration - * changes after that point will be ignored. - *

- * This method is also called automatically if LogFactory detects a - * commons-logging.properties file; every entry in that file is set - * automatically as an attribute here. - * - * @param name Name of the attribute to set - * @param value Value of the attribute to set, or {@code null} - * to remove any setting for this attribute - */ - @Override - public void setAttribute(final String name, final Object value) { - if (logConstructor != null) { - logDiagnostic("setAttribute: call too late; configuration already performed."); - } - - if (value == null) { - attributes.remove(name); - } else { - attributes.put(name, value); - } - - if (name.equals(TCCL_KEY)) { - useTCCL = value != null && Boolean.parseBoolean(value.toString()); - } - } - - // ------------------------------------------------------ - // Static Methods - // - // These methods only defined as workarounds for a java 1.2 bug; - // theoretically none of these are needed. - // ------------------------------------------------------ - - /** - * Gets the context ClassLoader. - * This method is a workaround for a java 1.2 compiler bug. - * - * @return the context ClassLoader - * @since 1.1 - */ - protected static ClassLoader getContextClassLoader() throws LogConfigurationException { - return LogFactory.getContextClassLoader(); - } - - /** - * Workaround for bug in Java1.2; in theory this method is not needed. - * - * @return Same as {@link LogFactory#isDiagnosticsEnabled()}. - * @see LogFactory#isDiagnosticsEnabled() - */ - protected static boolean isDiagnosticsEnabled() { - return LogFactory.isDiagnosticsEnabled(); - } - - /** - * Workaround for bug in Java1.2; in theory this method is not needed. {@link LogFactory#getClassLoader(Class)}. - * - * @param clazz See {@link LogFactory#getClassLoader(Class)}. - * @return See {@link LogFactory#getClassLoader(Class)}. - * @since 1.1 - */ - protected static ClassLoader getClassLoader(final Class clazz) { - return LogFactory.getClassLoader(clazz); - } - - // ------------------------------------------------------ Protected Methods - - /** - * Calculate and cache a string that uniquely identifies this instance, - * including which classloader the object was loaded from. - *

- * This string will later be prefixed to each "internal logging" message - * emitted, so that users can clearly see any unexpected behavior. - *

- * Note that this method does not detect whether internal logging is - * enabled or not, nor where to output stuff if it is; that is all - * handled by the parent LogFactory class. This method just computes - * its own unique prefix for log messages. - */ - private void initDiagnostics() { - // It would be nice to include an identifier of the context classloader - // that this LogFactoryImpl object is responsible for. However that - // isn't possible as that information isn't available. It is possible - // to figure this out by looking at the logging from LogFactory to - // see the context & impl ids from when this object was instantiated, - // in order to link the impl id output as this object's prefix back to - // the context it is intended to manage. - // Note that this prefix should be kept consistent with that - // in LogFactory. - final Class clazz = this.getClass(); - final ClassLoader classLoader = getClassLoader(clazz); - String classLoaderName; - try { - if (classLoader == null) { - classLoaderName = "BOOTLOADER"; - } else { - classLoaderName = objectId(classLoader); - } - } catch (final SecurityException e) { - classLoaderName = "UNKNOWN"; - } - diagnosticPrefix = "[LogFactoryImpl@" + System.identityHashCode(this) + " from " + classLoaderName + "] "; - } - - /** - * Output a diagnostic message to a user-specified destination (if the - * user has enabled diagnostic logging). - * - * @param msg diagnostic message - * @since 1.1 - */ - protected void logDiagnostic(final String msg) { + public LogFactoryImpl() { + initDiagnostics(); // method on this object if (isDiagnosticsEnabled()) { - logRawDiagnostic(diagnosticPrefix + msg); + logDiagnostic("Instance created."); } } - /** - * Return the fully qualified Java classname of the {@link Log} implementation we will be using. - * - * @return the fully qualified Java classname of the {@link Log} implementation we will be using. - * @deprecated Never invoked by this class; subclasses should not assume it will be. - */ - @Deprecated - protected String getLogClassName() { - if (logClassName == null) { - discoverLogImplementation(getClass().getName()); - } - - return logClassName; - } - - - /** - *

- * Return the {@code Constructor} that can be called to instantiate new {@link org.apache.commons.logging.Log} instances. - *

- * - *

- * IMPLEMENTATION NOTE - Race conditions caused by calling this method from more than one thread are ignored, because the same - * {@code Constructor} instance will ultimately be derived in all circumstances. - *

- * - * @return the {@code Constructor} that can be called to instantiate new {@link org.apache.commons.logging.Log} instances. - * - * @throws LogConfigurationException if a suitable constructor cannot be returned - * - * @deprecated Never invoked by this class; subclasses should not assume it will be. - */ - @Deprecated - protected Constructor getLogConstructor() - throws LogConfigurationException { - - // Return the previously identified Constructor (if any) - if (logConstructor == null) { - discoverLogImplementation(getClass().getName()); - } - - return logConstructor; - } - - /** - * Tests whether JDK 1.3 with Lumberjack logging available. - * - * @return whether JDK 1.3 with Lumberjack logging available. - * @deprecated Never invoked by this class; subclasses should not assume it will be. - */ - @Deprecated - protected boolean isJdk13LumberjackAvailable() { - return isLogLibraryAvailable( - "Jdk13Lumberjack", - "org.apache.commons.logging.impl.Jdk13LumberjackLogger"); - } - - /** - * Tests {@code true} whether JDK 1.4 or later logging is available. Also checks that the {@code Throwable} class supports {@code getStackTrace()}, - * which is required by Jdk14Logger. - * - * @return Whether JDK 1.4 or later logging is available. - * - * @deprecated Never invoked by this class; subclasses should not assume it will be. - */ - @Deprecated - protected boolean isJdk14Available() { - return isLogLibraryAvailable("Jdk14", "org.apache.commons.logging.impl.Jdk14Logger"); - } - - /** - * Tests whether a Log4J implementation available. - * - * @return whether a Log4J implementation available. - * - * @deprecated Never invoked by this class; subclasses should not assume it will be. - */ - @Deprecated - protected boolean isLog4JAvailable() { - return isLogLibraryAvailable("Log4J", LOGGING_IMPL_LOG4J_LOGGER); - } - - /** - * Create and return a new {@link org.apache.commons.logging.Log} instance for the specified name. - * - * @param name Name of the new logger - * @return a new {@link org.apache.commons.logging.Log} - * - * @throws LogConfigurationException if a new instance cannot be created - */ - protected Log newInstance(final String name) throws LogConfigurationException { - Log instance; - try { - if (logConstructor == null) { - instance = discoverLogImplementation(name); - } - else { - final Object[] params = { name }; - instance = (Log) logConstructor.newInstance(params); - } - - if (logMethod != null) { - final Object[] params = { this }; - logMethod.invoke(instance, params); - } - - return instance; - - } catch (final LogConfigurationException lce) { - - // this type of exception means there was a problem in discovery - // and we've already output diagnostics about the issue, etc.; - // just pass it on - throw lce; - - } catch (final InvocationTargetException e) { - // A problem occurred invoking the Constructor or Method - // previously discovered - final Throwable c = e.getTargetException(); - throw new LogConfigurationException(c == null ? e : c); - } catch (final Throwable t) { - handleThrowable(t); // may re-throw t - // A problem occurred invoking the Constructor or Method - // previously discovered - throw new LogConfigurationException(t); - } - } - - // ------------------------------------------------------ Private Methods - - /** - * Calls LogFactory.directGetContextClassLoader under the control of an - * AccessController class. This means that java code running under a - * security manager that forbids access to ClassLoaders will still work - * if this class is given appropriate privileges, even when the caller - * doesn't have such privileges. Without using an AccessController, the - * the entire call stack must have the privilege before the call is - * allowed. - * - * @return the context classloader associated with the current thread, - * or null if security doesn't allow it. - * - * @throws LogConfigurationException if there was some weird error while - * attempting to get the context classloader. - * - * @throws SecurityException if the current java security policy doesn't - * allow this class to access the context classloader. - */ - private static ClassLoader getContextClassLoaderInternal() - throws LogConfigurationException { - return (ClassLoader)AccessController.doPrivileged( - (PrivilegedAction) LogFactory::directGetContextClassLoader); - } - - /** - * Read the specified system property, using an AccessController so that - * the property can be read if JCL has been granted the appropriate - * security rights even if the calling code has not. - *

- * Take care not to expose the value returned by this method to the - * calling application in any way; otherwise the calling app can use that - * info to access data that should not be available to it. - */ - private static String getSystemProperty(final String key, final String def) - throws SecurityException { - return (String) AccessController.doPrivileged( - (PrivilegedAction) () -> System.getProperty(key, def)); - } - - /** - * Fetch the parent classloader of a specified classloader. - *

- * If a SecurityException occurs, null is returned. - *

- * Note that this method is non-static merely so logDiagnostic is available. - */ - private ClassLoader getParentClassLoader(final ClassLoader cl) { - try { - return (ClassLoader)AccessController.doPrivileged( - (PrivilegedAction) () -> cl.getParent()); - } catch (final SecurityException ex) { - logDiagnostic("[SECURITY] Unable to obtain parent classloader"); - return null; - } - - } - - /** - * Utility method to check whether a particular logging library is - * present and available for use. Note that this does not - * affect the future behavior of this class. - */ - private boolean isLogLibraryAvailable(final String name, final String classname) { - if (isDiagnosticsEnabled()) { - logDiagnostic("Checking for '" + name + "'."); - } - try { - final Log log = createLogFromClass( - classname, - this.getClass().getName(), // dummy category - false); - - if (log == null) { - if (isDiagnosticsEnabled()) { - logDiagnostic("Did not find '" + name + "'."); - } - return false; - } - if (isDiagnosticsEnabled()) { - logDiagnostic("Found '" + name + "'."); - } - return true; - } catch (final LogConfigurationException e) { - if (isDiagnosticsEnabled()) { - logDiagnostic("Logging system '" + name + "' is available but not useable."); - } - return false; - } - } - - /** - * Attempt to find an attribute (see method setAttribute) or a - * system property with the provided name and return its value. - *

- * The attributes associated with this object are checked before - * system properties in case someone has explicitly called setAttribute, - * or a configuration property has been set in a commons-logging.properties - * file. - * - * @return the value associated with the property, or null. - */ - private String getConfigurationValue(final String property) { - if (isDiagnosticsEnabled()) { - logDiagnostic("[ENV] Trying to get configuration for item " + property); - } - - final Object valueObj = getAttribute(property); - if (valueObj != null) { - if (isDiagnosticsEnabled()) { - logDiagnostic("[ENV] Found LogFactory attribute [" + valueObj + "] for " + property); - } - return valueObj.toString(); - } - - if (isDiagnosticsEnabled()) { - logDiagnostic("[ENV] No LogFactory attribute found for " + property); - } - - try { - // warning: minor security hole here, in that we potentially read a system - // property that the caller cannot, then output it in readable form as a - // diagnostic message. However it's only ever JCL-specific properties - // involved here, so the harm is truly trivial. - final String value = getSystemProperty(property, null); - if (value != null) { - if (isDiagnosticsEnabled()) { - logDiagnostic("[ENV] Found system property [" + value + "] for " + property); - } - return value; - } - - if (isDiagnosticsEnabled()) { - logDiagnostic("[ENV] No system property found for property " + property); - } - } catch (final SecurityException e) { - if (isDiagnosticsEnabled()) { - logDiagnostic("[ENV] Security prevented reading system property " + property); - } - } - - if (isDiagnosticsEnabled()) { - logDiagnostic("[ENV] No configuration defined for item " + property); - } - - return null; - } - - /** - * Get the setting for the user-configurable behavior specified by key. - * If nothing has explicitly been set, then return dflt. - */ - private boolean getBooleanConfiguration(final String key, final boolean dflt) { - final String val = getConfigurationValue(key); - if (val == null) { - return dflt; - } - return Boolean.parseBoolean(val); - } - - /** - * Initialize a number of variables that control the behavior of this - * class and that can be tweaked by the user. This is done when the first - * logger is created, not in the constructor of this class, because we - * need to give the user a chance to call method setAttribute in order to - * configure this object. - */ - private void initConfiguration() { - allowFlawedContext = getBooleanConfiguration(ALLOW_FLAWED_CONTEXT_PROPERTY, true); - allowFlawedDiscovery = getBooleanConfiguration(ALLOW_FLAWED_DISCOVERY_PROPERTY, true); - allowFlawedHierarchy = getBooleanConfiguration(ALLOW_FLAWED_HIERARCHY_PROPERTY, true); - } - - /** - * Attempts to create a Log instance for the given category name. - * Follows the discovery process described in the class javadoc. - * - * @param logCategory the name of the log category - * - * @throws LogConfigurationException if an error in discovery occurs, - * or if no adapter at all can be instantiated - */ - private Log discoverLogImplementation(final String logCategory) - throws LogConfigurationException { - if (isDiagnosticsEnabled()) { - logDiagnostic("Discovering a Log implementation..."); - } - - initConfiguration(); - - Log result = null; - - // See if the user specified the Log implementation to use - final String specifiedLogClassName = findUserSpecifiedLogClassName(); - - if (specifiedLogClassName != null) { - if (isDiagnosticsEnabled()) { - logDiagnostic("Attempting to load user-specified log class '" + - specifiedLogClassName + "'..."); - } - - result = createLogFromClass(specifiedLogClassName, - logCategory, - true); - if (result == null) { - final StringBuilder messageBuffer = new StringBuilder("User-specified log class '"); - messageBuffer.append(specifiedLogClassName); - messageBuffer.append("' cannot be found or is not useable."); - - // Mistyping or misspelling names is a common fault. - // Construct a good error message, if we can - informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_LOG4J_LOGGER); - informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_JDK14_LOGGER); - informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_LUMBERJACK_LOGGER); - informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_SIMPLE_LOGGER); - throw new LogConfigurationException(messageBuffer.toString()); - } - - return result; - } - - // No user specified log; try to discover what's on the classpath - // - // Note that we deliberately loop here over classesToDiscover and - // expect method createLogFromClass to loop over the possible source - // classloaders. The effect is: - // for each discoverable log adapter - // for each possible classloader - // see if it works - // - // It appears reasonable at first glance to do the opposite: - // for each possible classloader - // for each discoverable log adapter - // see if it works - // - // The latter certainly has advantages for user-installable logging - // libraries such as log4j; in a webapp for example this code should - // first check whether the user has provided any of the possible - // logging libraries before looking in the parent classloader. - // Unfortunately, however, Jdk14Logger will always work in jvm>=1.4, - // and SimpleLog will always work in any JVM. So the loop would never - // ever look for logging libraries in the parent classpath. Yet many - // users would expect that putting log4j there would cause it to be - // detected (and this is the historical JCL behavior). So we go with - // the first approach. A user that has bundled a specific logging lib - // in a webapp should use a commons-logging.properties file or a - // service file in META-INF to force use of that logging lib anyway, - // rather than relying on discovery. - - if (isDiagnosticsEnabled()) { - logDiagnostic( - "No user-specified Log implementation; performing discovery" + - " using the standard supported logging implementations..."); - } - for(int i=0; i=1.4, + // and SimpleLog will always work in any JVM. So the loop would never + // ever look for logging libraries in the parent classpath. Yet many + // users would expect that putting log4j there would cause it to be + // detected (and this is the historical JCL behavior). So we go with + // the first approach. A user that has bundled a specific logging lib + // in a webapp should use a commons-logging.properties file or a + // service file in META-INF to force use of that logging lib anyway, + // rather than relying on discovery. + + if (isDiagnosticsEnabled()) { + logDiagnostic( + "No user-specified Log implementation; performing discovery" + + " using the standard supported logging implementations..."); + } + for(int i=0; i + * The attributes associated with this object are checked before + * system properties in case someone has explicitly called setAttribute, + * or a configuration property has been set in a commons-logging.properties + * file. + * + * @return the value associated with the property, or null. + */ + private String getConfigurationValue(final String property) { + if (isDiagnosticsEnabled()) { + logDiagnostic("[ENV] Trying to get configuration for item " + property); + } + + final Object valueObj = getAttribute(property); + if (valueObj != null) { + if (isDiagnosticsEnabled()) { + logDiagnostic("[ENV] Found LogFactory attribute [" + valueObj + "] for " + property); + } + return valueObj.toString(); + } + + if (isDiagnosticsEnabled()) { + logDiagnostic("[ENV] No LogFactory attribute found for " + property); + } + + try { + // warning: minor security hole here, in that we potentially read a system + // property that the caller cannot, then output it in readable form as a + // diagnostic message. However it's only ever JCL-specific properties + // involved here, so the harm is truly trivial. + final String value = getSystemProperty(property, null); + if (value != null) { + if (isDiagnosticsEnabled()) { + logDiagnostic("[ENV] Found system property [" + value + "] for " + property); + } + return value; + } + + if (isDiagnosticsEnabled()) { + logDiagnostic("[ENV] No system property found for property " + property); + } + } catch (final SecurityException e) { + if (isDiagnosticsEnabled()) { + logDiagnostic("[ENV] Security prevented reading system property " + property); + } + } + + if (isDiagnosticsEnabled()) { + logDiagnostic("[ENV] No configuration defined for item " + property); + } + + return null; + } + + + /** + * Convenience method to derive a name from the specified class and + * call {@code getInstance(String)} with it. + * + * @param clazz Class for which a suitable Log name will be derived + * + * @throws LogConfigurationException if a suitable {@code Log} + * instance cannot be returned + */ + @Override + public Log getInstance(final Class clazz) throws LogConfigurationException { + return getInstance(clazz.getName()); + } + + /** + *

Construct (if necessary) and return a {@code Log} instance, + * using the factory's current set of configuration attributes.

+ * + *

NOTE - Depending upon the implementation of + * the {@code LogFactory} you are using, the {@code Log} + * instance you are returned may or may not be local to the current + * application, and may or may not be returned again on a subsequent + * call with the same name argument.

+ * + * @param name Logical name of the {@code Log} instance to be + * returned (the meaning of this name is only known to the underlying + * logging implementation that is being wrapped) + * + * @throws LogConfigurationException if a suitable {@code Log} + * instance cannot be returned + */ + @Override + public Log getInstance(final String name) throws LogConfigurationException { + Log instance = (Log) instances.get(name); + if (instance == null) { + instance = newInstance(name); + instances.put(name, instance); + } + return instance; + } + + /** + * Return the fully qualified Java classname of the {@link Log} implementation we will be using. + * + * @return the fully qualified Java classname of the {@link Log} implementation we will be using. + * @deprecated Never invoked by this class; subclasses should not assume it will be. + */ + @Deprecated + protected String getLogClassName() { + if (logClassName == null) { + discoverLogImplementation(getClass().getName()); + } + + return logClassName; + } + + /** + *

+ * Return the {@code Constructor} that can be called to instantiate new {@link org.apache.commons.logging.Log} instances. + *

+ * + *

+ * IMPLEMENTATION NOTE - Race conditions caused by calling this method from more than one thread are ignored, because the same + * {@code Constructor} instance will ultimately be derived in all circumstances. + *

+ * + * @return the {@code Constructor} that can be called to instantiate new {@link org.apache.commons.logging.Log} instances. + * + * @throws LogConfigurationException if a suitable constructor cannot be returned + * + * @deprecated Never invoked by this class; subclasses should not assume it will be. + */ + @Deprecated + protected Constructor getLogConstructor() + throws LogConfigurationException { + + // Return the previously identified Constructor (if any) + if (logConstructor == null) { + discoverLogImplementation(getClass().getName()); + } + + return logConstructor; + } + /** * Given two related classloaders, return the one which is a child of * the other. @@ -1236,6 +944,26 @@ public class LogFactoryImpl extends LogFactory { return null; } + // ------------------------------------------------------ Private Methods + + /** + * Fetch the parent classloader of a specified classloader. + *

+ * If a SecurityException occurs, null is returned. + *

+ * Note that this method is non-static merely so logDiagnostic is available. + */ + private ClassLoader getParentClassLoader(final ClassLoader cl) { + try { + return (ClassLoader)AccessController.doPrivileged( + (PrivilegedAction) () -> cl.getParent()); + } catch (final SecurityException ex) { + logDiagnostic("[SECURITY] Unable to obtain parent classloader"); + return null; + } + + } + /** * Generates an internal diagnostic logging of the discovery failure and * then throws a {@code LogConfigurationException} that wraps @@ -1389,4 +1117,276 @@ public class LogFactoryImpl extends LogFactory { } } } + + /** + * Appends message if the given name is similar to the candidate. + * @param messageBuffer {@code StringBuffer} the message should be appended to, + * not null + * @param name the (trimmed) name to be test against the candidate, not null + * @param candidate the candidate name (not null) + */ + private void informUponSimilarName(final StringBuilder messageBuffer, final String name, + final String candidate) { + if (name.equals(candidate)) { + // Don't suggest a name that is exactly the same as the one the + // user tried... + return; + } + + // If the user provides a name that is in the right package, and gets + // the first 5 characters of the adapter class right (ignoring case), + // then suggest the candidate adapter class name. + if (name.regionMatches(true, 0, candidate, 0, PKG_LEN + 5)) { + messageBuffer.append(" Did you mean '"); + messageBuffer.append(candidate); + messageBuffer.append("'?"); + } + } + + /** + * Initialize a number of variables that control the behavior of this + * class and that can be tweaked by the user. This is done when the first + * logger is created, not in the constructor of this class, because we + * need to give the user a chance to call method setAttribute in order to + * configure this object. + */ + private void initConfiguration() { + allowFlawedContext = getBooleanConfiguration(ALLOW_FLAWED_CONTEXT_PROPERTY, true); + allowFlawedDiscovery = getBooleanConfiguration(ALLOW_FLAWED_DISCOVERY_PROPERTY, true); + allowFlawedHierarchy = getBooleanConfiguration(ALLOW_FLAWED_HIERARCHY_PROPERTY, true); + } + + /** + * Calculate and cache a string that uniquely identifies this instance, + * including which classloader the object was loaded from. + *

+ * This string will later be prefixed to each "internal logging" message + * emitted, so that users can clearly see any unexpected behavior. + *

+ * Note that this method does not detect whether internal logging is + * enabled or not, nor where to output stuff if it is; that is all + * handled by the parent LogFactory class. This method just computes + * its own unique prefix for log messages. + */ + private void initDiagnostics() { + // It would be nice to include an identifier of the context classloader + // that this LogFactoryImpl object is responsible for. However that + // isn't possible as that information isn't available. It is possible + // to figure this out by looking at the logging from LogFactory to + // see the context & impl ids from when this object was instantiated, + // in order to link the impl id output as this object's prefix back to + // the context it is intended to manage. + // Note that this prefix should be kept consistent with that + // in LogFactory. + final Class clazz = this.getClass(); + final ClassLoader classLoader = getClassLoader(clazz); + String classLoaderName; + try { + if (classLoader == null) { + classLoaderName = "BOOTLOADER"; + } else { + classLoaderName = objectId(classLoader); + } + } catch (final SecurityException e) { + classLoaderName = "UNKNOWN"; + } + diagnosticPrefix = "[LogFactoryImpl@" + System.identityHashCode(this) + " from " + classLoaderName + "] "; + } + + /** + * Tests whether JDK 1.3 with Lumberjack logging available. + * + * @return whether JDK 1.3 with Lumberjack logging available. + * @deprecated Never invoked by this class; subclasses should not assume it will be. + */ + @Deprecated + protected boolean isJdk13LumberjackAvailable() { + return isLogLibraryAvailable( + "Jdk13Lumberjack", + "org.apache.commons.logging.impl.Jdk13LumberjackLogger"); + } + + /** + * Tests {@code true} whether JDK 1.4 or later logging is available. Also checks that the {@code Throwable} class supports {@code getStackTrace()}, + * which is required by Jdk14Logger. + * + * @return Whether JDK 1.4 or later logging is available. + * + * @deprecated Never invoked by this class; subclasses should not assume it will be. + */ + @Deprecated + protected boolean isJdk14Available() { + return isLogLibraryAvailable("Jdk14", "org.apache.commons.logging.impl.Jdk14Logger"); + } + + /** + * Tests whether a Log4J implementation available. + * + * @return whether a Log4J implementation available. + * + * @deprecated Never invoked by this class; subclasses should not assume it will be. + */ + @Deprecated + protected boolean isLog4JAvailable() { + return isLogLibraryAvailable("Log4J", LOGGING_IMPL_LOG4J_LOGGER); + } + + /** + * Utility method to check whether a particular logging library is + * present and available for use. Note that this does not + * affect the future behavior of this class. + */ + private boolean isLogLibraryAvailable(final String name, final String classname) { + if (isDiagnosticsEnabled()) { + logDiagnostic("Checking for '" + name + "'."); + } + try { + final Log log = createLogFromClass( + classname, + this.getClass().getName(), // dummy category + false); + + if (log == null) { + if (isDiagnosticsEnabled()) { + logDiagnostic("Did not find '" + name + "'."); + } + return false; + } + if (isDiagnosticsEnabled()) { + logDiagnostic("Found '" + name + "'."); + } + return true; + } catch (final LogConfigurationException e) { + if (isDiagnosticsEnabled()) { + logDiagnostic("Logging system '" + name + "' is available but not useable."); + } + return false; + } + } + + /** + * Output a diagnostic message to a user-specified destination (if the + * user has enabled diagnostic logging). + * + * @param msg diagnostic message + * @since 1.1 + */ + protected void logDiagnostic(final String msg) { + if (isDiagnosticsEnabled()) { + logRawDiagnostic(diagnosticPrefix + msg); + } + } + + /** + * Create and return a new {@link org.apache.commons.logging.Log} instance for the specified name. + * + * @param name Name of the new logger + * @return a new {@link org.apache.commons.logging.Log} + * + * @throws LogConfigurationException if a new instance cannot be created + */ + protected Log newInstance(final String name) throws LogConfigurationException { + Log instance; + try { + if (logConstructor == null) { + instance = discoverLogImplementation(name); + } + else { + final Object[] params = { name }; + instance = (Log) logConstructor.newInstance(params); + } + + if (logMethod != null) { + final Object[] params = { this }; + logMethod.invoke(instance, params); + } + + return instance; + + } catch (final LogConfigurationException lce) { + + // this type of exception means there was a problem in discovery + // and we've already output diagnostics about the issue, etc.; + // just pass it on + throw lce; + + } catch (final InvocationTargetException e) { + // A problem occurred invoking the Constructor or Method + // previously discovered + final Throwable c = e.getTargetException(); + throw new LogConfigurationException(c == null ? e : c); + } catch (final Throwable t) { + handleThrowable(t); // may re-throw t + // A problem occurred invoking the Constructor or Method + // previously discovered + throw new LogConfigurationException(t); + } + } + + /** + * Release any internal references to previously created + * {@link org.apache.commons.logging.Log} + * instances returned by this factory. This is useful in environments + * like servlet containers, which implement application reloading by + * throwing away a ClassLoader. Dangling references to objects in that + * class loader would prevent garbage collection. + */ + @Override + public void release() { + + logDiagnostic("Releasing all known loggers"); + instances.clear(); + } + + /** + * Remove any configuration attribute associated with the specified name. + * If there is no such attribute, no action is taken. + * + * @param name Name of the attribute to remove + */ + @Override + public void removeAttribute(final String name) { + attributes.remove(name); + } + + /** + * Set the configuration attribute with the specified name. Calling + * this with a {@code null} value is equivalent to calling + * {@code removeAttribute(name)}. + *

+ * This method can be used to set logging configuration programmatically + * rather than via system properties. It can also be used in code running + * within a container (such as a webapp) to configure behavior on a + * per-component level instead of globally as system properties would do. + * To use this method instead of a system property, call + *

+     * LogFactory.getFactory().setAttribute(...)
+     * 
+ * This must be done before the first Log object is created; configuration + * changes after that point will be ignored. + *

+ * This method is also called automatically if LogFactory detects a + * commons-logging.properties file; every entry in that file is set + * automatically as an attribute here. + * + * @param name Name of the attribute to set + * @param value Value of the attribute to set, or {@code null} + * to remove any setting for this attribute + */ + @Override + public void setAttribute(final String name, final Object value) { + if (logConstructor != null) { + logDiagnostic("setAttribute: call too late; configuration already performed."); + } + + if (value == null) { + attributes.remove(name); + } else { + attributes.put(name, value); + } + + if (name.equals(TCCL_KEY)) { + useTCCL = value != null && Boolean.parseBoolean(value.toString()); + } + } } diff --git a/src/main/java/org/apache/commons/logging/impl/LogKitLogger.java b/src/main/java/org/apache/commons/logging/impl/LogKitLogger.java index 11add6a..a05e8f0 100644 --- a/src/main/java/org/apache/commons/logging/impl/LogKitLogger.java +++ b/src/main/java/org/apache/commons/logging/impl/LogKitLogger.java @@ -62,49 +62,6 @@ public class LogKitLogger implements Log, Serializable { // --------------------------------------------------------- Public Methods - /** - * Gets the underlying Logger we are using. - * - * @return the underlying Logger we are using. - */ - public Logger getLogger() { - Logger result = logger; - if (result == null) { - synchronized(this) { - result = logger; - if (result == null) { - logger = result = Hierarchy.getDefaultHierarchy().getLoggerFor(name); - } - } - } - return result; - } - - // ----------------------------------------------------- Log Implementation - - /** - * Logs a message with {@code org.apache.log.Priority.DEBUG}. - * - * @param message to log - * @see org.apache.commons.logging.Log#trace(Object) - */ - @Override - public void trace(final Object message) { - debug(message); - } - - /** - * Logs a message with {@code org.apache.log.Priority.DEBUG}. - * - * @param message to log - * @param t log this cause - * @see org.apache.commons.logging.Log#trace(Object, Throwable) - */ - @Override - public void trace(final Object message, final Throwable t) { - debug(message, t); - } - /** * Logs a message with {@code org.apache.log.Priority.DEBUG}. * @@ -118,6 +75,8 @@ public class LogKitLogger implements Log, Serializable { } } + // ----------------------------------------------------- Log Implementation + /** * Logs a message with {@code org.apache.log.Priority.DEBUG}. * @@ -132,60 +91,6 @@ public class LogKitLogger implements Log, Serializable { } } - /** - * Logs a message with {@code org.apache.log.Priority.INFO}. - * - * @param message to log - * @see org.apache.commons.logging.Log#info(Object) - */ - @Override - public void info(final Object message) { - if (message != null) { - getLogger().info(String.valueOf(message)); - } - } - - /** - * Logs a message with {@code org.apache.log.Priority.INFO}. - * - * @param message to log - * @param t log this cause - * @see org.apache.commons.logging.Log#info(Object, Throwable) - */ - @Override - public void info(final Object message, final Throwable t) { - if (message != null) { - getLogger().info(String.valueOf(message), t); - } - } - - /** - * Logs a message with {@code org.apache.log.Priority.WARN}. - * - * @param message to log - * @see org.apache.commons.logging.Log#warn(Object) - */ - @Override - public void warn(final Object message) { - if (message != null) { - getLogger().warn(String.valueOf(message)); - } - } - - /** - * Logs a message with {@code org.apache.log.Priority.WARN}. - * - * @param message to log - * @param t log this cause - * @see org.apache.commons.logging.Log#warn(Object, Throwable) - */ - @Override - public void warn(final Object message, final Throwable t) { - if (message != null) { - getLogger().warn(String.valueOf(message), t); - } - } - /** * Logs a message with {@code org.apache.log.Priority.ERROR}. * @@ -240,6 +145,51 @@ public class LogKitLogger implements Log, Serializable { } } + /** + * Gets the underlying Logger we are using. + * + * @return the underlying Logger we are using. + */ + public Logger getLogger() { + Logger result = logger; + if (result == null) { + synchronized(this) { + result = logger; + if (result == null) { + logger = result = Hierarchy.getDefaultHierarchy().getLoggerFor(name); + } + } + } + return result; + } + + /** + * Logs a message with {@code org.apache.log.Priority.INFO}. + * + * @param message to log + * @see org.apache.commons.logging.Log#info(Object) + */ + @Override + public void info(final Object message) { + if (message != null) { + getLogger().info(String.valueOf(message)); + } + } + + /** + * Logs a message with {@code org.apache.log.Priority.INFO}. + * + * @param message to log + * @param t log this cause + * @see org.apache.commons.logging.Log#info(Object, Throwable) + */ + @Override + public void info(final Object message, final Throwable t) { + if (message != null) { + getLogger().info(String.valueOf(message), t); + } + } + /** * Checks whether the {@code LogKit} logger will log messages of priority {@code DEBUG}. */ @@ -287,4 +237,54 @@ public class LogKitLogger implements Log, Serializable { public boolean isWarnEnabled() { return getLogger().isWarnEnabled(); } + + /** + * Logs a message with {@code org.apache.log.Priority.DEBUG}. + * + * @param message to log + * @see org.apache.commons.logging.Log#trace(Object) + */ + @Override + public void trace(final Object message) { + debug(message); + } + + /** + * Logs a message with {@code org.apache.log.Priority.DEBUG}. + * + * @param message to log + * @param t log this cause + * @see org.apache.commons.logging.Log#trace(Object, Throwable) + */ + @Override + public void trace(final Object message, final Throwable t) { + debug(message, t); + } + + /** + * Logs a message with {@code org.apache.log.Priority.WARN}. + * + * @param message to log + * @see org.apache.commons.logging.Log#warn(Object) + */ + @Override + public void warn(final Object message) { + if (message != null) { + getLogger().warn(String.valueOf(message)); + } + } + + /** + * Logs a message with {@code org.apache.log.Priority.WARN}. + * + * @param message to log + * @param t log this cause + * @see org.apache.commons.logging.Log#warn(Object, Throwable) + */ + @Override + public void warn(final Object message, final Throwable t) { + if (message != null) { + getLogger().warn(String.valueOf(message), t); + } + } } diff --git a/src/main/java/org/apache/commons/logging/impl/NoOpLog.java b/src/main/java/org/apache/commons/logging/impl/NoOpLog.java index cf6b190..c5aed46 100644 --- a/src/main/java/org/apache/commons/logging/impl/NoOpLog.java +++ b/src/main/java/org/apache/commons/logging/impl/NoOpLog.java @@ -40,16 +40,6 @@ public class NoOpLog implements Log, Serializable { public NoOpLog(final String name) { } - /** Do nothing */ - @Override - public void trace(final Object message) { - } - - /** Do nothing */ - @Override - public void trace(final Object message, final Throwable t) { - } - /** Do nothing */ @Override public void debug(final Object message) { @@ -60,26 +50,6 @@ public class NoOpLog implements Log, Serializable { public void debug(final Object message, final Throwable t) { } - /** Do nothing */ - @Override - public void info(final Object message) { - } - - /** Do nothing */ - @Override - public void info(final Object message, final Throwable t) { - } - - /** Do nothing */ - @Override - public void warn(final Object message) { - } - - /** Do nothing */ - @Override - public void warn(final Object message, final Throwable t) { - } - /** Do nothing */ @Override public void error(final Object message) { @@ -100,6 +70,16 @@ public class NoOpLog implements Log, Serializable { public void fatal(final Object message, final Throwable t) { } + /** Do nothing */ + @Override + public void info(final Object message) { + } + + /** Do nothing */ + @Override + public void info(final Object message, final Throwable t) { + } + /** * Debug is never enabled. * @@ -159,4 +139,24 @@ public class NoOpLog implements Log, Serializable { public final boolean isWarnEnabled() { return false; } + + /** Do nothing */ + @Override + public void trace(final Object message) { + } + + /** Do nothing */ + @Override + public void trace(final Object message, final Throwable t) { + } + + /** Do nothing */ + @Override + public void warn(final Object message) { + } + + /** Do nothing */ + @Override + public void warn(final Object message, final Throwable t) { + } } diff --git a/src/main/java/org/apache/commons/logging/impl/SimpleLog.java b/src/main/java/org/apache/commons/logging/impl/SimpleLog.java index c78dd81..ad8cc4f 100644 --- a/src/main/java/org/apache/commons/logging/impl/SimpleLog.java +++ b/src/main/java/org/apache/commons/logging/impl/SimpleLog.java @@ -133,26 +133,6 @@ public class SimpleLog implements Log, Serializable { // ------------------------------------------------------------ Initializer - private static String getStringProperty(final String name) { - String prop = null; - try { - prop = System.getProperty(name); - } catch (final SecurityException e) { - // Ignore - } - return prop == null ? simpleLogProps.getProperty(name) : prop; - } - - private static String getStringProperty(final String name, final String dephault) { - final String prop = getStringProperty(name); - return prop == null ? dephault : prop; - } - - private static boolean getBooleanProperty(final String name, final boolean dephault) { - final String prop = getStringProperty(name); - return prop == null ? dephault : "true".equalsIgnoreCase(prop); - } - // Initialize class attributes. // Load properties file, if found. // Override with system properties. @@ -190,423 +170,9 @@ public class SimpleLog implements Log, Serializable { } } - // ------------------------------------------------------------- Attributes - - /** The name of this simple log instance */ - protected volatile String logName; - /** The current log level */ - protected volatile int currentLogLevel; - /** The short name of this simple log instance */ - private volatile String shortLogName; - - // ------------------------------------------------------------ Constructor - - /** - * Construct a simple log with given name. - * - * @param name log name - */ - public SimpleLog(String name) { - logName = name; - - // Set initial log level - // Used to be: set default log level to ERROR - // IMHO it should be lower, but at least info ( costin ). - setLevel(SimpleLog.LOG_LEVEL_INFO); - - // Set log level from properties - String lvl = getStringProperty(systemPrefix + "log." + logName); - int i = String.valueOf(name).lastIndexOf("."); - while(null == lvl && i > -1) { - name = name.substring(0,i); - lvl = getStringProperty(systemPrefix + "log." + name); - i = String.valueOf(name).lastIndexOf("."); - } - - if (null == lvl) { - lvl = getStringProperty(systemPrefix + "defaultlog"); - } - - if ("all".equalsIgnoreCase(lvl)) { - setLevel(SimpleLog.LOG_LEVEL_ALL); - } else if ("trace".equalsIgnoreCase(lvl)) { - setLevel(SimpleLog.LOG_LEVEL_TRACE); - } else if ("debug".equalsIgnoreCase(lvl)) { - setLevel(SimpleLog.LOG_LEVEL_DEBUG); - } else if ("info".equalsIgnoreCase(lvl)) { - setLevel(SimpleLog.LOG_LEVEL_INFO); - } else if ("warn".equalsIgnoreCase(lvl)) { - setLevel(SimpleLog.LOG_LEVEL_WARN); - } else if ("error".equalsIgnoreCase(lvl)) { - setLevel(SimpleLog.LOG_LEVEL_ERROR); - } else if ("fatal".equalsIgnoreCase(lvl)) { - setLevel(SimpleLog.LOG_LEVEL_FATAL); - } else if ("off".equalsIgnoreCase(lvl)) { - setLevel(SimpleLog.LOG_LEVEL_OFF); - } - } - - // -------------------------------------------------------- Properties - - /** - * Set logging level. - * - * @param currentLogLevel new logging level - */ - public void setLevel(final int currentLogLevel) { - this.currentLogLevel = currentLogLevel; - } - - /** - * Get logging level. - * - * @return logging level. - */ - public int getLevel() { - return currentLogLevel; - } - - // -------------------------------------------------------- Logging Methods - - /** - * Do the actual logging. - *

- * This method assembles the message and then calls {@code write()} - * to cause it to be written. - * - * @param type One of the LOG_LEVEL_XXX constants defining the log level - * @param message The message itself (typically a String) - * @param t The exception whose stack trace should be logged - */ - protected void log(final int type, final Object message, final Throwable t) { - // Use a string buffer for better performance - final StringBuilder buf = new StringBuilder(); - - // Append date-time if so configured - if (showDateTime) { - final Date now = new Date(); - String dateText; - synchronized(dateFormatter) { - dateText = dateFormatter.format(now); - } - buf.append(dateText); - buf.append(" "); - } - - // Append a readable representation of the log level - switch(type) { - case SimpleLog.LOG_LEVEL_TRACE: buf.append("[TRACE] "); break; - case SimpleLog.LOG_LEVEL_DEBUG: buf.append("[DEBUG] "); break; - case SimpleLog.LOG_LEVEL_INFO: buf.append("[INFO] "); break; - case SimpleLog.LOG_LEVEL_WARN: buf.append("[WARN] "); break; - case SimpleLog.LOG_LEVEL_ERROR: buf.append("[ERROR] "); break; - case SimpleLog.LOG_LEVEL_FATAL: buf.append("[FATAL] "); break; - } - - // Append the name of the log instance if so configured - if (showShortName) { - if (shortLogName == null) { - // Cut all but the last component of the name for both styles - final String slName = logName.substring(logName.lastIndexOf(".") + 1); - shortLogName = slName.substring(slName.lastIndexOf("/") + 1); - } - buf.append(String.valueOf(shortLogName)).append(" - "); - } else if (showLogName) { - buf.append(String.valueOf(logName)).append(" - "); - } - - // Append the message - buf.append(String.valueOf(message)); - - // Append stack trace if not null - if (t != null) { - buf.append(" <"); - buf.append(t.toString()); - buf.append(">"); - - final StringWriter sw = new StringWriter(1024); - final PrintWriter pw = new PrintWriter(sw); - t.printStackTrace(pw); - pw.close(); - buf.append(sw.toString()); - } - - // Print to the appropriate destination - write(buf); - } - - /** - * Write the content of the message accumulated in the specified - * {@code StringBuffer} to the appropriate output destination. The - * default implementation writes to {@code System.err}. - * - * @param buffer A {@code StringBuffer} containing the accumulated - * text to be logged - */ - protected void write(final StringBuffer buffer) { - System.err.println(buffer.toString()); - } - - /** - * Write the content of the message accumulated in the specified - * {@code StringBuffer} to the appropriate output destination. The - * default implementation writes to {@code System.err}. - * - * @param buffer A {@code StringBuffer} containing the accumulated - * text to be logged - */ - private void write(final Object buffer) { - System.err.println(buffer.toString()); - } - - /** - * Tests whether the given log level currently enabled. - * - * @param logLevel is this level enabled? - * @return whether the given log level currently enabled. - */ - protected boolean isLevelEnabled(final int logLevel) { - // log level are numerically ordered so can use simple numeric - // comparison - return logLevel >= currentLogLevel; - } - - // -------------------------------------------------------- Log Implementation - - /** - * Logs a message with - * {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_DEBUG}. - * - * @param message to log - * @see org.apache.commons.logging.Log#debug(Object) - */ - @Override - public final void debug(final Object message) { - if (isLevelEnabled(SimpleLog.LOG_LEVEL_DEBUG)) { - log(SimpleLog.LOG_LEVEL_DEBUG, message, null); - } - } - - /** - * Logs a message with - * {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_DEBUG}. - * - * @param message to log - * @param t log this cause - * @see org.apache.commons.logging.Log#debug(Object, Throwable) - */ - @Override - public final void debug(final Object message, final Throwable t) { - if (isLevelEnabled(SimpleLog.LOG_LEVEL_DEBUG)) { - log(SimpleLog.LOG_LEVEL_DEBUG, message, t); - } - } - - /** - * Logs a message with {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_TRACE}. - * - * @param message to log - * @see org.apache.commons.logging.Log#trace(Object) - */ - @Override - public final void trace(final Object message) { - if (isLevelEnabled(SimpleLog.LOG_LEVEL_TRACE)) { - log(SimpleLog.LOG_LEVEL_TRACE, message, null); - } - } - - /** - * Logs a message with {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_TRACE}. - * - * @param message to log - * @param t log this cause - * @see org.apache.commons.logging.Log#trace(Object, Throwable) - */ - @Override - public final void trace(final Object message, final Throwable t) { - if (isLevelEnabled(SimpleLog.LOG_LEVEL_TRACE)) { - log(SimpleLog.LOG_LEVEL_TRACE, message, t); - } - } - - /** - * Logs a message with {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_INFO}. - * - * @param message to log - * @see org.apache.commons.logging.Log#info(Object) - */ - @Override - public final void info(final Object message) { - if (isLevelEnabled(SimpleLog.LOG_LEVEL_INFO)) { - log(SimpleLog.LOG_LEVEL_INFO,message,null); - } - } - - /** - * Logs a message with {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_INFO}. - * - * @param message to log - * @param t log this cause - * @see org.apache.commons.logging.Log#info(Object, Throwable) - */ - @Override - public final void info(final Object message, final Throwable t) { - if (isLevelEnabled(SimpleLog.LOG_LEVEL_INFO)) { - log(SimpleLog.LOG_LEVEL_INFO, message, t); - } - } - - /** - * Logs a message with {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_WARN}. - * - * @param message to log - * @see org.apache.commons.logging.Log#warn(Object) - */ - @Override - public final void warn(final Object message) { - if (isLevelEnabled(SimpleLog.LOG_LEVEL_WARN)) { - log(SimpleLog.LOG_LEVEL_WARN, message, null); - } - } - - /** - * Logs a message with {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_WARN}. - * - * @param message to log - * @param t log this cause - * @see org.apache.commons.logging.Log#warn(Object, Throwable) - */ - @Override - public final void warn(final Object message, final Throwable t) { - if (isLevelEnabled(SimpleLog.LOG_LEVEL_WARN)) { - log(SimpleLog.LOG_LEVEL_WARN, message, t); - } - } - - /** - * Logs a message with {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_ERROR}. - * - * @param message to log - * @see org.apache.commons.logging.Log#error(Object) - */ - @Override - public final void error(final Object message) { - if (isLevelEnabled(SimpleLog.LOG_LEVEL_ERROR)) { - log(SimpleLog.LOG_LEVEL_ERROR, message, null); - } - } - - /** - * Logs a message with {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_ERROR}. - * - * @param message to log - * @param t log this cause - * @see org.apache.commons.logging.Log#error(Object, Throwable) - */ - @Override - public final void error(final Object message, final Throwable t) { - if (isLevelEnabled(SimpleLog.LOG_LEVEL_ERROR)) { - log(SimpleLog.LOG_LEVEL_ERROR, message, t); - } - } - - /** - * Log a message with {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_FATAL}. - * - * @param message to log - * @see org.apache.commons.logging.Log#fatal(Object) - */ - @Override - public final void fatal(final Object message) { - if (isLevelEnabled(SimpleLog.LOG_LEVEL_FATAL)) { - log(SimpleLog.LOG_LEVEL_FATAL, message, null); - } - } - - /** - * Logs a message with {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_FATAL}. - * - * @param message to log - * @param t log this cause - * @see org.apache.commons.logging.Log#fatal(Object, Throwable) - */ - @Override - public final void fatal(final Object message, final Throwable t) { - if (isLevelEnabled(SimpleLog.LOG_LEVEL_FATAL)) { - log(SimpleLog.LOG_LEVEL_FATAL, message, t); - } - } - - /** - * Are debug messages currently enabled? - *

- * This allows expensive operations such as {@code String} - * concatenation to be avoided when the message will be ignored by the - * logger. - */ - @Override - public final boolean isDebugEnabled() { - return isLevelEnabled(SimpleLog.LOG_LEVEL_DEBUG); - } - - /** - * Are error messages currently enabled? - *

- * This allows expensive operations such as {@code String} - * concatenation to be avoided when the message will be ignored by the - * logger. - */ - @Override - public final boolean isErrorEnabled() { - return isLevelEnabled(SimpleLog.LOG_LEVEL_ERROR); - } - - /** - * Are fatal messages currently enabled? - *

- * This allows expensive operations such as {@code String} - * concatenation to be avoided when the message will be ignored by the - * logger. - */ - @Override - public final boolean isFatalEnabled() { - return isLevelEnabled(SimpleLog.LOG_LEVEL_FATAL); - } - - /** - * Are info messages currently enabled? - *

- * This allows expensive operations such as {@code String} - * concatenation to be avoided when the message will be ignored by the - * logger. - */ - @Override - public final boolean isInfoEnabled() { - return isLevelEnabled(SimpleLog.LOG_LEVEL_INFO); - } - - /** - * Are trace messages currently enabled? - *

- * This allows expensive operations such as {@code String} - * concatenation to be avoided when the message will be ignored by the - * logger. - */ - @Override - public final boolean isTraceEnabled() { - return isLevelEnabled(SimpleLog.LOG_LEVEL_TRACE); - } - - /** - * Are warn messages currently enabled? - *

- * This allows expensive operations such as {@code String} - * concatenation to be avoided when the message will be ignored by the - * logger. - */ - @Override - public final boolean isWarnEnabled() { - return isLevelEnabled(SimpleLog.LOG_LEVEL_WARN); + private static boolean getBooleanProperty(final String name, final boolean dephault) { + final String prop = getStringProperty(name); + return prop == null ? dephault : "true".equalsIgnoreCase(prop); } /** @@ -682,5 +248,439 @@ public class SimpleLog implements Log, Serializable { } }); } + + // ------------------------------------------------------------- Attributes + + private static String getStringProperty(final String name) { + String prop = null; + try { + prop = System.getProperty(name); + } catch (final SecurityException e) { + // Ignore + } + return prop == null ? simpleLogProps.getProperty(name) : prop; + } + private static String getStringProperty(final String name, final String dephault) { + final String prop = getStringProperty(name); + return prop == null ? dephault : prop; + } + /** The name of this simple log instance */ + protected volatile String logName; + + // ------------------------------------------------------------ Constructor + + /** The current log level */ + protected volatile int currentLogLevel; + + // -------------------------------------------------------- Properties + + /** The short name of this simple log instance */ + private volatile String shortLogName; + + /** + * Construct a simple log with given name. + * + * @param name log name + */ + public SimpleLog(String name) { + logName = name; + + // Set initial log level + // Used to be: set default log level to ERROR + // IMHO it should be lower, but at least info ( costin ). + setLevel(SimpleLog.LOG_LEVEL_INFO); + + // Set log level from properties + String lvl = getStringProperty(systemPrefix + "log." + logName); + int i = String.valueOf(name).lastIndexOf("."); + while(null == lvl && i > -1) { + name = name.substring(0,i); + lvl = getStringProperty(systemPrefix + "log." + name); + i = String.valueOf(name).lastIndexOf("."); + } + + if (null == lvl) { + lvl = getStringProperty(systemPrefix + "defaultlog"); + } + + if ("all".equalsIgnoreCase(lvl)) { + setLevel(SimpleLog.LOG_LEVEL_ALL); + } else if ("trace".equalsIgnoreCase(lvl)) { + setLevel(SimpleLog.LOG_LEVEL_TRACE); + } else if ("debug".equalsIgnoreCase(lvl)) { + setLevel(SimpleLog.LOG_LEVEL_DEBUG); + } else if ("info".equalsIgnoreCase(lvl)) { + setLevel(SimpleLog.LOG_LEVEL_INFO); + } else if ("warn".equalsIgnoreCase(lvl)) { + setLevel(SimpleLog.LOG_LEVEL_WARN); + } else if ("error".equalsIgnoreCase(lvl)) { + setLevel(SimpleLog.LOG_LEVEL_ERROR); + } else if ("fatal".equalsIgnoreCase(lvl)) { + setLevel(SimpleLog.LOG_LEVEL_FATAL); + } else if ("off".equalsIgnoreCase(lvl)) { + setLevel(SimpleLog.LOG_LEVEL_OFF); + } + } + + // -------------------------------------------------------- Logging Methods + + /** + * Logs a message with + * {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_DEBUG}. + * + * @param message to log + * @see org.apache.commons.logging.Log#debug(Object) + */ + @Override + public final void debug(final Object message) { + if (isLevelEnabled(SimpleLog.LOG_LEVEL_DEBUG)) { + log(SimpleLog.LOG_LEVEL_DEBUG, message, null); + } + } + + /** + * Logs a message with + * {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_DEBUG}. + * + * @param message to log + * @param t log this cause + * @see org.apache.commons.logging.Log#debug(Object, Throwable) + */ + @Override + public final void debug(final Object message, final Throwable t) { + if (isLevelEnabled(SimpleLog.LOG_LEVEL_DEBUG)) { + log(SimpleLog.LOG_LEVEL_DEBUG, message, t); + } + } + + /** + * Logs a message with {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_ERROR}. + * + * @param message to log + * @see org.apache.commons.logging.Log#error(Object) + */ + @Override + public final void error(final Object message) { + if (isLevelEnabled(SimpleLog.LOG_LEVEL_ERROR)) { + log(SimpleLog.LOG_LEVEL_ERROR, message, null); + } + } + + /** + * Logs a message with {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_ERROR}. + * + * @param message to log + * @param t log this cause + * @see org.apache.commons.logging.Log#error(Object, Throwable) + */ + @Override + public final void error(final Object message, final Throwable t) { + if (isLevelEnabled(SimpleLog.LOG_LEVEL_ERROR)) { + log(SimpleLog.LOG_LEVEL_ERROR, message, t); + } + } + + // -------------------------------------------------------- Log Implementation + + /** + * Log a message with {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_FATAL}. + * + * @param message to log + * @see org.apache.commons.logging.Log#fatal(Object) + */ + @Override + public final void fatal(final Object message) { + if (isLevelEnabled(SimpleLog.LOG_LEVEL_FATAL)) { + log(SimpleLog.LOG_LEVEL_FATAL, message, null); + } + } + + /** + * Logs a message with {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_FATAL}. + * + * @param message to log + * @param t log this cause + * @see org.apache.commons.logging.Log#fatal(Object, Throwable) + */ + @Override + public final void fatal(final Object message, final Throwable t) { + if (isLevelEnabled(SimpleLog.LOG_LEVEL_FATAL)) { + log(SimpleLog.LOG_LEVEL_FATAL, message, t); + } + } + + /** + * Get logging level. + * + * @return logging level. + */ + public int getLevel() { + return currentLogLevel; + } + + /** + * Logs a message with {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_INFO}. + * + * @param message to log + * @see org.apache.commons.logging.Log#info(Object) + */ + @Override + public final void info(final Object message) { + if (isLevelEnabled(SimpleLog.LOG_LEVEL_INFO)) { + log(SimpleLog.LOG_LEVEL_INFO,message,null); + } + } + + /** + * Logs a message with {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_INFO}. + * + * @param message to log + * @param t log this cause + * @see org.apache.commons.logging.Log#info(Object, Throwable) + */ + @Override + public final void info(final Object message, final Throwable t) { + if (isLevelEnabled(SimpleLog.LOG_LEVEL_INFO)) { + log(SimpleLog.LOG_LEVEL_INFO, message, t); + } + } + + /** + * Are debug messages currently enabled? + *

+ * This allows expensive operations such as {@code String} + * concatenation to be avoided when the message will be ignored by the + * logger. + */ + @Override + public final boolean isDebugEnabled() { + return isLevelEnabled(SimpleLog.LOG_LEVEL_DEBUG); + } + + /** + * Are error messages currently enabled? + *

+ * This allows expensive operations such as {@code String} + * concatenation to be avoided when the message will be ignored by the + * logger. + */ + @Override + public final boolean isErrorEnabled() { + return isLevelEnabled(SimpleLog.LOG_LEVEL_ERROR); + } + + /** + * Are fatal messages currently enabled? + *

+ * This allows expensive operations such as {@code String} + * concatenation to be avoided when the message will be ignored by the + * logger. + */ + @Override + public final boolean isFatalEnabled() { + return isLevelEnabled(SimpleLog.LOG_LEVEL_FATAL); + } + + /** + * Are info messages currently enabled? + *

+ * This allows expensive operations such as {@code String} + * concatenation to be avoided when the message will be ignored by the + * logger. + */ + @Override + public final boolean isInfoEnabled() { + return isLevelEnabled(SimpleLog.LOG_LEVEL_INFO); + } + + /** + * Tests whether the given log level currently enabled. + * + * @param logLevel is this level enabled? + * @return whether the given log level currently enabled. + */ + protected boolean isLevelEnabled(final int logLevel) { + // log level are numerically ordered so can use simple numeric + // comparison + return logLevel >= currentLogLevel; + } + + /** + * Are trace messages currently enabled? + *

+ * This allows expensive operations such as {@code String} + * concatenation to be avoided when the message will be ignored by the + * logger. + */ + @Override + public final boolean isTraceEnabled() { + return isLevelEnabled(SimpleLog.LOG_LEVEL_TRACE); + } + + /** + * Are warn messages currently enabled? + *

+ * This allows expensive operations such as {@code String} + * concatenation to be avoided when the message will be ignored by the + * logger. + */ + @Override + public final boolean isWarnEnabled() { + return isLevelEnabled(SimpleLog.LOG_LEVEL_WARN); + } + + /** + * Do the actual logging. + *

+ * This method assembles the message and then calls {@code write()} + * to cause it to be written. + * + * @param type One of the LOG_LEVEL_XXX constants defining the log level + * @param message The message itself (typically a String) + * @param t The exception whose stack trace should be logged + */ + protected void log(final int type, final Object message, final Throwable t) { + // Use a string buffer for better performance + final StringBuilder buf = new StringBuilder(); + + // Append date-time if so configured + if (showDateTime) { + final Date now = new Date(); + String dateText; + synchronized(dateFormatter) { + dateText = dateFormatter.format(now); + } + buf.append(dateText); + buf.append(" "); + } + + // Append a readable representation of the log level + switch(type) { + case SimpleLog.LOG_LEVEL_TRACE: buf.append("[TRACE] "); break; + case SimpleLog.LOG_LEVEL_DEBUG: buf.append("[DEBUG] "); break; + case SimpleLog.LOG_LEVEL_INFO: buf.append("[INFO] "); break; + case SimpleLog.LOG_LEVEL_WARN: buf.append("[WARN] "); break; + case SimpleLog.LOG_LEVEL_ERROR: buf.append("[ERROR] "); break; + case SimpleLog.LOG_LEVEL_FATAL: buf.append("[FATAL] "); break; + } + + // Append the name of the log instance if so configured + if (showShortName) { + if (shortLogName == null) { + // Cut all but the last component of the name for both styles + final String slName = logName.substring(logName.lastIndexOf(".") + 1); + shortLogName = slName.substring(slName.lastIndexOf("/") + 1); + } + buf.append(String.valueOf(shortLogName)).append(" - "); + } else if (showLogName) { + buf.append(String.valueOf(logName)).append(" - "); + } + + // Append the message + buf.append(String.valueOf(message)); + + // Append stack trace if not null + if (t != null) { + buf.append(" <"); + buf.append(t.toString()); + buf.append(">"); + + final StringWriter sw = new StringWriter(1024); + final PrintWriter pw = new PrintWriter(sw); + t.printStackTrace(pw); + pw.close(); + buf.append(sw.toString()); + } + + // Print to the appropriate destination + write(buf); + } + + /** + * Set logging level. + * + * @param currentLogLevel new logging level + */ + public void setLevel(final int currentLogLevel) { + this.currentLogLevel = currentLogLevel; + } + + /** + * Logs a message with {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_TRACE}. + * + * @param message to log + * @see org.apache.commons.logging.Log#trace(Object) + */ + @Override + public final void trace(final Object message) { + if (isLevelEnabled(SimpleLog.LOG_LEVEL_TRACE)) { + log(SimpleLog.LOG_LEVEL_TRACE, message, null); + } + } + + /** + * Logs a message with {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_TRACE}. + * + * @param message to log + * @param t log this cause + * @see org.apache.commons.logging.Log#trace(Object, Throwable) + */ + @Override + public final void trace(final Object message, final Throwable t) { + if (isLevelEnabled(SimpleLog.LOG_LEVEL_TRACE)) { + log(SimpleLog.LOG_LEVEL_TRACE, message, t); + } + } + + /** + * Logs a message with {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_WARN}. + * + * @param message to log + * @see org.apache.commons.logging.Log#warn(Object) + */ + @Override + public final void warn(final Object message) { + if (isLevelEnabled(SimpleLog.LOG_LEVEL_WARN)) { + log(SimpleLog.LOG_LEVEL_WARN, message, null); + } + } + + /** + * Logs a message with {@code org.apache.commons.logging.impl.SimpleLog.LOG_LEVEL_WARN}. + * + * @param message to log + * @param t log this cause + * @see org.apache.commons.logging.Log#warn(Object, Throwable) + */ + @Override + public final void warn(final Object message, final Throwable t) { + if (isLevelEnabled(SimpleLog.LOG_LEVEL_WARN)) { + log(SimpleLog.LOG_LEVEL_WARN, message, t); + } + } + + /** + * Write the content of the message accumulated in the specified + * {@code StringBuffer} to the appropriate output destination. The + * default implementation writes to {@code System.err}. + * + * @param buffer A {@code StringBuffer} containing the accumulated + * text to be logged + */ + private void write(final Object buffer) { + System.err.println(buffer.toString()); + } + + /** + * Write the content of the message accumulated in the specified + * {@code StringBuffer} to the appropriate output destination. The + * default implementation writes to {@code System.err}. + * + * @param buffer A {@code StringBuffer} containing the accumulated + * text to be logged + */ + protected void write(final StringBuffer buffer) { + System.err.println(buffer.toString()); + } } diff --git a/src/main/java/org/apache/commons/logging/impl/WeakHashtable.java b/src/main/java/org/apache/commons/logging/impl/WeakHashtable.java index 751b8f5..e332954 100644 --- a/src/main/java/org/apache/commons/logging/impl/WeakHashtable.java +++ b/src/main/java/org/apache/commons/logging/impl/WeakHashtable.java @@ -109,6 +109,144 @@ import java.util.Set; */ public final class WeakHashtable extends Hashtable { + /** Entry implementation */ + private final static class Entry implements Map.Entry { + + private final Object key; + private final Object value; + + private Entry(final Object key, final Object value) { + this.key = key; + this.value = value; + } + + @Override + public boolean equals(final Object o) { + boolean result = false; + if (o instanceof Map.Entry) { + final Map.Entry entry = (Map.Entry) o; + result = (getKey()==null ? + entry.getKey() == null : + getKey().equals(entry.getKey())) && + (getValue()==null ? + entry.getValue() == null : + getValue().equals(entry.getValue())); + } + return result; + } + + @Override + public Object getKey() { + return key; + } + + @Override + public Object getValue() { + return value; + } + + @Override + public int hashCode() { + return (getKey()==null ? 0 : getKey().hashCode()) ^ + (getValue()==null ? 0 : getValue().hashCode()); + } + + @Override + public Object setValue(final Object value) { + throw new UnsupportedOperationException("Entry.setValue is not supported."); + } + } + + /** Wrapper giving correct symantics for equals and hash code */ + private final static class Referenced { + + private final WeakReference reference; + private final int hashCode; + + /** + * + * @throws NullPointerException if referant is {@code null} + */ + private Referenced(final Object referant) { + reference = new WeakReference(referant); + // Calc a permanent hashCode so calls to Hashtable.remove() + // work if the WeakReference has been cleared + hashCode = referant.hashCode(); + } + + /** + * + * @throws NullPointerException if key is {@code null} + */ + private Referenced(final Object key, final ReferenceQueue queue) { + reference = new WeakKey(key, queue, this); + // Calc a permanent hashCode so calls to Hashtable.remove() + // work if the WeakReference has been cleared + hashCode = key.hashCode(); + + } + + @Override + public boolean equals(final Object o) { + boolean result = false; + if (o instanceof Referenced) { + final Referenced otherKey = (Referenced) o; + final Object thisKeyValue = getValue(); + final Object otherKeyValue = otherKey.getValue(); + if (thisKeyValue == null) { + result = otherKeyValue == null; + + // Since our hash code was calculated from the original + // non-null referant, the above check breaks the + // hash code/equals contract, as two cleared Referenced + // objects could test equal but have different hash codes. + // We can reduce (not eliminate) the chance of this + // happening by comparing hash codes. + result = result && this.hashCode() == otherKey.hashCode(); + // In any case, as our c'tor does not allow null referants + // and Hashtable does not do equality checks between + // existing keys, normal hashtable operations should never + // result in an equals comparison between null referants + } + else + { + result = thisKeyValue.equals(otherKeyValue); + } + } + return result; + } + + private Object getValue() { + return reference.get(); + } + + @Override + public int hashCode() { + return hashCode; + } + } + + /** + * WeakReference subclass that holds a hard reference to an + * associated {@code value} and also makes accessible + * the Referenced object holding it. + */ + private final static class WeakKey extends WeakReference { + + private final Referenced referenced; + + private WeakKey(final Object key, + final ReferenceQueue queue, + final Referenced referenced) { + super(key, queue); + this.referenced = referenced; + } + + private Referenced getReferenced() { + return referenced; + } + } + /** Serializable version identifier. */ private static final long serialVersionUID = -1546036869799732453L; @@ -186,6 +324,15 @@ public final class WeakHashtable extends Hashtable { return super.get(referenceKey); } + /** + *@see Hashtable + */ + @Override + public boolean isEmpty() { + purge(); + return super.isEmpty(); + } + /** *@see Hashtable */ @@ -224,6 +371,41 @@ public final class WeakHashtable extends Hashtable { return unreferencedKeys; } + /** + * Purges all entries whose wrapped keys + * have been garbage collected. + */ + private void purge() { + final List toRemove = new ArrayList(); + synchronized (queue) { + WeakKey key; + while ((key = (WeakKey) queue.poll()) != null) { + toRemove.add(key.getReferenced()); + } + } + + // LOGGING-119: do the actual removal of the keys outside the sync block + // to prevent deadlock scenarios as purge() may be called from + // non-synchronized methods too + final int size = toRemove.size(); + for (int i = 0; i < size; i++) { + super.remove(toRemove.get(i)); + } + } + + /** + * Purges one entry whose wrapped key + * has been garbage collected. + */ + private void purgeOne() { + synchronized (queue) { + final WeakKey key = (WeakKey) queue.poll(); + if (key != null) { + super.remove(key.getReferenced()); + } + } + } + /** *@see Hashtable */ @@ -263,12 +445,13 @@ public final class WeakHashtable extends Hashtable { } /** - *@see Hashtable + * @see Hashtable */ @Override - public Collection values() { + protected void rehash() { + // purge here to save the effort of rehashing dead entries purge(); - return super.values(); + super.rehash(); } /** @@ -289,15 +472,6 @@ public final class WeakHashtable extends Hashtable { return super.remove(new Referenced(key)); } - /** - *@see Hashtable - */ - @Override - public boolean isEmpty() { - purge(); - return super.isEmpty(); - } - /** *@see Hashtable */ @@ -317,185 +491,11 @@ public final class WeakHashtable extends Hashtable { } /** - * @see Hashtable + *@see Hashtable */ @Override - protected void rehash() { - // purge here to save the effort of rehashing dead entries + public Collection values() { purge(); - super.rehash(); + return super.values(); } - - /** - * Purges all entries whose wrapped keys - * have been garbage collected. - */ - private void purge() { - final List toRemove = new ArrayList(); - synchronized (queue) { - WeakKey key; - while ((key = (WeakKey) queue.poll()) != null) { - toRemove.add(key.getReferenced()); - } - } - - // LOGGING-119: do the actual removal of the keys outside the sync block - // to prevent deadlock scenarios as purge() may be called from - // non-synchronized methods too - final int size = toRemove.size(); - for (int i = 0; i < size; i++) { - super.remove(toRemove.get(i)); - } - } - - /** - * Purges one entry whose wrapped key - * has been garbage collected. - */ - private void purgeOne() { - synchronized (queue) { - final WeakKey key = (WeakKey) queue.poll(); - if (key != null) { - super.remove(key.getReferenced()); - } - } - } - - /** Entry implementation */ - private final static class Entry implements Map.Entry { - - private final Object key; - private final Object value; - - private Entry(final Object key, final Object value) { - this.key = key; - this.value = value; - } - - @Override - public boolean equals(final Object o) { - boolean result = false; - if (o instanceof Map.Entry) { - final Map.Entry entry = (Map.Entry) o; - result = (getKey()==null ? - entry.getKey() == null : - getKey().equals(entry.getKey())) && - (getValue()==null ? - entry.getValue() == null : - getValue().equals(entry.getValue())); - } - return result; - } - - @Override - public int hashCode() { - return (getKey()==null ? 0 : getKey().hashCode()) ^ - (getValue()==null ? 0 : getValue().hashCode()); - } - - @Override - public Object setValue(final Object value) { - throw new UnsupportedOperationException("Entry.setValue is not supported."); - } - - @Override - public Object getValue() { - return value; - } - - @Override - public Object getKey() { - return key; - } - } - - /** Wrapper giving correct symantics for equals and hash code */ - private final static class Referenced { - - private final WeakReference reference; - private final int hashCode; - - /** - * - * @throws NullPointerException if referant is {@code null} - */ - private Referenced(final Object referant) { - reference = new WeakReference(referant); - // Calc a permanent hashCode so calls to Hashtable.remove() - // work if the WeakReference has been cleared - hashCode = referant.hashCode(); - } - - /** - * - * @throws NullPointerException if key is {@code null} - */ - private Referenced(final Object key, final ReferenceQueue queue) { - reference = new WeakKey(key, queue, this); - // Calc a permanent hashCode so calls to Hashtable.remove() - // work if the WeakReference has been cleared - hashCode = key.hashCode(); - - } - - @Override - public int hashCode() { - return hashCode; - } - - private Object getValue() { - return reference.get(); - } - - @Override - public boolean equals(final Object o) { - boolean result = false; - if (o instanceof Referenced) { - final Referenced otherKey = (Referenced) o; - final Object thisKeyValue = getValue(); - final Object otherKeyValue = otherKey.getValue(); - if (thisKeyValue == null) { - result = otherKeyValue == null; - - // Since our hash code was calculated from the original - // non-null referant, the above check breaks the - // hash code/equals contract, as two cleared Referenced - // objects could test equal but have different hash codes. - // We can reduce (not eliminate) the chance of this - // happening by comparing hash codes. - result = result && this.hashCode() == otherKey.hashCode(); - // In any case, as our c'tor does not allow null referants - // and Hashtable does not do equality checks between - // existing keys, normal hashtable operations should never - // result in an equals comparison between null referants - } - else - { - result = thisKeyValue.equals(otherKeyValue); - } - } - return result; - } - } - - /** - * WeakReference subclass that holds a hard reference to an - * associated {@code value} and also makes accessible - * the Referenced object holding it. - */ - private final static class WeakKey extends WeakReference { - - private final Referenced referenced; - - private WeakKey(final Object key, - final ReferenceQueue queue, - final Referenced referenced) { - super(key, queue); - this.referenced = referenced; - } - - private Referenced getReferenced() { - return referenced; - } - } } diff --git a/src/test/java/org/apache/commons/logging/AltHashtableTestCase.java b/src/test/java/org/apache/commons/logging/AltHashtableTestCase.java index 890de7f..28b515f 100644 --- a/src/test/java/org/apache/commons/logging/AltHashtableTestCase.java +++ b/src/test/java/org/apache/commons/logging/AltHashtableTestCase.java @@ -60,21 +60,6 @@ public class AltHashtableTestCase extends TestCase { AltHashtable.class.getName()); } - /** - * Verify that initialising the LogFactory class will cause it - * to instantiate an object of type specified in system property - * "org.apache.commons.logging.LogFactory.HashtableImpl". - */ - public void testType() { - // Here, the reference to the LogFactory class should cause the - // class to be loaded and initialized. It will see the property - // set and use the AltHashtable class. If other tests in this - // class have already been run within the same classloader then - // LogFactory will already have been initialized, but that - // doesn't change the effectiveness of this test. - assertTrue(LogFactory.factories instanceof AltHashtable); - } - /** * Verify that when LogFactory sees a context-classloader for the * first time that it creates a new entry in the LogFactory.factories @@ -91,4 +76,19 @@ public class AltHashtableTestCase extends TestCase { assertEquals(contextLoader, AltHashtable.lastKey); assertNotNull(AltHashtable.lastValue); } + + /** + * Verify that initialising the LogFactory class will cause it + * to instantiate an object of type specified in system property + * "org.apache.commons.logging.LogFactory.HashtableImpl". + */ + public void testType() { + // Here, the reference to the LogFactory class should cause the + // class to be loaded and initialized. It will see the property + // set and use the AltHashtable class. If other tests in this + // class have already been run within the same classloader then + // LogFactory will already have been initialized, but that + // doesn't change the effectiveness of this test. + assertTrue(LogFactory.factories instanceof AltHashtable); + } } diff --git a/src/test/java/org/apache/commons/logging/BadHashtablePropertyTestCase.java b/src/test/java/org/apache/commons/logging/BadHashtablePropertyTestCase.java index 967e426..d4b5e40 100644 --- a/src/test/java/org/apache/commons/logging/BadHashtablePropertyTestCase.java +++ b/src/test/java/org/apache/commons/logging/BadHashtablePropertyTestCase.java @@ -25,11 +25,11 @@ import java.util.Hashtable; */ public class BadHashtablePropertyTestCase extends TestCase { - public void testType() { - assertTrue(LogFactory.factories instanceof Hashtable); - } - public void testPutCalled() throws Exception { LogFactory.getLog(BadHashtablePropertyTestCase.class); } + + public void testType() { + assertTrue(LogFactory.factories instanceof Hashtable); + } } diff --git a/src/test/java/org/apache/commons/logging/BasicOperationsTestCase.java b/src/test/java/org/apache/commons/logging/BasicOperationsTestCase.java index 95d7973..838073d 100644 --- a/src/test/java/org/apache/commons/logging/BasicOperationsTestCase.java +++ b/src/test/java/org/apache/commons/logging/BasicOperationsTestCase.java @@ -27,18 +27,6 @@ import junit.framework.TestCase; */ public class BasicOperationsTestCase extends TestCase { - public void testIsEnabledClassLog() - { - final Log log = LogFactory.getLog(BasicOperationsTestCase.class); - executeIsEnabledTest(log); - } - - public void testIsEnabledNamedLog() - { - final Log log = LogFactory.getLog(BasicOperationsTestCase.class.getName()); - executeIsEnabledTest(log); - } - public void executeIsEnabledTest(final Log log) { try @@ -57,16 +45,22 @@ public class BasicOperationsTestCase extends TestCase } } - public void testMessageWithoutExceptionClassLog() + public void executeMessageWithExceptionTest(final Log log) { - final Log log = LogFactory.getLog(BasicOperationsTestCase.class); - executeMessageWithoutExceptionTest(log); - } - - public void testMessageWithoutExceptionNamedLog() - { - final Log log = LogFactory.getLog(BasicOperationsTestCase.class.getName()); - executeMessageWithoutExceptionTest(log); + try + { + log.trace("Hello, Mum", new ArithmeticException()); + log.debug("Hello, Mum", new ArithmeticException()); + log.info("Hello, Mum", new ArithmeticException()); + log.warn("Hello, Mum", new ArithmeticException()); + log.error("Hello, Mum", new ArithmeticException()); + log.fatal("Hello, Mum", new ArithmeticException()); + } + catch (final Throwable t) + { + t.printStackTrace(); + fail("Exception thrown: " + t); + } } public void executeMessageWithoutExceptionTest(final Log log) @@ -87,6 +81,18 @@ public class BasicOperationsTestCase extends TestCase } } + public void testIsEnabledClassLog() + { + final Log log = LogFactory.getLog(BasicOperationsTestCase.class); + executeIsEnabledTest(log); + } + + public void testIsEnabledNamedLog() + { + final Log log = LogFactory.getLog(BasicOperationsTestCase.class.getName()); + executeIsEnabledTest(log); + } + public void testMessageWithExceptionClassLog() { final Log log = LogFactory.getLog(BasicOperationsTestCase.class); @@ -99,21 +105,15 @@ public class BasicOperationsTestCase extends TestCase executeMessageWithExceptionTest(log); } - public void executeMessageWithExceptionTest(final Log log) + public void testMessageWithoutExceptionClassLog() { - try - { - log.trace("Hello, Mum", new ArithmeticException()); - log.debug("Hello, Mum", new ArithmeticException()); - log.info("Hello, Mum", new ArithmeticException()); - log.warn("Hello, Mum", new ArithmeticException()); - log.error("Hello, Mum", new ArithmeticException()); - log.fatal("Hello, Mum", new ArithmeticException()); - } - catch (final Throwable t) - { - t.printStackTrace(); - fail("Exception thrown: " + t); - } + final Log log = LogFactory.getLog(BasicOperationsTestCase.class); + executeMessageWithoutExceptionTest(log); + } + + public void testMessageWithoutExceptionNamedLog() + { + final Log log = LogFactory.getLog(BasicOperationsTestCase.class.getName()); + executeMessageWithoutExceptionTest(log); } } diff --git a/src/test/java/org/apache/commons/logging/GarbageCollectionHelper.java b/src/test/java/org/apache/commons/logging/GarbageCollectionHelper.java index 75d449d..0748b06 100644 --- a/src/test/java/org/apache/commons/logging/GarbageCollectionHelper.java +++ b/src/test/java/org/apache/commons/logging/GarbageCollectionHelper.java @@ -27,23 +27,6 @@ import java.util.concurrent.atomic.AtomicBoolean; // after: https://github.com/apache/logging-log4j2/blob/c47e98423b461731f7791fcb9ea1079cd451f365/log4j-core/src/test/java/org/apache/logging/log4j/core/GarbageCollectionHelper.java public final class GarbageCollectionHelper implements Closeable, Runnable { - private static final OutputStream SINK = new OutputStream() { - @Override - public void write(int b) { - } - - @Override - public void write(byte[] b) { - } - - @Override - public void write(byte[] b, int off, int len) { - } - }; - private final AtomicBoolean running = new AtomicBoolean(); - private final CountDownLatch latch = new CountDownLatch(1); - private final Thread gcThread = new Thread(new GcTask()); - class GcTask implements Runnable { @Override public void run() { @@ -64,13 +47,23 @@ public final class GarbageCollectionHelper implements Closeable, Runnable { } } } - - @Override - public void run() { - if (running.compareAndSet(false, true)) { - gcThread.start(); + private static final OutputStream SINK = new OutputStream() { + @Override + public void write(byte[] b) { } - } + + @Override + public void write(byte[] b, int off, int len) { + } + + @Override + public void write(int b) { + } + }; + private final AtomicBoolean running = new AtomicBoolean(); + private final CountDownLatch latch = new CountDownLatch(1); + + private final Thread gcThread = new Thread(new GcTask()); @Override public void close() { @@ -82,5 +75,12 @@ public final class GarbageCollectionHelper implements Closeable, Runnable { throw new RuntimeException(e); } } + + @Override + public void run() { + if (running.compareAndSet(false, true)) { + gcThread.start(); + } + } } diff --git a/src/test/java/org/apache/commons/logging/LoadTestCase.java b/src/test/java/org/apache/commons/logging/LoadTestCase.java index 874a7cc..2257324 100644 --- a/src/test/java/org/apache/commons/logging/LoadTestCase.java +++ b/src/test/java/org/apache/commons/logging/LoadTestCase.java @@ -22,10 +22,6 @@ import junit.framework.TestCase; * test to emulate container and application isolated from container */ public class LoadTestCase extends TestCase{ - //TODO: need some way to add service provider packages - static private String[] LOG_PCKG = {"org.apache.commons.logging", - "org.apache.commons.logging.impl"}; - /** * A custom classloader which "duplicates" logging classes available * in the parent classloader into itself. @@ -92,6 +88,41 @@ public class LoadTestCase extends TestCase{ } + //TODO: need some way to add service provider packages + static private String[] LOG_PCKG = {"org.apache.commons.logging", + "org.apache.commons.logging.impl"}; + + + private ClassLoader origContextClassLoader; + + private void execute(final Class cls) throws Exception { + cls.getConstructor().newInstance(); + } + + /** + * Load class UserClass via a temporary classloader which is a child of + * the classloader used to load this test class. + */ + private Class reload() throws Exception { + Class testObjCls = null; + final AppClassLoader appLoader = new AppClassLoader(this.getClass().getClassLoader()); + try { + + testObjCls = appLoader.loadClass(UserClass.class.getName()); + + } catch (final ClassNotFoundException cnfe) { + throw cnfe; + } catch (final Throwable t) { + t.printStackTrace(); + fail("AppClassLoader failed "); + } + + assertSame("app isolated", testObjCls.getClassLoader(), appLoader); + + return testObjCls; + + } + /** * Call the static setAllowFlawedContext method on the specified class @@ -104,6 +135,18 @@ public class LoadTestCase extends TestCase{ m.invoke(null, state); } + @Override + public void setUp() { + // save state before test starts so we can restore it when test ends + origContextClassLoader = Thread.currentThread().getContextClassLoader(); + } + + @Override + public void tearDown() { + // restore original state so a test can't stuff up later tests. + Thread.currentThread().setContextClassLoader(origContextClassLoader); + } + /** * Test what happens when we play various classloader tricks like those * that happen in web and j2ee containers. @@ -170,47 +213,4 @@ public class LoadTestCase extends TestCase{ // expected } } - - /** - * Load class UserClass via a temporary classloader which is a child of - * the classloader used to load this test class. - */ - private Class reload() throws Exception { - Class testObjCls = null; - final AppClassLoader appLoader = new AppClassLoader(this.getClass().getClassLoader()); - try { - - testObjCls = appLoader.loadClass(UserClass.class.getName()); - - } catch (final ClassNotFoundException cnfe) { - throw cnfe; - } catch (final Throwable t) { - t.printStackTrace(); - fail("AppClassLoader failed "); - } - - assertSame("app isolated", testObjCls.getClassLoader(), appLoader); - - return testObjCls; - - } - - - private void execute(final Class cls) throws Exception { - cls.getConstructor().newInstance(); - } - - @Override - public void setUp() { - // save state before test starts so we can restore it when test ends - origContextClassLoader = Thread.currentThread().getContextClassLoader(); - } - - @Override - public void tearDown() { - // restore original state so a test can't stuff up later tests. - Thread.currentThread().setContextClassLoader(origContextClassLoader); - } - - private ClassLoader origContextClassLoader; } diff --git a/src/test/java/org/apache/commons/logging/PathableClassLoader.java b/src/test/java/org/apache/commons/logging/PathableClassLoader.java index 136d8e8..2b36d22 100644 --- a/src/test/java/org/apache/commons/logging/PathableClassLoader.java +++ b/src/test/java/org/apache/commons/logging/PathableClassLoader.java @@ -96,94 +96,6 @@ public class PathableClassLoader extends URLClassLoader { super(NO_URLS, parent); } - /** - * Allow caller to explicitly add paths. Generally this not a good idea; - * use addLogicalLib instead, then define the location for that logical - * library in the build.xml file. - */ - @Override - public void addURL(final URL url) { - super.addURL(url); - } - - /** - * Specify whether this classloader should ask the parent classloader - * to resolve a class first, before trying to resolve it via its own - * classpath. - *

- * Checking with the parent first is the normal approach for java, but - * components within containers such as servlet engines can use - * child-first lookup instead, to allow the components to override libs - * which are visible in shared classloaders provided by the container. - *

- * Note that the method getResources always behaves as if parentFirst=true, - * because of limitations in java 1.4; see the javadoc for method - * getResourcesInOrder for details. - *

- * This value defaults to true. - */ - public void setParentFirst(final boolean state) { - parentFirst = state; - } - - /** - * For classes with the specified prefix, get them from the system - * classpath which is active at the point this method is called. - *

- * This method is just a shortcut for - *

-     * useExplicitLoader(prefix, ClassLoader.getSystemClassLoader());
-     * 
- *

- * Of course, this assumes that the classes of interest are already - * in the classpath of the system classloader. - */ - public void useSystemLoader(final String prefix) { - useExplicitLoader(prefix, ClassLoader.getSystemClassLoader()); - - } - - /** - * Specify a classloader to use for specific java packages. - *

- * The specified classloader is normally a loader that is NOT - * an ancestor of this classloader. In particular, this loader - * may have the bootloader as its parent, but be configured to - * see specific other classes (eg the junit library loaded - * via the system classloader). - *

- * The differences between using this method, and using - * addLogicalLib are: - *

    - *
  • If code calls getClassLoader on a class loaded via - * "lookaside", then traces up its inheritance chain, it - * will see the "real" classloaders. When the class is remapped - * into this classloader via addLogicalLib, the classloader - * chain seen is this object plus ancestors. - *
  • If two different jars contain classes in the same - * package, then it is not possible to load both jars into - * the same "lookaside" classloader (eg the system classloader) - * then map one of those subsets from here. Of course they could - * be loaded into two different "lookaside" classloaders and - * then a prefix used to map from here to one of those classloaders. - *
- */ - public void useExplicitLoader(final String prefix, final ClassLoader loader) { - if (lookasides == null) { - lookasides = new HashMap(); - } - lookasides.put(prefix, loader); - } - - /** - * Specify a collection of logical libraries. See addLogicalLib. - */ - public void addLogicalLib(final String[] logicalLibs) { - for (final String logicalLib : logicalLibs) { - addLogicalLib(logicalLib); - } - } - /** * Specify a logical library to be included in the classpath used to * locate classes. @@ -231,6 +143,101 @@ public class PathableClassLoader extends URLClassLoader { + " as a System property."); } + /** + * Specify a collection of logical libraries. See addLogicalLib. + */ + public void addLogicalLib(final String[] logicalLibs) { + for (final String logicalLib : logicalLibs) { + addLogicalLib(logicalLib); + } + } + + /** + * Allow caller to explicitly add paths. Generally this not a good idea; + * use addLogicalLib instead, then define the location for that logical + * library in the build.xml file. + */ + @Override + public void addURL(final URL url) { + super.addURL(url); + } + + /** + * Same as parent class method except that when parentFirst is false + * the resource is looked for in the local classpath before the parent + * loader is consulted. + */ + @Override + public URL getResource(final String name) { + if (parentFirst) { + return super.getResource(name); + } + final URL local = super.findResource(name); + if (local != null) { + return local; + } + return super.getResource(name); + } + + /** + * Same as parent class method except that when parentFirst is false + * the resource is looked for in the local classpath before the parent + * loader is consulted. + */ + @Override + public InputStream getResourceAsStream(final String name) { + if (parentFirst) { + return super.getResourceAsStream(name); + } + final URL local = super.findResource(name); + if (local != null) { + try { + return local.openStream(); + } catch (final IOException e) { + // TODO: check if this is right or whether we should + // fall back to trying parent. The javadoc doesn't say... + return null; + } + } + return super.getResourceAsStream(name); + } + + /** + * Emulate a proper implementation of getResources which respects the + * setting for parentFirst. + *

+ * Note that it's not possible to override the inherited getResources, as + * it's declared final in java1.4 (thought that's been removed for 1.5). + * The inherited implementation always behaves as if parentFirst=true. + */ + public Enumeration getResourcesInOrder(final String name) throws IOException { + if (parentFirst) { + return super.getResources(name); + } + final Enumeration localUrls = super.findResources(name); + + final ClassLoader parent = getParent(); + if (parent == null) { + // Alas, there is no method to get matching resources + // from a null (BOOT) parent classloader. Calling + // ClassLoader.getSystemClassLoader isn't right. Maybe + // calling Class.class.getResources(name) would do? + // + // However for the purposes of unit tests, we can + // simply assume that no relevant resources are + // loadable from the parent; unit tests will never be + // putting any of their resources in a "boot" classloader + // path! + return localUrls; + } + final Enumeration parentUrls = parent.getResources(name); + + final ArrayList localItems = toList(localUrls); + final ArrayList parentItems = toList(parentUrls); + localItems.addAll(parentItems); + return Collections.enumeration(localItems); + } + /** * If the classloader that loaded this class has this logical lib in its * path, then return the matching URL otherwise return null. @@ -328,56 +335,23 @@ public class PathableClassLoader extends URLClassLoader { } /** - * Same as parent class method except that when parentFirst is false - * the resource is looked for in the local classpath before the parent - * loader is consulted. - */ - @Override - public URL getResource(final String name) { - if (parentFirst) { - return super.getResource(name); - } - final URL local = super.findResource(name); - if (local != null) { - return local; - } - return super.getResource(name); - } - - /** - * Emulate a proper implementation of getResources which respects the - * setting for parentFirst. + * Specify whether this classloader should ask the parent classloader + * to resolve a class first, before trying to resolve it via its own + * classpath. *

- * Note that it's not possible to override the inherited getResources, as - * it's declared final in java1.4 (thought that's been removed for 1.5). - * The inherited implementation always behaves as if parentFirst=true. + * Checking with the parent first is the normal approach for java, but + * components within containers such as servlet engines can use + * child-first lookup instead, to allow the components to override libs + * which are visible in shared classloaders provided by the container. + *

+ * Note that the method getResources always behaves as if parentFirst=true, + * because of limitations in java 1.4; see the javadoc for method + * getResourcesInOrder for details. + *

+ * This value defaults to true. */ - public Enumeration getResourcesInOrder(final String name) throws IOException { - if (parentFirst) { - return super.getResources(name); - } - final Enumeration localUrls = super.findResources(name); - - final ClassLoader parent = getParent(); - if (parent == null) { - // Alas, there is no method to get matching resources - // from a null (BOOT) parent classloader. Calling - // ClassLoader.getSystemClassLoader isn't right. Maybe - // calling Class.class.getResources(name) would do? - // - // However for the purposes of unit tests, we can - // simply assume that no relevant resources are - // loadable from the parent; unit tests will never be - // putting any of their resources in a "boot" classloader - // path! - return localUrls; - } - final Enumeration parentUrls = parent.getResources(name); - - final ArrayList localItems = toList(localUrls); - final ArrayList parentItems = toList(parentUrls); - localItems.addAll(parentItems); - return Collections.enumeration(localItems); + public void setParentFirst(final boolean state) { + parentFirst = state; } /** @@ -400,25 +374,51 @@ public class PathableClassLoader extends URLClassLoader { } /** - * Same as parent class method except that when parentFirst is false - * the resource is looked for in the local classpath before the parent - * loader is consulted. + * Specify a classloader to use for specific java packages. + *

+ * The specified classloader is normally a loader that is NOT + * an ancestor of this classloader. In particular, this loader + * may have the bootloader as its parent, but be configured to + * see specific other classes (eg the junit library loaded + * via the system classloader). + *

+ * The differences between using this method, and using + * addLogicalLib are: + *

    + *
  • If code calls getClassLoader on a class loaded via + * "lookaside", then traces up its inheritance chain, it + * will see the "real" classloaders. When the class is remapped + * into this classloader via addLogicalLib, the classloader + * chain seen is this object plus ancestors. + *
  • If two different jars contain classes in the same + * package, then it is not possible to load both jars into + * the same "lookaside" classloader (eg the system classloader) + * then map one of those subsets from here. Of course they could + * be loaded into two different "lookaside" classloaders and + * then a prefix used to map from here to one of those classloaders. + *
*/ - @Override - public InputStream getResourceAsStream(final String name) { - if (parentFirst) { - return super.getResourceAsStream(name); + public void useExplicitLoader(final String prefix, final ClassLoader loader) { + if (lookasides == null) { + lookasides = new HashMap(); } - final URL local = super.findResource(name); - if (local != null) { - try { - return local.openStream(); - } catch (final IOException e) { - // TODO: check if this is right or whether we should - // fall back to trying parent. The javadoc doesn't say... - return null; - } - } - return super.getResourceAsStream(name); + lookasides.put(prefix, loader); + } + + /** + * For classes with the specified prefix, get them from the system + * classpath which is active at the point this method is called. + *

+ * This method is just a shortcut for + *

+     * useExplicitLoader(prefix, ClassLoader.getSystemClassLoader());
+     * 
+ *

+ * Of course, this assumes that the classes of interest are already + * in the classpath of the system classloader. + */ + public void useSystemLoader(final String prefix) { + useExplicitLoader(prefix, ClassLoader.getSystemClassLoader()); + } } diff --git a/src/test/java/org/apache/commons/logging/impl/WeakHashtableTestCase.java b/src/test/java/org/apache/commons/logging/impl/WeakHashtableTestCase.java index 03fb52b..88b18ec 100644 --- a/src/test/java/org/apache/commons/logging/impl/WeakHashtableTestCase.java +++ b/src/test/java/org/apache/commons/logging/impl/WeakHashtableTestCase.java @@ -31,22 +31,39 @@ import junit.framework.TestCase; public class WeakHashtableTestCase extends TestCase { + public static class StupidThread extends Thread { + + public StupidThread(final String name) { + super(name); + } + + @Override + public void run() { + for (int i = 0; i < RUN_LOOPS; i++) { + hashtable.put("key" + ":" + i % 10, Boolean.TRUE); + if (i % 50 == 0) { + yield(); + } + } + } + } private static final int WAIT_FOR_THREAD_COMPLETION = 5000; // 5 seconds private static final int RUN_LOOPS = 3000; private static final int OUTER_LOOP = 400; + private static final int THREAD_COUNT = 10; private static WeakHashtable hashtable; /** Maximum number of iterations before our test fails */ private static final int MAX_GC_ITERATIONS = 50; - private WeakHashtable weakHashtable; private Long keyOne; private Long keyTwo; private Long keyThree; private Long valueOne; private Long valueTwo; + private Long valueThree; public WeakHashtableTestCase(final String testName) { @@ -162,6 +179,33 @@ public class WeakHashtableTestCase extends TestCase { assertTrue(keySet.contains(keyThree)); } + public void testLOGGING_119() throws Exception { + final Thread [] t = new Thread[THREAD_COUNT]; + for (int j=1; j <= OUTER_LOOP; j++) { + hashtable = new WeakHashtable(); + for (int i = 0; i < t.length; i++) { + t[i] = new StupidThread("Thread:" + i); + t[i].setDaemon(true); // Otherwise we cannot exit + t[i].start(); + } + for (final Thread element : t) { + element.join(WAIT_FOR_THREAD_COMPLETION); + if (element.isAlive()) { + break; // at least one thread is stuck + } + } + int active=0; + for (final Thread element : t) { + if (element.isAlive()) { + active++; + } + } + if (active > 0) { + fail("Attempt: " + j + " Stuck threads: " + active); + } + } + } + /** Tests public Object put(Object key, Object value) */ public void testPut() throws Exception { final Long anotherKey = new Long(2004); @@ -266,48 +310,4 @@ public class WeakHashtableTestCase extends TestCase { // Test that the released objects are not taking space in the table assertEquals("underlying table not emptied", 0, weakHashtable.size()); } - - public static class StupidThread extends Thread { - - public StupidThread(final String name) { - super(name); - } - - @Override - public void run() { - for (int i = 0; i < RUN_LOOPS; i++) { - hashtable.put("key" + ":" + i % 10, Boolean.TRUE); - if (i % 50 == 0) { - yield(); - } - } - } - } - - public void testLOGGING_119() throws Exception { - final Thread [] t = new Thread[THREAD_COUNT]; - for (int j=1; j <= OUTER_LOOP; j++) { - hashtable = new WeakHashtable(); - for (int i = 0; i < t.length; i++) { - t[i] = new StupidThread("Thread:" + i); - t[i].setDaemon(true); // Otherwise we cannot exit - t[i].start(); - } - for (final Thread element : t) { - element.join(WAIT_FOR_THREAD_COMPLETION); - if (element.isAlive()) { - break; // at least one thread is stuck - } - } - int active=0; - for (final Thread element : t) { - if (element.isAlive()) { - active++; - } - } - if (active > 0) { - fail("Attempt: " + j + " Stuck threads: " + active); - } - } - } } diff --git a/src/test/java/org/apache/commons/logging/jdk14/CustomConfigAPITestCase.java b/src/test/java/org/apache/commons/logging/jdk14/CustomConfigAPITestCase.java index 5fe968a..15793f6 100644 --- a/src/test/java/org/apache/commons/logging/jdk14/CustomConfigAPITestCase.java +++ b/src/test/java/org/apache/commons/logging/jdk14/CustomConfigAPITestCase.java @@ -30,10 +30,6 @@ import org.apache.commons.logging.PathableTestSuite; public class CustomConfigAPITestCase extends CustomConfigTestCase { - public CustomConfigAPITestCase(final String name) { - super(name); - } - /** * Return the tests included in this test suite. */ @@ -58,4 +54,8 @@ public class CustomConfigAPITestCase extends CustomConfigTestCase { final Class testClass = child.loadClass(CustomConfigAPITestCase.class.getName()); return new PathableTestSuite(testClass, child); } + + public CustomConfigAPITestCase(final String name) { + super(name); + } } diff --git a/src/test/java/org/apache/commons/logging/jdk14/CustomConfigFullTestCase.java b/src/test/java/org/apache/commons/logging/jdk14/CustomConfigFullTestCase.java index 710fa00..f15d26b 100644 --- a/src/test/java/org/apache/commons/logging/jdk14/CustomConfigFullTestCase.java +++ b/src/test/java/org/apache/commons/logging/jdk14/CustomConfigFullTestCase.java @@ -32,11 +32,6 @@ import org.apache.commons.logging.PathableClassLoader; public class CustomConfigFullTestCase extends CustomConfigTestCase { - public CustomConfigFullTestCase(final String name) { - super(name); - } - - /** * Return the tests included in this test suite. */ @@ -60,4 +55,9 @@ public class CustomConfigFullTestCase extends CustomConfigTestCase { final Class testClass = child.loadClass(CustomConfigFullTestCase.class.getName()); return new PathableTestSuite(testClass, child); } + + + public CustomConfigFullTestCase(final String name) { + super(name); + } } diff --git a/src/test/java/org/apache/commons/logging/jdk14/CustomConfigTestCase.java b/src/test/java/org/apache/commons/logging/jdk14/CustomConfigTestCase.java index 0c6abbb..5f7cf83 100644 --- a/src/test/java/org/apache/commons/logging/jdk14/CustomConfigTestCase.java +++ b/src/test/java/org/apache/commons/logging/jdk14/CustomConfigTestCase.java @@ -48,82 +48,6 @@ public class CustomConfigTestCase extends DefaultConfigTestCase { // ----------------------------------------------------------- Constructors - /** - *

Construct a new instance of this test case.

- * - * @param name Name of the test case - */ - public CustomConfigTestCase(final String name) { - super(name); - } - - - // ----------------------------------------------------- Instance Variables - - - /** - *

The customized {@code Handler} we will be using.

- */ - protected TestHandler handler; - - - /** - *

The underlying {@code Handler}s we will be using.

- */ - protected Handler handlers[]; - - - /** - *

The underlying {@code Logger} we will be using.

- */ - protected Logger logger; - - - /** - *

The underlying {@code LogManager} we will be using.

- */ - protected LogManager manager; - - - /** - *

The message levels that should have been logged.

- */ - protected Level[] testLevels = - { Level.FINE, Level.INFO, Level.WARNING, Level.SEVERE, Level.SEVERE }; - - - /** - *

The message strings that should have been logged.

- */ - protected String[] testMessages = - { "debug", "info", "warn", "error", "fatal" }; - - - // ------------------------------------------- JUnit Infrastructure Methods - - - /** - * Given the name of a class that is somewhere in the classpath of the provided - * classloader, return the contents of the corresponding .class file. - */ - protected static byte[] readClass(final String name, final ClassLoader srcCL) throws Exception { - final String resName = name.replace('.', '/') + ".class"; - System.err.println("Trying to load resource [" + resName + "]"); - final InputStream is = srcCL.getResourceAsStream(resName); - final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - System.err.println("Reading resource [" + resName + "]"); - final byte[] buf = new byte[1000]; - for(;;) { - final int read = is.read(buf); - if (read <= 0) { - break; - } - baos.write(buf, 0, read); - } - is.close(); - return baos.toByteArray(); - } - /** * Make a class available in the system classloader even when its classfile is * not present in the classpath configured for that classloader. This only @@ -159,17 +83,30 @@ public class CustomConfigTestCase extends DefaultConfigTestCase { } } + + // ----------------------------------------------------- Instance Variables + + /** - * Set up instance variables required by this test case. + * Given the name of a class that is somewhere in the classpath of the provided + * classloader, return the contents of the corresponding .class file. */ - @Override - public void setUp() throws Exception { - setUpManager - ("org/apache/commons/logging/jdk14/CustomConfig.properties"); - setUpLogger(this.getClass().getName()); - setUpHandlers(); - setUpFactory(); - setUpLog(this.getClass().getName()); + protected static byte[] readClass(final String name, final ClassLoader srcCL) throws Exception { + final String resName = name.replace('.', '/') + ".class"; + System.err.println("Trying to load resource [" + resName + "]"); + final InputStream is = srcCL.getResourceAsStream(resName); + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + System.err.println("Reading resource [" + resName + "]"); + final byte[] buf = new byte[1000]; + for(;;) { + final int read = is.read(buf); + if (read <= 0) { + break; + } + baos.write(buf, 0, read); + } + is.close(); + return baos.toByteArray(); } @@ -195,6 +132,198 @@ public class CustomConfigTestCase extends DefaultConfigTestCase { return new PathableTestSuite(testClass, cl); } + + /** + *

The customized {@code Handler} we will be using.

+ */ + protected TestHandler handler; + + + /** + *

The underlying {@code Handler}s we will be using.

+ */ + protected Handler handlers[]; + + + /** + *

The underlying {@code Logger} we will be using.

+ */ + protected Logger logger; + + + /** + *

The underlying {@code LogManager} we will be using.

+ */ + protected LogManager manager; + + + // ------------------------------------------- JUnit Infrastructure Methods + + + /** + *

The message levels that should have been logged.

+ */ + protected Level[] testLevels = + { Level.FINE, Level.INFO, Level.WARNING, Level.SEVERE, Level.SEVERE }; + + /** + *

The message strings that should have been logged.

+ */ + protected String[] testMessages = + { "debug", "info", "warn", "error", "fatal" }; + + /** + *

Construct a new instance of this test case.

+ * + * @param name Name of the test case + */ + public CustomConfigTestCase(final String name) { + super(name); + } + + + // Check the log instance + @Override + protected void checkLog() { + + assertNotNull("Log exists", log); + assertEquals("Log class", + "org.apache.commons.logging.impl.Jdk14Logger", + log.getClass().getName()); + + // Assert which logging levels have been enabled + assertTrue(log.isFatalEnabled()); + assertTrue(log.isErrorEnabled()); + assertTrue(log.isWarnEnabled()); + assertTrue(log.isInfoEnabled()); + assertTrue(log.isDebugEnabled()); + assertFalse(log.isTraceEnabled()); + + } + + // Check the recorded messages + protected void checkLogRecords(final boolean thrown) { + final Iterator records = handler.records(); + for (int i = 0; i < testMessages.length; i++) { + assertTrue(records.hasNext()); + final LogRecord record = (LogRecord) records.next(); + assertEquals("LogRecord level", + testLevels[i], record.getLevel()); + assertEquals("LogRecord message", + testMessages[i], record.getMessage()); + assertTrue("LogRecord class", + record.getSourceClassName().startsWith( + "org.apache.commons.logging.jdk14.CustomConfig")); + if (thrown) { + assertEquals("LogRecord method", + "logExceptionMessages", + record.getSourceMethodName()); + } else { + assertEquals("LogRecord method", + "logPlainMessages", + record.getSourceMethodName()); + } + if (thrown) { + assertNotNull("LogRecord thrown", record.getThrown()); + assertTrue("LogRecord thrown type", + record.getThrown() instanceof DummyException); + } else { + assertNull("LogRecord thrown", + record.getThrown()); + } + } + assertFalse(records.hasNext()); + handler.flush(); + } + + + // ----------------------------------------------------------- Test Methods + + + // Log the messages with exceptions + protected void logExceptionMessages() { + final Throwable t = new DummyException(); + log.trace("trace", t); // Should not actually get logged + log.debug("debug", t); + log.info("info", t); + log.warn("warn", t); + log.error("error", t); + log.fatal("fatal", t); + } + + + // Log the plain messages + protected void logPlainMessages() { + log.trace("trace"); // Should not actually get logged + log.debug("debug"); + log.info("info"); + log.warn("warn"); + log.error("error"); + log.fatal("fatal"); + } + + + /** + * Set up instance variables required by this test case. + */ + @Override + public void setUp() throws Exception { + setUpManager + ("org/apache/commons/logging/jdk14/CustomConfig.properties"); + setUpLogger(this.getClass().getName()); + setUpHandlers(); + setUpFactory(); + setUpLog(this.getClass().getName()); + } + + + // Set up handlers instance + protected void setUpHandlers() throws Exception { + Logger parent = logger; + while (parent.getParent() != null) { + parent = parent.getParent(); + } + handlers = parent.getHandlers(); + + // The CustomConfig.properties file explicitly defines one handler class + // to be attached to the root logger, so if it isn't there then + // something is badly wrong... + // + // Yes this testing is also done in testPristineHandlers but + // unfortunately: + // * we need to set up the handlers variable here, + // * we don't want that to be set up incorrectly, as that can + // produce weird error messages in other tests, and + // * we can't rely on testPristineHandlers being the first + // test to run. + // so we need to test things here too. + assertNotNull("No Handlers defined for JDK14 logging", handlers); + assertEquals("Unexpected number of handlers for JDK14 logging", 1, handlers.length); + assertNotNull("Handler is null", handlers[0]); + assertTrue("Handler not of expected type", handlers[0] instanceof TestHandler); + handler = (TestHandler) handlers[0]; + } + + + // Set up logger instance + protected void setUpLogger(final String name) throws Exception { + logger = Logger.getLogger(name); + } + + + // -------------------------------------------------------- Support Methods + + + // Set up LogManager instance + protected void setUpManager(final String config) throws Exception { + manager = LogManager.getLogManager(); + final InputStream is = + this.getClass().getClassLoader().getResourceAsStream(config); + manager.readConfiguration(is); + is.close(); + } + + /** * Tear down instance variables required by this test case. */ @@ -207,9 +336,6 @@ public class CustomConfigTestCase extends DefaultConfigTestCase { } - // ----------------------------------------------------------- Test Methods - - // Test logging message strings with exceptions public void testExceptionMessages() throws Exception { @@ -267,130 +393,4 @@ public class CustomConfigTestCase extends DefaultConfigTestCase { } - // -------------------------------------------------------- Support Methods - - - // Check the log instance - @Override - protected void checkLog() { - - assertNotNull("Log exists", log); - assertEquals("Log class", - "org.apache.commons.logging.impl.Jdk14Logger", - log.getClass().getName()); - - // Assert which logging levels have been enabled - assertTrue(log.isFatalEnabled()); - assertTrue(log.isErrorEnabled()); - assertTrue(log.isWarnEnabled()); - assertTrue(log.isInfoEnabled()); - assertTrue(log.isDebugEnabled()); - assertFalse(log.isTraceEnabled()); - - } - - - // Check the recorded messages - protected void checkLogRecords(final boolean thrown) { - final Iterator records = handler.records(); - for (int i = 0; i < testMessages.length; i++) { - assertTrue(records.hasNext()); - final LogRecord record = (LogRecord) records.next(); - assertEquals("LogRecord level", - testLevels[i], record.getLevel()); - assertEquals("LogRecord message", - testMessages[i], record.getMessage()); - assertTrue("LogRecord class", - record.getSourceClassName().startsWith( - "org.apache.commons.logging.jdk14.CustomConfig")); - if (thrown) { - assertEquals("LogRecord method", - "logExceptionMessages", - record.getSourceMethodName()); - } else { - assertEquals("LogRecord method", - "logPlainMessages", - record.getSourceMethodName()); - } - if (thrown) { - assertNotNull("LogRecord thrown", record.getThrown()); - assertTrue("LogRecord thrown type", - record.getThrown() instanceof DummyException); - } else { - assertNull("LogRecord thrown", - record.getThrown()); - } - } - assertFalse(records.hasNext()); - handler.flush(); - } - - - // Log the messages with exceptions - protected void logExceptionMessages() { - final Throwable t = new DummyException(); - log.trace("trace", t); // Should not actually get logged - log.debug("debug", t); - log.info("info", t); - log.warn("warn", t); - log.error("error", t); - log.fatal("fatal", t); - } - - - // Log the plain messages - protected void logPlainMessages() { - log.trace("trace"); // Should not actually get logged - log.debug("debug"); - log.info("info"); - log.warn("warn"); - log.error("error"); - log.fatal("fatal"); - } - - - // Set up handlers instance - protected void setUpHandlers() throws Exception { - Logger parent = logger; - while (parent.getParent() != null) { - parent = parent.getParent(); - } - handlers = parent.getHandlers(); - - // The CustomConfig.properties file explicitly defines one handler class - // to be attached to the root logger, so if it isn't there then - // something is badly wrong... - // - // Yes this testing is also done in testPristineHandlers but - // unfortunately: - // * we need to set up the handlers variable here, - // * we don't want that to be set up incorrectly, as that can - // produce weird error messages in other tests, and - // * we can't rely on testPristineHandlers being the first - // test to run. - // so we need to test things here too. - assertNotNull("No Handlers defined for JDK14 logging", handlers); - assertEquals("Unexpected number of handlers for JDK14 logging", 1, handlers.length); - assertNotNull("Handler is null", handlers[0]); - assertTrue("Handler not of expected type", handlers[0] instanceof TestHandler); - handler = (TestHandler) handlers[0]; - } - - - // Set up logger instance - protected void setUpLogger(final String name) throws Exception { - logger = Logger.getLogger(name); - } - - - // Set up LogManager instance - protected void setUpManager(final String config) throws Exception { - manager = LogManager.getLogManager(); - final InputStream is = - this.getClass().getClassLoader().getResourceAsStream(config); - manager.readConfiguration(is); - is.close(); - } - - } diff --git a/src/test/java/org/apache/commons/logging/jdk14/DefaultConfigTestCase.java b/src/test/java/org/apache/commons/logging/jdk14/DefaultConfigTestCase.java index a8b14c8..1645881 100644 --- a/src/test/java/org/apache/commons/logging/jdk14/DefaultConfigTestCase.java +++ b/src/test/java/org/apache/commons/logging/jdk14/DefaultConfigTestCase.java @@ -39,34 +39,6 @@ import org.apache.commons.logging.PathableTestSuite; */ public class DefaultConfigTestCase extends TestCase { - /** - *

Construct a new instance of this test case.

- * - * @param name Name of the test case - */ - public DefaultConfigTestCase(final String name) { - super(name); - } - - /** - *

The {@link LogFactory} implementation we have selected.

- */ - protected LogFactory factory; - - /** - *

The {@link Log} implementation we have selected.

- */ - protected Log log; - - /** - * Set up instance variables required by this test case. - */ - @Override - public void setUp() throws Exception { - setUpFactory(); - setUpLog("TestLogger"); - } - /** * Return the tests included in this test suite. */ @@ -81,50 +53,22 @@ public class DefaultConfigTestCase extends TestCase { } /** - * Tear down instance variables required by this test case. + *

The {@link LogFactory} implementation we have selected.

*/ - @Override - public void tearDown() { - log = null; - factory = null; - LogFactory.releaseAll(); - } + protected LogFactory factory; - // Test pristine Log instance - public void testPristineLog() { - checkLog(); - } - - // Test pristine LogFactory instance - public void testPristineFactory() { - assertNotNull("LogFactory exists", factory); - assertEquals("LogFactory class", - "org.apache.commons.logging.impl.LogFactoryImpl", - factory.getClass().getName()); - - final String[] names = factory.getAttributeNames(); - assertNotNull("Names exists", names); - assertEquals("Names empty", 0, names.length); - } - - - // Test Serializability of Log instance - public void testSerializable() throws Exception { - - // Serialize and deserialize the instance - final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - final ObjectOutputStream oos = new ObjectOutputStream(baos); - oos.writeObject(log); - oos.close(); - final ByteArrayInputStream bais = - new ByteArrayInputStream(baos.toByteArray()); - final ObjectInputStream ois = new ObjectInputStream(bais); - log = (Log) ois.readObject(); - ois.close(); - - // Check the characteristics of the resulting object - checkLog(); + /** + *

The {@link Log} implementation we have selected.

+ */ + protected Log log; + /** + *

Construct a new instance of this test case.

+ * + * @param name Name of the test case + */ + public DefaultConfigTestCase(final String name) { + super(name); } // Check the log instance @@ -145,6 +89,15 @@ public class DefaultConfigTestCase extends TestCase { } + /** + * Set up instance variables required by this test case. + */ + @Override + public void setUp() throws Exception { + setUpFactory(); + setUpLog("TestLogger"); + } + // Set up factory instance protected void setUpFactory() throws Exception { factory = LogFactory.getFactory(); @@ -155,4 +108,51 @@ public class DefaultConfigTestCase extends TestCase { log = LogFactory.getLog(name); } + + /** + * Tear down instance variables required by this test case. + */ + @Override + public void tearDown() { + log = null; + factory = null; + LogFactory.releaseAll(); + } + + // Test pristine LogFactory instance + public void testPristineFactory() { + assertNotNull("LogFactory exists", factory); + assertEquals("LogFactory class", + "org.apache.commons.logging.impl.LogFactoryImpl", + factory.getClass().getName()); + + final String[] names = factory.getAttributeNames(); + assertNotNull("Names exists", names); + assertEquals("Names empty", 0, names.length); + } + + // Test pristine Log instance + public void testPristineLog() { + checkLog(); + } + + // Test Serializability of Log instance + public void testSerializable() throws Exception { + + // Serialize and deserialize the instance + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + final ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(log); + oos.close(); + final ByteArrayInputStream bais = + new ByteArrayInputStream(baos.toByteArray()); + final ObjectInputStream ois = new ObjectInputStream(bais); + log = (Log) ois.readObject(); + ois.close(); + + // Check the characteristics of the resulting object + checkLog(); + + } + } diff --git a/src/test/java/org/apache/commons/logging/jdk14/TestHandler.java b/src/test/java/org/apache/commons/logging/jdk14/TestHandler.java index 6dae742..eaa7306 100644 --- a/src/test/java/org/apache/commons/logging/jdk14/TestHandler.java +++ b/src/test/java/org/apache/commons/logging/jdk14/TestHandler.java @@ -32,10 +32,6 @@ public class TestHandler extends Handler { // The set of logged records for this handler private final List records = new ArrayList(); - public Iterator records() { - return records.iterator(); - } - @Override public void close() { } @@ -50,4 +46,8 @@ public class TestHandler extends Handler { records.add(record); } + public Iterator records() { + return records.iterator(); + } + } diff --git a/src/test/java/org/apache/commons/logging/log4j/StandardTests.java b/src/test/java/org/apache/commons/logging/log4j/StandardTests.java index 895eaaf..b1700da 100644 --- a/src/test/java/org/apache/commons/logging/log4j/StandardTests.java +++ b/src/test/java/org/apache/commons/logging/log4j/StandardTests.java @@ -51,91 +51,6 @@ public abstract class StandardTests extends TestCase { public Throwable throwable; } - /** - * Set up instance variables required by this test case. - */ - @Override - public void setUp() throws Exception { - LogFactory.releaseAll(); - } - - /** - * Tear down instance variables required by this test case. - */ - @Override - public void tearDown() { - LogFactory.releaseAll(); - } - - /** - * Modify log4j's setup so that all messages actually logged get redirected - * into the specified list. - *

- * This method also sets the logging level to INFO so that we - * can test whether messages are getting properly filtered. - */ - public abstract void setUpTestAppender(List logEvents) throws Exception; - - /** - * Test that a LogFactory gets created as expected. - */ - public void testCreateFactory() { - final LogFactory factory = LogFactory.getFactory(); - assertNotNull("LogFactory exists", factory); - assertEquals("LogFactory class", - "org.apache.commons.logging.impl.LogFactoryImpl", - factory.getClass().getName()); - - final String[] names = factory.getAttributeNames(); - assertNotNull("Names exists", names); - assertEquals("Names empty", 0, names.length); - } - - /** - * Verify that we can log messages without exceptions. - */ - public void testPlainMessages() throws Exception { - final List logEvents = new ArrayList(); - setUpTestAppender(logEvents); - final Log log = LogFactory.getLog("test-category"); - logPlainMessages(log); - checkLoggingEvents(logEvents, false); - } - - /** - * Verify that we can log exception messages. - */ - public void testExceptionMessages() throws Exception { - final List logEvents = new ArrayList(); - setUpTestAppender(logEvents); - final Log log = LogFactory.getLog("test-category"); - logExceptionMessages(log); - checkLoggingEvents(logEvents, true); - } - - /** - * Test Serializability of Log instance - */ - public void testSerializable() throws Exception { - final List logEvents = new ArrayList(); - setUpTestAppender(logEvents); - final Log log = LogFactory.getLog("test-category"); - - final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - final ObjectOutputStream oos = new ObjectOutputStream(baos); - oos.writeObject(log); - oos.close(); - final ByteArrayInputStream bais = - new ByteArrayInputStream(baos.toByteArray()); - final ObjectInputStream ois = new ObjectInputStream(bais); - final Log newLog = (Log) ois.readObject(); - ois.close(); - - // Check the characteristics of the resulting object - logExceptionMessages(newLog); - checkLoggingEvents(logEvents, true); - } - /** * Verify that the TestAppender has received the expected * number of messages. This assumes that: @@ -178,6 +93,18 @@ public abstract class StandardTests extends TestCase { assertEquals("Exception data incorrect", ev.throwable!=null, thrown); } + /** + * Log messages with exceptions + */ + private void logExceptionMessages(final Log log) { + final Throwable t = new DummyException(); + log.trace("trace", t); // Should not actually get logged + log.debug("debug", t); // Should not actually get logged + log.info("info", t); + log.warn("warn", t); + log.error("error", t); + log.fatal("fatal", t); + } /** * Log plain messages. @@ -192,15 +119,88 @@ public abstract class StandardTests extends TestCase { } /** - * Log messages with exceptions + * Set up instance variables required by this test case. */ - private void logExceptionMessages(final Log log) { - final Throwable t = new DummyException(); - log.trace("trace", t); // Should not actually get logged - log.debug("debug", t); // Should not actually get logged - log.info("info", t); - log.warn("warn", t); - log.error("error", t); - log.fatal("fatal", t); + @Override + public void setUp() throws Exception { + LogFactory.releaseAll(); + } + + /** + * Modify log4j's setup so that all messages actually logged get redirected + * into the specified list. + *

+ * This method also sets the logging level to INFO so that we + * can test whether messages are getting properly filtered. + */ + public abstract void setUpTestAppender(List logEvents) throws Exception; + + /** + * Tear down instance variables required by this test case. + */ + @Override + public void tearDown() { + LogFactory.releaseAll(); + } + + /** + * Test that a LogFactory gets created as expected. + */ + public void testCreateFactory() { + final LogFactory factory = LogFactory.getFactory(); + assertNotNull("LogFactory exists", factory); + assertEquals("LogFactory class", + "org.apache.commons.logging.impl.LogFactoryImpl", + factory.getClass().getName()); + + final String[] names = factory.getAttributeNames(); + assertNotNull("Names exists", names); + assertEquals("Names empty", 0, names.length); + } + + /** + * Verify that we can log exception messages. + */ + public void testExceptionMessages() throws Exception { + final List logEvents = new ArrayList(); + setUpTestAppender(logEvents); + final Log log = LogFactory.getLog("test-category"); + logExceptionMessages(log); + checkLoggingEvents(logEvents, true); + } + + + /** + * Verify that we can log messages without exceptions. + */ + public void testPlainMessages() throws Exception { + final List logEvents = new ArrayList(); + setUpTestAppender(logEvents); + final Log log = LogFactory.getLog("test-category"); + logPlainMessages(log); + checkLoggingEvents(logEvents, false); + } + + /** + * Test Serializability of Log instance + */ + public void testSerializable() throws Exception { + final List logEvents = new ArrayList(); + setUpTestAppender(logEvents); + final Log log = LogFactory.getLog("test-category"); + + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + final ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(log); + oos.close(); + final ByteArrayInputStream bais = + new ByteArrayInputStream(baos.toByteArray()); + final ObjectInputStream ois = new ObjectInputStream(bais); + final Log newLog = (Log) ois.readObject(); + ois.close(); + + // Check the characteristics of the resulting object + logExceptionMessages(newLog); + checkLoggingEvents(logEvents, true); } } diff --git a/src/test/java/org/apache/commons/logging/log4j/log4j12/TestAppender.java b/src/test/java/org/apache/commons/logging/log4j/log4j12/TestAppender.java index 6214176..b5a42a0 100644 --- a/src/test/java/org/apache/commons/logging/log4j/log4j12/TestAppender.java +++ b/src/test/java/org/apache/commons/logging/log4j/log4j12/TestAppender.java @@ -33,6 +33,12 @@ import org.apache.log4j.spi.LoggingEvent; public class TestAppender extends AppenderSkeleton { + // The set of logged events for this appender + private final List events; + + // ----------------------------------------------------- Instance Variables + + /** * Constructor. */ @@ -40,12 +46,6 @@ public class TestAppender extends AppenderSkeleton { events = logEvents; } - // ----------------------------------------------------- Instance Variables - - - // The set of logged events for this appender - private final List events; - // ------------------------------------------------------- Appender Methods diff --git a/src/test/java/org/apache/commons/logging/logkit/StandardTestCase.java b/src/test/java/org/apache/commons/logging/logkit/StandardTestCase.java index b999b4c..9abc18c 100644 --- a/src/test/java/org/apache/commons/logging/logkit/StandardTestCase.java +++ b/src/test/java/org/apache/commons/logging/logkit/StandardTestCase.java @@ -42,21 +42,6 @@ public class StandardTestCase extends AbstractLogTest { // ----------------------------------------------------- Instance Variables - /** - *

The {@link LogFactory} implementation we have selected.

- */ - protected LogFactory factory; - - - /** - *

The {@link Log} implementation we have selected.

- */ - protected Log log; - - - // ------------------------------------------- JUnit Infrastructure Methods - - /** * Return the tests included in this test suite. */ @@ -73,6 +58,51 @@ public class StandardTestCase extends AbstractLogTest { return new PathableTestSuite(testClass, loader); } + + /** + *

The {@link LogFactory} implementation we have selected.

+ */ + protected LogFactory factory; + + + // ------------------------------------------- JUnit Infrastructure Methods + + + /** + *

The {@link Log} implementation we have selected.

+ */ + protected Log log; + + // Check the standard log instance + protected void checkStandard() { + + assertNotNull("Log exists", log); + assertEquals("Log class", + "org.apache.commons.logging.impl.LogKitLogger", + log.getClass().getName()); + + // Can we call level checkers with no exceptions? + // Note that by default *everything* is enabled for LogKit + assertTrue(log.isTraceEnabled()); + assertTrue(log.isDebugEnabled()); + assertTrue(log.isInfoEnabled()); + assertTrue(log.isWarnEnabled()); + assertTrue(log.isErrorEnabled()); + assertTrue(log.isFatalEnabled()); + } + + /** + * Override the abstract method from the parent class so that the + * inherited tests can access the right Log object type. + */ + @Override + public Log getLogObject() + { + return new LogKitLogger(this.getClass().getName()); + } + + // ----------------------------------------------------------- Test Methods + /** * Set up instance variables required by this test case. */ @@ -98,18 +128,6 @@ public class StandardTestCase extends AbstractLogTest { LogFactory.releaseAll(); } - // ----------------------------------------------------------- Test Methods - - /** - * Override the abstract method from the parent class so that the - * inherited tests can access the right Log object type. - */ - @Override - public Log getLogObject() - { - return new LogKitLogger(this.getClass().getName()); - } - // Test pristine LogFactory instance public void testPristineFactory() { @@ -128,6 +146,9 @@ public class StandardTestCase extends AbstractLogTest { checkStandard(); } + + // -------------------------------------------------------- Support Methods + // Test Serializability of standard instance public void testSerializable() throws Exception { checkStandard(); @@ -145,25 +166,4 @@ public class StandardTestCase extends AbstractLogTest { checkStandard(); } - - - // -------------------------------------------------------- Support Methods - - // Check the standard log instance - protected void checkStandard() { - - assertNotNull("Log exists", log); - assertEquals("Log class", - "org.apache.commons.logging.impl.LogKitLogger", - log.getClass().getName()); - - // Can we call level checkers with no exceptions? - // Note that by default *everything* is enabled for LogKit - assertTrue(log.isTraceEnabled()); - assertTrue(log.isDebugEnabled()); - assertTrue(log.isInfoEnabled()); - assertTrue(log.isWarnEnabled()); - assertTrue(log.isErrorEnabled()); - assertTrue(log.isFatalEnabled()); - } } diff --git a/src/test/java/org/apache/commons/logging/noop/NoOpLogTestCase.java b/src/test/java/org/apache/commons/logging/noop/NoOpLogTestCase.java index 5af10f6..a57c72a 100644 --- a/src/test/java/org/apache/commons/logging/noop/NoOpLogTestCase.java +++ b/src/test/java/org/apache/commons/logging/noop/NoOpLogTestCase.java @@ -34,6 +34,33 @@ import org.apache.commons.logging.AbstractLogTest; */ public class NoOpLogTestCase extends AbstractLogTest { + private void checkLog(final Log log) { + + assertNotNull("Log exists", log); + assertEquals("Log class", + "org.apache.commons.logging.impl.NoOpLog", + log.getClass().getName()); + + // Can we call level checkers with no exceptions? + // Note that *everything* is permanently disabled for NoOpLog + assertFalse(log.isTraceEnabled()); + assertFalse(log.isDebugEnabled()); + assertFalse(log.isInfoEnabled()); + assertFalse(log.isWarnEnabled()); + assertFalse(log.isErrorEnabled()); + assertFalse(log.isFatalEnabled()); + } + + /** + * Override the abstract method from the parent class so that the + * inherited tests can access the right Log object type. + */ + @Override + public Log getLogObject() + { + return new NoOpLog(this.getClass().getName()); + } + /** * Set up instance variables required by this test case. */ @@ -55,15 +82,8 @@ public class NoOpLogTestCase extends AbstractLogTest System.getProperties().remove("org.apache.commons.logging.Log"); } - /** - * Override the abstract method from the parent class so that the - * inherited tests can access the right Log object type. - */ - @Override - public Log getLogObject() - { - return new NoOpLog(this.getClass().getName()); - } + + // -------------------------------------------------------- Support Methods // Test Serializability of standard instance public void testSerializable() throws Exception { @@ -83,24 +103,4 @@ public class NoOpLogTestCase extends AbstractLogTest checkLog(log); } - - - // -------------------------------------------------------- Support Methods - - private void checkLog(final Log log) { - - assertNotNull("Log exists", log); - assertEquals("Log class", - "org.apache.commons.logging.impl.NoOpLog", - log.getClass().getName()); - - // Can we call level checkers with no exceptions? - // Note that *everything* is permanently disabled for NoOpLog - assertFalse(log.isTraceEnabled()); - assertFalse(log.isDebugEnabled()); - assertFalse(log.isInfoEnabled()); - assertFalse(log.isWarnEnabled()); - assertFalse(log.isErrorEnabled()); - assertFalse(log.isFatalEnabled()); - } } diff --git a/src/test/java/org/apache/commons/logging/pathable/ChildFirstTestCase.java b/src/test/java/org/apache/commons/logging/pathable/ChildFirstTestCase.java index a43ef51..a4a7f0d 100644 --- a/src/test/java/org/apache/commons/logging/pathable/ChildFirstTestCase.java +++ b/src/test/java/org/apache/commons/logging/pathable/ChildFirstTestCase.java @@ -90,6 +90,19 @@ public class ChildFirstTestCase extends TestCase { return new PathableTestSuite(testClass, context); } + /** + * Utility method to convert an enumeration-of-URLs into an array of URLs. + */ + private static URL[] toURLArray(final Enumeration e) { + final ArrayList l = new ArrayList(); + while (e.hasMoreElements()) { + final URL u = (URL) e.nextElement(); + l.add(u); + } + final URL[] tmp = new URL[l.size()]; + return (URL[]) l.toArray(tmp); + } + /** * Utility method to return the set of all classloaders in the * parent chain starting from the one that loaded the class for @@ -220,6 +233,34 @@ public class ChildFirstTestCase extends TestCase { resource.toString().indexOf("/commons-logging-adapters-1.") > 0); } + /** + * Test that getResourceAsStream works. + */ + public void testResourceAsStream() throws Exception { + java.io.InputStream is; + + // verify the classloader hierarchy + final ClassLoader contextLoader = Thread.currentThread().getContextClassLoader(); + final ClassLoader childLoader = contextLoader.getParent(); + final ClassLoader parentLoader = childLoader.getParent(); + final ClassLoader bootLoader = parentLoader.getParent(); + assertNull("Unexpected classloader hierarchy", bootLoader); + + // getResourceAsStream where no instances exist + is = childLoader.getResourceAsStream("nosuchfile"); + assertNull("Invalid resource returned non-null stream", is); + + // getResourceAsStream where resource does exist + is = childLoader.getResourceAsStream("org/apache/commons/logging/Log.class"); + assertNotNull("Null returned for valid resource", is); + is.close(); + + // It would be nice to test parent-first ordering here, but that would require + // having a resource with the same name in both the parent and child loaders, + // but with different contents. That's a little tricky to set up so we'll + // skip that for now. + } + /** * Test that the various flavours of ClassLoader.getResources work as expected. */ @@ -273,45 +314,4 @@ public class ChildFirstTestCase extends TestCase { assertTrue("Incorrect source for Log4JLogger class", urlsToStrings[1].indexOf("/commons-logging-adapters-1.") > 0); } - - /** - * Utility method to convert an enumeration-of-URLs into an array of URLs. - */ - private static URL[] toURLArray(final Enumeration e) { - final ArrayList l = new ArrayList(); - while (e.hasMoreElements()) { - final URL u = (URL) e.nextElement(); - l.add(u); - } - final URL[] tmp = new URL[l.size()]; - return (URL[]) l.toArray(tmp); - } - - /** - * Test that getResourceAsStream works. - */ - public void testResourceAsStream() throws Exception { - java.io.InputStream is; - - // verify the classloader hierarchy - final ClassLoader contextLoader = Thread.currentThread().getContextClassLoader(); - final ClassLoader childLoader = contextLoader.getParent(); - final ClassLoader parentLoader = childLoader.getParent(); - final ClassLoader bootLoader = parentLoader.getParent(); - assertNull("Unexpected classloader hierarchy", bootLoader); - - // getResourceAsStream where no instances exist - is = childLoader.getResourceAsStream("nosuchfile"); - assertNull("Invalid resource returned non-null stream", is); - - // getResourceAsStream where resource does exist - is = childLoader.getResourceAsStream("org/apache/commons/logging/Log.class"); - assertNotNull("Null returned for valid resource", is); - is.close(); - - // It would be nice to test parent-first ordering here, but that would require - // having a resource with the same name in both the parent and child loaders, - // but with different contents. That's a little tricky to set up so we'll - // skip that for now. - } } diff --git a/src/test/java/org/apache/commons/logging/pathable/GeneralTestCase.java b/src/test/java/org/apache/commons/logging/pathable/GeneralTestCase.java index 0d0da99..cd527b7 100644 --- a/src/test/java/org/apache/commons/logging/pathable/GeneralTestCase.java +++ b/src/test/java/org/apache/commons/logging/pathable/GeneralTestCase.java @@ -31,6 +31,31 @@ import org.apache.commons.logging.PathableTestSuite; public class GeneralTestCase extends TestCase { + /** + * Verify that the context classloader is a custom one, then reset it to + * a non-custom one. + */ + private static void checkAndSetContext() { + final ClassLoader contextLoader = Thread.currentThread().getContextClassLoader(); + assertEquals("ContextLoader is of unexpected type", + contextLoader.getClass().getName(), + PathableClassLoader.class.getName()); + + final URL[] noUrls = new URL[0]; + Thread.currentThread().setContextClassLoader(new URLClassLoader(noUrls)); + } + + /** + * Verify that a certain system property is not set, then set it. + */ + private static void checkAndSetProperties() { + String prop = System.getProperty("no.such.property"); + assertNull("no.such.property is unexpectedly defined", prop); + System.setProperty("no.such.property", "dummy value"); + prop = System.getProperty("no.such.property"); + assertNotNull("no.such.property is unexpectedly undefined", prop); + } + /** * Set up a custom classloader hierarchy for this test case. */ @@ -49,53 +74,6 @@ public class GeneralTestCase extends TestCase { return new PathableTestSuite(testClass, loader); } - /** - * Verify that a certain system property is not set, then set it. - */ - private static void checkAndSetProperties() { - String prop = System.getProperty("no.such.property"); - assertNull("no.such.property is unexpectedly defined", prop); - System.setProperty("no.such.property", "dummy value"); - prop = System.getProperty("no.such.property"); - assertNotNull("no.such.property is unexpectedly undefined", prop); - } - - /** - * Verify that when a test method modifies the system properties they are - * reset before the next test is run. - *

- * This method works in conjunction with testResetProps2. There is no - * way of knowing which test method junit will run first, but it doesn't - * matter; whichever one of them runs first will modify the system properties. - * If the PathableTestSuite isn't resetting the system properties then whichever - * of them runs second will fail. Of course if other methods are run in-between - * then those methods might also fail... - */ - public void testResetProps1() { - checkAndSetProperties(); - } - - /** - * See testResetProps1. - */ - public void testResetProps2() { - checkAndSetProperties(); - } - - /** - * Verify that the context classloader is a custom one, then reset it to - * a non-custom one. - */ - private static void checkAndSetContext() { - final ClassLoader contextLoader = Thread.currentThread().getContextClassLoader(); - assertEquals("ContextLoader is of unexpected type", - contextLoader.getClass().getName(), - PathableClassLoader.class.getName()); - - final URL[] noUrls = new URL[0]; - Thread.currentThread().setContextClassLoader(new URLClassLoader(noUrls)); - } - /** * Verify that when a test method modifies the context classloader it is * reset before the next test is run. @@ -117,4 +95,26 @@ public class GeneralTestCase extends TestCase { public void testResetContext2() { checkAndSetContext(); } + + /** + * Verify that when a test method modifies the system properties they are + * reset before the next test is run. + *

+ * This method works in conjunction with testResetProps2. There is no + * way of knowing which test method junit will run first, but it doesn't + * matter; whichever one of them runs first will modify the system properties. + * If the PathableTestSuite isn't resetting the system properties then whichever + * of them runs second will fail. Of course if other methods are run in-between + * then those methods might also fail... + */ + public void testResetProps1() { + checkAndSetProperties(); + } + + /** + * See testResetProps1. + */ + public void testResetProps2() { + checkAndSetProperties(); + } } diff --git a/src/test/java/org/apache/commons/logging/pathable/ParentFirstTestCase.java b/src/test/java/org/apache/commons/logging/pathable/ParentFirstTestCase.java index a2b13b7..8a4c937 100644 --- a/src/test/java/org/apache/commons/logging/pathable/ParentFirstTestCase.java +++ b/src/test/java/org/apache/commons/logging/pathable/ParentFirstTestCase.java @@ -89,6 +89,19 @@ public class ParentFirstTestCase extends TestCase { return new PathableTestSuite(testClass, context); } + /** + * Utility method to convert an enumeration-of-URLs into an array of URLs. + */ + private static URL[] toURLArray(final Enumeration e) { + final ArrayList l = new ArrayList(); + while (e.hasMoreElements()) { + final URL u = (URL) e.nextElement(); + l.add(u); + } + final URL[] tmp = new URL[l.size()]; + return (URL[]) l.toArray(tmp); + } + /** * Utility method to return the set of all classloaders in the * parent chain starting from the one that loaded the class for @@ -218,6 +231,34 @@ public class ParentFirstTestCase extends TestCase { resource.toString().indexOf("/commons-logging-1.") > 0); } + /** + * Test that getResourceAsStream works. + */ + public void testResourceAsStream() throws Exception { + java.io.InputStream is; + + // verify the classloader hierarchy + final ClassLoader contextLoader = Thread.currentThread().getContextClassLoader(); + final ClassLoader childLoader = contextLoader.getParent(); + final ClassLoader parentLoader = childLoader.getParent(); + final ClassLoader bootLoader = parentLoader.getParent(); + assertNull("Unexpected classloader hierarchy", bootLoader); + + // getResourceAsStream where no instances exist + is = childLoader.getResourceAsStream("nosuchfile"); + assertNull("Invalid resource returned non-null stream", is); + + // getResourceAsStream where resource does exist + is = childLoader.getResourceAsStream("org/apache/commons/logging/Log.class"); + assertNotNull("Null returned for valid resource", is); + is.close(); + + // It would be nice to test parent-first ordering here, but that would require + // having a resource with the same name in both the parent and child loaders, + // but with different contents. That's a little tricky to set up so we'll + // skip that for now. + } + /** * Test that the various flavours of ClassLoader.getResources work as expected. */ @@ -265,45 +306,4 @@ public class ParentFirstTestCase extends TestCase { urlsToStrings[1].indexOf("/commons-logging-adapters-1.") > 0); } - - /** - * Utility method to convert an enumeration-of-URLs into an array of URLs. - */ - private static URL[] toURLArray(final Enumeration e) { - final ArrayList l = new ArrayList(); - while (e.hasMoreElements()) { - final URL u = (URL) e.nextElement(); - l.add(u); - } - final URL[] tmp = new URL[l.size()]; - return (URL[]) l.toArray(tmp); - } - - /** - * Test that getResourceAsStream works. - */ - public void testResourceAsStream() throws Exception { - java.io.InputStream is; - - // verify the classloader hierarchy - final ClassLoader contextLoader = Thread.currentThread().getContextClassLoader(); - final ClassLoader childLoader = contextLoader.getParent(); - final ClassLoader parentLoader = childLoader.getParent(); - final ClassLoader bootLoader = parentLoader.getParent(); - assertNull("Unexpected classloader hierarchy", bootLoader); - - // getResourceAsStream where no instances exist - is = childLoader.getResourceAsStream("nosuchfile"); - assertNull("Invalid resource returned non-null stream", is); - - // getResourceAsStream where resource does exist - is = childLoader.getResourceAsStream("org/apache/commons/logging/Log.class"); - assertNotNull("Null returned for valid resource", is); - is.close(); - - // It would be nice to test parent-first ordering here, but that would require - // having a resource with the same name in both the parent and child loaders, - // but with different contents. That's a little tricky to set up so we'll - // skip that for now. - } } diff --git a/src/test/java/org/apache/commons/logging/security/MockSecurityManager.java b/src/test/java/org/apache/commons/logging/security/MockSecurityManager.java index 0621603..b68b08a 100644 --- a/src/test/java/org/apache/commons/logging/security/MockSecurityManager.java +++ b/src/test/java/org/apache/commons/logging/security/MockSecurityManager.java @@ -28,9 +28,9 @@ import java.security.Permissions; */ public class MockSecurityManager extends SecurityManager { - private final Permissions permissions = new Permissions(); private static final Permission setSecurityManagerPerm = new RuntimePermission("setSecurityManager"); + private final Permissions permissions = new Permissions(); private int untrustedCodeCount; @@ -46,18 +46,6 @@ public class MockSecurityManager extends SecurityManager { permissions.add(p); } - /** - * This returns the number of times that a check of a permission failed - * due to stack-walking tracing up into untrusted code. Any non-zero - * value indicates a bug in JCL, ie a situation where code was not - * correctly wrapped in an AccessController block. The result of such a - * bug is that signing JCL is not sufficient to allow JCL to perform - * the operation; the caller would need to be signed too. - */ - public int getUntrustedCodeCount() { - return untrustedCodeCount; - } - @Override public void checkPermission(final Permission p) throws SecurityException { if (setSecurityManagerPerm.implies(p)) { @@ -147,4 +135,16 @@ public class MockSecurityManager extends SecurityManager { } } } + + /** + * This returns the number of times that a check of a permission failed + * due to stack-walking tracing up into untrusted code. Any non-zero + * value indicates a bug in JCL, ie a situation where code was not + * correctly wrapped in an AccessController block. The result of such a + * bug is that signing JCL is not sufficient to allow JCL to perform + * the operation; the caller would need to be signed too. + */ + public int getUntrustedCodeCount() { + return untrustedCodeCount; + } } diff --git a/src/test/java/org/apache/commons/logging/security/SecurityAllowedTestCase.java b/src/test/java/org/apache/commons/logging/security/SecurityAllowedTestCase.java index 4cdd442..7b156a6 100644 --- a/src/test/java/org/apache/commons/logging/security/SecurityAllowedTestCase.java +++ b/src/test/java/org/apache/commons/logging/security/SecurityAllowedTestCase.java @@ -43,8 +43,6 @@ import org.apache.commons.logging.PathableTestSuite; */ public class SecurityAllowedTestCase extends TestCase { - private SecurityManager oldSecMgr; - // Dummy special hashtable, so we can tell JCL to use this instead of // the standard one. public static class CustomHashtable extends Hashtable { @@ -69,6 +67,8 @@ public class SecurityAllowedTestCase extends TestCase return new PathableTestSuite(testClass, parent); } + private SecurityManager oldSecMgr; + @Override public void setUp() { // save security manager so it can be restored in tearDown diff --git a/src/test/java/org/apache/commons/logging/security/SecurityForbiddenTestCase.java b/src/test/java/org/apache/commons/logging/security/SecurityForbiddenTestCase.java index 17525ec..7a8957e 100644 --- a/src/test/java/org/apache/commons/logging/security/SecurityForbiddenTestCase.java +++ b/src/test/java/org/apache/commons/logging/security/SecurityForbiddenTestCase.java @@ -48,9 +48,6 @@ import org.apache.commons.logging.PathableTestSuite; */ public class SecurityForbiddenTestCase extends TestCase { - private SecurityManager oldSecMgr; - private ClassLoader otherClassLoader; - // Dummy special hashtable, so we can tell JCL to use this instead of // the standard one. public static class CustomHashtable extends Hashtable { @@ -60,7 +57,6 @@ public class SecurityForbiddenTestCase extends TestCase */ private static final long serialVersionUID = 7224652794746236024L; } - /** * Return the tests included in this test suite. */ @@ -75,6 +71,27 @@ public class SecurityForbiddenTestCase extends TestCase return new PathableTestSuite(testClass, parent); } + private SecurityManager oldSecMgr; + + private ClassLoader otherClassLoader; + + /** + * Loads a class with the given classloader. + */ + private Object loadClass(final String name, final ClassLoader classLoader) { + try { + final Class clazz = classLoader.loadClass(name); + final Object obj = clazz.getConstructor().newInstance(); + return obj; + } catch ( final Exception e ) { + final StringWriter sw = new StringWriter(); + final PrintWriter pw = new PrintWriter(sw); + e.printStackTrace(pw); + fail("Unexpected exception:" + e.getMessage() + ":" + sw.toString()); + } + return null; + } + @Override public void setUp() { // save security manager so it can be restored in tearDown @@ -175,21 +192,4 @@ public class SecurityForbiddenTestCase extends TestCase fail("Unexpected exception:" + t.getMessage() + ":" + sw.toString()); } } - - /** - * Loads a class with the given classloader. - */ - private Object loadClass(final String name, final ClassLoader classLoader) { - try { - final Class clazz = classLoader.loadClass(name); - final Object obj = clazz.getConstructor().newInstance(); - return obj; - } catch ( final Exception e ) { - final StringWriter sw = new StringWriter(); - final PrintWriter pw = new PrintWriter(sw); - e.printStackTrace(pw); - fail("Unexpected exception:" + e.getMessage() + ":" + sw.toString()); - } - return null; - } } diff --git a/src/test/java/org/apache/commons/logging/simple/CustomConfigTestCase.java b/src/test/java/org/apache/commons/logging/simple/CustomConfigTestCase.java index f5f71b2..21aee0c 100644 --- a/src/test/java/org/apache/commons/logging/simple/CustomConfigTestCase.java +++ b/src/test/java/org/apache/commons/logging/simple/CustomConfigTestCase.java @@ -37,52 +37,6 @@ import org.apache.commons.logging.impl.SimpleLog; */ public class CustomConfigTestCase extends DefaultConfigTestCase { - /** - *

The expected log records.

- */ - protected List expected; - - /** - *

The message levels that should have been logged.

- */ - /* - protected Level[] testLevels = - { Level.FINE, Level.INFO, Level.WARNING, Level.SEVERE, Level.SEVERE }; - */ - - /** - *

The message strings that should have been logged.

- */ - protected String[] testMessages = - { "debug", "info", "warn", "error", "fatal" }; - - /** - * Set system properties that will control the LogFactory/Log objects - * when they are created. Subclasses can override this method to - * define properties that suit them. - */ - @Override - public void setProperties() { - System.setProperty( - "org.apache.commons.logging.Log", - "org.apache.commons.logging.simple.DecoratedSimpleLog"); - System.setProperty( - "org.apache.commons.logging.simplelog.defaultlog", - "debug"); - } - - /** - * Set up instance variables required by this test case. - */ - @Override - public void setUp() throws Exception { - LogFactory.releaseAll(); - setProperties(); - expected = new ArrayList(); - setUpFactory(); - setUpLog("DecoratedLogger"); - } - /** * Return the tests included in this test suite. *

@@ -108,37 +62,23 @@ public class CustomConfigTestCase extends DefaultConfigTestCase { } /** - * Tear down instance variables required by this test case. + *

The message levels that should have been logged.

*/ - @Override - public void tearDown() { - super.tearDown(); - expected = null; - } + /* + protected Level[] testLevels = + { Level.FINE, Level.INFO, Level.WARNING, Level.SEVERE, Level.SEVERE }; + */ - // Test logging message strings with exceptions - public void testExceptionMessages() throws Exception { - ((DecoratedSimpleLog) log).clearCache(); - logExceptionMessages(); - checkExpected(); - } + /** + *

The expected log records.

+ */ + protected List expected; - // Test logging plain message strings - public void testPlainMessages() throws Exception { - ((DecoratedSimpleLog) log).clearCache(); - logPlainMessages(); - checkExpected(); - } - - // Test Serializability of standard instance - @Override - public void testSerializable() throws Exception { - ((DecoratedSimpleLog) log).clearCache(); - logPlainMessages(); - super.testSerializable(); - logExceptionMessages(); - checkExpected(); - } + /** + *

The message strings that should have been logged.

+ */ + protected String[] testMessages = + { "debug", "info", "warn", "error", "fatal" }; // Check the decorated log instance @Override @@ -169,19 +109,12 @@ public class CustomConfigTestCase extends DefaultConfigTestCase { } - /** Hook for subclassses */ - protected void checkShowDateTime() { - assertFalse(((DecoratedSimpleLog) log).getShowDateTime()); - } - /** Hook for subclasses */ protected void checkDecoratedDateTime() { assertEquals("yyyy/MM/dd HH:mm:ss:SSS zzz", ((DecoratedSimpleLog) log).getDateTimeFormat()); } - - // Check the actual log records against the expected ones protected void checkExpected() { final List acts = ((DecoratedSimpleLog) log).getCache(); @@ -196,6 +129,10 @@ public class CustomConfigTestCase extends DefaultConfigTestCase { } } + /** Hook for subclassses */ + protected void checkShowDateTime() { + assertFalse(((DecoratedSimpleLog) log).getShowDateTime()); + } // Check the standard log instance @Override @@ -203,7 +140,6 @@ public class CustomConfigTestCase extends DefaultConfigTestCase { checkDecorated(); } - // Log the messages with exceptions protected void logExceptionMessages() { // Generate log records @@ -223,7 +159,6 @@ public class CustomConfigTestCase extends DefaultConfigTestCase { expected.add(new LogRecord(SimpleLog.LOG_LEVEL_FATAL, "fatal", t)); } - // Log the plain messages protected void logPlainMessages() { // Generate log records @@ -241,4 +176,69 @@ public class CustomConfigTestCase extends DefaultConfigTestCase { expected.add(new LogRecord(SimpleLog.LOG_LEVEL_ERROR, "error", null)); expected.add(new LogRecord(SimpleLog.LOG_LEVEL_FATAL, "fatal", null)); } + + /** + * Set system properties that will control the LogFactory/Log objects + * when they are created. Subclasses can override this method to + * define properties that suit them. + */ + @Override + public void setProperties() { + System.setProperty( + "org.apache.commons.logging.Log", + "org.apache.commons.logging.simple.DecoratedSimpleLog"); + System.setProperty( + "org.apache.commons.logging.simplelog.defaultlog", + "debug"); + } + + /** + * Set up instance variables required by this test case. + */ + @Override + public void setUp() throws Exception { + LogFactory.releaseAll(); + setProperties(); + expected = new ArrayList(); + setUpFactory(); + setUpLog("DecoratedLogger"); + } + + + + /** + * Tear down instance variables required by this test case. + */ + @Override + public void tearDown() { + super.tearDown(); + expected = null; + } + + + // Test logging message strings with exceptions + public void testExceptionMessages() throws Exception { + ((DecoratedSimpleLog) log).clearCache(); + logExceptionMessages(); + checkExpected(); + } + + + // Test logging plain message strings + public void testPlainMessages() throws Exception { + ((DecoratedSimpleLog) log).clearCache(); + logPlainMessages(); + checkExpected(); + } + + + // Test Serializability of standard instance + @Override + public void testSerializable() throws Exception { + ((DecoratedSimpleLog) log).clearCache(); + logPlainMessages(); + super.testSerializable(); + logExceptionMessages(); + checkExpected(); + } } diff --git a/src/test/java/org/apache/commons/logging/simple/DateTimeCustomConfigTestCase.java b/src/test/java/org/apache/commons/logging/simple/DateTimeCustomConfigTestCase.java index 2344ad4..7e9de24 100644 --- a/src/test/java/org/apache/commons/logging/simple/DateTimeCustomConfigTestCase.java +++ b/src/test/java/org/apache/commons/logging/simple/DateTimeCustomConfigTestCase.java @@ -59,6 +59,28 @@ public class DateTimeCustomConfigTestCase extends CustomConfigTestCase { } + /** Checks that the date time format has been successfully set */ + @Override + protected void checkDecoratedDateTime() { + assertEquals("Expected date format to be set", "dd.mm.yyyy", + ((DecoratedSimpleLog) log).getDateTimeFormat()); + // try the formatter + final Date now = new Date(); + final DateFormat formatter = ((DecoratedSimpleLog) log).getDateTimeFormatter(); + final SimpleDateFormat sampleFormatter = new SimpleDateFormat("dd.mm.yyyy"); + assertEquals("Date should be formatters to pattern dd.mm.yyyy", + sampleFormatter.format(now), formatter.format(now)); + } + + /** Hook for subclassses */ + @Override + protected void checkShowDateTime() { + assertTrue(((DecoratedSimpleLog) log).getShowDateTime()); + } + + + // ----------------------------------------------------------- Methods + /** * Set up system properties required by this unit test. Here, we * set up the props defined in the parent class setProperties method, @@ -84,26 +106,4 @@ public class DateTimeCustomConfigTestCase extends CustomConfigTestCase { super.setUp(); } - - // ----------------------------------------------------------- Methods - - /** Checks that the date time format has been successfully set */ - @Override - protected void checkDecoratedDateTime() { - assertEquals("Expected date format to be set", "dd.mm.yyyy", - ((DecoratedSimpleLog) log).getDateTimeFormat()); - // try the formatter - final Date now = new Date(); - final DateFormat formatter = ((DecoratedSimpleLog) log).getDateTimeFormatter(); - final SimpleDateFormat sampleFormatter = new SimpleDateFormat("dd.mm.yyyy"); - assertEquals("Date should be formatters to pattern dd.mm.yyyy", - sampleFormatter.format(now), formatter.format(now)); - } - - /** Hook for subclassses */ - @Override - protected void checkShowDateTime() { - assertTrue(((DecoratedSimpleLog) log).getShowDateTime()); - } - } diff --git a/src/test/java/org/apache/commons/logging/simple/DecoratedSimpleLog.java b/src/test/java/org/apache/commons/logging/simple/DecoratedSimpleLog.java index 6de9df5..8877e63 100644 --- a/src/test/java/org/apache/commons/logging/simple/DecoratedSimpleLog.java +++ b/src/test/java/org/apache/commons/logging/simple/DecoratedSimpleLog.java @@ -40,58 +40,17 @@ public class DecoratedSimpleLog extends SimpleLog { */ private static final long serialVersionUID = 196544280770017153L; - public DecoratedSimpleLog(final String name) { - super(name); - } + // Cache of logged records + protected ArrayList cache = new ArrayList(); // ------------------------------------------------------------- Properties - public DateFormat getDateTimeFormatter() { - return dateFormatter; + public DecoratedSimpleLog(final String name) { + super(name); } - public String getDateTimeFormat() { - return dateTimeFormat; - } - - - public String getLogName() { - return logName; - } - - - public boolean getShowDateTime() { - return showDateTime; - } - - - public boolean getShowShortName() { - return showShortName; - } - - - // ------------------------------------------------------- Protected Methods - - - // Cache logged messages - @Override - protected void log(final int type, final Object message, final Throwable t) { - - super.log(type, message, t); - cache.add(new LogRecord(type, message, t)); - - } - - - // ---------------------------------------------------------- Public Methods - - - // Cache of logged records - protected ArrayList cache = new ArrayList(); - - // Clear cache public void clearCache() { cache.clear(); @@ -104,4 +63,45 @@ public class DecoratedSimpleLog extends SimpleLog { } + public String getDateTimeFormat() { + return dateTimeFormat; + } + + + public DateFormat getDateTimeFormatter() { + return dateFormatter; + } + + + // ------------------------------------------------------- Protected Methods + + + public String getLogName() { + return logName; + } + + + // ---------------------------------------------------------- Public Methods + + + public boolean getShowDateTime() { + return showDateTime; + } + + + public boolean getShowShortName() { + return showShortName; + } + + + // Cache logged messages + @Override + protected void log(final int type, final Object message, final Throwable t) { + + super.log(type, message, t); + cache.add(new LogRecord(type, message, t)); + + } + + } diff --git a/src/test/java/org/apache/commons/logging/simple/DefaultConfigTestCase.java b/src/test/java/org/apache/commons/logging/simple/DefaultConfigTestCase.java index 1e3fade..d2d887d 100644 --- a/src/test/java/org/apache/commons/logging/simple/DefaultConfigTestCase.java +++ b/src/test/java/org/apache/commons/logging/simple/DefaultConfigTestCase.java @@ -40,16 +40,6 @@ import org.apache.commons.logging.impl.SimpleLog; public class DefaultConfigTestCase extends TestCase { - /** - *

The {@link LogFactory} implementation we have selected.

- */ - protected LogFactory factory; - - /** - *

The {@link Log} implementation we have selected.

- */ - protected Log log; - /** * Return the tests included in this test suite. *

@@ -75,78 +65,14 @@ public class DefaultConfigTestCase extends TestCase { } /** - * Set system properties that will control the LogFactory/Log objects - * when they are created. Subclasses can override this method to - * define properties that suit them. + *

The {@link LogFactory} implementation we have selected.

*/ - public void setProperties() { - System.setProperty( - "org.apache.commons.logging.Log", - "org.apache.commons.logging.impl.SimpleLog"); - } + protected LogFactory factory; /** - * Set up instance variables required by this test case. + *

The {@link Log} implementation we have selected.

*/ - @Override - public void setUp() throws Exception { - LogFactory.releaseAll(); - setProperties(); - setUpFactory(); - setUpLog("TestLogger"); - } - - /** - * Tear down instance variables required by this test case. - */ - @Override - public void tearDown() { - log = null; - factory = null; - LogFactory.releaseAll(); - } - - // Test pristine DecoratedSimpleLog instance - public void testPristineDecorated() { - setUpDecorated("DecoratedLogger"); - checkDecorated(); - } - - // Test pristine Log instance - public void testPristineLog() { - checkStandard(); - } - - // Test pristine LogFactory instance - public void testPristineFactory() { - assertNotNull("LogFactory exists", factory); - assertEquals("LogFactory class", - "org.apache.commons.logging.impl.LogFactoryImpl", - factory.getClass().getName()); - - final String[] names = factory.getAttributeNames(); - assertNotNull("Names exists", names); - assertEquals("Names empty", 0, names.length); - } - - // Test Serializability of standard instance - public void testSerializable() throws Exception { - - // Serialize and deserialize the instance - final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - final ObjectOutputStream oos = new ObjectOutputStream(baos); - oos.writeObject(log); - oos.close(); - final ByteArrayInputStream bais = - new ByteArrayInputStream(baos.toByteArray()); - final ObjectInputStream ois = new ObjectInputStream(bais); - log = (Log) ois.readObject(); - ois.close(); - - // Check the characteristics of the resulting object - checkStandard(); - - } + protected Log log; // Check the decorated log instance protected void checkDecorated() { @@ -175,7 +101,6 @@ public class DefaultConfigTestCase extends TestCase { assertTrue(((DecoratedSimpleLog) log).getShowShortName()); } - // Check the standard log instance protected void checkStandard() { assertNotNull("Log exists", log); @@ -195,22 +120,97 @@ public class DefaultConfigTestCase extends TestCase { assertEquals(SimpleLog.LOG_LEVEL_INFO, ((SimpleLog) log).getLevel()); } + /** + * Set system properties that will control the LogFactory/Log objects + * when they are created. Subclasses can override this method to + * define properties that suit them. + */ + public void setProperties() { + System.setProperty( + "org.apache.commons.logging.Log", + "org.apache.commons.logging.impl.SimpleLog"); + } + + /** + * Set up instance variables required by this test case. + */ + @Override + public void setUp() throws Exception { + LogFactory.releaseAll(); + setProperties(); + setUpFactory(); + setUpLog("TestLogger"); + } // Set up decorated log instance protected void setUpDecorated(final String name) { log = new DecoratedSimpleLog(name); } - // Set up factory instance protected void setUpFactory() throws Exception { factory = LogFactory.getFactory(); } - // Set up log instance protected void setUpLog(final String name) throws Exception { log = LogFactory.getLog(name); } + /** + * Tear down instance variables required by this test case. + */ + @Override + public void tearDown() { + log = null; + factory = null; + LogFactory.releaseAll(); + } + + + // Test pristine DecoratedSimpleLog instance + public void testPristineDecorated() { + setUpDecorated("DecoratedLogger"); + checkDecorated(); + } + + + // Test pristine LogFactory instance + public void testPristineFactory() { + assertNotNull("LogFactory exists", factory); + assertEquals("LogFactory class", + "org.apache.commons.logging.impl.LogFactoryImpl", + factory.getClass().getName()); + + final String[] names = factory.getAttributeNames(); + assertNotNull("Names exists", names); + assertEquals("Names empty", 0, names.length); + } + + + // Test pristine Log instance + public void testPristineLog() { + checkStandard(); + } + + + // Test Serializability of standard instance + public void testSerializable() throws Exception { + + // Serialize and deserialize the instance + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + final ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(log); + oos.close(); + final ByteArrayInputStream bais = + new ByteArrayInputStream(baos.toByteArray()); + final ObjectInputStream ois = new ObjectInputStream(bais); + log = (Log) ois.readObject(); + ois.close(); + + // Check the characteristics of the resulting object + checkStandard(); + + } + } diff --git a/src/test/java/org/apache/commons/logging/simple/LogRecord.java b/src/test/java/org/apache/commons/logging/simple/LogRecord.java index 2eb5371..5099080 100644 --- a/src/test/java/org/apache/commons/logging/simple/LogRecord.java +++ b/src/test/java/org/apache/commons/logging/simple/LogRecord.java @@ -30,14 +30,14 @@ public class LogRecord implements Serializable { */ private static final long serialVersionUID = -5254831759209770665L; + public int type; + + public Object message; + public Throwable t; public LogRecord(final int type, final Object message, final Throwable t) { this.type = type; this.message = message; this.t = t; } - public int type; - public Object message; - public Throwable t; - } diff --git a/src/test/java/org/apache/commons/logging/tccl/custom/MyLog.java b/src/test/java/org/apache/commons/logging/tccl/custom/MyLog.java index 7dce5e1..38b3cc9 100644 --- a/src/test/java/org/apache/commons/logging/tccl/custom/MyLog.java +++ b/src/test/java/org/apache/commons/logging/tccl/custom/MyLog.java @@ -22,6 +22,23 @@ public class MyLog implements Log { public MyLog(final String category) {} + @Override + public void debug(final Object message) {} + @Override + public void debug(final Object message, final Throwable t) {} + @Override + public void error(final Object message) {} + @Override + public void error(final Object message, final Throwable t) {} + @Override + public void fatal(final Object message) {} + @Override + public void fatal(final Object message, final Throwable t) {} + + @Override + public void info(final Object message) {} + @Override + public void info(final Object message, final Throwable t) {} @Override public boolean isDebugEnabled() { return false; } @Override @@ -34,29 +51,12 @@ public class MyLog implements Log { public boolean isTraceEnabled() { return false; } @Override public boolean isWarnEnabled() { return false; } - @Override public void trace(final Object message) {} @Override public void trace(final Object message, final Throwable t) {} @Override - public void debug(final Object message) {} - @Override - public void debug(final Object message, final Throwable t) {} - @Override - public void info(final Object message) {} - @Override - public void info(final Object message, final Throwable t) {} - @Override public void warn(final Object message) {} @Override public void warn(final Object message, final Throwable t) {} - @Override - public void error(final Object message) {} - @Override - public void error(final Object message, final Throwable t) {} - @Override - public void fatal(final Object message) {} - @Override - public void fatal(final Object message, final Throwable t) {} }