[libcommons-logging-java] 57/66: New upstream version 1.2

Emmanuel Bourg ebourg-guest at moszumanska.debian.org
Fri Jan 12 10:57:44 UTC 2018


This is an automated email from the git hooks/post-receive script.

ebourg-guest pushed a commit to branch master
in repository libcommons-logging-java.

commit fb43508e123017a4306ce500efbd0789ff9734cb
Author: Emmanuel Bourg <ebourg at apache.org>
Date:   Fri Jan 12 11:07:53 2018 +0100

    New upstream version 1.2
---
 LICENSE.txt                                        |  202 +++
 NOTICE.txt                                         |    6 +
 PROPOSAL.html                                      |  121 ++
 README.txt                                         |   50 +
 RELEASE-NOTES.txt                                  |   31 +
 build-testing.xml                                  |  350 ++++
 build.properties.sample                            |   59 +
 build.xml                                          |  776 +++++++++
 checkstyle.xml                                     |   69 +
 license-header.txt                                 |   16 +
 pmd.xml                                            |   26 +
 pom.xml                                            |  547 +++++++
 src/changes/changes.xml                            |  112 ++
 src/changes/release-notes.vm                       |  109 ++
 src/conf/MANIFEST.MF                               |   28 +
 src/main/assembly/bin.xml                          |   47 +
 src/main/assembly/src.xml                          |   52 +
 src/main/java/org/apache/commons/logging/Log.java  |  216 +++
 .../commons/logging/LogConfigurationException.java |   80 +
 .../org/apache/commons/logging/LogFactory.java     | 1703 ++++++++++++++++++++
 .../java/org/apache/commons/logging/LogSource.java |  219 +++
 .../apache/commons/logging/impl/AvalonLogger.java  |  298 ++++
 .../logging/impl/Jdk13LumberjackLogger.java        |  302 ++++
 .../apache/commons/logging/impl/Jdk14Logger.java   |  273 ++++
 .../apache/commons/logging/impl/Log4JLogger.java   |  312 ++++
 .../commons/logging/impl/LogFactoryImpl.java       | 1393 ++++++++++++++++
 .../apache/commons/logging/impl/LogKitLogger.java  |  269 ++++
 .../org/apache/commons/logging/impl/NoOpLog.java   |  104 ++
 .../logging/impl/ServletContextCleaner.java        |  136 ++
 .../org/apache/commons/logging/impl/SimpleLog.java |  649 ++++++++
 .../apache/commons/logging/impl/WeakHashtable.java |  482 ++++++
 .../org/apache/commons/logging/impl/package.html   |   22 +
 .../java/org/apache/commons/logging/package.html   |  255 +++
 src/main/java/overview.html                        |   35 +
 src/media/logo.png                                 |  Bin 0 -> 12258 bytes
 src/media/logo.xcf                                 |  Bin 0 -> 21491 bytes
 src/site/resources/images/logo.png                 |  Bin 0 -> 12258 bytes
 src/site/site.xml                                  |   64 +
 src/site/xdoc/building.xml                         |   74 +
 src/site/xdoc/download_logging.xml                 |  138 ++
 src/site/xdoc/guide.xml                            |  850 ++++++++++
 src/site/xdoc/index.xml                            |  144 ++
 src/site/xdoc/issue-tracking.xml                   |  102 ++
 src/site/xdoc/junit-report.xml                     |   39 +
 src/site/xdoc/mail-lists.xml                       |  202 +++
 src/site/xdoc/proposal.xml                         |  127 ++
 src/site/xdoc/tech.xml                             |  653 ++++++++
 src/site/xdoc/troubleshooting.xml                  |  467 ++++++
 .../apache/commons/logging/AbstractLogTest.java    |   94 ++
 .../org/apache/commons/logging/AltHashtable.java   |   37 +
 .../commons/logging/AltHashtableTestCase.java      |   93 ++
 .../logging/BadHashtablePropertyTestCase.java      |   35 +
 .../commons/logging/BasicOperationsTestCase.java   |  119 ++
 .../org/apache/commons/logging/DummyException.java |   28 +
 .../org/apache/commons/logging/LoadTestCase.java   |  225 +++
 .../org/apache/commons/logging/LogTestCase.java    |   31 +
 .../commons/logging/NullClassLoaderTestCase.java   |   41 +
 .../commons/logging/PathableClassLoader.java       |  436 +++++
 .../apache/commons/logging/PathableTestSuite.java  |  147 ++
 .../apache/commons/logging/SimpleLogTestCase.java  |   27 +
 .../java/org/apache/commons/logging/UserClass.java |   41 +
 .../logging/avalon/AvalonLoggerTestCase.java       |   44 +
 .../config/FirstPriorityConfigTestCase.java        |  125 ++
 .../logging/config/PriorityConfigTestCase.java     |  130 ++
 .../logging/impl/WeakHashtableTestCase.java        |  313 ++++
 .../logging/jdk14/CustomConfigAPITestCase.java     |   61 +
 .../logging/jdk14/CustomConfigFullTestCase.java    |   63 +
 .../logging/jdk14/CustomConfigTestCase.java        |  395 +++++
 .../logging/jdk14/DefaultConfigTestCase.java       |  191 +++
 .../apache/commons/logging/jdk14/TestHandler.java  |   70 +
 .../commons/logging/log4j/StandardTests.java       |  216 +++
 .../log4j12/ApiClasspathStandardTestCase.java      |   52 +
 .../log4j12/AppClasspathStandardTestCase.java      |   47 +
 .../log4j12/ChildClasspathStandardTestCase.java    |   50 +
 .../log4j/log4j12/Log4j12StandardTests.java        |   42 +
 .../log4j12/ParentClasspathStandardTestCase.java   |   49 +
 .../logging/log4j/log4j12/TestAppender.java        |   82 +
 .../commons/logging/logkit/StandardTestCase.java   |  166 ++
 .../commons/logging/noop/NoOpLogTestCase.java      |  103 ++
 .../logging/pathable/ChildFirstTestCase.java       |  317 ++++
 .../commons/logging/pathable/GeneralTestCase.java  |  120 ++
 .../logging/pathable/ParentFirstTestCase.java      |  308 ++++
 .../commons/logging/security/DummyClass.java       |   21 +
 .../logging/security/MockSecurityManager.java      |  149 ++
 .../logging/security/SecurityAllowedTestCase.java  |  140 ++
 .../security/SecurityForbiddenTestCase.java        |  191 +++
 .../logging/servlet/BasicServletTestCase.java      |   72 +
 .../logging/simple/CustomConfigTestCase.java       |  277 ++++
 .../simple/DateTimeCustomConfigTestCase.java       |  105 ++
 .../commons/logging/simple/DecoratedSimpleLog.java |  106 ++
 .../logging/simple/DefaultConfigTestCase.java      |  249 +++
 .../apache/commons/logging/simple/LogRecord.java   |   43 +
 .../commons/logging/tccl/BadTCCLTestCase.java      |   49 +
 .../commons/logging/tccl/NullTCCLTestCase.java     |   46 +
 .../apache/commons/logging/tccl/custom/MyLog.java  |   44 +
 .../logging/tccl/custom/MyLogFactoryImpl.java      |   32 +
 .../logging/tccl/log/TcclDisabledTestCase.java     |  160 ++
 .../logging/tccl/log/TcclEnabledTestCase.java      |  155 ++
 .../tccl/logfactory/TcclDisabledTestCase.java      |  156 ++
 .../tccl/logfactory/TcclEnabledTestCase.java       |  147 ++
 .../config/nopriority/commons-logging.properties   |   18 +
 .../config/priority10/commons-logging.properties   |   19 +
 .../config/priority20/commons-logging.properties   |   19 +
 .../config/priority20a/commons-logging.properties  |   19 +
 .../commons/logging/jdk14/CustomConfig.properties  |   25 +
 .../props_disable_tccl/commons-logging.properties  |   20 +
 .../props_enable_tccl/commons-logging.properties   |   19 +
 .../props_disable_tccl/commons-logging.properties  |   19 +
 .../props_enable_tccl/commons-logging.properties   |   18 +
 109 files changed, 18825 insertions(+)

diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/NOTICE.txt b/NOTICE.txt
new file mode 100644
index 0000000..556bd03
--- /dev/null
+++ b/NOTICE.txt
@@ -0,0 +1,6 @@
+Apache Commons Logging
+Copyright 2003-2014 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
diff --git a/PROPOSAL.html b/PROPOSAL.html
new file mode 100644
index 0000000..45e40d3
--- /dev/null
+++ b/PROPOSAL.html
@@ -0,0 +1,121 @@
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+ 
+      http://www.apache.org/licenses/LICENSE-2.0
+ 
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+
+<html>
+<head>
+<title>Proposal for Logging Library Package</title>
+</head>
+<body bgcolor="white">
+
+<div align="center">
+<h1>Proposal for <em>Logging</em> Package</h1>
+</div>
+
+<h3>(0) Rationale</h3>
+
+<p>There is a great need for debugging and logging information inside of
+Commons components such as HTTPClient and dbcp.  However, there are many
+logging APIs out there and it is difficult to choose among them.
+</p>
+
+<p>The Logging package will be an ultra-thin bridge between different logging
+libraries.  Commons components may use the Logging JAR to remove
+compile-time/runtime dependencies on any particular logging package,
+and contributors may write Log implementations for the library of their choice.
+</p>
+
+<h3>(1) Scope of the Package</h3>
+
+<p>The package shall create and maintain a package that provides extremely
+basic logging functionality and bridges to other, more sophisticated logging
+implementations.
+</p>
+
+<p>
+The package should :
+<ul>
+<li>Have an API which should be as simple to use as possible</li>
+<li>Provide support for log4j</li>
+<li>Provide pluggable support for other logging APIs</li>
+</ul>
+</p>
+
+<p>
+Non-goals:
+<ul>
+<li>This package will not perform logging itself, except at the most basic
+    level.</li>
+<li>We do not seek to become a "standard" API.</li>
+</ul>
+</p>
+
+<h3>(1.5) Interaction With Other Packages</h3>
+
+<p><em>Logging</em> relies on:
+</p>
+
+<ul>
+  <li>Java Development Kit (Version 1.1 or later)
+  </li>
+  <li>Avalon Framework (compile-time dependency only unless this Log
+      implementation is selected at runtime)
+  <li>Avalon LogKit (compile-time dependency only unless this Log
+      implementation is selected at runtime)
+  <li>JDK 1.4 (compile-time dependency only unless this log implementation
+      is selected at runtime).
+  <li>Log4J (compile-time dependency only unless this Log
+      implementation is selected at runtime)</li>
+  <li><a href="http://sourceforge.net/projects/lumberjack/">Lumberjack</a>
+      (compile-time dependency only unless this Log
+      implementation is selected at runtime)</li>
+</ul>
+
+<h3>(2) Required Jakarta-Commons Resources</h3>
+
+<ul>
+<li>CVS Repository - New directory <code>logging</code> in the 
+<code>jakarta-commons</code> CVS repository.</li>
+
+<li>Initial Committers - The list is provided below. </li>
+
+<li>Mailing List - Discussions will take place on the general
+<em>dev at commons.apache.org</em> mailing list. To help list
+subscribers identify messages of interest, it is suggested that the
+message subject of messages about this component be prefixed with
+[Logging].</li>
+
+<li>Bugzilla - New component "Logging" under the "Commons" product
+category, with appropriate version identifiers as needed.</li>
+
+<li>Jyve FAQ - New category "commons-logging" (when available).</li>
+</ul>
+
+
+<h3>(4) Initial Committers</h3>
+
+<p>The initial committers on the Logging component shall be:</p>
+
+<ul>
+  <li>Morgan Delagrange</li>
+  <li>Rodney Waldhoff</li>
+  <li>Craig McClanahan</li>
+</ul>
+
+</body>
+</html>
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..9394f1f
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,50 @@
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+
+Logging
+-------
+
+Getting started:
+
+1) Build the jar file
+
+  If you have the source distribution you will need to build the jar file
+  using Maven 2/3.  For instructions on downloading and installing Maven see
+  http://maven.apache.org/.
+
+  To build execute the command 'mvn package verify'.
+  The jar file will be built in the target directory.
+  
+  Note: the unit tests are executed during the verify phase and require that
+  the respective jar files have been created successfully.
+
+2) Generate the documentation
+
+  Run the 'mvn verify site' command.  The documentation will be written
+  to the target/site directory.  The documentation has some examples of
+  how to use this package as well as a troubleshooting guide.
+
+3) Create source and binary distributions
+
+  Run the 'mvn verify site assembly:assembly' command.  The source and binary
+  distributions are created in the 'target' directory.
+
+4) Use
+
+  Simply include the jar file built in step #1 in your classpath.  Import the
+  classes that you want to use and you are ready to go!
+
diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
new file mode 100644
index 0000000..6376134
--- /dev/null
+++ b/RELEASE-NOTES.txt
@@ -0,0 +1,31 @@
+              Apache Commons Logging
+                  Version 1.2
+                  RELEASE NOTES
+
+The Apache Commons Logging team is pleased to announce
+the release of Apache Commons Logging 1.2
+
+Apache Commons Logging is a thin adapter allowing configurable
+bridging to other, well known logging systems.
+
+This is a maintenance release containing bug fixes.
+Java 1.2 or later is required.
+
+Changes in this version include:
+
+Fixed Bugs:
+o LOGGING-37:   Improve performance of LogFactory#getFactory() by calling
+                Thread#currentThread()#getContextClassLoader() directly instead
+                of using reflection. As a consequence support for JDK 1.1 has
+                been dropped. Thanks to Matthias Ernst, Archie Cobbs.
+o LOGGING-156:  Fix SecurityAllowedTestCase when executed with OpenJDK 1.7 due
+                to an additional required RuntimePermission. Thanks to Mikolaj Izdebski.
+o LOGGING-157:  Fix javadoc to comply with javadoc tool from jdk 1.8. Thanks to Ville Skyttä.
+
+
+Historical list of changes: http://commons.apache.org/proper/commons-logging/changes-report.html
+
+For complete information on Apache Commons Logging, including instructions on how to submit bug reports,
+patches, or suggestions for improvement, see the Apache Commons Logging website:
+
+http://commons.apache.org/proper/commons-logging/
\ No newline at end of file
diff --git a/build-testing.xml b/build-testing.xml
new file mode 100644
index 0000000..e06112c
--- /dev/null
+++ b/build-testing.xml
@@ -0,0 +1,350 @@
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+ 
+      http://www.apache.org/licenses/LICENSE-2.0
+ 
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+
+<!--
+  - Ant file for running tests for commons-logging.
+  -
+  - Quick build instructions:
+  -  * mvn package
+  -  * ant getlibs
+  -  * cp build.properties.sample build.properties
+  -  * Depending on which platform you are on, do either
+  -      set JAVA_COMPILER=NONE
+  -    or
+  -      setenv JAVA_COMPILER NONE
+  -  * ant -lib lib/junit-3.8.1.jar -f build-testing.xml test
+  -
+  - Note that we have to run Ant without the JIT compiler to get around bugs in
+  - the 1.2 JVM. That's why we need to set JAVA_COMPILER to NONE.
+  - See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4240622
+  -
+  - Note that this build file uses the optional <junit> task. While the
+  - task "adapter" class comes by default with ant 1.6+, the junit libraries
+  - (3.8.1 or later) are also required to be made available to ant. This
+  - requires that you do one of the following:
+  -  * create directory ~/.ant/lib and place the junit jar there
+  -  * put the junit jar in $ANT_HOME/lib
+  -  * run ant as "ant -lib path-to-junit-jar"
+  -  * put the junit jar in $CLASSPATH
+  -
+  - Note when running these test before a JCL release it is strongly
+  - recommended that a 1.2 JVM is used.
+  -
+  - $Id: build-testing.xml 558565 2007-07-22 23:58:45Z dennisl $
+  -->
+
+<project name="Logging" default="all" basedir=".">
+
+
+<!-- ========== Initialize Properties ===================================== -->
+
+
+  <property file="build.properties"/>                <!-- Component local   -->
+  <property file="../build.properties"/>             <!-- Commons local     -->
+  <property file="${user.home}/build.properties"/>   <!-- User local        -->
+
+
+<!-- ========== External Dependencies ===================================== -->
+
+
+  <!-- The directories corresponding to your necessary dependencies -->
+  <property name="junit.home"              value="/usr/local/junit3.5"/>
+  <property name="jakarta.home"            value="../.."/>
+
+  <!--
+    - The names of the unit tests to run. By default all tests are run, but
+    - this can be overridden from the command line by something like:
+    -   ant -Dtestmatch=**/FooTestCase test
+    -->
+  <property name="testmatch" value="**/*TestCase"/>
+
+
+<!-- ========== Derived Values ============================================ -->
+
+
+  <!-- The locations of necessary jar files -->
+  <property name="junit.jar"               value="junit-3.8.1.jar"/>
+  <property name="log4j12.jar"             value="log4j-1.2.12.jar"/>
+  <property name="log4j13.jar"             value="log4j-1.3.0.jar"/>
+  <property name="logkit.jar"              value="logkit-1.0.1.jar"/>
+  <property name="avalon-framework.jar"    value="avalon-framework-4.1.3.jar"/>
+  <property name="servletapi.jar"          value="servletapi-2.3.jar"/>
+
+
+<!-- ========== Component Declarations ==================================== -->
+
+
+  <!-- The name of this component -->
+  <property name="component.name"          value="logging"/>
+
+  <!-- The primary package name of this component -->
+  <property name="component.package"       value="org.apache.commons.logging"/>
+
+  <!-- The title of this component -->
+  <property name="component.title"         value="Logging Wrapper Library"/>
+
+  <!-- The current version number of this component -->
+  <property name="component.version"       value="1.1.1-SNAPSHOT"/>
+
+  <!-- The base directory for compilation targets -->
+  <property name="build.home"              value="${basedir}/target"/>
+
+  <!-- The base directory for component configuration files -->
+  <property name="conf.home"               value="src/conf"/>
+
+  <!-- jar names -->
+  <property name="core.jar.name" value="commons-${component.name}-${component.version}.jar"/>
+  <property name="api.jar.name" value="commons-${component.name}-api-${component.version}.jar"/>
+  <property name="adapters.jar.name" value="commons-${component.name}-adapters-${component.version}.jar"/>
+  <property name="src.ide.name" value="commons-${component.name}-${component.version}-ide.zip"/>
+
+  <!-- Construct compile classpath -->
+  <path id="compile.classpath">
+    <pathelement location="${build.home}/classes"/>
+    <pathelement location="${junit.jar}"/>
+    <pathelement location="${logkit.jar}"/>
+    <pathelement location="${avalon-framework.jar}"/>
+    <pathelement location="${servletapi.jar}"/>
+  </path>
+
+
+<!-- ========== Test Execution Defaults =================================== -->
+
+
+  <!-- 
+    - Construct unit test classpath (generic tests).
+    -
+    - Note that unit tests that use the PathableTestSuite approach don't need
+    - any of this (except junit). However unit tests that don't use PathableTestSuite
+    - to configure their classpath will need the basic classes to be provided
+    - via this mechanism.
+    -->
+  <path id="test.classpath">
+    <pathelement location="${build.home}/classes"/>
+    <pathelement location="${build.home}/test-classes"/>
+    <pathelement location="${junit.jar}"/>
+    <pathelement location="${logkit.jar}"/>
+    <pathelement location="${avalon-framework.jar}"/>
+    <pathelement location="${conf.home}"/>
+    <pathelement location="${servletapi.jar}"/>
+  </path>
+
+  <!-- Construct unit test classpath (Log4J tests) -->
+  <path id="test.classpath.log4j13">
+    <pathelement location="${build.home}/classes"/>
+    <pathelement location="${build.home}/test-classes"/>
+    <pathelement location="${junit.jar}"/>
+    <pathelement location="${log4j13.jar}"/>
+  </path>
+
+  <!-- Construct unit test classpath (Log4J tests) -->
+  <path id="test.classpath.log4j12">
+    <pathelement location="${build.home}/classes"/>
+    <pathelement location="${build.home}/test-classes"/>
+    <pathelement location="${junit.jar}"/>
+    <pathelement location="${log4j12.jar}"/>
+  </path>
+
+  <!-- Should all tests fail if one does? -->
+  <property name="test.failonerror"        value="true"/>
+
+  <!-- The test runner to execute -->
+  <property name="test.runner" value="junit.textui.TestRunner"/>
+
+  <!-- libs to pass to the tests -->
+  <property name="test.sysprops.testclasses" value="${build.home}/test-classes"/>
+  <property name="test.sysprops.log4j12" value="${log4j12.jar}"/>
+  <property name="test.sysprops.log4j13" value="${log4j13.jar}"/>
+  <property name="test.sysprops.logkit" value="${logkit.jar}"/>
+  <property name="test.sysprops.servlet-api" value="${servletapi.jar}"/>
+  <property name="test.sysprops.commons-logging" value="${build.home}/${core.jar.name}"/>
+  <property name="test.sysprops.commons-logging-api" value="${build.home}/${api.jar.name}"/>
+  <property name="test.sysprops.commons-logging-adapters" value="${build.home}/${adapters.jar.name}"/>
+  <propertyset id="test-lib-props">
+    <propertyref prefix="test.sysprops."/>
+    <mapper type="glob" from="test.sysprops.*" to="*"/>
+  </propertyset>
+
+<!-- ========== Executable Targets ======================================== -->
+
+
+  <!--
+    - Running this target will download all the necessary dependencies into the "lib" subdirectory.
+    -->
+  <property name="getlibs.base" value="http://repo1.maven.org/maven"/>
+  <target name="getlibs">
+    <mkdir dir="lib"/>
+    <get dest="lib/junit-3.8.1.jar" src="${getlibs.base}/junit/jars/junit-3.8.1.jar"/>
+    <get dest="lib/logkit-1.0.1.jar" src="${getlibs.base}/logkit/jars/logkit-1.0.1.jar"/>
+    <get dest="lib/avalon-framework-4.1.3.jar" src="${getlibs.base}/avalon-framework/jars/avalon-framework-4.1.3.jar"/>
+    <get dest="lib/log4j-1.2.12.jar" src="${getlibs.base}/log4j/jars/log4j-1.2.12.jar"/>
+    <get dest="lib/servletapi-2.3.jar" src="${getlibs.base}/servletapi/jars/servletapi-2.3.jar"/>
+  </target>
+
+  <target name="init"
+   description="Initialize and evaluate conditionals">
+    <echo message="-------- ${component.title} ${component.version} --------"/>
+    <filter  token="name"                  value="${component.name}"/>
+    <filter  token="package"               value="${component.package}"/>
+    <filter  token="version"               value="${component.version}"/>
+  </target>  
+  
+  <target name="prepare" depends="init"
+   description="Prepare build directory">
+
+    <echo>
+    Log4j12: ${log4j12.jar}
+    <!-- Note: log4j13 support is not available in the 1.1 release. -->
+    <!--Log4j13: ${log4j13.jar}-->
+    LogKit: ${logkit.jar}
+    Avalon-Framework: ${avalon-framework.jar}
+    </echo>
+
+    <mkdir dir="${build.home}"/>
+    <mkdir dir="${build.home}/classes"/>
+    <mkdir dir="${build.home}/conf"/>
+    <mkdir dir="${build.home}/tests"/>
+  </target>
+
+
+  <target name='discovery' depends='init'>
+    <available property="jdk.1.4.present"
+               classname="java.util.logging.Logger"/>
+
+    <available property="logkit.present"
+               classpathref="compile.classpath"
+               classname="org.apache.log.Logger"/>
+
+    <available property="avalon-framework.present"
+               classpathref="compile.classpath"
+               classname="org.apache.avalon.framework.logger.Logger"/>
+
+    <available file="${log4j12.jar}" property="log4j12.present"/>
+    <available file="${log4j13.jar}" property="log4j13.present"/>
+    <available file="${build.home}/docs" property="maven.generated.docs.present"/>
+  </target>
+
+  <target name="log4j12-test-warning" unless='log4j12.jar' depends='init,discovery'>
+    <echo>
+    *** WARNING ***
+    Log4J 1.2.x Jar not found: Cannot execute 1.2.x tests
+    </echo>
+  </target>
+    
+  <target name="show-lib-presence">
+    <echo  message="jdk.1.4.present=${jdk.1.4.present}"/>
+    <echo  message="log4j12.present=${log4j12.present}"/>
+    <!-- Note: log4j13 support is not available in the 1.1 release. -->
+    <!--<echo  message="log4j13.present=${log4j13.present}"/>-->
+    <echo  message="logkit.present=${logkit.present}"/>
+    <echo  message="avalon-framework.present=${avalon-framework.present}"/>
+  </target>
+
+
+  <target name="all" depends="test"
+   description="Test all components"/>
+
+
+<!-- ========== Unit Test Targets ========================================= -->
+
+
+  <!--
+    - Target to run all unit tests.
+    -
+    - The batchtest task auto-detects what tests are available without
+    - any need to define TestSuite objects in the code to compose
+    - sets of tests to be run.
+    -
+    - Details of the unit test results for each TestCase will appear in
+    - a file in directory ${build.home}/test-reports, together with any
+    - output to stdout or stderr generated by the test code.
+    -
+    - If you're having problems running this target due to the use of
+    - the "junit" task below, see the comments at the head of this file.
+    - 
+    - Individual tests (or subsets of tests) can be run by doing
+    -   ant -Dtestmatch=**/FooTestCase testall
+    -->
+
+  <target name="test" depends="log4j12-test-warning"
+   description="Run all unit tests">
+    <echo message="Test output can be found in directory ${build.home}/test-reports."/>
+    <delete dir="${build.home}/test-reports"/>
+    <mkdir dir="${build.home}/test-reports"/>
+
+      <echo message="executing tests [${testmatch}.java]"/>
+    <!--
+      - Note that the fork/forkmode settings define default behaviour for tests. 
+      - The <test> and <batchtest> tags can override these settings if needed.
+      - The default settings cause a single VM to be created in which all of
+      - the tests are then run. 
+      -->
+    <junit printsummary="off" showoutput="no" fork="yes" forkmode="once" failureproperty="test.failure">
+      <!-- plain output to file; brief output to console. -->
+      <formatter type="plain"/>
+      <formatter usefile="false" type="brief"/>
+
+      <!-- 
+        - Provide a set of properties pointing to the logging libs for
+        - the use of the PathableClassLoader class used by some unit tests.
+        -->
+      <syspropertyset refid="test-lib-props"/>
+      <classpath refid="test.classpath"/>
+
+      <!--
+        - Uncomment this to enable logging diagnostics for tests
+        - <jvmarg value="-Dorg.apache.commons.logging.diagnostics.dest=STDERR"/>
+        -->
+
+      <!--
+        - Auto-detect the tests to run. Checking the ${build.home}/tests
+        - directory for .class files rather than the src/test directory
+        - for .java files means that when we run the tests on platforms
+        - where some components (eg jdk14 logging) is not available, 
+        - just ensuring the tests are skipped from the compile will
+        - also cause them to be skipped from the testing.
+        -
+        - This does introduce the danger that if tests accidentally
+        - fail to compile then we won't notice it here. However that
+        - should have been reported earlier anyway.
+        -->
+      <batchtest todir="${build.home}/test-reports">
+        <fileset dir="${build.home}/test-classes">
+          <include name="${testmatch}.class"/>
+          <!--
+            - Exclude the jdk14 tests because we are running these tests on
+            - a jvm < 1.4
+            -->
+          <exclude name="org/apache/commons/logging/jdk14/*"/>
+          <!--
+            - Exclude the security tests because they rely on the
+            - MockSecurityManager that uses code that was first introduced in
+            - Java 1.4
+            -->
+          <exclude name="org/apache/commons/logging/security/*"/>
+        </fileset>
+      </batchtest>
+    </junit>
+
+    <fail if="test.failure">
+      One or more unit tests failed.
+    </fail>
+  </target>
+
+</project>
diff --git a/build.properties.sample b/build.properties.sample
new file mode 100644
index 0000000..a9f1afc
--- /dev/null
+++ b/build.properties.sample
@@ -0,0 +1,59 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+# 
+#      http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+######################################################################
+#
+# TO USE:
+# 
+# Copy this file to build.properties and either
+#  
+#    a) Use 'ant getlibs' to populate the default directory 
+#       with dependencies
+# or b) Change the property values to appropriate values
+#
+########################################################################
+
+# Apache Log4j 1.2.x series
+log4j12.jar=lib/log4j-1.2.12.jar
+
+# Apache Log4j 1.3.x series
+# Note: Log4j 1.3 support not available in the 1.1 release
+#log4j13.jar=lib/log4j-1.3.0.jar
+
+# logkit.jar - Avalon LogKit classes (see http://jakarta.apache.org/avalon)
+logkit.jar=lib/logkit-1.0.1.jar
+
+# Avalon framework - used for wrapper for avalon framework logger
+avalon-framework.jar=lib/avalon-framework-4.1.3.jar
+
+# ServletApi - used to build ServletContextCleaner class
+servletapi.jar=lib/servletapi-2.3.jar
+
+#
+# if you want to run the test cases, junit needs to be in the classpath.
+# the build.xml uses a default value so you might not need to set this property.
+# Note that version junit 3.8 is required and 3.8.1 recommended.
+# 
+junit.jar=lib/junit-3.8.1.jar
+
+# Maven properties (for web site build)
+# Those committers using agents may like to use
+#maven.username=rdonkin
+#logging.cvs=lserver:rdonkin at cvs.apache.org:/home/cvs
+
+
+# The path to a 1.4 JSDK javac
+# Optional - used when building with a 1.2 JVM for releases
+# executable.javac1.4=/opt/java/jdks/j2sdk1.4.2_10/bin/javac
diff --git a/build.xml b/build.xml
new file mode 100644
index 0000000..f7a5923
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,776 @@
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+ 
+      http://www.apache.org/licenses/LICENSE-2.0
+ 
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+
+<!--
+  - Ant build file for commons-logging.
+  -
+  - Quick build instructions:
+  -  * ant getlibs
+  -  * cp build.properties.sample build.properties
+  -  * ant -lib lib/junit-3.8.1.jar dist 
+  -
+  - Note that this build file uses the optional <junit> task. While the
+  - task "adapter" class comes by default with ant 1.6+, the junit libraries
+  - (3.8.1 or later) are also required to be made available to ant. This
+  - requires that you do one of the following:
+  -  * create directory ~/.ant/lib and place the junit jar there
+  -  * put the junit jar in $ANT_HOME/lib
+  -  * run ant as "ant -lib path-to-junit-jar"
+  -  * put the junit jar in $CLASSPATH
+  -
+  - Note when building JCL for release it is strongly recommended that a 1.2 JVM
+  - is used for the main compile and the home.jdk4 property used to specify
+  - the path to a 1.4 J2SDK. This will be used to compile those classes
+  - which require the 1.4 API. 
+  -
+  - $Id: build.xml 1608092 2014-07-05 18:11:22Z tn $
+  -->
+
+<project name="Logging" default="all" basedir=".">
+
+
+<!-- ========== Initialize Properties ===================================== -->
+
+
+  <property file="build.properties"/>                <!-- Component local   -->
+  <property file="../build.properties"/>             <!-- Commons local     -->
+  <property file="${user.home}/build.properties"/>   <!-- User local        -->
+
+
+<!-- ========== External Dependencies ===================================== -->
+
+
+  <!-- The directories corresponding to your necessary dependencies -->
+  <property name="junit.home"              value="/usr/local/junit3.5"/>
+  <property name="jakarta.home"            value="../.."/>
+  <property name="commons.home"            value="../.."/>
+
+  <!--
+    - The names of the unit tests to run. By default all tests are run, but
+    - this can be overridden from the command line by something like:
+    -   ant -Dtestmatch=**/FooTestCase test
+    -->
+  <property name="testmatch" value="**/*TestCase"/>
+
+
+<!-- ========== Derived Values ============================================ -->
+
+
+  <!-- The locations of necessary jar files -->
+  <property name="junit.jar"               value="junit-3.8.1.jar"/>
+  <property name="log4j12.jar"             value="log4j-1.2.17.jar"/>
+  <property name="log4j13.jar"             value="log4j-1.3.0.jar"/>
+  <property name="logkit.jar"              value="logkit-1.0.1.jar"/>
+  <property name="avalon-framework.jar"    value="avalon-framework-4.1.5.jar"/>
+  <property name="servletapi.jar"          value="servletapi-2.3.jar"/>
+
+
+<!-- ========== Component Declarations ==================================== -->
+
+
+  <!-- The name of this component -->
+  <property name="component.name"          value="logging"/>
+
+  <!-- The primary package name of this component -->
+  <property name="component.package"       value="org.apache.commons.logging"/>
+
+  <!-- The title of this component -->
+  <property name="component.title"         value="Logging Wrapper Library"/>
+
+  <!-- The current version number of this component -->
+  <property name="component.version"       value="1.2"/>
+
+  <!-- The base directory for compilation targets -->
+  <property name="build.home"              value="${basedir}/target"/>
+
+  <!-- The base directory for component configuration files -->
+  <property name="conf.home"               value="src/conf"/>
+
+  <!-- The base directory for distribution targets -->
+  <property name="dist.home"               value="dist"/>
+
+  <!-- The base directory for releases  -->
+  <property name="artifacts.home"          value="artifacts"/>
+
+  <!-- The base directory for component sources -->
+  <property name="source.home"             value="src/main/java"/>
+
+  <!-- The base directory for unit test sources -->
+  <property name="test.home"               value="src/test/java"/>
+
+  <!-- The base directory for unit test resources -->
+  <property name="test.resources"          value="src/test/resources"/>
+
+  <!-- jar names -->
+  <property name="core.jar.name" value="commons-${component.name}-${component.version}.jar"/>
+  <property name="api.jar.name" value="commons-${component.name}-api-${component.version}.jar"/>
+  <property name="adapters.jar.name" value="commons-${component.name}-adapters-${component.version}.jar"/>
+  <property name="src.ide.name" value="commons-${component.name}-${component.version}-ide.zip"/>
+
+  <!-- dist names -->
+  <property name="windows.dist.name" value="commons-${component.name}-${component.version}.zip"/>
+  <property name="nix.dist.name" value="commons-${component.name}-${component.version}.tar.gz"/>
+  
+<!-- ========== Compiler Defaults ========================================= -->
+
+  <!-- Version of java class files to generate. -->
+  <property name="target.version" value="1.2"/>
+
+  <!-- Version of java source to accept -->
+  <property name="source.version" value="1.2"/>
+
+  <!-- Should Java compilations set the 'debug' compiler option? -->
+  <property name="compile.debug"           value="true"/>
+
+  <!-- Should Java compilations set the 'deprecation' compiler option? -->
+  <property name="compile.deprecation"     value="false"/>
+
+  <!-- Should Java compilations set the 'optimize' compiler option? -->
+  <property name="compile.optimize"        value="false"/>
+
+  <!-- Construct compile classpath -->
+  <path id="compile.classpath">
+    <pathelement location="${build.home}/classes"/>
+    <pathelement location="${junit.jar}"/>
+    <pathelement location="${logkit.jar}"/>
+    <pathelement location="${avalon-framework.jar}"/>
+    <pathelement location="${servletapi.jar}"/>
+  </path>
+
+
+<!-- ========== Test Execution Defaults =================================== -->
+
+
+  <!-- 
+    - Construct unit test classpath (generic tests).
+    -
+    - Note that unit tests that use the PathableTestSuite approach don't need
+    - any of this (except junit). However unit tests that don't use PathableTestSuite
+    - to configure their classpath will need the basic classes to be provided
+    - via this mechanism.
+    -->
+  <path id="test.classpath">
+    <pathelement location="${build.home}/classes"/>
+    <pathelement location="${build.home}/tests"/>
+    <pathelement location="${junit.jar}"/>
+    <pathelement location="${logkit.jar}"/>
+    <pathelement location="${avalon-framework.jar}"/>
+    <pathelement location="${conf.home}"/>
+    <pathelement location="${servletapi.jar}"/>
+  </path>
+
+  <!-- Construct unit test classpath (Log4J tests) -->
+  <path id="test.classpath.log4j13">
+    <pathelement location="${build.home}/classes"/>
+    <pathelement location="${build.home}/tests"/>
+    <pathelement location="${junit.jar}"/>
+    <pathelement location="${log4j13.jar}"/>
+  </path>
+
+  <!-- Construct unit test classpath (Log4J tests) -->
+  <path id="test.classpath.log4j12">
+    <pathelement location="${build.home}/classes"/>
+    <pathelement location="${build.home}/tests"/>
+    <pathelement location="${junit.jar}"/>
+    <pathelement location="${log4j12.jar}"/>
+  </path>
+
+  <!-- Construct javadoc classpath -->
+  <path id="javadoc.classpath">
+    <path refid="compile.classpath"/>
+    <pathelement location="${log4j12.jar}"/>
+  </path>
+
+  <!-- Should all tests fail if one does? -->
+  <property name="test.failonerror"        value="true"/>
+
+  <!-- The test runner to execute -->
+  <property name="test.runner" value="junit.textui.TestRunner"/>
+
+  <!-- libs to pass to the tests -->
+  <property name="test.sysprops.testclasses" value="${build.home}/tests"/>
+  <property name="test.sysprops.log4j12" value="${log4j12.jar}"/>
+  <property name="test.sysprops.log4j13" value="${log4j13.jar}"/>
+  <property name="test.sysprops.logkit" value="${logkit.jar}"/>
+  <property name="test.sysprops.servlet-api" value="${servletapi.jar}"/>
+  <property name="test.sysprops.commons-logging" value="${build.home}/${core.jar.name}"/>
+  <property name="test.sysprops.commons-logging-api" value="${build.home}/${api.jar.name}"/>
+  <property name="test.sysprops.commons-logging-adapters" value="${build.home}/${adapters.jar.name}"/>
+  <propertyset id="test-lib-props">
+    <propertyref prefix="test.sysprops."/>
+    <mapper type="glob" from="test.sysprops.*" to="*"/>
+  </propertyset>
+
+<!-- ========== Executable Targets ======================================== -->
+
+
+  <!--
+    - Running this target will download all the necessary dependencies into the "lib" subdirectory.
+    -->
+  <property name="getlibs.base" value="http://repo1.maven.org/maven"/>
+  <target name="getlibs">
+    <mkdir dir="lib"/>
+    <get dest="lib/junit-3.8.1.jar" src="${getlibs.base}/junit/jars/junit-3.8.1.jar"/>
+    <get dest="lib/logkit-1.0.1.jar" src="${getlibs.base}/logkit/jars/logkit-1.0.1.jar"/>
+    <get dest="lib/avalon-framework-4.1.3.jar" src="${getlibs.base}/avalon-framework/jars/avalon-framework-4.1.3.jar"/>
+    <get dest="lib/log4j-1.2.12.jar" src="${getlibs.base}/log4j/jars/log4j-1.2.12.jar"/>
+    <get dest="lib/servletapi-2.3.jar" src="${getlibs.base}/servletapi/jars/servletapi-2.3.jar"/>
+  </target>
+
+  <target name="init"
+   description="Initialize and evaluate conditionals">
+    <echo message="-------- ${component.title} ${component.version} --------"/>
+    <filter  token="name"                  value="${component.name}"/>
+    <filter  token="package"               value="${component.package}"/>
+    <filter  token="version"               value="${component.version}"/>
+  </target>  
+  
+  <target name="prepare" depends="init"
+   description="Prepare build directory">
+
+    <echo>
+    Log4j12: ${log4j12.jar}
+    <!-- Note: log4j13 support is not available in the 1.1 release. -->
+    <!--Log4j13: ${log4j13.jar}-->
+    LogKit: ${logkit.jar}
+    Avalon-Framework: ${avalon-framework.jar}
+    </echo>
+
+    <mkdir dir="${build.home}"/>
+    <mkdir dir="${build.home}/classes"/>
+    <mkdir dir="${build.home}/conf"/>
+    <mkdir dir="${build.home}/tests"/>
+  </target>
+
+
+  <target name="static" depends="prepare"
+   description="Copy static files to build directory">
+    <tstamp/>
+    <copy  todir="${build.home}/conf" filtering="on">
+      <fileset dir="${conf.home}" includes="*.MF"/>
+      <fileset dir="${conf.home}" includes="*.properties"/>
+    </copy>
+  </target>
+
+  <target name="compile" depends="static,compile-only"
+          description="Compile shareable components"/>
+
+  <target name='discovery' depends='init'>
+    <available property="jdk.1.4.present"
+               classname="java.util.logging.Logger"/>
+
+    <available property="logkit.present"
+               classpathref="compile.classpath"
+               classname="org.apache.log.Logger"/>
+
+    <available property="avalon-framework.present"
+               classpathref="compile.classpath"
+               classname="org.apache.avalon.framework.logger.Logger"/>
+
+    <available file="${log4j12.jar}" property="log4j12.present"/>
+    <available file="${log4j13.jar}" property="log4j13.present"/>
+    <available file="${build.home}/docs" property="maven.generated.docs.present"/>
+  </target>
+
+  <target name="log4j12-warning" unless='log4j12.present' depends='init,discovery'>
+    <echo>
+    *** WARNING ***
+    Log4j 1.2 not found: Cannot Build Log4JLogger
+    </echo>
+  </target>
+  
+  <target name="log4j13-warning" unless='log4j13.present' depends='init,discovery'>
+    <!--
+      - Note: log4j13 support is not available in the 1.1 release.
+      - If we add it in a future release, the code below should be uncommented.
+      -->
+    <!--
+    <echo>
+    *** WARNING ***
+    Log4j 1.3 not found: Cannot Build Log4J13Logger
+    </echo>
+    -->
+  </target>
+  
+  <target name="logkit-warning" unless='logkit.present' depends='init,discovery'>
+    <echo>
+    *** WARNING ***
+    LogKit not found: Cannot Build LogKitLogger
+    </echo>
+  </target>
+  
+  <target name="avalon-framework-warning" unless='avalon-framework.present' depends='init,discovery'>
+    <echo>
+    *** WARNING ***
+    Avalon-Framework not found: Cannot Build AvalonLogger
+    </echo>
+  </target>
+  
+  <target name="jdk1.4-warning" unless='jdk.1.4.present' depends='init,discovery'>
+    <echo>
+    *** WARNING ***
+    JDK 1.4 not present: Cannot Build Jdk14Logger
+    </echo>
+  </target>
+  
+  <target name="log4j12-test-warning" unless='log4j12.jar' depends='init,discovery'>
+    <echo>
+    *** WARNING ***
+    Log4J 1.2.x Jar not found: Cannot execute 1.2.x tests
+    </echo>
+  </target>
+    
+  <target name='warning' 
+    depends='log4j12-warning,log4j13-warning,logkit-warning,jdk1.4-warning,avalon-framework-warning,compile-1.4'/>
+
+  <target name="compile-only" 
+    depends="prepare,discovery,warning,show-lib-presence,compile-non-log4j,compile-log4j12,compile-log4j13,build-jar"/>
+
+  <target name="show-lib-presence">
+    <echo  message="jdk.1.4.present=${jdk.1.4.present}"/>
+    <echo  message="log4j12.present=${log4j12.present}"/>
+    <!-- Note: log4j13 support is not available in the 1.1 release. -->
+    <!--<echo  message="log4j13.present=${log4j13.present}"/>-->
+    <echo  message="logkit.present=${logkit.present}"/>
+    <echo  message="avalon-framework.present=${avalon-framework.present}"/>
+  </target>
+
+  <target name="compile-non-log4j" depends="prepare,discovery">
+    <!-- compile everything except Log4J classes -->
+    <javac srcdir="${source.home}"
+           destdir="${build.home}/classes"
+           debug="${compile.debug}"
+           deprecation="${compile.deprecation}"
+           optimize="${compile.optimize}"
+           source="${source.version}"
+           target="${target.version}">
+
+      <classpath refid="compile.classpath"/>
+    
+      <exclude name="org/apache/commons/logging/impl/Log4J*.java"/>
+
+      <exclude name="org/apache/commons/logging/impl/Jdk13LumberjackLogger.java"
+               unless="jdk.1.4.present"/>
+      <exclude name="org/apache/commons/logging/impl/Jdk14Logger.java"
+               unless="jdk.1.4.present"/>
+      <exclude name="org/apache/commons/logging/impl/LogKitLogger.java"
+               unless="logkit.present"/>
+      <exclude name="org/apache/commons/logging/impl/AvalonLogger.java"
+               unless="avalon-framework.present"/>
+    </javac>
+  </target>
+
+  <target name="compile-1.4" depends="prepare,discovery,compile-non-log4j" if='executable.javac1.4'>
+    <!-- 
+      - Compiles those classes which require a 1.4+ JSDK.
+      - This target will only be executed when ant is running a pre-1.4 JVM
+      - and the home.jdk4 property is set.
+      - This configuration is typically used to create a release only.
+      -->
+    <echo  message=""/>
+    <echo  message="************************************************************"/>
+    <echo  message="    Compiling 1.4 only classes using compiler@${executable.javac1.4}"/>
+    <echo  message="************************************************************"/>
+    <echo  message=""/>
+    <javac srcdir="${source.home}"
+           destdir="${build.home}/classes"
+           debug="${compile.debug}"
+           deprecation="${compile.deprecation}"
+           optimize="${compile.optimize}"
+           source="${source.version}"
+           target="${target.version}"
+           compiler='javac1.4'
+           fork='yes'
+           executable='${executable.javac1.4}'>
+
+      <classpath refid="compile.classpath"/>
+    
+      <include name="org/apache/commons/logging/impl/Jdk13LumberjackLogger.java"
+               unless="jdk.1.4.present"/>
+      <include name="org/apache/commons/logging/impl/Jdk14Logger.java"
+               unless="jdk.1.4.present"/>
+    </javac>
+  </target>
+
+  <target name="compile-log4j12" depends="prepare,discovery">
+    <!-- compile the log4j1.2 support classes -->
+    <javac srcdir="${source.home}"
+           destdir="${build.home}/classes"
+           debug="${compile.debug}"
+           deprecation="${compile.deprecation}"
+           optimize="${compile.optimize}"
+           source="${source.version}"
+           target="${target.version}">
+
+      <classpath refid="compile.classpath"/>
+      <classpath>
+        <!--
+        <pathelement refid="compile.classpath"/>
+        <classpath refid="compile.classpath"/>
+        -->
+        <pathelement location="${log4j12.jar}"/> 
+      </classpath>
+
+      <include name="org/apache/commons/logging/impl/Log4JLogger.java"
+               if="log4j12.present"/>
+    </javac>
+  </target>
+
+  <target name="compile-log4j13" depends="prepare,discovery">
+    <!-- compile the log4j1.3 support classes -->
+    <javac srcdir="${source.home}"
+           destdir="${build.home}/classes"
+           debug="${compile.debug}"
+           deprecation="${compile.deprecation}"
+           optimize="${compile.optimize}"
+           source="${source.version}"
+           target="${target.version}">
+
+      <classpath refid="compile.classpath"/>
+      <classpath>
+        <pathelement location="${log4j13.jar}"/> 
+      </classpath>
+
+    <!--
+      - Note: log4j13 support not available in 1.1 release. However if we do add it
+      - in a future release, this entry will pick it up. In the meantime, this
+      - simply compiles no classes.
+      -->
+      <include name="org/apache/commons/logging/impl/Log4J13Logger.java"
+               if="log4j13.present"/>
+    </javac>
+  </target>
+
+  <target name="build-jar">
+    <copy  todir="${build.home}/classes" filtering="on">
+      <fileset dir="${source.home}" excludes="**/*.java"/>
+    </copy>
+    <mkdir      dir="${build.home}/classes/META-INF"/>
+    <copy      file="LICENSE.txt"
+              todir="${build.home}/classes/META-INF"/>
+    <copy      file="NOTICE.txt"
+              todir="${build.home}/classes/META-INF"/>
+
+    <jar jarfile="${build.home}/${core.jar.name}"
+         basedir="${build.home}/classes"
+         manifest="${build.home}/conf/MANIFEST.MF">
+      <include name="org/apache/commons/logging/**" />
+      <include name="META-INF/LICENSE.txt"/>
+      <include name="META-INF/NOTICE.txt"/>
+      <exclude name="**/package.html"/>
+    </jar>
+
+    <jar jarfile="${build.home}/${api.jar.name}"
+         basedir="${build.home}/classes"
+         manifest="${build.home}/conf/MANIFEST.MF">
+      <include name="org/apache/commons/logging/*.class" />
+      <include name="org/apache/commons/logging/impl/LogFactoryImpl*.class" />
+      <include name="org/apache/commons/logging/impl/WeakHashtable*.class" />
+      <include name="org/apache/commons/logging/impl/SimpleLog*.class" />
+      <include name="org/apache/commons/logging/impl/NoOpLog*.class" />
+      <include name="org/apache/commons/logging/impl/Jdk14Logger.class" />
+      <include name="META-INF/LICENSE.txt"/>
+      <include name="META-INF/NOTICE.txt"/>
+      <exclude name="**/package.html"/>
+    </jar>
+
+    <jar jarfile="${build.home}/${adapters.jar.name}"
+         basedir="${build.home}/classes"
+         manifest="${build.home}/conf/MANIFEST.MF">
+      <include name="org/apache/commons/logging/impl/**.class" />
+      <include name="META-INF/LICENSE.txt"/>
+      <include name="META-INF/NOTICE.txt"/>
+      <exclude name="org/apache/commons/logging/impl/WeakHashtable*.class" />
+      <exclude name="org/apache/commons/logging/impl/LogFactoryImpl*.class" />
+    </jar>
+  </target>
+
+  <target name='compile.jdk1.4.tests' if='jdk.1.4.present'>
+    <javac  srcdir="${test.home}"
+           destdir="${build.home}/tests"
+             debug="${compile.debug}"
+       deprecation="${compile.deprecation}"
+          optimize="${compile.optimize}"
+            source="${source.version}"
+            target="${target.version}">
+      <classpath refid="test.classpath"/>
+        <include name='**/jdk14/**'/>
+    </javac>
+  </target>
+
+  <target name='compile.log4j.tests' if='log4j12.present'>
+    <javac  srcdir="${test.home}"
+           destdir="${build.home}/tests"
+             debug="${compile.debug}"
+       deprecation="${compile.deprecation}"
+          optimize="${compile.optimize}"
+            source="${source.version}"
+            target="${target.version}">
+      <classpath refid="test.classpath.log4j12"/>
+        <include name='**/log4j/**'/>
+        <!-- NOTE -->
+        <!--
+        Pathable tests do not reference the Log4J Logger directly
+        but try to load them by reflection from particular loaders.
+        They will therefore fail unless this logger is available.
+        -->
+        <include name='**/pathable/**'/>
+        <!-- END NOTE -->
+    </javac>
+  </target>
+
+
+  <target name='compile.avalon.tests' if='avalon-framework.present'>
+    <javac  srcdir="${test.home}"
+           destdir="${build.home}/tests"
+             debug="${compile.debug}"
+       deprecation="${compile.deprecation}"
+          optimize="${compile.optimize}"
+            source="${source.version}"
+            target="${target.version}">
+      <classpath refid="test.classpath"/>
+      <include name='**/avalon/**'/>
+    </javac>
+  </target>
+  
+  <target name='compile.logkit.tests' if='logkit.present'>
+    <javac  srcdir="${test.home}"
+           destdir="${build.home}/tests"
+             debug="${compile.debug}"
+       deprecation="${compile.deprecation}"
+          optimize="${compile.optimize}"
+            source="${source.version}"
+            target="${target.version}">
+      <classpath refid="test.classpath"/>
+      <include name='**/logkit/**'/>
+    </javac>
+  </target>
+
+  <target name="compile.tests" depends="compile"
+   description="Compile unit test cases">
+    <javac  srcdir="${test.home}"
+           destdir="${build.home}/tests"
+             debug="${compile.debug}"
+       deprecation="${compile.deprecation}"
+          optimize="${compile.optimize}"
+            source="${source.version}"
+            target="${target.version}">
+      <classpath refid="test.classpath"/>
+      <exclude name='**/jdk14/**'/>
+      <exclude name='**/log4j/**'/>
+      <!-- NOTE -->
+      <!--
+      Pathable tests do not reference the Log4J Logger directly
+      but try to load them by reflection from particular loaders.
+      They will therefore fail unless this logger is available.
+      -->
+      <exclude name='**/pathable/**'/>
+      <!-- END NOTE -->
+      <exclude name='**/avalon/**'/>
+      <exclude name='**/logkit/**'/>
+    </javac>
+    <antcall target='compile.log4j.tests'/>
+    <antcall target='compile.jdk1.4.tests'/>
+    <antcall target='compile.avalon.tests'/>
+    <antcall target='compile.logkit.tests'/>
+    <copy    todir="${build.home}/tests" filtering="on">
+      <fileset dir="${test.resources}" excludes="**/*.java"/>
+    </copy>
+    <jar jarfile="${build.home}/commons-${component.name}-tests.jar"
+         basedir="${build.home}/tests"
+        manifest="${build.home}/conf/MANIFEST.MF">
+      <exclude name="org/apache/commons/logging/Wrapper.class"/>
+      <exclude name="org/apache/commons/logging/jdk14/TestHandler.class"
+                 if="jdk.1.4.present"/>
+    </jar>
+
+  </target>
+
+
+  <target name="clean" description="Clean build and distribution directories">
+    <mkdir dir='${build.home}'/>
+    <delete includeemptydirs='yes'>
+      <fileset dir="${build.home}" excludes='docs/**/*'/>
+    </delete>
+    <delete    dir="${dist.home}"/>
+    <delete    dir="${artifacts.home}"/>
+  </target>
+
+
+  <target name="all" depends="clean,compile,test"
+   description="Clean and compile all components"/>
+
+
+  <target name="maven-docs-warning" unless="maven.generated.docs.present" depends="discovery">
+    <echo>
+    *** WARNING ***
+    Maven generated documentation not found: Documentation distribution will be empty
+    </echo>
+  </target>
+  
+
+  <target name="dist" depends="all,maven-docs-warning"
+     description="Create binary distribution">
+  
+    <!--
+      - Maven is used to generate the documentation.
+      - However, we cannot assume that it has been run.
+      - So, create the appropriate directories.
+      -->
+    <mkdir dir='${build.home}'/>
+    <mkdir dir='${build.home}/docs'/>
+    <mkdir dir='${build.home}/docs/apidocs'/>
+  
+    <!--
+      - Create a dist directory to hold all the files that go into a distribution.
+      - Copy the needed files from the build directory to the dist directory.
+      -->
+    <mkdir dir="${dist.home}"/>
+    <copy todir="${dist.home}">
+      <fileset dir=".">
+        <include name="LICENSE.txt"/>
+        <include name="NOTICE.txt"/>
+        <include name="RELEASE-NOTES.txt"/>
+      </fileset>
+      <fileset dir="${build.home}">
+        <include name="${core.jar.name}"/>
+        <include name="${api.jar.name}"/>
+        <include name="${adapters.jar.name}"/>
+      </fileset>
+    </copy>
+  
+    <!-- Copy documentation generated by maven -->
+    <mkdir dir="${dist.home}/docs"/>
+    <copy todir="${dist.home}/docs">
+      <fileset dir="${build.home}/docs"/>
+    </copy>
+  
+    <!--
+      - And copy the source too; we don't have separate source and binary distributions
+      - for logging; the source is so small there's little point.
+      -->
+    <mkdir dir="${dist.home}/src"/>
+    <copy todir="${dist.home}/src" filtering="on">
+      <fileset dir="${source.home}"/>
+    </copy>
+    <zip destfile='${dist.home}/${src.ide.name}'>
+      <zipfileset dir='${dist.home}/src'/>
+      <zipfileset dir='${dist.home}/docs/apidocs'/>
+      <zipfileset dir='${dist.home}' prefix='META-INF'>
+          <include name="LICENSE.txt"/>
+          <include name="NOTICE.txt"/>
+      </zipfileset>
+    </zip>
+  
+    <!-- Create release artifacts in the artifacts directory -->
+    <mkdir dir="${artifacts.home}"/>
+    <fixcrlf srcdir='${dist.home}' eol='dos' includes='**/*.txt,**/*.java,**/*.html'/>
+    <zip destfile='${artifacts.home}/${windows.dist.name}'>
+      <zipfileset dir='${dist.home}' prefix='commons-${component.name}-${component.version}'/>
+    </zip>
+    <fixcrlf srcdir='${dist.home}' eol='unix' includes='**/*.txt,**/*.java,**/*.html'/>
+    <tar compression="gzip" destfile='${artifacts.home}/${nix.dist.name}' longfile='gnu'>
+      <tarfileset dir='${dist.home}' prefix='commons-${component.name}-${component.version}'/>
+    </tar>
+    <copy todir="${artifacts.home}">
+      <fileset dir="${build.home}">
+        <include name="${core.jar.name}"/>
+        <include name="${api.jar.name}"/>
+        <include name="${adapters.jar.name}"/>
+      </fileset>
+    </copy>
+  </target>
+
+
+<!-- ========== Unit Test Targets ========================================= -->
+
+
+  <!--
+    - Target to run all unit tests.
+    -
+    - The batchtest task auto-detects what tests are available without
+    - any need to define TestSuite objects in the code to compose
+    - sets of tests to be run.
+    -
+    - Details of the unit test results for each TestCase will appear in
+    - a file in directory ${build.home}/test-reports, together with any
+    - output to stdout or stderr generated by the test code.
+    -
+    - If you're having problems running this target due to the use of
+    - the "junit" task below, see the comments at the head of this file.
+    - 
+    - Individual tests (or subsets of tests) can be run by doing
+    -   ant -Dtestmatch=**/FooTestCase testall
+    -->
+
+  <target name="test" depends="log4j12-test-warning, compile.tests"
+   description="Run all unit tests">
+    <echo message="Test output can be found in directory ${build.home}/test-reports."/>
+    <delete dir="${build.home}/test-reports"/>
+    <mkdir dir="${build.home}/test-reports"/>
+
+    <echo message="executing tests [${testmatch}.java]"/>
+    <!--
+      - Note that the fork/forkmode settings define default behaviour for tests. 
+      - The <test> and <batchtest> tags can override these settings if needed.
+      - The default settings cause a single VM to be created in which all of
+      - the tests are then run. 
+      -->
+    <junit printsummary="off" showoutput="no" fork="yes" forkmode="once" failureproperty="test.failure">
+      <!-- plain output to file; brief output to console. -->
+      <formatter type="plain"/>
+      <formatter usefile="false" type="brief"/>
+
+      <!-- 
+        - Provide a set of properties pointing to the logging libs for
+        - the use of the PathableClassLoader class used by some unit tests.
+        -->
+      <syspropertyset refid="test-lib-props"/>
+      <classpath refid="test.classpath"/>
+
+      <!--
+        - Uncomment this to enable logging diagnostics for tests
+        - <jvmarg value="-Dorg.apache.commons.logging.diagnostics.dest=STDERR"/>
+        -->
+
+      <!--
+        - Auto-detect the tests to run. Checking the ${build.home}/tests
+        - directory for .class files rather than the src/test directory
+        - for .java files means that when we run the tests on platforms
+        - where some components (eg jdk14 logging) is not available, 
+        - just ensuring the tests are skipped from the compile will
+        - also cause them to be skipped from the testing.
+        -
+        - This does introduce the danger that if tests accidentally
+        - fail to compile then we won't notice it here. However that
+        - should have been reported earlier anyway.
+        -->
+      <batchtest todir="${build.home}/test-reports">
+        <fileset dir="${build.home}/tests">
+          <include name="${testmatch}.class"/>
+        </fileset>
+      </batchtest>
+    </junit>
+
+    <fail if="test.failure">
+      One or more unit tests failed.
+    </fail>
+  </target>
+
+</project>
diff --git a/checkstyle.xml b/checkstyle.xml
new file mode 100644
index 0000000..cec133f
--- /dev/null
+++ b/checkstyle.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<!DOCTYPE module PUBLIC
+    "-//Puppy Crawl//DTD Check Configuration 1.1//EN"
+    "http://www.puppycrawl.com/dtds/configuration_1_1.dtd">
+
+<!-- commons logging customization of default Checkstyle behavior -->
+<module name="Checker">
+  <property name="localeLanguage" value="en" />
+  <property name="severity" value="warning"/>
+
+  <!-- Checks whether files end with a new line. -->
+  <!-- See http://checkstyle.sf.net/config_misc.html#NewlineAtEndOfFile -->
+  <module name="NewlineAtEndOfFile" />
+
+  <!-- Verify that EVERY source file has the appropriate license -->
+  <module name="Header">
+    <property name="headerFile" value="${checkstyle.header.file}" />
+  </module>
+
+  <!-- Checks for Tab characters -->
+  <!-- See http://checkstyle.sourceforge.net/config_whitespace.html#FileTabCharacter -->
+  <module name="FileTabCharacter">
+    <property name="fileExtensions" value="java" />
+  </module>
+
+  <!-- Checks for white space at the end of the line -->
+  <!-- See http://checkstyle.sourceforge.net/config_regexp.html -->
+  <module name="RegexpSingleline">
+    <property name="format" value="\s+$" />
+    <property name="message" value="Line has trailing spaces." />
+    <property name="fileExtensions" value="java" />
+  </module>
+
+  <!-- @author tags are deprecated -->
+  <module name="RegexpSingleline">
+    <property name="format" value="^\s+\*\s+ at author\s" />
+    <property name="message" value="Deprecated @author tag" />
+    <property name="fileExtensions" value="java" />
+    <property name="severity" value="warning" />
+  </module>
+
+  <module name="TreeWalker">
+    <property name="cacheFile" value="target/cachefile" />
+    <module name="OperatorWrap">
+      <property name="option" value="eol" />
+    </module>
+    <module name="LineLength">
+      <property name="max" value="120"/>
+    </module>
+  </module>
+
+</module>
diff --git a/license-header.txt b/license-header.txt
new file mode 100644
index 0000000..ae6f28c
--- /dev/null
+++ b/license-header.txt
@@ -0,0 +1,16 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
diff --git a/pmd.xml b/pmd.xml
new file mode 100644
index 0000000..ceefc84
--- /dev/null
+++ b/pmd.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<ruleset name="mybraces"
+    xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
+  <description>Excludes from default PMD rules.</description>
+  <rule ref="rulesets/java/unusedcode.xml">
+    <exclude name="UnnecessaryParentheses"/>
+  </rule>
+</ruleset>
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..cdad31c
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,547 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <!--
+    - Note that due to the special requirements of logging unit-tests, most
+    - tests are executed in the "integration-test" phase rather than the
+    - "test" phase. Please run "mvn integration-test" to run the full suite of
+    - available unit tests.
+    -->
+  <parent>
+    <groupId>org.apache.commons</groupId>
+    <artifactId>commons-parent</artifactId>
+    <version>34</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>commons-logging</groupId>
+  <artifactId>commons-logging</artifactId>
+  <name>Apache Commons Logging</name>
+  <version>1.2</version>
+  <description>Apache Commons Logging is a thin adapter allowing configurable bridging to other,
+    well known logging systems.</description>
+  <url>http://commons.apache.org/proper/commons-logging/</url>
+
+  <issueManagement>
+    <system>JIRA</system>
+    <url>http://issues.apache.org/jira/browse/LOGGING</url>
+  </issueManagement>
+
+  <inceptionYear>2001</inceptionYear>
+
+  <developers>
+    <developer>
+      <id>baliuka</id>
+      <name>Juozas Baliuka</name>
+      <email>baliuka at apache.org</email>
+      <roles>
+        <role>Java Developer</role>
+      </roles>
+    </developer>
+    <developer>
+      <id>morgand</id>
+      <name>Morgan Delagrange</name>
+      <email>morgand at apache.org</email>
+      <organization>Apache</organization>
+      <roles>
+        <role>Java Developer</role>
+      </roles>
+    </developer>
+    <developer>
+      <id>donaldp</id>
+      <name>Peter Donald</name>
+      <email>donaldp at apache.org</email>
+    </developer>
+    <developer>
+      <id>rdonkin</id>
+      <name>Robert Burrell Donkin</name>
+      <email>rdonkin at apache.org</email>
+      <organization>The Apache Software Foundation</organization>
+    </developer>
+    <developer>
+      <id>skitching</id>
+      <name>Simon Kitching</name>
+      <email>skitching at apache.org</email>
+      <organization>The Apache Software Foundation</organization>
+    </developer>
+    <developer>
+      <id>dennisl</id>
+      <name>Dennis Lundberg</name>
+      <email>dennisl at apache.org</email>
+      <organization>The Apache Software Foundation</organization>
+    </developer>
+    <developer>
+      <id>costin</id>
+      <name>Costin Manolache</name>
+      <email>costin at apache.org</email>
+      <organization>The Apache Software Foundation</organization>
+    </developer>
+    <developer>
+      <id>craigmcc</id>
+      <name>Craig McClanahan</name>
+      <email>craigmcc at apache.org</email>
+      <organization>The Apache Software Foundation</organization>
+    </developer>
+    <developer>
+      <id>tn</id>
+      <name>Thomas Neidhart</name>
+      <email>tn at apache.org</email>
+      <organization>The Apache Software Foundation</organization>
+    </developer>
+    <developer>
+      <id>sanders</id>
+      <name>Scott Sanders</name>
+      <email>sanders at apache.org</email>
+      <organization>The Apache Software Foundation</organization>
+    </developer>
+    <developer>
+      <id>rsitze</id>
+      <name>Richard Sitze</name>
+      <email>rsitze at apache.org</email>
+      <organization>The Apache Software Foundation</organization>
+    </developer>
+    <developer>
+      <id>bstansberry</id>
+      <name>Brian Stansberry</name>
+    </developer>
+    <developer>
+      <id>rwaldhoff</id>
+      <name>Rodney Waldhoff</name>
+      <email>rwaldhoff at apache.org</email>
+      <organization>The Apache Software Foundation</organization>
+    </developer>
+  </developers>
+  <contributors>
+    <contributor>
+      <name>Matthew P. Del Buono</name>
+      <roles>
+        <role>Provided patch</role>
+      </roles>
+    </contributor>
+    <contributor>
+      <name>Vince Eagen</name>
+      <email>vince256 at comcast dot net</email>
+      <roles>
+        <role>Lumberjack logging abstraction</role>
+      </roles>
+    </contributor>
+    <contributor>
+      <name>Peter Lawrey</name>
+      <roles>
+        <role>Provided patch</role>
+      </roles>
+    </contributor>
+    <contributor>
+      <name>Berin Loritsch</name>
+      <email>bloritsch at apache dot org</email>
+      <roles>
+        <role>Lumberjack logging abstraction</role>
+        <role>JDK 1.4 logging abstraction</role>
+      </roles>
+    </contributor>
+    <contributor>
+      <name>Philippe Mouawad</name>
+      <roles>
+        <role>Provided patch</role>
+      </roles>
+    </contributor>
+    <contributor>
+      <name>Neeme Praks</name>
+      <email>neeme at apache dot org</email>
+      <roles>
+        <role>Avalon logging abstraction</role>
+      </roles>
+    </contributor>
+    
+  </contributors>
+  
+  <scm>
+    <connection>scm:svn:http://svn.apache.org/repos/asf/commons/proper/logging/trunk</connection>
+    <developerConnection>scm:svn:https://svn.apache.org/repos/asf/commons/proper/logging/trunk</developerConnection>
+    <url>http://svn.apache.org/repos/asf/commons/proper/logging/trunk</url>
+  </scm>
+
+  <build>
+    <plugins>
+
+      <!--
+        - We want to create four jarfiles from this project: normal, tests, api
+        - and adapters. The first two are handled by the normal jar:jar and
+        - jar:test-jar targets.
+        - The jar plugin with some includes/excludes is used to create the other
+        - ones.
+        -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <executions>
+          <execution>
+            <!--
+              - The custom test framework requires the unit test code to be
+              - in a jarfile so it can control its place in the classpath.
+              -->
+            <id>testjar</id>
+            <phase>package</phase>
+            <goals>
+              <goal>test-jar</goal>
+            </goals>
+            <configuration>
+              <jarName>commons-logging</jarName>
+            </configuration>
+          </execution>
+
+          <execution>
+            <id>apijar</id>
+            <phase>package</phase>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+            <configuration>
+              <jarName>${project.artifactId}-api-${project.version}</jarName>
+              <includes>
+                <include>org/apache/commons/logging/*.class</include>
+                <include>org/apache/commons/logging/impl/LogFactoryImpl*.class</include>
+                <include>org/apache/commons/logging/impl/WeakHashtable*.class</include>
+                <include>org/apache/commons/logging/impl/SimpleLog*.class</include>
+                <include>org/apache/commons/logging/impl/NoOpLog*.class</include>
+                <include>org/apache/commons/logging/impl/Jdk14Logger.class</include>
+                <include>META-INF/LICENSE.txt</include>
+                <include>META-INF/NOTICE.txt</include>
+              </includes>
+              <excludes>
+                <exclude>**/package.html</exclude>
+              </excludes>
+            </configuration>
+          </execution>
+
+          <execution>
+            <id>adaptersjar</id>
+            <phase>package</phase>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+            <configuration>
+              <jarName>${project.artifactId}-adapters-${project.version}</jarName>
+              <includes>
+                <include>org/apache/commons/logging/impl/**.class</include>
+                <include>META-INF/LICENSE.txt</include>
+                <include>META-INF/NOTICE.txt</include>
+              </includes>
+              <excludes>
+                <exclude>org/apache/commons/logging/impl/WeakHashtable*.class</exclude>
+                <exclude>org/apache/commons/logging/impl/LogFactoryImpl*.class</exclude>
+              </excludes>
+            </configuration>
+          </execution>
+          
+          <!--
+            - Define the full jar last, the deploy/install plugin seems to be broken
+            - and takes the last definition from here.
+            -->
+          <execution>
+            <id>fulljar</id>
+            <phase>package</phase>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+            <configuration>
+              <jarName>${project.artifactId}-${project.version}</jarName>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-antrun-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>site.resources</id>
+            <phase>site</phase>
+            <configuration>
+              <target>
+                <copy todir="${project.reporting.outputDirectory}">
+                  <fileset dir="${basedir}">
+                    <include name="RELEASE-NOTES.txt" />
+                  </fileset>
+                </copy>
+              </target>
+            </configuration>
+            <goals>
+              <goal>run</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <!--
+          - Attach the adapters and api jars to the normal artifact. This way
+          - they will be deployed when the normal artifact is deployed.
+          -->
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>build-helper-maven-plugin</artifactId>
+        <version>1.0</version>
+        <executions>
+          <execution>
+            <id>attach-artifacts</id>
+            <phase>package</phase>
+            <goals>
+              <goal>attach-artifact</goal>
+            </goals>
+            <configuration>
+              <artifacts>
+                <artifact>
+                  <file>${project.build.directory}/${project.artifactId}-adapters-${project.version}.jar</file>
+                  <type>jar</type>
+                  <classifier>adapters</classifier>
+                </artifact>
+                <artifact>
+                  <file>${project.build.directory}/${project.artifactId}-api-${project.version}.jar</file>
+                  <type>jar</type>
+                  <classifier>api</classifier>
+                </artifact>
+              </artifacts>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <!--
+          - Many of JCL's tests use tricky techniques to place the generated
+          - JCL jarfiles on the classpath in various configurations. This means
+          - that those tests must be run *after* the "package" build phase.
+          -
+          - In order to not mess with the Ant build we "disable" the normal test
+          - phase. This is done by skipping the execution of the surefire plugin.
+          -->
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <skip>true</skip>
+        </configuration>
+      </plugin>
+
+      <plugin>
+        <!--
+          - Many of JCL's tests use tricky techniques to place the generated
+          - JCL jarfiles on the classpath in various configurations. This means
+          - that those tests must be run *after* the "package" build phase.
+          -
+          - Disable cobertura report generation as this does not work correctly
+          - with integration-tests and the normal unit tests are disabled too.
+          -->
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>cobertura-maven-plugin</artifactId>
+        <version>${commons.cobertura.version}</version>
+        <configuration>
+          <skip>true</skip>
+        </configuration>
+      </plugin>
+
+      <plugin>
+        <!--
+          - Many of JCL's tests use tricky techniques to place the generated
+          - JCL jarfiles on the classpath in various configurations. This means
+          - that those tests must be run *after* the "package" build phase.
+          -
+          - For this we use the failsafe plugin which is bound to the
+          - "integration-test" phase by default.
+          -->
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-failsafe-plugin</artifactId>
+        <version>${commons.surefire.version}</version>        
+        <executions>
+          <execution>
+            <id>integration-test</id>
+            <goals>
+              <goal>integration-test</goal>
+              <goal>verify</goal>
+            </goals>
+            <configuration>
+              <runOrder>${failsafe.runorder}</runOrder>
+              <includes>
+                <include>**/*TestCase.java</include>
+              </includes>
+              <systemPropertyVariables>
+                <!--
+                  <org.apache.commons.logging.diagnostics.dest>STDOUT</org.apache.commons.logging.diagnostics.dest>
+                -->
+                  <log4j12>${log4j:log4j:jar}</log4j12>
+                  <logkit>${logkit:logkit:jar}</logkit>
+                  <servlet-api>${javax.servlet:servlet-api:jar}</servlet-api>
+                  <commons-logging>target/${project.build.finalName}.jar</commons-logging>
+                  <commons-logging-api>target/${project.artifactId}-api-${project.version}.jar</commons-logging-api>
+                  <commons-logging-adapters>target/${project.artifactId}-adapters-${project.version}.jar</commons-logging-adapters>
+                  <testclasses>target/commons-logging-tests.jar</testclasses>
+              </systemPropertyVariables>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-assembly-plugin</artifactId>
+        <version>2.3</version>
+        <configuration>
+          <descriptors>
+            <descriptor>src/main/assembly/bin.xml</descriptor>
+            <descriptor>src/main/assembly/src.xml</descriptor>
+          </descriptors>
+          <tarLongFileMode>gnu</tarLongFileMode>
+        </configuration>
+      </plugin>
+      <!-- Define properties for referencing dependencies -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-dependency-plugin</artifactId>
+        <version>2.4</version>
+        <executions>
+          <execution>
+            <goals>
+              <goal>properties</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-scm-publish-plugin</artifactId>
+        <configuration>
+          <ignorePathsToDelete>
+            <ignorePathToDelete>javadocs</ignorePathToDelete>
+            <ignorePathToDelete>commons-logging-**</ignorePathToDelete>
+          </ignorePathsToDelete>
+        </configuration>
+      </plugin>
+
+    </plugins>
+  </build>
+
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>3.8.1</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>log4j</groupId>
+      <artifactId>log4j</artifactId>
+      <version>1.2.17</version>
+      <optional>true</optional>
+    </dependency>
+    <dependency>
+      <groupId>logkit</groupId>
+      <artifactId>logkit</artifactId>
+      <version>1.0.1</version>
+      <optional>true</optional>
+    </dependency>
+    <dependency>
+      <groupId>avalon-framework</groupId>
+      <artifactId>avalon-framework</artifactId>
+      <version>4.1.5</version>
+      <optional>true</optional>
+    </dependency>
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>servlet-api</artifactId>
+      <version>2.3</version>
+      <scope>provided</scope>
+      <optional>true</optional>
+    </dependency>
+  </dependencies>
+
+  <reporting>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+        <version>2.7</version>
+        <configuration>
+          <configLocation>${basedir}/checkstyle.xml</configLocation>
+          <enableRulesSummary>false</enableRulesSummary>
+          <headerFile>${basedir}/license-header.txt</headerFile>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>clirr-maven-plugin</artifactId>
+        <version>2.2.2</version>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>jdepend-maven-plugin</artifactId>
+        <version>2.0-beta-1</version>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>findbugs-maven-plugin</artifactId>
+        <version>2.5.2</version>
+        <configuration>
+          <skip>true</skip>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-pmd-plugin</artifactId>
+        <version>3.0.1</version>
+        <configuration>
+          <!-- targetJdk 1.1, 1.2 is not supported -->
+          <targetJdk>1.3</targetJdk>
+          <linkXref>true</linkXref>
+          <rulesets>
+            <ruleset>${basedir}/pmd.xml</ruleset>
+          </rulesets>
+        </configuration>
+      </plugin>
+    </plugins>
+  </reporting>
+
+  <distributionManagement>
+    <site>
+      <id>apache.website</id>
+      <url>${commons.deployment.protocol}://people.apache.org/www/commons.apache.org/logging/</url>
+    </site>
+  </distributionManagement>
+
+  <properties>
+    <maven.compiler.source>1.2</maven.compiler.source>
+    <maven.compiler.target>1.2</maven.compiler.target>
+    <commons.componentid>logging</commons.componentid>
+    <commons.release.version>1.2</commons.release.version>
+    <commons.jira.id>LOGGING</commons.jira.id>
+    <commons.jira.pid>12310484</commons.jira.pid>
+    <!-- The RC version used in the staging repository URL. -->
+    <commons.rc.version>RC2</commons.rc.version>
+    <commons.surefire.version>2.12</commons.surefire.version>
+    <skipSurefireReport>true</skipSurefireReport>
+    <!-- Allow default test run order to be changed -->
+    <failsafe.runorder>filesystem</failsafe.runorder>
+    
+    <commons.osgi.import>
+      javax.servlet;version="[2.1.0, 3.0.0)";resolution:=optional,
+      org.apache.avalon.framework.logger;version="[4.1.3, 4.1.5]";resolution:=optional,
+      org.apache.log;version="[1.0.1, 1.0.1]";resolution:=optional,
+      org.apache.log4j;version="[1.2.15, 2.0.0)";resolution:=optional
+    </commons.osgi.import>
+  </properties>
+</project>
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
new file mode 100644
index 0000000..3153d82
--- /dev/null
+++ b/src/changes/changes.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0"?>
+<!--
+
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+-->
+<!--
+This file is also used by the maven-changes-plugin to generate the release notes.
+Useful ways of finding items to add to this file are:
+
+1.  Add items when you fix a bug or add a feature (this makes the
+release process easy :-).
+
+2.  Do a JIRA search for tickets closed since the previous release.
+
+3.  Use the report generated by the maven-changelog-plugin to see all
+SVN commits. TBA how to use this with SVN.
+
+To generate the release notes from this file:
+
+mvn changes:announcement-generate -Prelease-notes [-Dchanges.version=nn]
+then tweak the formatting if necessary
+and commit
+
+The <action> type attribute can be add,update,fix,remove.
+-->
+
+<document xmlns="http://maven.apache.org/changes/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/changes/1.0.0 http://maven.apache.org/xsd/changes-1.0.0.xsd">
+  <properties>
+    <title>Release Notes</title>
+  </properties>
+  <body>
+    <release version="1.2" date="2014-07-11" description="This is a maintenance release containing bug fixes. Java 1.2 or later is required.">
+      <action issue="LOGGING-37" dev="tn" type="fix" due-to="Matthias Ernst,Archie Cobbs">
+        Improve performance of LogFactory#getFactory() by calling Thread#currentThread()#getContextClassLoader()
+        directly instead of using reflection. As a consequence support for JDK 1.1 has been dropped.
+      </action>
+      <action issue="LOGGING-156" dev="tn" type="fix" due-to="Mikolaj Izdebski">
+        Fix SecurityAllowedTestCase when executed with OpenJDK 1.7 due to an additional required RuntimePermission.
+      </action>
+      <action issue="LOGGING-157" dev="tn" type="fix" due-to="Ville Skyttä">
+        Fix javadoc to comply with javadoc tool from jdk 1.8.
+      </action>
+    </release>
+    <release version="1.1.3" date="2013-05-23" description="This is a maintenance release containing bug fixes.">
+      <action issue="LOGGING-151" dev="tn" type="fix" due-to="Krzysztof Daniel">
+        Use "org.apache.commons.logging" as bundle symbolic name.
+      </action>
+    </release>
+    <release version="1.1.2" date="2013-03-20" description="This is a maintenance release containing bug fixes.">
+      <action issue="LOGGING-124" dev="tn" type="fix" due-to="Christian Schneider">
+        The jar manifest now contains proper OSGi-related metadata information.
+      </action>
+      <action issue="LOGGING-144" dev="tn" type="fix" due-to="Sebastian Bazley">
+        LogFactory and LogFactoryImpl will not swallow certain errors anymore (ThreadDeath
+        and VirtualMachineError).
+      </action>
+      <action issue="LOGGING-135" dev="tn" type="update" due-to="Sebastian Bazley">
+        Improved thread-safety for several log adapters, including AvalonLogger, SimpleLog,
+        Log4JLogger, LogKitLogger.
+      </action>
+      <action issue="LOGGING-138" dev="tn" type="update" due-to="Luke Lu">
+        In case of a discovery failure now also the stacktrace of the cause will be
+        added to the diagnostic message.
+      </action>
+      <action issue="LOGGING-132" dev="tn" type="fix" due-to="Nathan Niesen">
+        Jdk14Logger now correctly uses the specified logger name.
+      </action>
+      <action issue="LOGGING-133" dev="tn" type="update" due-to="Shevek">
+        Change scope of Jdk14Logger.log(Level, String, Throwable) to protected, allowing
+        subclasses to modify the logging output.
+      </action>
+      <action issue="LOGGING-146" dev="tn" type="fix" due-to="Sebastian Bazley">
+        Properly synchronize access to protected static field LogFactory.nullClassLoaderFactory.
+      </action>
+      <action issue="LOGGING-119" dev="tn" type="fix" due-to="Nitzan Niv, Philippe Mouawad">
+        Prevent potential deadlock scenario in WeakHashtable.
+      </action>
+      <action issue="LOGGING-130" dev="sebb" type="fix" due-to="Matthew P. Del Buono">
+        Potential missing privileged block for class loader.
+      </action>
+      <action issue="LOGGING-145" dev="sebb" type="fix">
+        LogFactoryImpl.setAttribute - possible NPE.
+      </action>
+      <action issue="LOGGING-142" dev="sebb" type="fix" due-to="Jingguo Yao">
+        Log4JLogger uses deprecated static members of Priority such as INFO.
+      </action>
+      <action issue="LOGGING-128" dev="sebb" type="fix" due-to="Peter Lawrey">
+        Static analysis suggests a number of potential improvements.
+      </action>
+      <action issue="LOGGING-147" dev="sebb" type="fix">
+        SimpleLog.log - unsafe update of shortLogName.
+      </action>
+      <action issue="LOGGING-148" dev="sebb" type="fix">
+        LogFactory.diagnosticPrefix and diagnosticsStream could be final.
+      </action>
+    </release>
+  </body>
+</document>
diff --git a/src/changes/release-notes.vm b/src/changes/release-notes.vm
new file mode 100644
index 0000000..c0e2f57
--- /dev/null
+++ b/src/changes/release-notes.vm
@@ -0,0 +1,109 @@
+## Licensed to the Apache Software Foundation (ASF) under one
+## or more contributor license agreements.  See the NOTICE file
+## distributed with this work for additional information
+## regarding copyright ownership.  The ASF licenses this file
+## to you under the Apache License, Version 2.0 (the
+## "License"); you may not use this file except in compliance
+## with the License.  You may obtain a copy of the License at
+##
+##  http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing,
+## software distributed under the License is distributed on an
+## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+## KIND, either express or implied.  See the License for the
+## specific language governing permissions and limitations
+## under the License.
+              Apache ${project.name}
+                  Version ${version}
+                  RELEASE NOTES
+
+The ${developmentTeam} is pleased to announce the release of Apache ${project.name} ${version}
+
+$introduction.replaceAll("(?<!\015)\012", "
+").replaceAll("(?m)^ +","")
+
+## N.B. the available variables are described here:
+## http://maven.apache.org/plugins/maven-changes-plugin/examples/using-a-custom-announcement-template.html
+## Hack to improve layout: replace all pairs of spaces with a single new-line
+$release.description.replaceAll("  ", "
+")
+
+## set up indent sizes. Only change indent1
+#set($props=${project.properties})
+#set($jiralen=$props.get("commons.jira.id").length())
+## indent1 =   POOL-nnnn:
+#set($blanklen=$jiralen+6)## +6 for "-nnnn:"
+## must be at least as long as the longest JIRA id
+#set($blanks="                                  ")
+#set($indent1=$blanks.substring(0,$blanklen))
+## indent2 allows for issue wrapper
+#set($indent2="$indent1   ")
+##
+#macro ( processaction )
+## Use replaceAll to fix up LF-only line ends on Windows.
+#set($action=$actionItem.getAction().replaceAll("\n","
+"))
+## Fix up indentation for multi-line action descriptions
+#set($action=$action.replaceAll("(?m)^  +",$indent2))
+#if ($actionItem.getIssue())
+#set($issue="$actionItem.getIssue():")
+## Pad shorter issue numbers
+#if ($issue.length() < $indent1.length())#set ($issue="$issue ")#end
+#if ($issue.length() < $indent1.length())#set ($issue="$issue ")#end
+#if ($issue.length() < $indent1.length())#set ($issue="$issue ")#end
+#else
+#set($issue=$indent1)
+#end
+#if ($actionItem.getDueTo())
+#set($dueto=" Thanks to $actionItem.getDueTo().")
+#else
+#set($dueto="")
+#end
+o $issue ${action}$dueto
+#set($action="")
+#set($issue="")
+#set($dueto="")
+#end
+##
+#if ($release.getActions().size() == 0)
+No changes defined in this version.
+#else
+Changes in this version include:
+
+#if ($release.getActions('add').size() !=0)
+New features:
+#foreach($actionItem in $release.getActions('add'))
+#processaction()
+#end 
+#end
+
+#if ($release.getActions('fix').size() !=0)
+Fixed Bugs:
+#foreach($actionItem in $release.getActions('fix'))
+#processaction()
+#end
+#end
+
+#if ($release.getActions('update').size() !=0)
+Changes:
+#foreach($actionItem in $release.getActions('update'))
+#processaction()
+#end
+#end
+
+#if ($release.getActions('remove').size() !=0)
+Removed:
+#foreach($actionItem in $release.getActions('remove'))
+#processaction()
+#end
+#end
+## End of main loop
+#end
+
+Historical list of changes: ${project.url}changes-report.html
+
+For complete information on ${project.name}, including instructions on how to submit bug reports,
+patches, or suggestions for improvement, see the Apache ${project.name} website:
+
+${project.url}
\ No newline at end of file
diff --git a/src/conf/MANIFEST.MF b/src/conf/MANIFEST.MF
new file mode 100644
index 0000000..5182e07
--- /dev/null
+++ b/src/conf/MANIFEST.MF
@@ -0,0 +1,28 @@
+Manifest-Version: 1.0
+Export-Package: org.apache.commons.logging;version="1.2.0",org.apache.
+ commons.logging.impl;version="1.2.0"
+Implementation-Title: Commons Logging
+Implementation-Vendor: The Apache Software Foundation
+Implementation-Vendor-Id: org.apache
+Specification-Title: Commons Logging
+Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt
+Bundle-SymbolicName: org.apache.commons.logging
+X-Compile-Target-JDK: 1.2
+Implementation-Version: 1.2
+Specification-Vendor: The Apache Software Foundation
+Bundle-Name: Commons Logging
+Created-By: Apache Maven Bundle Plugin
+X-Compile-Source-JDK: 1.2
+Bundle-Vendor: The Apache Software Foundation
+Bundle-Version: 1.2.0
+Bundle-ManifestVersion: 2
+Bundle-Description: Commons Logging is a thin adapter allowing configu
+ rable bridging to other,    well known logging systems.
+Bundle-DocURL: http://commons.apache.org/proper/commons-logging/
+Import-Package: javax.servlet;resolution:=optional;version="[2.1.0,3.0
+ .0)",org.apache.avalon.framework.logger;resolution:=optional;version=
+ "[4.1.3,4.1.5]",org.apache.log;resolution:=optional;version="[1.0.1,1
+ .0.1]",org.apache.log4j;resolution:=optional;version="[1.2.15,2.0.0)"
+Include-Resource: META-INF/NOTICE.txt=NOTICE.txt,META-INF/LICENSE.txt=
+ LICENSE.txt
+Specification-Version: 1.2
diff --git a/src/main/assembly/bin.xml b/src/main/assembly/bin.xml
new file mode 100644
index 0000000..5172b82
--- /dev/null
+++ b/src/main/assembly/bin.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<assembly>
+  <id>bin</id>
+  <formats>
+    <format>tar.gz</format>
+    <format>zip</format>
+  </formats>
+  <includeSiteDirectory>false</includeSiteDirectory>
+  <fileSets>
+    <fileSet>
+      <includes>
+        <include>LICENSE*</include>
+        <include>NOTICE*</include>
+        <include>RELEASE-NOTES*</include>
+      </includes>
+    </fileSet>
+    <fileSet>
+      <directory>target</directory>
+      <outputDirectory></outputDirectory>
+      <includes>
+        <include>*.jar</include>
+      </includes>
+    </fileSet>
+    <fileSet>
+        <directory>target/site/apidocs</directory>
+        <outputDirectory>apidocs</outputDirectory>
+    </fileSet>
+  </fileSets>
+</assembly>
\ No newline at end of file
diff --git a/src/main/assembly/src.xml b/src/main/assembly/src.xml
new file mode 100644
index 0000000..2de93ab
--- /dev/null
+++ b/src/main/assembly/src.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<assembly>
+  <id>src</id>
+  <formats>
+    <format>tar.gz</format>
+    <format>zip</format>
+  </formats>
+  <baseDirectory>${project.artifactId}-${project.version}-src</baseDirectory>
+  <fileSets>
+    <fileSet>
+      <includes>
+        <include>checkstyle.xml</include>
+        <include>README.txt</include>
+        <include>LICENSE.txt</include>
+        <include>NOTICE.txt</include>
+        <include>pmd.xml</include>
+        <include>pom.xml</include>
+        <include>PROPOSAL.html</include>
+        <include>RELEASE-NOTES.txt</include>
+        <include>STATUS.txt</include>
+        <include>build.xml</include>
+        <include>build-testing.xml</include>
+        <include>build.properties.sample</include>
+        <include>license-header.txt</include>
+      </includes>
+    </fileSet>
+    <fileSet>
+      <directory>src</directory>
+      <excludes>
+        <exclude>**/download*.cgi</exclude>
+      </excludes>
+    </fileSet>
+  </fileSets>
+</assembly>
\ No newline at end of file
diff --git a/src/main/java/org/apache/commons/logging/Log.java b/src/main/java/org/apache/commons/logging/Log.java
new file mode 100644
index 0000000..5c5523d
--- /dev/null
+++ b/src/main/java/org/apache/commons/logging/Log.java
@@ -0,0 +1,216 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging;
+
+/**
+ * A simple logging interface abstracting logging APIs.  In order to be
+ * instantiated successfully by {@link LogFactory}, classes that implement
+ * this interface must have a constructor that takes a single String
+ * parameter representing the "name" of this Log.
+ * <p>
+ * The six logging levels used by <code>Log</code> are (in order):
+ * <ol>
+ * <li>trace (the least serious)</li>
+ * <li>debug</li>
+ * <li>info</li>
+ * <li>warn</li>
+ * <li>error</li>
+ * <li>fatal (the most serious)</li>
+ * </ol>
+ * The mapping of these log levels to the concepts used by the underlying
+ * logging system is implementation dependent.
+ * The implementation should ensure, though, that this ordering behaves
+ * as expected.
+ * <p>
+ * Performance is often a logging concern.
+ * By examining the appropriate property,
+ * a component can avoid expensive operations (producing information
+ * to be logged).
+ * <p>
+ * For example,
+ * <pre>
+ *    if (log.isDebugEnabled()) {
+ *        ... do something expensive ...
+ *        log.debug(theResult);
+ *    }
+ * </pre>
+ * <p>
+ * Configuration of the underlying logging system will generally be done
+ * external to the Logging APIs, through whatever mechanism is supported by
+ * that system.
+ *
+ * @version $Id: Log.java 1606045 2014-06-27 12:11:56Z tn $
+ */
+public interface Log {
+
+    /**
+     * Logs a message with debug log level.
+     *
+     * @param message log this message
+     */
+    void debug(Object message);
+
+    /**
+     * Logs an error with debug log level.
+     *
+     * @param message log this message
+     * @param t log this cause
+     */
+    void debug(Object message, Throwable t);
+
+    /**
+     * Logs a message with error log level.
+     *
+     * @param message log this message
+     */
+    void error(Object message);
+
+    /**
+     * Logs an error with error log level.
+     *
+     * @param message log this message
+     * @param t log this cause
+     */
+    void error(Object message, Throwable t);
+
+    /**
+     * Logs a message with fatal log level.
+     *
+     * @param message log this message
+     */
+    void fatal(Object message);
+
+    /**
+     * Logs an error with fatal log level.
+     *
+     * @param message log this message
+     * @param t log this cause
+     */
+    void fatal(Object message, Throwable t);
+
+    /**
+     * Logs a message with info log level.
+     *
+     * @param message log this message
+     */
+    void info(Object message);
+
+    /**
+     * Logs an error with info log level.
+     *
+     * @param message log this message
+     * @param t log this cause
+     */
+    void info(Object message, Throwable t);
+
+    /**
+     * Is debug logging currently enabled?
+     * <p>
+     * Call this method to prevent having to perform expensive operations
+     * (for example, <code>String</code> concatenation)
+     * when the log level is more than debug.
+     *
+     * @return true if debug is enabled in the underlying logger.
+     */
+    boolean isDebugEnabled();
+
+    /**
+     * Is error logging currently enabled?
+     * <p>
+     * Call this method to prevent having to perform expensive operations
+     * (for example, <code>String</code> concatenation)
+     * when the log level is more than error.
+     *
+     * @return true if error is enabled in the underlying logger.
+     */
+    boolean isErrorEnabled();
+
+    /**
+     * Is fatal logging currently enabled?
+     * <p>
+     * Call this method to prevent having to perform expensive operations
+     * (for example, <code>String</code> concatenation)
+     * when the log level is more than fatal.
+     *
+     * @return true if fatal is enabled in the underlying logger.
+     */
+    boolean isFatalEnabled();
+
+    /**
+     * Is info logging currently enabled?
+     * <p>
+     * Call this method to prevent having to perform expensive operations
+     * (for example, <code>String</code> concatenation)
+     * when the log level is more than info.
+     *
+     * @return true if info is enabled in the underlying logger.
+     */
+    boolean isInfoEnabled();
+
+    /**
+     * Is trace logging currently enabled?
+     * <p>
+     * Call this method to prevent having to perform expensive operations
+     * (for example, <code>String</code> concatenation)
+     * when the log level is more than trace.
+     *
+     * @return true if trace is enabled in the underlying logger.
+     */
+    boolean isTraceEnabled();
+
+    /**
+     * Is warn logging currently enabled?
+     * <p>
+     * Call this method to prevent having to perform expensive operations
+     * (for example, <code>String</code> concatenation)
+     * when the log level is more than warn.
+     *
+     * @return true if warn is enabled in the underlying logger.
+     */
+    boolean isWarnEnabled();
+
+    /**
+     * Logs a message with trace log level.
+     *
+     * @param message log this message
+     */
+    void trace(Object message);
+
+    /**
+     * Logs an error with trace log level.
+     *
+     * @param message log this message
+     * @param t log this cause
+     */
+    void trace(Object message, Throwable t);
+
+    /**
+     * Logs a message with warn log level.
+     *
+     * @param message log this message
+     */
+    void warn(Object message);
+
+    /**
+     * Logs an error with warn log level.
+     *
+     * @param message log this message
+     * @param t log this cause
+     */
+    void warn(Object message, Throwable t);
+}
diff --git a/src/main/java/org/apache/commons/logging/LogConfigurationException.java b/src/main/java/org/apache/commons/logging/LogConfigurationException.java
new file mode 100644
index 0000000..e612e9b
--- /dev/null
+++ b/src/main/java/org/apache/commons/logging/LogConfigurationException.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging;
+
+/**
+ * An exception that is thrown only if a suitable <code>LogFactory</code>
+ * or <code>Log</code> instance cannot be created by the corresponding
+ * factory methods.
+ *
+ * @version $Id: LogConfigurationException.java 1432663 2013-01-13 17:24:18Z tn $
+ */
+public class LogConfigurationException extends RuntimeException {
+
+    /** Serializable version identifier. */
+    private static final long serialVersionUID = 8486587136871052495L;
+
+    /**
+     * Construct a new exception with <code>null</code> as its detail message.
+     */
+    public LogConfigurationException() {
+        super();
+    }
+
+    /**
+     * Construct a new exception with the specified detail message.
+     *
+     * @param message The detail message
+     */
+    public LogConfigurationException(String message) {
+        super(message);
+    }
+
+    /**
+     * Construct a new exception with the specified cause and a derived
+     * detail message.
+     *
+     * @param cause The underlying cause
+     */
+    public LogConfigurationException(Throwable cause) {
+        this(cause == null ? null : cause.toString(), cause);
+    }
+
+    /**
+     * Construct a new exception with the specified detail message and cause.
+     *
+     * @param message The detail message
+     * @param cause The underlying cause
+     */
+    public LogConfigurationException(String message, Throwable cause) {
+        super(message + " (Caused by " + cause + ")");
+        this.cause = cause; // Two-argument version requires JDK 1.4 or later
+    }
+
+    /**
+     * The underlying cause of this exception.
+     */
+    protected Throwable cause = null;
+
+    /**
+     * Return the underlying cause of this exception (if any).
+     */
+    public Throwable getCause() {
+        return this.cause;
+    }
+}
diff --git a/src/main/java/org/apache/commons/logging/LogFactory.java b/src/main/java/org/apache/commons/logging/LogFactory.java
new file mode 100644
index 0000000..705cb96
--- /dev/null
+++ b/src/main/java/org/apache/commons/logging/LogFactory.java
@@ -0,0 +1,1703 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging;
+
+import java.io.BufferedReader;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+
+/**
+ * Factory for creating {@link Log} instances, with discovery and
+ * configuration features similar to that employed by standard Java APIs
+ * such as JAXP.
+ * <p>
+ * <strong>IMPLEMENTATION NOTE</strong> - This implementation is heavily
+ * based on the SAXParserFactory and DocumentBuilderFactory implementations
+ * (corresponding to the JAXP pluggability APIs) found in Apache Xerces.
+ *
+ * @version $Id: LogFactory.java 1606041 2014-06-27 11:56:59Z tn $
+ */
+public abstract class LogFactory {
+    // Implementation note re AccessController usage
+    //
+    // It is important to keep code invoked via an AccessController to small
+    // auditable blocks. Such code must carefully evaluate all user input
+    // (parameters, system properties, config file contents, etc). As an
+    // example, a Log implementation should not write to its logfile
+    // with an AccessController anywhere in the call stack, otherwise an
+    // insecure application could configure the log implementation to write
+    // to a protected file using the privileges granted to JCL rather than
+    // to the calling application.
+    //
+    // Under no circumstance should a non-private method return data that is
+    // retrieved via an AccessController. That would allow an insecure app
+    // to invoke that method and obtain data that it is not permitted to have.
+    //
+    // Invoking user-supplied code with an AccessController set is not a major
+    // issue (eg invoking the constructor of the class specified by
+    // HASHTABLE_IMPLEMENTATION_PROPERTY). That class will be in a different
+    // trust domain, and therefore must have permissions to do whatever it
+    // is trying to do regardless of the permissions granted to JCL. There is
+    // a slight issue in that untrusted code may point that environment var
+    // to another trusted library, in which case the code runs if both that
+    // lib and JCL have the necessary permissions even when the untrusted
+    // caller does not. That's a pretty hard route to exploit though.
+
+    // ----------------------------------------------------- Manifest Constants
+
+    /**
+     * The name (<code>priority</code>) of the key in the config file used to
+     * specify the priority of that particular config file. The associated value
+     * is a floating-point number; higher values take priority over lower values.
+     */
+    public static final String PRIORITY_KEY = "priority";
+
+    /**
+     * The name (<code>use_tccl</code>) of the key in the config file used
+     * to specify whether logging classes should be loaded via the thread
+     * context class loader (TCCL), or not. By default, the TCCL is used.
+     */
+    public static final String TCCL_KEY = "use_tccl";
+
+    /**
+     * The name (<code>org.apache.commons.logging.LogFactory</code>) of the property
+     * used to identify the LogFactory implementation
+     * class name. This can be used as a system property, or as an entry in a
+     * configuration properties file.
+     */
+    public static final String FACTORY_PROPERTY = "org.apache.commons.logging.LogFactory";
+
+    /**
+     * The fully qualified class name of the fallback <code>LogFactory</code>
+     * implementation class to use, if no other can be found.
+     */
+    public static final String FACTORY_DEFAULT = "org.apache.commons.logging.impl.LogFactoryImpl";
+
+    /**
+     * The name (<code>commons-logging.properties</code>) of the properties file to search for.
+     */
+    public static final String FACTORY_PROPERTIES = "commons-logging.properties";
+
+    /**
+     * JDK1.3+ <a href="http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#Service%20Provider">
+     * 'Service Provider' specification</a>.
+     */
+    protected static final String SERVICE_ID =
+        "META-INF/services/org.apache.commons.logging.LogFactory";
+
+    /**
+     * The name (<code>org.apache.commons.logging.diagnostics.dest</code>)
+     * of the property used to enable internal commons-logging
+     * diagnostic output, in order to get information on what logging
+     * implementations are being discovered, what classloaders they
+     * are loaded through, etc.
+     * <p>
+     * If a system property of this name is set then the value is
+     * assumed to be the name of a file. The special strings
+     * STDOUT or STDERR (case-sensitive) indicate output to
+     * System.out and System.err respectively.
+     * <p>
+     * Diagnostic logging should be used only to debug problematic
+     * configurations and should not be set in normal production use.
+     */
+    public static final String DIAGNOSTICS_DEST_PROPERTY =
+        "org.apache.commons.logging.diagnostics.dest";
+
+    /**
+     * When null (the usual case), no diagnostic output will be
+     * generated by LogFactory or LogFactoryImpl. When non-null,
+     * interesting events will be written to the specified object.
+     */
+    private static PrintStream diagnosticsStream = null;
+
+    /**
+     * A string that gets prefixed to every message output by the
+     * logDiagnostic method, so that users can clearly see which
+     * LogFactory class is generating the output.
+     */
+    private static final String diagnosticPrefix;
+
+    /**
+     * Setting this system property
+     * (<code>org.apache.commons.logging.LogFactory.HashtableImpl</code>)
+     * value allows the <code>Hashtable</code> used to store
+     * classloaders to be substituted by an alternative implementation.
+     * <p>
+     * <strong>Note:</strong> <code>LogFactory</code> will print:
+     * <pre>
+     * [ERROR] LogFactory: Load of custom hashtable failed
+     * </pre>
+     * to system error and then continue using a standard Hashtable.
+     * <p>
+     * <strong>Usage:</strong> Set this property when Java is invoked
+     * and <code>LogFactory</code> will attempt to load a new instance
+     * of the given implementation class.
+     * For example, running the following ant scriplet:
+     * <pre>
+     *  <java classname="${test.runner}" fork="yes" failonerror="${test.failonerror}">
+     *     ...
+     *     <sysproperty
+     *        key="org.apache.commons.logging.LogFactory.HashtableImpl"
+     *        value="org.apache.commons.logging.AltHashtable"/>
+     *  </java>
+     * </pre>
+     * will mean that <code>LogFactory</code> will load an instance of
+     * <code>org.apache.commons.logging.AltHashtable</code>.
+     * <p>
+     * A typical use case is to allow a custom
+     * Hashtable implementation using weak references to be substituted.
+     * This will allow classloaders to be garbage collected without
+     * the need to release them (on 1.3+ JVMs only, of course ;).
+     */
+    public static final String HASHTABLE_IMPLEMENTATION_PROPERTY =
+        "org.apache.commons.logging.LogFactory.HashtableImpl";
+
+    /** Name used to load the weak hashtable implementation by names. */
+    private static final String WEAK_HASHTABLE_CLASSNAME =
+        "org.apache.commons.logging.impl.WeakHashtable";
+
+    /**
+     * A reference to the classloader that loaded this class. This is the
+     * same as LogFactory.class.getClassLoader(). However computing this
+     * value isn't quite as simple as that, as we potentially need to use
+     * AccessControllers etc. It's more efficient to compute it once and
+     * cache it here.
+     */
+    private static final ClassLoader thisClassLoader;
+
+    // ----------------------------------------------------------- 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</code> if there is no such attribute.
+     *
+     * @param name Name of the attribute to return
+     */
+    public abstract Object getAttribute(String 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.
+     */
+    public abstract String[] getAttributeNames();
+
+    /**
+     * Convenience method to derive a name from the specified class and
+     * call <code>getInstance(String)</code> with it.
+     *
+     * @param clazz Class for which a suitable Log name will be derived
+     * @throws LogConfigurationException if a suitable <code>Log</code>
+     *  instance cannot be returned
+     */
+    public abstract Log getInstance(Class clazz)
+        throws LogConfigurationException;
+
+    /**
+     * Construct (if necessary) and return a <code>Log</code> instance,
+     * using the factory's current set of configuration attributes.
+     * <p>
+     * <strong>NOTE</strong> - Depending upon the implementation of
+     * the <code>LogFactory</code> you are using, the <code>Log</code>
+     * 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</code> 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</code>
+     *  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</code> value is equivalent to calling
+     * <code>removeAttribute(name)</code>.
+     *
+     * @param name Name of the attribute to set
+     * @param value Value of the attribute to set, or <code>null</code>
+     *  to remove any setting for this attribute
+     */
+    public abstract void setAttribute(String name, Object value);
+
+    // ------------------------------------------------------- Static Variables
+
+    /**
+     * The previously constructed <code>LogFactory</code> instances, keyed by
+     * the <code>ClassLoader</code> with which it was created.
+     */
+    protected static Hashtable factories = null;
+
+    /**
+     * Previously constructed <code>LogFactory</code> instance as in the
+     * <code>factories</code> map, but for the case where
+     * <code>getClassLoader</code> returns <code>null</code>.
+     * This can happen when:
+     * <ul>
+     * <li>using JDK1.1 and the calling code is loaded via the system
+     *  classloader (very common)</li>
+     * <li>using JDK1.2+ and the calling code is loaded via the boot
+     *  classloader (only likely for embedded systems work).</li>
+     * </ul>
+     * Note that <code>factories</code> is a <i>Hashtable</i> (not a HashMap),
+     * and hashtables don't allow null as a key.
+     * @deprecated since 1.1.2
+     */
+    protected static volatile LogFactory nullClassLoaderFactory = null;
+
+    /**
+     * Create the hashtable which will be used to store a map of
+     * (context-classloader -> logfactory-object). Version 1.2+ of Java
+     * supports "weak references", allowing a custom Hashtable class
+     * to be used which uses only weak references to its keys. Using weak
+     * references can fix memory leaks on webapp unload in some cases (though
+     * not all). Version 1.1 of Java does not support weak references, so we
+     * must dynamically determine which we are using. And just for fun, this
+     * code also supports the ability for a system property to specify an
+     * arbitrary Hashtable implementation name.
+     * <p>
+     * Note that the correct way to ensure no memory leaks occur is to ensure
+     * that LogFactory.release(contextClassLoader) is called whenever a
+     * webapp is undeployed.
+     */
+    private static final Hashtable createFactoryStore() {
+        Hashtable result = null;
+        String storeImplementationClass;
+        try {
+            storeImplementationClass = getSystemProperty(HASHTABLE_IMPLEMENTATION_PROPERTY, null);
+        } catch (SecurityException ex) {
+            // Permissions don't allow this to be accessed. Default to the "modern"
+            // weak hashtable implementation if it is available.
+            storeImplementationClass = null;
+        }
+
+        if (storeImplementationClass == null) {
+            storeImplementationClass = WEAK_HASHTABLE_CLASSNAME;
+        }
+        try {
+            Class implementationClass = Class.forName(storeImplementationClass);
+            result = (Hashtable) implementationClass.newInstance();
+        } catch (Throwable t) {
+            handleThrowable(t); // may re-throw t
+
+            // ignore
+            if (!WEAK_HASHTABLE_CLASSNAME.equals(storeImplementationClass)) {
+                // if the user's trying to set up a custom implementation, give a clue
+                if (isDiagnosticsEnabled()) {
+                    // use internal logging to issue the warning
+                    logDiagnostic("[ERROR] LogFactory: Load of custom hashtable failed");
+                } else {
+                    // we *really* want this output, even if diagnostics weren't
+                    // explicitly enabled by the user.
+                    System.err.println("[ERROR] LogFactory: Load of custom hashtable failed");
+                }
+            }
+        }
+        if (result == null) {
+            result = new Hashtable();
+        }
+        return result;
+    }
+
+    // --------------------------------------------------------- Static Methods
+
+    /** Utility method to safely trim a string. */
+    private static String trim(String src) {
+        if (src == null) {
+            return null;
+        }
+        return src.trim();
+    }
+
+    /**
+     * Checks whether the supplied Throwable is one that needs to be
+     * re-thrown and ignores all others.
+     *
+     * The following errors are re-thrown:
+     * <ul>
+     *   <li>ThreadDeath</li>
+     *   <li>VirtualMachineError</li>
+     * </ul>
+     *
+     * @param t the Throwable to check
+     */
+    protected static void handleThrowable(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
+    }
+
+    /**
+     * Construct (if necessary) and return a <code>LogFactory</code>
+     * instance, using the following ordered lookup procedure to determine
+     * the name of the implementation class to be loaded.
+     * <p>
+     * <ul>
+     * <li>The <code>org.apache.commons.logging.LogFactory</code> system
+     *     property.</li>
+     * <li>The JDK 1.3 Service Discovery mechanism</li>
+     * <li>Use the properties file <code>commons-logging.properties</code>
+     *     file, if found in the class path of this class.  The configuration
+     *     file is in standard <code>java.util.Properties</code> format and
+     *     contains the fully qualified name of the implementation class
+     *     with the key being the system property defined above.</li>
+     * <li>Fall back to a default implementation class
+     *     (<code>org.apache.commons.logging.impl.LogFactoryImpl</code>).</li>
+     * </ul>
+     * <p>
+     * <em>NOTE</em> - If the properties file method of identifying the
+     * <code>LogFactory</code> implementation class is utilized, all of the
+     * properties defined in this file will be set as configuration attributes
+     * on the corresponding <code>LogFactory</code> instance.
+     * <p>
+     * <em>NOTE</em> - In a multi-threaded environment it is possible
+     * that two different instances will be returned for the same
+     * classloader environment.
+     *
+     * @throws LogConfigurationException if the implementation class is not
+     *  available or cannot be instantiated.
+     */
+    public static LogFactory getFactory() throws LogConfigurationException {
+        // Identify the class loader we will be using
+        ClassLoader contextClassLoader = getContextClassLoaderInternal();
+
+        if (contextClassLoader == null) {
+            // This is an odd enough situation to report about. This
+            // output will be a nuisance on JDK1.1, as the system
+            // classloader is null in that environment.
+            if (isDiagnosticsEnabled()) {
+                logDiagnostic("Context classloader is null.");
+            }
+        }
+
+        // Return any previously registered factory for this class loader
+        LogFactory factory = getCachedFactory(contextClassLoader);
+        if (factory != null) {
+            return factory;
+        }
+
+        if (isDiagnosticsEnabled()) {
+            logDiagnostic(
+                    "[LOOKUP] LogFactory implementation requested for the first time for context classloader " +
+                    objectId(contextClassLoader));
+            logHierarchy("[LOOKUP] ", contextClassLoader);
+        }
+
+        // Load properties file.
+        //
+        // If the properties file exists, then its contents are used as
+        // "attributes" on the LogFactory implementation class. One particular
+        // property may also control which LogFactory concrete subclass is
+        // used, but only if other discovery mechanisms fail..
+        //
+        // As the properties file (if it exists) will be used one way or
+        // another in the end we may as well look for it first.
+
+        Properties props = getConfigurationFile(contextClassLoader, FACTORY_PROPERTIES);
+
+        // Determine whether we will be using the thread context class loader to
+        // load logging classes or not by checking the loaded properties file (if any).
+        ClassLoader baseClassLoader = contextClassLoader;
+        if (props != null) {
+            String useTCCLStr = props.getProperty(TCCL_KEY);
+            if (useTCCLStr != null) {
+                // The Boolean.valueOf(useTCCLStr).booleanValue() formulation
+                // is required for Java 1.2 compatibility.
+                if (Boolean.valueOf(useTCCLStr).booleanValue() == false) {
+                    // Don't use current context classloader when locating any
+                    // LogFactory or Log classes, just use the class that loaded
+                    // this abstract class. When this class is deployed in a shared
+                    // classpath of a container, it means webapps cannot deploy their
+                    // own logging implementations. It also means that it is up to the
+                    // implementation whether to load library-specific config files
+                    // from the TCCL or not.
+                    baseClassLoader = thisClassLoader;
+                }
+            }
+        }
+
+        // Determine which concrete LogFactory subclass to use.
+        // First, try a global system property
+        if (isDiagnosticsEnabled()) {
+            logDiagnostic("[LOOKUP] Looking for system property [" + FACTORY_PROPERTY +
+                          "] to define the LogFactory subclass to use...");
+        }
+
+        try {
+            String factoryClass = getSystemProperty(FACTORY_PROPERTY, null);
+            if (factoryClass != null) {
+                if (isDiagnosticsEnabled()) {
+                    logDiagnostic("[LOOKUP] Creating an instance of LogFactory class '" + factoryClass +
+                                  "' as specified by system property " + FACTORY_PROPERTY);
+                }
+                factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);
+            } else {
+                if (isDiagnosticsEnabled()) {
+                    logDiagnostic("[LOOKUP] No system property [" + FACTORY_PROPERTY + "] defined.");
+                }
+            }
+        } catch (SecurityException e) {
+            if (isDiagnosticsEnabled()) {
+                logDiagnostic("[LOOKUP] A security exception occurred while trying to create an" +
+                              " instance of the custom factory class" + ": [" + trim(e.getMessage()) +
+                              "]. Trying alternative implementations...");
+            }
+            // ignore
+        } catch (RuntimeException e) {
+            // This is not consistent with the behaviour when a bad LogFactory class is
+            // specified in a services file.
+            //
+            // One possible exception that can occur here is a ClassCastException when
+            // the specified class wasn't castable to this LogFactory type.
+            if (isDiagnosticsEnabled()) {
+                logDiagnostic("[LOOKUP] An exception occurred while trying to create an" +
+                              " instance of the custom factory class" + ": [" +
+                              trim(e.getMessage()) +
+                              "] as specified by a system property.");
+            }
+            throw e;
+        }
+
+        // Second, try to find a service by using the JDK1.3 class
+        // discovery mechanism, which involves putting a file with the name
+        // of an interface class in the META-INF/services directory, where the
+        // contents of the file is a single line specifying a concrete class
+        // that implements the desired interface.
+
+        if (factory == null) {
+            if (isDiagnosticsEnabled()) {
+                logDiagnostic("[LOOKUP] Looking for a resource file of name [" + SERVICE_ID +
+                              "] to define the LogFactory subclass to use...");
+            }
+            try {
+                final InputStream is = getResourceAsStream(contextClassLoader, SERVICE_ID);
+
+                if( is != null ) {
+                    // This code is needed by EBCDIC and other strange systems.
+                    // It's a fix for bugs reported in xerces
+                    BufferedReader rd;
+                    try {
+                        rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
+                    } catch (java.io.UnsupportedEncodingException e) {
+                        rd = new BufferedReader(new InputStreamReader(is));
+                    }
+
+                    String factoryClassName = rd.readLine();
+                    rd.close();
+
+                    if (factoryClassName != null && ! "".equals(factoryClassName)) {
+                        if (isDiagnosticsEnabled()) {
+                            logDiagnostic("[LOOKUP]  Creating an instance of LogFactory class " +
+                                          factoryClassName +
+                                          " as specified by file '" + SERVICE_ID +
+                                          "' which was present in the path of the context classloader.");
+                        }
+                        factory = newFactory(factoryClassName, baseClassLoader, contextClassLoader );
+                    }
+                } else {
+                    // is == null
+                    if (isDiagnosticsEnabled()) {
+                        logDiagnostic("[LOOKUP] No resource file with name '" + SERVICE_ID + "' found.");
+                    }
+                }
+            } catch (Exception ex) {
+                // note: if the specified LogFactory class wasn't compatible with LogFactory
+                // for some reason, a ClassCastException will be caught here, and attempts will
+                // continue to find a compatible class.
+                if (isDiagnosticsEnabled()) {
+                    logDiagnostic(
+                        "[LOOKUP] A security exception occurred while trying to create an" +
+                        " instance of the custom factory class" +
+                        ": [" + trim(ex.getMessage()) +
+                        "]. Trying alternative implementations...");
+                }
+                // ignore
+            }
+        }
+
+        // Third try looking into the properties file read earlier (if found)
+
+        if (factory == null) {
+            if (props != null) {
+                if (isDiagnosticsEnabled()) {
+                    logDiagnostic(
+                        "[LOOKUP] Looking in properties file for entry with key '" + FACTORY_PROPERTY +
+                        "' to define the LogFactory subclass to use...");
+                }
+                String factoryClass = props.getProperty(FACTORY_PROPERTY);
+                if (factoryClass != null) {
+                    if (isDiagnosticsEnabled()) {
+                        logDiagnostic(
+                            "[LOOKUP] Properties file specifies LogFactory subclass '" + factoryClass + "'");
+                    }
+                    factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);
+
+                    // TODO: think about whether we need to handle exceptions from newFactory
+                } else {
+                    if (isDiagnosticsEnabled()) {
+                        logDiagnostic("[LOOKUP] Properties file has no entry specifying LogFactory subclass.");
+                    }
+                }
+            } else {
+                if (isDiagnosticsEnabled()) {
+                    logDiagnostic("[LOOKUP] No properties file available to determine" + " LogFactory subclass from..");
+                }
+            }
+        }
+
+        // Fourth, try the fallback implementation class
+
+        if (factory == null) {
+            if (isDiagnosticsEnabled()) {
+                logDiagnostic(
+                    "[LOOKUP] Loading the default LogFactory implementation '" + FACTORY_DEFAULT +
+                    "' via the same classloader that loaded this LogFactory" +
+                    " class (ie not looking in the context classloader).");
+            }
+
+            // Note: unlike the above code which can try to load custom LogFactory
+            // implementations via the TCCL, we don't try to load the default LogFactory
+            // implementation via the context classloader because:
+            // * that can cause problems (see comments in newFactory method)
+            // * no-one should be customising the code of the default class
+            // Yes, we do give up the ability for the child to ship a newer
+            // version of the LogFactoryImpl class and have it used dynamically
+            // by an old LogFactory class in the parent, but that isn't
+            // necessarily a good idea anyway.
+            factory = newFactory(FACTORY_DEFAULT, thisClassLoader, contextClassLoader);
+        }
+
+        if (factory != null) {
+            /**
+             * Always cache using context class loader.
+             */
+            cacheFactory(contextClassLoader, factory);
+
+            if (props != null) {
+                Enumeration names = props.propertyNames();
+                while (names.hasMoreElements()) {
+                    String name = (String) names.nextElement();
+                    String value = props.getProperty(name);
+                    factory.setAttribute(name, value);
+                }
+            }
+        }
+
+        return factory;
+    }
+
+    /**
+     * Convenience method to return a named logger, without the application
+     * having to care about factories.
+     *
+     * @param clazz Class from which a log name will be derived
+     * @throws LogConfigurationException if a suitable <code>Log</code>
+     *  instance cannot be returned
+     */
+    public static Log getLog(Class clazz) throws LogConfigurationException {
+        return getFactory().getInstance(clazz);
+    }
+
+    /**
+     * Convenience method to return a named logger, without the application
+     * having to care about factories.
+     *
+     * @param name Logical name of the <code>Log</code> 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</code>
+     *  instance cannot be returned
+     */
+    public static Log getLog(String name) throws LogConfigurationException {
+        return getFactory().getInstance(name);
+    }
+
+    /**
+     * 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()</code> on
+     * each of them.
+     *
+     * @param classLoader ClassLoader for which to release the LogFactory
+     */
+    public static void release(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()</code> 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()) {
+                LogFactory element = (LogFactory) elements.nextElement();
+                element.release();
+            }
+            factories.clear();
+
+            if (nullClassLoaderFactory != null) {
+                nullClassLoaderFactory.release();
+                nullClassLoaderFactory = null;
+            }
+        }
+    }
+
+    // ------------------------------------------------------ Protected Methods
+
+    /**
+     * Safely get access to the classloader for the specified class.
+     * <p>
+     * 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.
+     * <p>
+     * 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.
+     * <p>
+     * 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.
+     *
+     * @since 1.1
+     */
+    protected static ClassLoader getClassLoader(Class clazz) {
+        try {
+            return clazz.getClassLoader();
+        } catch (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.
+     * <p>
+     * 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.
+     * <p>
+     * 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() {
+                public Object run() {
+                    return directGetContextClassLoader();
+                }
+            });
+    }
+
+    /**
+     * Return the thread context class loader if available; otherwise return null.
+     * <p>
+     * Most/all code should call getContextClassLoaderInternal rather than
+     * calling this method directly.
+     * <p>
+     * The thread context class loader is available for JDK 1.2
+     * or later, if certain security conditions are met.
+     * <p>
+     * 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 (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(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;
+        } else {
+            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(ClassLoader classLoader, 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</code>
+     * implementation class, loaded by the specified class loader.
+     * If that fails, try the class loader used to load this
+     * (abstract) LogFactory.
+     * <h2>ClassLoader conflicts</h2>
+     * <p>
+     * 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.
+     * <p>
+     * The problem is the same one that can occur when loading a concrete Log
+     * subclass via a context classloader.
+     * <p>
+     * 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 at childloader. It cannot be cast to
+     * LogFactory at 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.
+     * <p>
+     * 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</code>
+     *  implementation class
+     * @param classLoader ClassLoader from which to load this class
+     * @param contextClassLoader is the context that this new factory will
+     *  manage logging for.
+     * @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.
+        Object result = AccessController.doPrivileged(
+            new PrivilegedAction() {
+                public Object run() {
+                    return createFactory(factoryClass, classLoader);
+                }
+            });
+
+        if (result instanceof LogConfigurationException) {
+            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.
+     * <p>
+     * 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.
+     */
+    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
+     * @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(String factoryClass, 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.newInstance();
+
+                } catch (ClassNotFoundException ex) {
+                    if (classLoader == thisClassLoader) {
+                        // 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 (NoClassDefFoundError e) {
+                    if (classLoader == thisClassLoader) {
+                        // 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 (ClassCastException e) {
+                    if (classLoader == thisClassLoader) {
+                        // There's no point in falling through to the code below that
+                        // tries again with thisClassLoader, 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 StringBuffer msg = new StringBuffer();
+                        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 thisClassLoader.
+                    // 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 (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);
+        }
+    }
+
+    /**
+     * Determines whether the given class actually implements <code>LogFactory</code>.
+     * Diagnostic information is also logged.
+     * <p>
+     * <strong>Usage:</strong> to diagnose whether a classloader conflict is the cause
+     * of incompatibility. The test used is whether the class is assignable from
+     * the <code>LogFactory</code> class loaded by the class's classloader.
+     * @param logFactoryClass <code>Class</code> which may implement <code>LogFactory</code>
+     * @return true if the <code>logFactoryClass</code> does extend
+     * <code>LogFactory</code> when that class is loaded via the same
+     * classloader that loaded the <code>logFactoryClass</code>.
+     */
+    private static boolean implementsLogFactory(Class logFactoryClass) {
+        boolean implementsLogFactory = false;
+        if (logFactoryClass != null) {
+            try {
+                ClassLoader logFactoryClassLoader = logFactoryClass.getClassLoader();
+                if (logFactoryClassLoader == null) {
+                    logDiagnostic("[CUSTOM LOG FACTORY] was loaded by the boot classloader");
+                } else {
+                    logHierarchy("[CUSTOM LOG FACTORY] ", logFactoryClassLoader);
+                    Class factoryFromCustomLoader
+                        = Class.forName("org.apache.commons.logging.LogFactory", false, logFactoryClassLoader);
+                    implementsLogFactory = factoryFromCustomLoader.isAssignableFrom(logFactoryClass);
+                    if (implementsLogFactory) {
+                        logDiagnostic("[CUSTOM LOG FACTORY] " + logFactoryClass.getName() +
+                                      " implements LogFactory but was loaded by an incompatible classloader.");
+                    } else {
+                        logDiagnostic("[CUSTOM LOG FACTORY] " + logFactoryClass.getName() +
+                                      " does not implement LogFactory.");
+                    }
+                }
+            } catch (SecurityException e) {
+                //
+                // The application is running within a hostile security environment.
+                // This will make it very hard to diagnose issues with JCL.
+                // Consider running less securely whilst debugging this issue.
+                //
+                logDiagnostic("[CUSTOM LOG FACTORY] SecurityException thrown whilst trying to determine whether " +
+                              "the compatibility was caused by a classloader conflict: " + e.getMessage());
+            } catch (LinkageError e) {
+                //
+                // This should be an unusual circumstance.
+                // LinkageError's usually indicate that a dependent class has incompatibly changed.
+                // Another possibility may be an exception thrown by an initializer.
+                // Time for a clean rebuild?
+                //
+                logDiagnostic("[CUSTOM LOG FACTORY] LinkageError thrown whilst trying to determine whether " +
+                              "the compatibility was caused by a classloader conflict: " + e.getMessage());
+            } catch (ClassNotFoundException e) {
+                //
+                // LogFactory cannot be loaded by the classloader which loaded the custom factory implementation.
+                // The custom implementation is not viable until this is corrected.
+                // Ensure that the JCL jar and the custom class are available from the same classloader.
+                // Running with diagnostics on should give information about the classloaders used
+                // to load the custom factory.
+                //
+                logDiagnostic("[CUSTOM LOG FACTORY] LogFactory class cannot be loaded by classloader which loaded " +
+                              "the custom LogFactory implementation. Is the custom factory in the right classloader?");
+            }
+        }
+        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() {
+                public Object run() {
+                    if (loader != null) {
+                        return loader.getResourceAsStream(name);
+                    } else {
+                        return ClassLoader.getSystemResourceAsStream(name);
+                    }
+                }
+            });
+    }
+
+    /**
+     * Given a filename, return an enumeration of URLs pointing to
+     * all the occurrences of that filename in the classpath.
+     * <p>
+     * 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.
+     * <p>
+     * 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) {
+        PrivilegedAction action =
+            new PrivilegedAction() {
+                public Object run() {
+                    try {
+                        if (loader != null) {
+                            return loader.getResources(name);
+                        } else {
+                            return ClassLoader.getSystemResources(name);
+                        }
+                    } catch (IOException e) {
+                        if (isDiagnosticsEnabled()) {
+                            logDiagnostic("Exception while trying to find configuration file " +
+                                          name + ":" + e.getMessage());
+                        }
+                        return null;
+                    } catch (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;
+                    }
+                }
+            };
+        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.
+     * <p>
+     * {@code Null} is returned if the URL cannot be opened.
+     */
+    private static Properties getProperties(final URL url) {
+        PrivilegedAction action =
+            new PrivilegedAction() {
+                public Object run() {
+                    InputStream stream = null;
+                    try {
+                        // We must ensure that useCaches is set to false, as the
+                        // default behaviour of java is to cache file handles, and
+                        // this "locks" files, preventing hot-redeploy on windows.
+                        URLConnection connection = url.openConnection();
+                        connection.setUseCaches(false);
+                        stream = connection.getInputStream();
+                        if (stream != null) {
+                            Properties props = new Properties();
+                            props.load(stream);
+                            stream.close();
+                            stream = null;
+                            return props;
+                        }
+                    } catch (IOException e) {
+                        if (isDiagnosticsEnabled()) {
+                            logDiagnostic("Unable to read URL " + url);
+                        }
+                    } finally {
+                        if (stream != null) {
+                            try {
+                                stream.close();
+                            } catch (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.
+     * <p>
+     * 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.
+     * <p>
+     * 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.
+     * <p>
+     * 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(ClassLoader classLoader, String fileName) {
+        Properties props = null;
+        double priority = 0.0;
+        URL propsUrl = null;
+        try {
+            Enumeration urls = getResources(classLoader, fileName);
+
+            if (urls == null) {
+                return null;
+            }
+
+            while (urls.hasMoreElements()) {
+                URL url = (URL) urls.nextElement();
+
+                Properties newProps = getProperties(url);
+                if (newProps != null) {
+                    if (props == null) {
+                        propsUrl = url;
+                        props = newProps;
+                        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 {
+                        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 (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.
+     * <p>
+     * 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() {
+                    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
+     * output by setting the system property named {@link #DIAGNOSTICS_DEST_PROPERTY} to
+     * a filename, or the special values STDOUT or STDERR.
+     */
+    private static PrintStream initDiagnostics() {
+        String dest;
+        try {
+            dest = getSystemProperty(DIAGNOSTICS_DEST_PROPERTY, null);
+            if (dest == null) {
+                return null;
+            }
+        } catch (SecurityException ex) {
+            // We must be running in some very secure environment.
+            // We just have to assume output is not wanted..
+            return null;
+        }
+
+        if (dest.equals("STDOUT")) {
+            return System.out;
+        } else if (dest.equals("STDERR")) {
+            return System.err;
+        } else {
+            try {
+                // open the file in append mode
+                FileOutputStream fos = new FileOutputStream(dest, true);
+                return new PrintStream(fos);
+            } catch (IOException ex) {
+                // We should report this to the user - but how?
+                return null;
+            }
+        }
+    }
+
+    /**
+     * Indicates true if the user has enabled internal logging.
+     * <p>
+     * By the way, sorry for the incorrect grammar, but calling this method
+     * areDiagnosticsEnabled just isn't java beans style.
+     *
+     * @return true if calls to logDiagnostic will have any effect.
+     * @since 1.1
+     */
+    protected static boolean isDiagnosticsEnabled() {
+        return diagnosticsStream != null;
+    }
+
+    /**
+     * Write the specified message to the internal logging destination.
+     * <p>
+     * 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 at ....,
+     * while subclasses should put SomeSubClass at ...
+     * <p>
+     * Subclasses should instead compute their own prefix, then call
+     * logRawDiagnostic. Note that calling isDiagnosticsEnabled is
+     * fine for subclasses.
+     * <p>
+     * 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(String msg) {
+        if (diagnosticsStream != null) {
+            diagnosticsStream.print(diagnosticPrefix);
+            diagnosticsStream.println(msg);
+            diagnosticsStream.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(String msg) {
+        if (diagnosticsStream != null) {
+            diagnosticsStream.println(msg);
+            diagnosticsStream.flush();
+        }
+    }
+
+    /**
+     * Generate useful diagnostics regarding the classloader tree for
+     * the specified class.
+     * <p>
+     * As an example, if the specified class was loaded via a webapp's
+     * classloader, then you may get the following output:
+     * <pre>
+     * Class com.acme.Foo was loaded via classloader 11111
+     * ClassLoader tree: 11111 -> 22222 (SYSTEM) -> 33333 -> BOOT
+     * </pre>
+     * <p>
+     * This method returns immediately if isDiagnosticsEnabled()
+     * returns false.
+     *
+     * @param clazz is the class whose classloader + tree are to be
+     * output.
+     */
+    private static void logClassLoaderEnvironment(Class clazz) {
+        if (!isDiagnosticsEnabled()) {
+            return;
+        }
+
+        try {
+            // Deliberately use System.getProperty here instead of getSystemProperty; if
+            // the overall security policy for the calling application forbids access to
+            // these variables then we do not want to output them to the diagnostic stream.
+            logDiagnostic("[ENV] Extension directories (java.ext.dir): " + System.getProperty("java.ext.dir"));
+            logDiagnostic("[ENV] Application classpath (java.class.path): " + System.getProperty("java.class.path"));
+        } catch (SecurityException ex) {
+            logDiagnostic("[ENV] Security setting prevent interrogation of system classpaths.");
+        }
+
+        String className = clazz.getName();
+        ClassLoader classLoader;
+
+        try {
+            classLoader = getClassLoader(clazz);
+        } catch (SecurityException ex) {
+            // not much useful diagnostics we can print here!
+            logDiagnostic("[ENV] Security forbids determining the classloader for " + className);
+            return;
+        }
+
+        logDiagnostic("[ENV] Class " + className + " was loaded via classloader " + objectId(classLoader));
+        logHierarchy("[ENV] Ancestry of classloader which loaded " + className + " is ", classLoader);
+    }
+
+    /**
+     * Logs diagnostic messages about the given classloader
+     * and it's hierarchy. The prefix is prepended to the message
+     * and is intended to make it easier to understand the logs.
+     * @param prefix
+     * @param classLoader
+     */
+    private static void logHierarchy(String prefix, ClassLoader classLoader) {
+        if (!isDiagnosticsEnabled()) {
+            return;
+        }
+        ClassLoader systemClassLoader;
+        if (classLoader != null) {
+            final String classLoaderString = classLoader.toString();
+            logDiagnostic(prefix + objectId(classLoader) + " == '" + classLoaderString + "'");
+        }
+
+        try {
+            systemClassLoader = ClassLoader.getSystemClassLoader();
+        } catch (SecurityException ex) {
+            logDiagnostic(prefix + "Security forbids determining the system classloader.");
+            return;
+        }
+        if (classLoader != null) {
+            final StringBuffer buf = new StringBuffer(prefix + "ClassLoader tree:");
+            for(;;) {
+                buf.append(objectId(classLoader));
+                if (classLoader == systemClassLoader) {
+                    buf.append(" (SYSTEM) ");
+                }
+
+                try {
+                    classLoader = classLoader.getParent();
+                } catch (SecurityException ex) {
+                    buf.append(" --> SECRET");
+                    break;
+                }
+
+                buf.append(" --> ");
+                if (classLoader == null) {
+                    buf.append("BOOT");
+                    break;
+                }
+            }
+            logDiagnostic(buf.toString());
+        }
+    }
+
+    /**
+     * Returns a string that uniquely identifies the specified object, including
+     * its class.
+     * <p>
+     * The returned string is of form "classname at hashcode", ie is the same as
+     * the return value of the Object.toString() method, but works even when
+     * the specified object's class has overidden the toString method.
+     *
+     * @param o may be null.
+     * @return a string of form classname at hashcode, or "null" if param o is null.
+     * @since 1.1
+     */
+    public static String objectId(Object o) {
+        if (o == null) {
+            return "null";
+        } else {
+            return o.getClass().getName() + "@" + System.identityHashCode(o);
+        }
+    }
+
+    // ----------------------------------------------------------------------
+    // Static initialiser block to perform initialisation at class load time.
+    //
+    // We can't do this in the class constructor, as there are many
+    // static methods on this class that can be called before any
+    // LogFactory instances are created, and they depend upon this
+    // stuff having been set up.
+    //
+    // Note that this block must come after any variable declarations used
+    // by any methods called from this block, as we want any static initialiser
+    // associated with the variable to run first. If static initialisers for
+    // variables run after this code, then (a) their value might be needed
+    // by methods called from here, and (b) they might *override* any value
+    // computed here!
+    //
+    // So the wisest thing to do is just to place this code at the very end
+    // of the class file.
+    // ----------------------------------------------------------------------
+
+    static {
+        // note: it's safe to call methods before initDiagnostics (though
+        // diagnostic output gets discarded).
+        thisClassLoader = getClassLoader(LogFactory.class);
+        // 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 {
+            ClassLoader classLoader = thisClassLoader;
+            if (thisClassLoader == null) {
+                classLoaderName = "BOOTLOADER";
+            } else {
+                classLoaderName = objectId(classLoader);
+            }
+        } catch (SecurityException e) {
+            classLoaderName = "UNKNOWN";
+        }
+        diagnosticPrefix = "[LogFactory from " + classLoaderName + "] ";
+        diagnosticsStream = initDiagnostics();
+        logClassLoaderEnvironment(LogFactory.class);
+        factories = createFactoryStore();
+        if (isDiagnosticsEnabled()) {
+            logDiagnostic("BOOTSTRAP COMPLETED");
+        }
+    }
+}
diff --git a/src/main/java/org/apache/commons/logging/LogSource.java b/src/main/java/org/apache/commons/logging/LogSource.java
new file mode 100644
index 0000000..95a12c0
--- /dev/null
+++ b/src/main/java/org/apache/commons/logging/LogSource.java
@@ -0,0 +1,219 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging;
+
+import java.lang.reflect.Constructor;
+import java.util.Hashtable;
+
+import org.apache.commons.logging.impl.NoOpLog;
+
+/**
+ * Factory for creating {@link Log} instances.  Applications should call
+ * the <code>makeNewLogInstance()</code> method to instantiate new instances
+ * of the configured {@link Log} implementation class.
+ * <p>
+ * By default, calling <code>getInstance()</code> will use the following
+ * algorithm:
+ * <ul>
+ * <li>If Log4J is available, return an instance of
+ *     <code>org.apache.commons.logging.impl.Log4JLogger</code>.</li>
+ * <li>If JDK 1.4 or later is available, return an instance of
+ *     <code>org.apache.commons.logging.impl.Jdk14Logger</code>.</li>
+ * <li>Otherwise, return an instance of
+ *     <code>org.apache.commons.logging.impl.NoOpLog</code>.</li>
+ * </ul>
+ * <p>
+ * You can change the default behavior in one of two ways:
+ * <ul>
+ * <li>On the startup command line, set the system property
+ *     <code>org.apache.commons.logging.log</code> to the name of the
+ *     <code>org.apache.commons.logging.Log</code> implementation class
+ *     you want to use.</li>
+ * <li>At runtime, call <code>LogSource.setLogImplementation()</code>.</li>
+ * </ul>
+ *
+ * @deprecated Use {@link LogFactory} instead - The default factory
+ *  implementation performs exactly the same algorithm as this class did
+ *
+ * @version $Id: LogSource.java 1432675 2013-01-13 17:53:30Z tn $
+ */
+public class LogSource {
+
+    // ------------------------------------------------------- Class Attributes
+
+    static protected Hashtable logs = new Hashtable();
+
+    /** Is log4j available (in the current classpath) */
+    static protected boolean log4jIsAvailable = false;
+
+    /** Is JDK 1.4 logging available */
+    static protected boolean jdk14IsAvailable = false;
+
+    /** Constructor for current log class */
+    static protected Constructor logImplctor = null;
+
+    // ----------------------------------------------------- Class Initializers
+
+    static {
+
+        // Is Log4J Available?
+        try {
+            log4jIsAvailable = null != Class.forName("org.apache.log4j.Logger");
+        } catch (Throwable t) {
+            log4jIsAvailable = false;
+        }
+
+        // Is JDK 1.4 Logging Available?
+        try {
+            jdk14IsAvailable = null != Class.forName("java.util.logging.Logger") &&
+                               null != Class.forName("org.apache.commons.logging.impl.Jdk14Logger");
+        } catch (Throwable t) {
+            jdk14IsAvailable = false;
+        }
+
+        // Set the default Log implementation
+        String name = null;
+        try {
+            name = System.getProperty("org.apache.commons.logging.log");
+            if (name == null) {
+                name = System.getProperty("org.apache.commons.logging.Log");
+            }
+        } catch (Throwable t) {
+        }
+        if (name != null) {
+            try {
+                setLogImplementation(name);
+            } catch (Throwable t) {
+                try {
+                    setLogImplementation("org.apache.commons.logging.impl.NoOpLog");
+                } catch (Throwable u) {
+                    // ignored
+                }
+            }
+        } else {
+            try {
+                if (log4jIsAvailable) {
+                    setLogImplementation("org.apache.commons.logging.impl.Log4JLogger");
+                } else if (jdk14IsAvailable) {
+                    setLogImplementation("org.apache.commons.logging.impl.Jdk14Logger");
+                } else {
+                    setLogImplementation("org.apache.commons.logging.impl.NoOpLog");
+                }
+            } catch (Throwable t) {
+                try {
+                    setLogImplementation("org.apache.commons.logging.impl.NoOpLog");
+                } catch (Throwable u) {
+                    // ignored
+                }
+            }
+        }
+
+    }
+
+    // ------------------------------------------------------------ Constructor
+
+    /** Don't allow others to create instances. */
+    private LogSource() {
+    }
+
+    // ---------------------------------------------------------- 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).
+     */
+    static public void setLogImplementation(String classname)
+        throws LinkageError, NoSuchMethodException, SecurityException, ClassNotFoundException {
+        try {
+            Class logclass = Class.forName(classname);
+            Class[] argtypes = new Class[1];
+            argtypes[0] = "".getClass();
+            logImplctor = logclass.getConstructor(argtypes);
+        } catch (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).
+     */
+    static public void setLogImplementation(Class logclass)
+        throws LinkageError, ExceptionInInitializerError, NoSuchMethodException, SecurityException {
+        Class[] argtypes = new Class[1];
+        argtypes[0] = "".getClass();
+        logImplctor = logclass.getConstructor(argtypes);
+    }
+
+    /** Get a <code>Log</code> instance by class name. */
+    static public Log getInstance(String name) {
+        Log log = (Log) logs.get(name);
+        if (null == log) {
+            log = makeNewLogInstance(name);
+            logs.put(name, log);
+        }
+        return log;
+    }
+
+    /** Get a <code>Log</code> instance by class. */
+    static public Log getInstance(Class clazz) {
+        return getInstance(clazz.getName());
+    }
+
+    /**
+     * Create a new {@link Log} implementation, based on the given <i>name</i>.
+     * <p>
+     * The specific {@link Log} implementation returned is determined by the
+     * value of the <tt>org.apache.commons.logging.log</tt> property. The value
+     * of <tt>org.apache.commons.logging.log</tt> may be set to the fully specified
+     * name of a class that implements the {@link Log} interface. This class must
+     * also have a public constructor that takes a single {@link String} argument
+     * (containing the <i>name</i> of the {@link Log} to be constructed.
+     * <p>
+     * When <tt>org.apache.commons.logging.log</tt> is not set, or when no corresponding
+     * class can be found, this method will return a Log4JLogger if the log4j Logger
+     * class is available in the {@link LogSource}'s classpath, or a Jdk14Logger if we
+     * are on a JDK 1.4 or later system, or NoOpLog if neither of the above conditions is true.
+     *
+     * @param name the log name (or category)
+     */
+    static public Log makeNewLogInstance(String name) {
+        Log log;
+        try {
+            Object[] args = { name };
+            log = (Log) logImplctor.newInstance(args);
+        } catch (Throwable t) {
+            log = null;
+        }
+        if (null == log) {
+            log = new NoOpLog(name);
+        }
+        return log;
+    }
+
+    /**
+     * Returns a {@link String} array containing the names of
+     * all logs known to me.
+     */
+    static public String[] getLogNames() {
+        return (String[]) logs.keySet().toArray(new String[logs.size()]);
+    }
+}
diff --git a/src/main/java/org/apache/commons/logging/impl/AvalonLogger.java b/src/main/java/org/apache/commons/logging/impl/AvalonLogger.java
new file mode 100644
index 0000000..344462b
--- /dev/null
+++ b/src/main/java/org/apache/commons/logging/impl/AvalonLogger.java
@@ -0,0 +1,298 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.impl;
+
+import org.apache.avalon.framework.logger.Logger;
+import org.apache.commons.logging.Log;
+
+/**
+ * Implementation of commons-logging Log interface that delegates all
+ * logging calls to the Avalon logging abstraction: the Logger interface.
+ * <p>
+ * There are two ways in which this class can be used:
+ * <ul>
+ * <li>the instance can be constructed with an Avalon logger
+ * (by calling {@link #AvalonLogger(Logger)}). In this case, it acts
+ * as a simple thin wrapping implementation over the logger. This is
+ * particularly useful when using a property setter.
+ * </li>
+ * <li>the {@link #setDefaultLogger} class property can be called which
+ * sets the ancestral Avalon logger for this class. Any <code>AvalonLogger</code>
+ * instances created through the <code>LogFactory</code> mechanisms will output
+ * to child loggers of this <code>Logger</code>.
+ * </li>
+ * </ul>
+ * <p>
+ * <strong>Note:</strong> <code>AvalonLogger</code> does not implement Serializable
+ * because the constructors available for it make this impossible to achieve in all
+ * circumstances; there is no way to "reconnect" to an underlying Logger object on
+ * deserialization if one was just passed in to the constructor of the original
+ * object. This class <i>was</i> marked Serializable in the 1.0.4 release of
+ * commons-logging, but this never actually worked (a NullPointerException would
+ * be thrown as soon as the deserialized object was used), so removing this marker
+ * is not considered to be an incompatible change.
+ *
+ * @version $Id: AvalonLogger.java 1435115 2013-01-18 12:40:19Z tn $
+ */
+public class AvalonLogger implements Log {
+
+    /** Ancestral Avalon logger. */
+    private static volatile Logger defaultLogger = null;
+    /** Avalon logger used to perform log. */
+    private final transient Logger logger;
+
+    /**
+     * Constructs an <code>AvalonLogger</code> that outputs to the given
+     * <code>Logger</code> instance.
+     *
+     * @param logger the Avalon logger implementation to delegate to
+     */
+    public AvalonLogger(Logger logger) {
+        this.logger = logger;
+    }
+
+    /**
+     * Constructs an <code>AvalonLogger</code> that will log to a child
+     * of the <code>Logger</code> set by calling {@link #setDefaultLogger}.
+     *
+     * @param name the name of the avalon logger implementation to delegate to
+     */
+    public AvalonLogger(String name) {
+        if (defaultLogger == null) {
+            throw new NullPointerException("default logger has to be specified if this constructor is used!");
+        }
+        this.logger = defaultLogger.getChildLogger(name);
+    }
+
+    /**
+     * Gets the Avalon logger implementation used to perform logging.
+     *
+     * @return avalon logger implementation
+     */
+    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(Logger logger) {
+        defaultLogger = logger;
+    }
+
+    /**
+    * Logs a message with <code>org.apache.avalon.framework.logger.Logger.debug</code>.
+    *
+    * @param message to log
+    * @param t log this cause
+    * @see org.apache.commons.logging.Log#debug(Object, Throwable)
+     */
+    public void debug(Object message, Throwable t) {
+        if (getLogger().isDebugEnabled()) {
+            getLogger().debug(String.valueOf(message), t);
+        }
+    }
+
+    /**
+     * Logs a message with <code>org.apache.avalon.framework.logger.Logger.debug</code>.
+     *
+     * @param message to log.
+     * @see org.apache.commons.logging.Log#debug(Object)
+     */
+    public void debug(Object message) {
+        if (getLogger().isDebugEnabled()) {
+            getLogger().debug(String.valueOf(message));
+        }
+    }
+
+    /**
+     * Logs a message with <code>org.apache.avalon.framework.logger.Logger.error</code>.
+     *
+     * @param message to log
+     * @param t log this cause
+     * @see org.apache.commons.logging.Log#error(Object, Throwable)
+     */
+    public void error(Object message, Throwable t) {
+        if (getLogger().isErrorEnabled()) {
+            getLogger().error(String.valueOf(message), t);
+        }
+    }
+
+    /**
+     * Logs a message with <code>org.apache.avalon.framework.logger.Logger.error</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#error(Object)
+     */
+    public void error(Object message) {
+        if (getLogger().isErrorEnabled()) {
+            getLogger().error(String.valueOf(message));
+        }
+    }
+
+    /**
+     * Logs a message with <code>org.apache.avalon.framework.logger.Logger.fatalError</code>.
+     *
+     * @param message to log.
+     * @param t log this cause.
+     * @see org.apache.commons.logging.Log#fatal(Object, Throwable)
+     */
+    public void fatal(Object message, Throwable t) {
+        if (getLogger().isFatalErrorEnabled()) {
+            getLogger().fatalError(String.valueOf(message), t);
+        }
+    }
+
+    /**
+     * Logs a message with <code>org.apache.avalon.framework.logger.Logger.fatalError</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#fatal(Object)
+     */
+    public void fatal(Object message) {
+        if (getLogger().isFatalErrorEnabled()) {
+            getLogger().fatalError(String.valueOf(message));
+        }
+    }
+
+    /**
+     * Logs a message with <code>org.apache.avalon.framework.logger.Logger.info</code>.
+     *
+     * @param message to log
+     * @param t log this cause
+     * @see org.apache.commons.logging.Log#info(Object, Throwable)
+     */
+    public void info(Object message, Throwable t) {
+        if (getLogger().isInfoEnabled()) {
+            getLogger().info(String.valueOf(message), t);
+        }
+    }
+
+    /**
+     * Logs a message with <code>org.apache.avalon.framework.logger.Logger.info</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#info(Object)
+     */
+    public void info(Object message) {
+        if (getLogger().isInfoEnabled()) {
+            getLogger().info(String.valueOf(message));
+        }
+    }
+
+    /**
+     * Is logging to <code>org.apache.avalon.framework.logger.Logger.debug</code> enabled?
+     * @see org.apache.commons.logging.Log#isDebugEnabled()
+     */
+    public boolean isDebugEnabled() {
+        return getLogger().isDebugEnabled();
+    }
+
+    /**
+     * Is logging to <code>org.apache.avalon.framework.logger.Logger.error</code> enabled?
+     * @see org.apache.commons.logging.Log#isErrorEnabled()
+     */
+    public boolean isErrorEnabled() {
+        return getLogger().isErrorEnabled();
+    }
+
+    /**
+     * Is logging to <code>org.apache.avalon.framework.logger.Logger.fatalError</code> enabled?
+     * @see org.apache.commons.logging.Log#isFatalEnabled()
+     */
+    public boolean isFatalEnabled() {
+        return getLogger().isFatalErrorEnabled();
+    }
+
+    /**
+     * Is logging to <code>org.apache.avalon.framework.logger.Logger.info</code> enabled?
+     * @see org.apache.commons.logging.Log#isInfoEnabled()
+     */
+    public boolean isInfoEnabled() {
+        return getLogger().isInfoEnabled();
+    }
+
+    /**
+     * Is logging to <code>org.apache.avalon.framework.logger.Logger.debug</code> enabled?
+     * @see org.apache.commons.logging.Log#isTraceEnabled()
+     */
+    public boolean isTraceEnabled() {
+        return getLogger().isDebugEnabled();
+    }
+
+    /**
+     * Is logging to <code>org.apache.avalon.framework.logger.Logger.warn</code> enabled?
+     * @see org.apache.commons.logging.Log#isWarnEnabled()
+     */
+    public boolean isWarnEnabled() {
+        return getLogger().isWarnEnabled();
+    }
+
+    /**
+     * Logs a message with <code>org.apache.avalon.framework.logger.Logger.debug</code>.
+     *
+     * @param message to log.
+     * @param t log this cause.
+     * @see org.apache.commons.logging.Log#trace(Object, Throwable)
+     */
+    public void trace(Object message, Throwable t) {
+        if (getLogger().isDebugEnabled()) {
+            getLogger().debug(String.valueOf(message), t);
+        }
+    }
+
+    /**
+     * Logs a message with <code>org.apache.avalon.framework.logger.Logger.debug</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#trace(Object)
+     */
+    public void trace(Object message) {
+        if (getLogger().isDebugEnabled()) {
+            getLogger().debug(String.valueOf(message));
+        }
+    }
+
+    /**
+     * Logs a message with <code>org.apache.avalon.framework.logger.Logger.warn</code>.
+     *
+     * @param message to log
+     * @param t log this cause
+     * @see org.apache.commons.logging.Log#warn(Object, Throwable)
+     */
+    public void warn(Object message, Throwable t) {
+        if (getLogger().isWarnEnabled()) {
+            getLogger().warn(String.valueOf(message), t);
+        }
+    }
+
+    /**
+     * Logs a message with <code>org.apache.avalon.framework.logger.Logger.warn</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#warn(Object)
+     */
+    public void warn(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
new file mode 100644
index 0000000..da3faa4
--- /dev/null
+++ b/src/main/java/org/apache/commons/logging/impl/Jdk13LumberjackLogger.java
@@ -0,0 +1,302 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.impl;
+
+import java.io.Serializable;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.logging.LogRecord;
+import java.util.StringTokenizer;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import org.apache.commons.logging.Log;
+
+/**
+ * Implementation of the <code>org.apache.commons.logging.Log</code>
+ * interface that wraps the standard JDK logging mechanisms that are
+ * available in SourceForge's Lumberjack for JDKs prior to 1.4.
+ *
+ * @version $Id: Jdk13LumberjackLogger.java 1432663 2013-01-13 17:24:18Z tn $
+ * @since 1.1
+ */
+public class Jdk13LumberjackLogger implements Log, Serializable {
+
+    /** Serializable version identifier. */
+    private static final long serialVersionUID = -8649807923527610591L;
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The underlying Logger implementation we are using.
+     */
+    protected transient Logger logger = null;
+    protected String name = null;
+    private String sourceClassName = "unknown";
+    private String sourceMethodName = "unknown";
+    private boolean classAndMethodFound = false;
+
+    /**
+     * This member variable simply ensures that any attempt to initialise
+     * 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
+
+    /**
+     * Construct a named instance of this Logger.
+     *
+     * @param name Name of the logger to be constructed
+     */
+    public Jdk13LumberjackLogger(String name) {
+        this.name = name;
+        logger = getLogger();
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+    private void log( Level level, String msg, Throwable ex ) {
+        if( getLogger().isLoggable(level) ) {
+            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 {
+            Throwable throwable = new Throwable();
+            throwable.fillInStackTrace();
+            StringWriter stringWriter = new StringWriter();
+            PrintWriter printWriter = new PrintWriter( stringWriter );
+            throwable.printStackTrace( printWriter );
+            String traceString = stringWriter.getBuffer().toString();
+            StringTokenizer tokenizer =
+                new StringTokenizer( traceString, "\n" );
+            tokenizer.nextToken();
+            String line = tokenizer.nextToken();
+            while ( line.indexOf( this.getClass().getName() )  == -1 ) {
+                line = tokenizer.nextToken();
+            }
+            while ( line.indexOf( this.getClass().getName() ) >= 0 ) {
+                line = tokenizer.nextToken();
+            }
+            int start = line.indexOf( "at " ) + 3;
+            int end = line.indexOf( '(' );
+            String temp = line.substring( start, end );
+            int lastPeriod = temp.lastIndexOf( '.' );
+            sourceClassName = temp.substring( 0, lastPeriod );
+            sourceMethodName = temp.substring( lastPeriod + 1 );
+        } catch ( Exception ex ) {
+            // ignore - leave class and methodname unknown
+        }
+        classAndMethodFound = true;
+    }
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.FINE</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#debug(Object)
+     */
+    public void debug(Object message) {
+        log(Level.FINE, String.valueOf(message), null);
+    }
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.FINE</code>.
+     *
+     * @param message to log
+     * @param exception log this cause
+     * @see org.apache.commons.logging.Log#debug(Object, Throwable)
+     */
+    public void debug(Object message, Throwable exception) {
+        log(Level.FINE, String.valueOf(message), exception);
+    }
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.SEVERE</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#error(Object)
+     */
+    public void error(Object message) {
+        log(Level.SEVERE, String.valueOf(message), null);
+    }
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.SEVERE</code>.
+     *
+     * @param message to log
+     * @param exception log this cause
+     * @see org.apache.commons.logging.Log#error(Object, Throwable)
+     */
+    public void error(Object message, Throwable exception) {
+        log(Level.SEVERE, String.valueOf(message), exception);
+    }
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.SEVERE</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#fatal(Object)
+     */
+    public void fatal(Object message) {
+        log(Level.SEVERE, String.valueOf(message), null);
+    }
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.SEVERE</code>.
+     *
+     * @param message to log
+     * @param exception log this cause
+     * @see org.apache.commons.logging.Log#fatal(Object, Throwable)
+     */
+    public void fatal(Object message, Throwable exception) {
+        log(Level.SEVERE, String.valueOf(message), exception);
+    }
+
+    /**
+     * Return the native Logger instance we are using.
+     */
+    public Logger getLogger() {
+        if (logger == null) {
+            logger = Logger.getLogger(name);
+        }
+        return logger;
+    }
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.INFO</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#info(Object)
+     */
+    public void info(Object message) {
+        log(Level.INFO, String.valueOf(message), null);
+    }
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.INFO</code>.
+     *
+     * @param message to log
+     * @param exception log this cause
+     * @see org.apache.commons.logging.Log#info(Object, Throwable)
+     */
+    public void info(Object message, Throwable exception) {
+        log(Level.INFO, String.valueOf(message), exception);
+    }
+
+    /**
+     * Is debug logging currently enabled?
+     */
+    public boolean isDebugEnabled() {
+        return getLogger().isLoggable(Level.FINE);
+    }
+
+    /**
+     * Is error logging currently enabled?
+     */
+    public boolean isErrorEnabled() {
+        return getLogger().isLoggable(Level.SEVERE);
+    }
+
+    /**
+     * Is fatal logging currently enabled?
+     */
+    public boolean isFatalEnabled() {
+        return getLogger().isLoggable(Level.SEVERE);
+    }
+
+    /**
+     * Is info logging currently enabled?
+     */
+    public boolean isInfoEnabled() {
+        return getLogger().isLoggable(Level.INFO);
+    }
+
+    /**
+     * Is trace logging currently enabled?
+     */
+    public boolean isTraceEnabled() {
+        return getLogger().isLoggable(Level.FINEST);
+    }
+
+    /**
+     * Is warn logging currently enabled?
+     */
+    public boolean isWarnEnabled() {
+        return getLogger().isLoggable(Level.WARNING);
+    }
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.FINEST</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#trace(Object)
+     */
+    public void trace(Object message) {
+        log(Level.FINEST, String.valueOf(message), null);
+    }
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.FINEST</code>.
+     *
+     * @param message to log
+     * @param exception log this cause
+     * @see org.apache.commons.logging.Log#trace(Object, Throwable)
+     */
+    public void trace(Object message, Throwable exception) {
+        log(Level.FINEST, String.valueOf(message), exception);
+    }
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.WARNING</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#warn(Object)
+     */
+    public void warn(Object message) {
+        log(Level.WARNING, String.valueOf(message), null);
+    }
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.WARNING</code>.
+     *
+     * @param message to log
+     * @param exception log this cause
+     * @see org.apache.commons.logging.Log#warn(Object, Throwable)
+     */
+    public void warn(Object message, Throwable exception) {
+        log(Level.WARNING, String.valueOf(message), exception);
+    }
+}
diff --git a/src/main/java/org/apache/commons/logging/impl/Jdk14Logger.java b/src/main/java/org/apache/commons/logging/impl/Jdk14Logger.java
new file mode 100644
index 0000000..7a19acb
--- /dev/null
+++ b/src/main/java/org/apache/commons/logging/impl/Jdk14Logger.java
@@ -0,0 +1,273 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.impl;
+
+import java.io.Serializable;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.commons.logging.Log;
+
+/**
+ * Implementation of the <code>org.apache.commons.logging.Log</code>
+ * interface that wraps the standard JDK logging mechanisms that were
+ * introduced in the Merlin release (JDK 1.4).
+ *
+ * @version $Id: Jdk14Logger.java 1448063 2013-02-20 10:01:41Z tn $
+ */
+public class Jdk14Logger implements Log, Serializable {
+
+    /** Serializable version identifier. */
+    private static final long serialVersionUID = 4784713551416303804L;
+
+    /**
+     * This member variable simply ensures that any attempt to initialise
+     * 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
+
+    /**
+     * Construct a named instance of this Logger.
+     *
+     * @param name Name of the logger to be constructed
+     */
+    public Jdk14Logger(String name) {
+        this.name = name;
+        logger = getLogger();
+    }
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * The underlying Logger implementation we are using.
+     */
+    protected transient Logger logger = null;
+
+    /**
+     * The name of the logger we are wrapping.
+     */
+    protected String name = null;
+
+    // --------------------------------------------------------- Protected Methods
+
+    protected void log( Level level, String msg, Throwable ex ) {
+        Logger logger = getLogger();
+        if (logger.isLoggable(level)) {
+            // Hack (?) to get the stack trace.
+            Throwable dummyException = new Throwable();
+            StackTraceElement locations[] = dummyException.getStackTrace();
+            // LOGGING-132: use the provided logger name instead of the class name
+            String cname = name;
+            String method = "unknown";
+            // Caller will be the third element
+            if( locations != null && locations.length > 2 ) {
+                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 Methods
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.FINE</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#debug(Object)
+     */
+    public void debug(Object message) {
+        log(Level.FINE, String.valueOf(message), null);
+    }
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.FINE</code>.
+     *
+     * @param message to log
+     * @param exception log this cause
+     * @see org.apache.commons.logging.Log#debug(Object, Throwable)
+     */
+    public void debug(Object message, Throwable exception) {
+        log(Level.FINE, String.valueOf(message), exception);
+    }
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.SEVERE</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#error(Object)
+     */
+    public void error(Object message) {
+        log(Level.SEVERE, String.valueOf(message), null);
+    }
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.SEVERE</code>.
+     *
+     * @param message to log
+     * @param exception log this cause
+     * @see org.apache.commons.logging.Log#error(Object, Throwable)
+     */
+    public void error(Object message, Throwable exception) {
+        log(Level.SEVERE, String.valueOf(message), exception);
+    }
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.SEVERE</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#fatal(Object)
+     */
+    public void fatal(Object message) {
+        log(Level.SEVERE, String.valueOf(message), null);
+    }
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.SEVERE</code>.
+     *
+     * @param message to log
+     * @param exception log this cause
+     * @see org.apache.commons.logging.Log#fatal(Object, Throwable)
+     */
+    public void fatal(Object message, Throwable exception) {
+        log(Level.SEVERE, String.valueOf(message), exception);
+    }
+
+    /**
+     * Return the native Logger instance we are using.
+     */
+    public Logger getLogger() {
+        if (logger == null) {
+            logger = Logger.getLogger(name);
+        }
+        return logger;
+    }
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.INFO</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#info(Object)
+     */
+    public void info(Object message) {
+        log(Level.INFO, String.valueOf(message), null);
+    }
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.INFO</code>.
+     *
+     * @param message to log
+     * @param exception log this cause
+     * @see org.apache.commons.logging.Log#info(Object, Throwable)
+     */
+    public void info(Object message, Throwable exception) {
+        log(Level.INFO, String.valueOf(message), exception);
+    }
+
+    /**
+     * Is debug logging currently enabled?
+     */
+    public boolean isDebugEnabled() {
+        return getLogger().isLoggable(Level.FINE);
+    }
+
+    /**
+     * Is error logging currently enabled?
+     */
+    public boolean isErrorEnabled() {
+        return getLogger().isLoggable(Level.SEVERE);
+    }
+
+    /**
+     * Is fatal logging currently enabled?
+     */
+    public boolean isFatalEnabled() {
+        return getLogger().isLoggable(Level.SEVERE);
+    }
+
+    /**
+     * Is info logging currently enabled?
+     */
+    public boolean isInfoEnabled() {
+        return getLogger().isLoggable(Level.INFO);
+    }
+
+    /**
+     * Is trace logging currently enabled?
+     */
+    public boolean isTraceEnabled() {
+        return getLogger().isLoggable(Level.FINEST);
+    }
+
+    /**
+     * Is warn logging currently enabled?
+     */
+    public boolean isWarnEnabled() {
+        return getLogger().isLoggable(Level.WARNING);
+    }
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.FINEST</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#trace(Object)
+     */
+    public void trace(Object message) {
+        log(Level.FINEST, String.valueOf(message), null);
+    }
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.FINEST</code>.
+     *
+     * @param message to log
+     * @param exception log this cause
+     * @see org.apache.commons.logging.Log#trace(Object, Throwable)
+     */
+    public void trace(Object message, Throwable exception) {
+        log(Level.FINEST, String.valueOf(message), exception);
+    }
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.WARNING</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#warn(Object)
+     */
+    public void warn(Object message) {
+        log(Level.WARNING, String.valueOf(message), null);
+    }
+
+    /**
+     * Logs a message with <code>java.util.logging.Level.WARNING</code>.
+     *
+     * @param message to log
+     * @param exception log this cause
+     * @see org.apache.commons.logging.Log#warn(Object, Throwable)
+     */
+    public void warn(Object message, Throwable exception) {
+        log(Level.WARNING, String.valueOf(message), exception);
+    }
+}
diff --git a/src/main/java/org/apache/commons/logging/impl/Log4JLogger.java b/src/main/java/org/apache/commons/logging/impl/Log4JLogger.java
new file mode 100644
index 0000000..de3b140
--- /dev/null
+++ b/src/main/java/org/apache/commons/logging/impl/Log4JLogger.java
@@ -0,0 +1,312 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.impl;
+
+import java.io.Serializable;
+import org.apache.commons.logging.Log;
+import org.apache.log4j.Logger;
+import org.apache.log4j.Priority;
+import org.apache.log4j.Level;
+
+/**
+ * Implementation of {@link Log} that maps directly to a
+ * <strong>Logger</strong> for log4J version 1.2.
+ * <p>
+ * Initial configuration of the corresponding Logger instances should be done
+ * in the usual manner, as outlined in the Log4J documentation.
+ * <p>
+ * The reason this logger is distinct from the 1.3 logger is that in version 1.2
+ * of Log4J:
+ * <ul>
+ * <li>class Logger takes Priority parameters not Level parameters.
+ * <li>class Level extends Priority
+ * </ul>
+ * Log4J1.3 is expected to change Level so it no longer extends Priority, which is
+ * a non-binary-compatible change. The class generated by compiling this code against
+ * log4j 1.2 will therefore not run against log4j 1.3.
+ *
+ * @version $Id: Log4JLogger.java 1448119 2013-02-20 12:28:04Z tn $
+ */
+public class Log4JLogger implements Log, Serializable {
+
+    /** Serializable version identifier. */
+    private static final long serialVersionUID = 5160705895411730424L;
+
+    // ------------------------------------------------------------- Attributes
+
+    /** 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 = null;
+
+    /** Logger name */
+    private final String name;
+
+    private static final Priority traceLevel;
+
+    // ------------------------------------------------------------
+    // Static Initializer.
+    //
+    // Note that this must come after the static variable declarations
+    // otherwise initialiser 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
+            throw new InstantiationError("Log4J 1.2 not available");
+        }
+
+        // Releases of log4j1.2 >= 1.2.12 have Priority.TRACE available, earlier
+        // versions do not. If TRACE is not available, then we have to map
+        // calls to Log.trace(...) onto the DEBUG level.
+
+        Priority _traceLevel;
+        try {
+            _traceLevel = (Priority) Level.class.getDeclaredField("TRACE").get(null);
+        } catch(Exception ex) {
+            // ok, trace not available
+            _traceLevel = Level.DEBUG;
+        }
+        traceLevel = _traceLevel;
+    }
+
+    // ------------------------------------------------------------ Constructor
+
+    public Log4JLogger() {
+        name = null;
+    }
+
+    /**
+     * Base constructor.
+     */
+    public Log4JLogger(String name) {
+        this.name = name;
+        this.logger = getLogger();
+    }
+
+    /**
+     * For use with a log4j factory.
+     */
+    public Log4JLogger(Logger logger) {
+        if (logger == null) {
+            throw new IllegalArgumentException(
+                "Warning - null logger in constructor; possible log4j misconfiguration.");
+        }
+        this.name = logger.getName();
+        this.logger = logger;
+    }
+
+    /**
+     * Logs a message with <code>org.apache.log4j.Priority.TRACE</code>.
+     * When using a log4j version that does not support the <code>TRACE</code>
+     * level, the message will be logged at the <code>DEBUG</code> level.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#trace(Object)
+     */
+    public void trace(Object message) {
+        getLogger().log(FQCN, traceLevel, message, null);
+    }
+
+    /**
+     * Logs a message with <code>org.apache.log4j.Priority.TRACE</code>.
+     * When using a log4j version that does not support the <code>TRACE</code>
+     * level, the message will be logged at the <code>DEBUG</code> level.
+     *
+     * @param message to log
+     * @param t log this cause
+     * @see org.apache.commons.logging.Log#trace(Object, Throwable)
+     */
+    public void trace(Object message, Throwable t) {
+        getLogger().log(FQCN, traceLevel, message, t);
+    }
+
+    /**
+     * Logs a message with <code>org.apache.log4j.Priority.DEBUG</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#debug(Object)
+     */
+    public void debug(Object message) {
+        getLogger().log(FQCN, Level.DEBUG, message, null);
+    }
+
+    /**
+     * Logs a message with <code>org.apache.log4j.Priority.DEBUG</code>.
+     *
+     * @param message to log
+     * @param t log this cause
+     * @see org.apache.commons.logging.Log#debug(Object, Throwable)
+     */
+    public void debug(Object message, Throwable t) {
+        getLogger().log(FQCN, Level.DEBUG, message, t);
+    }
+
+    /**
+     * Logs a message with <code>org.apache.log4j.Priority.INFO</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#info(Object)
+     */
+    public void info(Object message) {
+        getLogger().log(FQCN, Level.INFO, message, null);
+    }
+
+    /**
+     * Logs a message with <code>org.apache.log4j.Priority.INFO</code>.
+     *
+     * @param message to log
+     * @param t log this cause
+     * @see org.apache.commons.logging.Log#info(Object, Throwable)
+     */
+    public void info(Object message, Throwable t) {
+        getLogger().log(FQCN, Level.INFO, message, t);
+    }
+
+    /**
+     * Logs a message with <code>org.apache.log4j.Priority.WARN</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#warn(Object)
+     */
+    public void warn(Object message) {
+        getLogger().log(FQCN, Level.WARN, message, null);
+    }
+
+    /**
+     * Logs a message with <code>org.apache.log4j.Priority.WARN</code>.
+     *
+     * @param message to log
+     * @param t log this cause
+     * @see org.apache.commons.logging.Log#warn(Object, Throwable)
+     */
+    public void warn(Object message, Throwable t) {
+        getLogger().log(FQCN, Level.WARN, message, t);
+    }
+
+    /**
+     * Logs a message with <code>org.apache.log4j.Priority.ERROR</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#error(Object)
+     */
+    public void error(Object message) {
+        getLogger().log(FQCN, Level.ERROR, message, null);
+    }
+
+    /**
+     * Logs a message with <code>org.apache.log4j.Priority.ERROR</code>.
+     *
+     * @param message to log
+     * @param t log this cause
+     * @see org.apache.commons.logging.Log#error(Object, Throwable)
+     */
+    public void error(Object message, Throwable t) {
+        getLogger().log(FQCN, Level.ERROR, message, t);
+    }
+
+    /**
+     * Logs a message with <code>org.apache.log4j.Priority.FATAL</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#fatal(Object)
+     */
+    public void fatal(Object message) {
+        getLogger().log(FQCN, Level.FATAL, message, null);
+    }
+
+    /**
+     * Logs a message with <code>org.apache.log4j.Priority.FATAL</code>.
+     *
+     * @param message to log
+     * @param t log this cause
+     * @see org.apache.commons.logging.Log#fatal(Object, Throwable)
+     */
+    public void fatal(Object message, Throwable t) {
+        getLogger().log(FQCN, Level.FATAL, message, t);
+    }
+
+    /**
+     * Return the native Logger instance we are using.
+     */
+    public Logger getLogger() {
+        Logger result = logger;
+        if (result == null) {
+            synchronized(this) {
+                result = logger;
+                if (result == null) {
+                    logger = result = Logger.getLogger(name);
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Check whether the Log4j Logger used is enabled for <code>DEBUG</code> priority.
+     */
+    public boolean isDebugEnabled() {
+        return getLogger().isDebugEnabled();
+    }
+
+    /**
+     * Check whether the Log4j Logger used is enabled for <code>ERROR</code> priority.
+     */
+    public boolean isErrorEnabled() {
+        return getLogger().isEnabledFor(Level.ERROR);
+    }
+
+    /**
+     * Check whether the Log4j Logger used is enabled for <code>FATAL</code> priority.
+     */
+    public boolean isFatalEnabled() {
+        return getLogger().isEnabledFor(Level.FATAL);
+    }
+
+    /**
+     * Check whether the Log4j Logger used is enabled for <code>INFO</code> priority.
+     */
+    public boolean isInfoEnabled() {
+        return getLogger().isInfoEnabled();
+    }
+
+    /**
+     * Check whether the Log4j Logger used is enabled for <code>TRACE</code> priority.
+     * When using a log4j version that does not support the TRACE level, this call
+     * will report whether <code>DEBUG</code> is enabled or not.
+     */
+    public boolean isTraceEnabled() {
+        return getLogger().isEnabledFor(traceLevel);
+    }
+
+    /**
+     * Check whether the Log4j Logger used is enabled for <code>WARN</code> priority.
+     */
+    public boolean isWarnEnabled() {
+        return getLogger().isEnabledFor(Level.WARN);
+    }
+}
diff --git a/src/main/java/org/apache/commons/logging/impl/LogFactoryImpl.java b/src/main/java/org/apache/commons/logging/impl/LogFactoryImpl.java
new file mode 100644
index 0000000..7ec3c27
--- /dev/null
+++ b/src/main/java/org/apache/commons/logging/impl/LogFactoryImpl.java
@@ -0,0 +1,1393 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.impl;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Hashtable;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogConfigurationException;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Concrete subclass of {@link LogFactory} that implements the
+ * following algorithm to dynamically select a logging implementation
+ * class to instantiate a wrapper for:
+ * <ul>
+ * <li>Use a factory configuration attribute named
+ *     <code>org.apache.commons.logging.Log</code> to identify the
+ *     requested implementation class.</li>
+ * <li>Use the <code>org.apache.commons.logging.Log</code> system property
+ *     to identify the requested implementation class.</li>
+ * <li>If <em>Log4J</em> is available, return an instance of
+ *     <code>org.apache.commons.logging.impl.Log4JLogger</code>.</li>
+ * <li>If <em>JDK 1.4 or later</em> is available, return an instance of
+ *     <code>org.apache.commons.logging.impl.Jdk14Logger</code>.</li>
+ * <li>Otherwise, return an instance of
+ *     <code>org.apache.commons.logging.impl.SimpleLog</code>.</li>
+ * </ul>
+ * <p>
+ * If the selected {@link Log} implementation class has a
+ * <code>setLogFactory()</code> method that accepts a {@link LogFactory}
+ * parameter, this method will be called on each newly created instance
+ * to identify the associated factory.  This makes factory configuration
+ * attributes available to the Log instance, if it so desires.
+ * <p>
+ * This factory will remember previously created <code>Log</code> instances
+ * for the same name, and will return them on repeated requests to the
+ * <code>getInstance()</code> method.
+ *
+ * @version $Id: LogFactoryImpl.java 1449064 2013-02-22 14:49:22Z tn $
+ */
+public class LogFactoryImpl extends LogFactory {
+
+    /** Log4JLogger class name */
+    private static final String LOGGING_IMPL_LOG4J_LOGGER = "org.apache.commons.logging.impl.Log4JLogger";
+    /** Jdk14Logger class name */
+    private static final String LOGGING_IMPL_JDK14_LOGGER = "org.apache.commons.logging.impl.Jdk14Logger";
+    /** Jdk13LumberjackLogger class name */
+    private static final String LOGGING_IMPL_LUMBERJACK_LOGGER =
+            "org.apache.commons.logging.impl.Jdk13LumberjackLogger";
+
+    /** SimpleLog class name */
+    private static final String LOGGING_IMPL_SIMPLE_LOGGER = "org.apache.commons.logging.impl.SimpleLog";
+
+    private static final String PKG_IMPL="org.apache.commons.logging.impl.";
+    private static final int PKG_LEN = PKG_IMPL.length();
+
+    // ----------------------------------------------------------- Constructors
+
+    /**
+     * Public no-arguments constructor required by the lookup mechanism.
+     */
+    public LogFactoryImpl() {
+        super();
+        initDiagnostics();  // method on this object
+        if (isDiagnosticsEnabled()) {
+            logDiagnostic("Instance created.");
+        }
+    }
+
+    // ----------------------------------------------------- Manifest Constants
+
+    /**
+     * The name (<code>org.apache.commons.logging.Log</code>) of the system
+     * property identifying our {@link Log} implementation class.
+     */
+    public static final String LOG_PROPERTY = "org.apache.commons.logging.Log";
+
+    /**
+     * The deprecated system property used for backwards compatibility with
+     * old versions of JCL.
+     */
+    protected static final String LOG_PROPERTY_OLD = "org.apache.commons.logging.log";
+
+    /**
+     * The name (<code>org.apache.commons.logging.Log.allowFlawedContext</code>)
+     * of the system property which can be set true/false to
+     * determine system behaviour when a bad context-classloader is encountered.
+     * When set to false, a LogConfigurationException is thrown if
+     * LogFactoryImpl is loaded via a child classloader of the TCCL (this
+     * should never happen in sane systems).
+     *
+     * Default behaviour: true (tolerates bad context classloaders)
+     *
+     * See also method setAttribute.
+     */
+    public static final String ALLOW_FLAWED_CONTEXT_PROPERTY =
+        "org.apache.commons.logging.Log.allowFlawedContext";
+
+    /**
+     * The name (<code>org.apache.commons.logging.Log.allowFlawedDiscovery</code>)
+     * of the system property which can be set true/false to
+     * determine system behaviour when a bad logging adapter class is
+     * encountered during logging discovery. When set to false, an
+     * exception will be thrown and the app will fail to start. When set
+     * to true, discovery will continue (though the user might end up
+     * with a different logging implementation than they expected).
+     * <p>
+     * Default behaviour: true (tolerates bad logging adapters)
+     *
+     * See also method setAttribute.
+     */
+    public static final String ALLOW_FLAWED_DISCOVERY_PROPERTY =
+        "org.apache.commons.logging.Log.allowFlawedDiscovery";
+
+    /**
+     * The name (<code>org.apache.commons.logging.Log.allowFlawedHierarchy</code>)
+     * of the system property which can be set true/false to
+     * determine system behaviour when a logging adapter class is
+     * encountered which has bound to the wrong Log class implementation.
+     * When set to false, an exception will be thrown and the app will fail
+     * to start. When set to true, discovery will continue (though the user
+     * might end up with a different logging implementation than they expected).
+     * <p>
+     * Default behaviour: true (tolerates bad Log class hierarchy)
+     *
+     * See also method setAttribute.
+     */
+    public static final String ALLOW_FLAWED_HIERARCHY_PROPERTY =
+        "org.apache.commons.logging.Log.allowFlawedHierarchy";
+
+    /**
+     * The names of classes that will be tried (in order) as logging
+     * adapters. Each class is expected to implement the Log interface,
+     * and to throw NoClassDefFound or ExceptionInInitializerError when
+     * loaded if the underlying logging library is not available. Any
+     * other error indicates that the underlying logging library is available
+     * but broken/unusable for some reason.
+     */
+    private static final String[] classesToDiscover = {
+            LOGGING_IMPL_LOG4J_LOGGER,
+            "org.apache.commons.logging.impl.Jdk14Logger",
+            "org.apache.commons.logging.impl.Jdk13LumberjackLogger",
+            "org.apache.commons.logging.impl.SimpleLog"
+    };
+
+    // ----------------------------------------------------- Instance Variables
+
+    /**
+     * Determines whether logging classes should be loaded using the thread-context
+     * classloader, or via the classloader that loaded this LogFactoryImpl class.
+     */
+    private boolean useTCCL = true;
+
+    /**
+     * The string prefixed to every message output by the logDiagnostic method.
+     */
+    private String diagnosticPrefix;
+
+    /**
+     * Configuration attributes.
+     */
+    protected Hashtable attributes = new Hashtable();
+
+    /**
+     * The {@link org.apache.commons.logging.Log} instances that have
+     * already been created, keyed by logger name.
+     */
+    protected Hashtable instances = new Hashtable();
+
+    /**
+     * Name of the class implementing the Log interface.
+     */
+    private String logClassName;
+
+    /**
+     * The one-argument constructor of the
+     * {@link org.apache.commons.logging.Log}
+     * implementation class that will be used to create new instances.
+     * This value is initialized by <code>getLogConstructor()</code>,
+     * and then returned repeatedly.
+     */
+    protected Constructor logConstructor = null;
+
+    /**
+     * The signature of the Constructor to be used.
+     */
+    protected Class logConstructorSignature[] = { java.lang.String.class };
+
+    /**
+     * The one-argument <code>setLogFactory</code> method of the selected
+     * {@link org.apache.commons.logging.Log} method, if it exists.
+     */
+    protected Method logMethod = null;
+
+    /**
+     * The signature of the <code>setLogFactory</code> method to be used.
+     */
+    protected Class logMethodSignature[] = { LogFactory.class };
+
+    /**
+     * See getBaseClassLoader and initConfiguration.
+     */
+    private boolean allowFlawedContext;
+
+    /**
+     * See handleFlawedDiscovery and initConfiguration.
+     */
+    private boolean allowFlawedDiscovery;
+
+    /**
+     * See handleFlawedHierarchy and initConfiguration.
+     */
+    private boolean allowFlawedHierarchy;
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Return the configuration attribute with the specified name (if any),
+     * or <code>null</code> if there is no such attribute.
+     *
+     * @param name Name of the attribute to return
+     */
+    public Object getAttribute(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.
+     */
+    public String[] getAttributeNames() {
+        return (String[]) attributes.keySet().toArray(new String[attributes.size()]);
+    }
+
+    /**
+     * Convenience method to derive a name from the specified class and
+     * call <code>getInstance(String)</code> with it.
+     *
+     * @param clazz Class for which a suitable Log name will be derived
+     *
+     * @exception LogConfigurationException if a suitable <code>Log</code>
+     *  instance cannot be returned
+     */
+    public Log getInstance(Class clazz) throws LogConfigurationException {
+        return getInstance(clazz.getName());
+    }
+
+    /**
+     * <p>Construct (if necessary) and return a <code>Log</code> instance,
+     * using the factory's current set of configuration attributes.</p>
+     *
+     * <p><strong>NOTE</strong> - Depending upon the implementation of
+     * the <code>LogFactory</code> you are using, the <code>Log</code>
+     * 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.</p>
+     *
+     * @param name Logical name of the <code>Log</code> instance to be
+     *  returned (the meaning of this name is only known to the underlying
+     *  logging implementation that is being wrapped)
+     *
+     * @exception LogConfigurationException if a suitable <code>Log</code>
+     *  instance cannot be returned
+     */
+    public Log getInstance(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.
+     */
+    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
+     */
+    public void removeAttribute(String name) {
+        attributes.remove(name);
+    }
+
+    /**
+     * Set the configuration attribute with the specified name.  Calling
+     * this with a <code>null</code> value is equivalent to calling
+     * <code>removeAttribute(name)</code>.
+     * <p>
+     * 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 behaviour on a
+     * per-component level instead of globally as system properties would do.
+     * To use this method instead of a system property, call
+     * <pre>
+     * LogFactory.getFactory().setAttribute(...)
+     * </pre>
+     * This must be done before the first Log object is created; configuration
+     * changes after that point will be ignored.
+     * <p>
+     * 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</code>
+     *  to remove any setting for this attribute
+     */
+    public void setAttribute(String name, 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.valueOf(value.toString()).booleanValue();
+        }
+    }
+
+    // ------------------------------------------------------
+    // 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.
+     * @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.
+     * See LogFactory.isDiagnosticsEnabled.
+     */
+    protected static boolean isDiagnosticsEnabled() {
+        return LogFactory.isDiagnosticsEnabled();
+    }
+
+    /**
+     * Workaround for bug in Java1.2; in theory this method is not needed.
+     * See LogFactory.getClassLoader.
+     * @since 1.1
+     */
+    protected static ClassLoader getClassLoader(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.
+     * <p>
+     * This string will later be prefixed to each "internal logging" message
+     * emitted, so that users can clearly see any unexpected behaviour.
+     * <p>
+     * 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.
+        Class clazz = this.getClass();
+        ClassLoader classLoader = getClassLoader(clazz);
+        String classLoaderName;
+        try {
+            if (classLoader == null) {
+                classLoaderName = "BOOTLOADER";
+            } else {
+                classLoaderName = objectId(classLoader);
+            }
+        } catch (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(String msg) {
+        if (isDiagnosticsEnabled()) {
+            logRawDiagnostic(diagnosticPrefix + msg);
+        }
+    }
+
+    /**
+     * 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.
+     */
+    protected String getLogClassName() {
+        if (logClassName == null) {
+            discoverLogImplementation(getClass().getName());
+        }
+
+        return logClassName;
+    }
+
+
+    /**
+     * <p>Return the <code>Constructor</code> that can be called to instantiate
+     * new {@link org.apache.commons.logging.Log} instances.</p>
+     *
+     * <p><strong>IMPLEMENTATION NOTE</strong> - Race conditions caused by
+     * calling this method from more than one thread are ignored, because
+     * the same <code>Constructor</code> instance will ultimately be derived
+     * in all circumstances.</p>
+     *
+     * @exception LogConfigurationException if a suitable constructor
+     *  cannot be returned
+     *
+     * @deprecated  Never invoked by this class; subclasses should not assume
+     *              it will be.
+     */
+    protected Constructor getLogConstructor()
+        throws LogConfigurationException {
+
+        // Return the previously identified Constructor (if any)
+        if (logConstructor == null) {
+            discoverLogImplementation(getClass().getName());
+        }
+
+        return logConstructor;
+    }
+
+    /**
+     * Is <em>JDK 1.3 with Lumberjack</em> logging available?
+     *
+     * @deprecated  Never invoked by this class; subclasses should not assume
+     *              it will be.
+     */
+    protected boolean isJdk13LumberjackAvailable() {
+        return isLogLibraryAvailable(
+                "Jdk13Lumberjack",
+                "org.apache.commons.logging.impl.Jdk13LumberjackLogger");
+    }
+
+    /**
+     * Return <code>true</code> if <em>JDK 1.4 or later</em> logging
+     * is available.  Also checks that the <code>Throwable</code> class
+     * supports <code>getStackTrace()</code>, which is required by
+     * Jdk14Logger.
+     *
+     * @deprecated  Never invoked by this class; subclasses should not assume
+     *              it will be.
+     */
+    protected boolean isJdk14Available() {
+        return isLogLibraryAvailable(
+                "Jdk14",
+                "org.apache.commons.logging.impl.Jdk14Logger");
+    }
+
+    /**
+     * Is a <em>Log4J</em> implementation available?
+     *
+     * @deprecated  Never invoked by this class; subclasses should not assume
+     *              it will be.
+     */
+    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
+     *
+     * @exception LogConfigurationException if a new instance cannot
+     *  be created
+     */
+    protected Log newInstance(String name) throws LogConfigurationException {
+        Log instance;
+        try {
+            if (logConstructor == null) {
+                instance = discoverLogImplementation(name);
+            }
+            else {
+                Object params[] = { name };
+                instance = (Log) logConstructor.newInstance(params);
+            }
+
+            if (logMethod != null) {
+                Object params[] = { this };
+                logMethod.invoke(instance, params);
+            }
+
+            return instance;
+
+        } catch (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 (InvocationTargetException e) {
+            // A problem occurred invoking the Constructor or Method
+            // previously discovered
+            Throwable c = e.getTargetException();
+            throw new LogConfigurationException(c == null ? e : c);
+        } catch (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(
+            new PrivilegedAction() {
+                public Object run() {
+                    return 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.
+     * <p>
+     * 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() {
+                    public Object run() {
+                        return System.getProperty(key, def);
+                    }
+                });
+    }
+
+    /**
+     * Fetch the parent classloader of a specified classloader.
+     * <p>
+     * If a SecurityException occurs, null is returned.
+     * <p>
+     * Note that this method is non-static merely so logDiagnostic is available.
+     */
+    private ClassLoader getParentClassLoader(final ClassLoader cl) {
+        try {
+            return (ClassLoader)AccessController.doPrivileged(
+                    new PrivilegedAction() {
+                        public Object run() {
+                            return cl.getParent();
+                        }
+                    });
+        } catch (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 <i>not</i>
+     * affect the future behaviour of this class.
+     */
+    private boolean isLogLibraryAvailable(String name, String classname) {
+        if (isDiagnosticsEnabled()) {
+            logDiagnostic("Checking for '" + name + "'.");
+        }
+        try {
+            Log log = createLogFromClass(
+                        classname,
+                        this.getClass().getName(), // dummy category
+                        false);
+
+            if (log == null) {
+                if (isDiagnosticsEnabled()) {
+                    logDiagnostic("Did not find '" + name + "'.");
+                }
+                return false;
+            } else {
+                if (isDiagnosticsEnabled()) {
+                    logDiagnostic("Found '" + name + "'.");
+                }
+                return true;
+            }
+        } catch (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.
+     * <p>
+     * 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(String property) {
+        if (isDiagnosticsEnabled()) {
+            logDiagnostic("[ENV] Trying to get configuration for item " + property);
+        }
+
+        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.
+            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 (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 behaviour specified by key.
+     * If nothing has explicitly been set, then return dflt.
+     */
+    private boolean getBooleanConfiguration(String key, boolean dflt) {
+        String val = getConfigurationValue(key);
+        if (val == null) {
+            return dflt;
+        }
+        return Boolean.valueOf(val).booleanValue();
+    }
+
+    /**
+     * Initialize a number of variables that control the behaviour 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(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
+        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) {
+                StringBuffer messageBuffer =  new StringBuffer("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 behaviour). 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<classesToDiscover.length && result == null; ++i) {
+            result = createLogFromClass(classesToDiscover[i], logCategory, true);
+        }
+
+        if (result == null) {
+            throw new LogConfigurationException
+                        ("No suitable Log implementation");
+        }
+
+        return result;
+    }
+
+    /**
+     * Appends message if the given name is similar to the candidate.
+     * @param messageBuffer <code>StringBuffer</code> 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 StringBuffer 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("'?");
+        }
+    }
+
+    /**
+     * Checks system properties and the attribute map for
+     * a Log implementation specified by the user under the
+     * property names {@link #LOG_PROPERTY} or {@link #LOG_PROPERTY_OLD}.
+     *
+     * @return classname specified by the user, or <code>null</code>
+     */
+    private String findUserSpecifiedLogClassName() {
+        if (isDiagnosticsEnabled()) {
+            logDiagnostic("Trying to get log class from attribute '" + LOG_PROPERTY + "'");
+        }
+        String specifiedClass = (String) getAttribute(LOG_PROPERTY);
+
+        if (specifiedClass == null) { // @deprecated
+            if (isDiagnosticsEnabled()) {
+                logDiagnostic("Trying to get log class from attribute '" +
+                              LOG_PROPERTY_OLD + "'");
+            }
+            specifiedClass = (String) getAttribute(LOG_PROPERTY_OLD);
+        }
+
+        if (specifiedClass == null) {
+            if (isDiagnosticsEnabled()) {
+                logDiagnostic("Trying to get log class from system property '" +
+                          LOG_PROPERTY + "'");
+            }
+            try {
+                specifiedClass = getSystemProperty(LOG_PROPERTY, null);
+            } catch (SecurityException e) {
+                if (isDiagnosticsEnabled()) {
+                    logDiagnostic("No access allowed to system property '" +
+                        LOG_PROPERTY + "' - " + e.getMessage());
+                }
+            }
+        }
+
+        if (specifiedClass == null) { // @deprecated
+            if (isDiagnosticsEnabled()) {
+                logDiagnostic("Trying to get log class from system property '" +
+                          LOG_PROPERTY_OLD + "'");
+            }
+            try {
+                specifiedClass = getSystemProperty(LOG_PROPERTY_OLD, null);
+            } catch (SecurityException e) {
+                if (isDiagnosticsEnabled()) {
+                    logDiagnostic("No access allowed to system property '" +
+                        LOG_PROPERTY_OLD + "' - " + e.getMessage());
+                }
+            }
+        }
+
+        // Remove any whitespace; it's never valid in a classname so its
+        // presence just means a user mistake. As we know what they meant,
+        // we may as well strip the spaces.
+        if (specifiedClass != null) {
+            specifiedClass = specifiedClass.trim();
+        }
+
+        return specifiedClass;
+    }
+
+    /**
+     * Attempts to load the given class, find a suitable constructor,
+     * and instantiate an instance of Log.
+     *
+     * @param logAdapterClassName classname of the Log implementation
+     * @param logCategory  argument to pass to the Log implementation's constructor
+     * @param affectState  <code>true</code> if this object's state should
+     *  be affected by this method call, <code>false</code> otherwise.
+     * @return  an instance of the given class, or null if the logging
+     *  library associated with the specified adapter is not available.
+     * @throws LogConfigurationException if there was a serious error with
+     *  configuration and the handleFlawedDiscovery method decided this
+     *  problem was fatal.
+     */
+    private Log createLogFromClass(String logAdapterClassName,
+                                   String logCategory,
+                                   boolean affectState)
+        throws LogConfigurationException {
+
+        if (isDiagnosticsEnabled()) {
+            logDiagnostic("Attempting to instantiate '" + logAdapterClassName + "'");
+        }
+
+        Object[] params = { logCategory };
+        Log logAdapter = null;
+        Constructor constructor = null;
+
+        Class logAdapterClass = null;
+        ClassLoader currentCL = getBaseClassLoader();
+
+        for(;;) {
+            // Loop through the classloader hierarchy trying to find
+            // a viable classloader.
+            logDiagnostic("Trying to load '" + logAdapterClassName + "' from classloader " + objectId(currentCL));
+            try {
+                if (isDiagnosticsEnabled()) {
+                    // Show the location of the first occurrence of the .class file
+                    // in the classpath. This is the location that ClassLoader.loadClass
+                    // will load the class from -- unless the classloader is doing
+                    // something weird.
+                    URL url;
+                    String resourceName = logAdapterClassName.replace('.', '/') + ".class";
+                    if (currentCL != null) {
+                        url = currentCL.getResource(resourceName );
+                    } else {
+                        url = ClassLoader.getSystemResource(resourceName + ".class");
+                    }
+
+                    if (url == null) {
+                        logDiagnostic("Class '" + logAdapterClassName + "' [" + resourceName + "] cannot be found.");
+                    } else {
+                        logDiagnostic("Class '" + logAdapterClassName + "' was found at '" + url + "'");
+                    }
+                }
+
+                Class c;
+                try {
+                    c = Class.forName(logAdapterClassName, true, currentCL);
+                } catch (ClassNotFoundException originalClassNotFoundException) {
+                    // The current classloader was unable to find the log adapter
+                    // in this or any ancestor classloader. There's no point in
+                    // trying higher up in the hierarchy in this case..
+                    String msg = originalClassNotFoundException.getMessage();
+                    logDiagnostic("The log adapter '" + logAdapterClassName + "' is not available via classloader " +
+                                  objectId(currentCL) + ": " + msg.trim());
+                    try {
+                        // Try the class classloader.
+                        // This may work in cases where the TCCL
+                        // does not contain the code executed or JCL.
+                        // This behaviour indicates that the application
+                        // classloading strategy is not consistent with the
+                        // Java 1.2 classloading guidelines but JCL can
+                        // and so should handle this case.
+                        c = Class.forName(logAdapterClassName);
+                    } catch (ClassNotFoundException secondaryClassNotFoundException) {
+                        // no point continuing: this adapter isn't available
+                        msg = secondaryClassNotFoundException.getMessage();
+                        logDiagnostic("The log adapter '" + logAdapterClassName +
+                                      "' is not available via the LogFactoryImpl class classloader: " + msg.trim());
+                        break;
+                    }
+                }
+
+                constructor = c.getConstructor(logConstructorSignature);
+                Object o = constructor.newInstance(params);
+
+                // Note that we do this test after trying to create an instance
+                // [rather than testing Log.class.isAssignableFrom(c)] so that
+                // we don't complain about Log hierarchy problems when the
+                // adapter couldn't be instantiated anyway.
+                if (o instanceof Log) {
+                    logAdapterClass = c;
+                    logAdapter = (Log) o;
+                    break;
+                }
+
+                // Oops, we have a potential problem here. An adapter class
+                // has been found and its underlying lib is present too, but
+                // there are multiple Log interface classes available making it
+                // impossible to cast to the type the caller wanted. We
+                // certainly can't use this logger, but we need to know whether
+                // to keep on discovering or terminate now.
+                //
+                // The handleFlawedHierarchy method will throw
+                // LogConfigurationException if it regards this problem as
+                // fatal, and just return if not.
+                handleFlawedHierarchy(currentCL, c);
+            } catch (NoClassDefFoundError e) {
+                // We were able to load the adapter but it had references to
+                // other classes that could not be found. This simply means that
+                // the underlying logger library is not present in this or any
+                // ancestor classloader. There's no point in trying higher up
+                // in the hierarchy in this case..
+                String msg = e.getMessage();
+                logDiagnostic("The log adapter '" + logAdapterClassName +
+                              "' is missing dependencies when loaded via classloader " + objectId(currentCL) +
+                              ": " + msg.trim());
+                break;
+            } catch (ExceptionInInitializerError e) {
+                // A static initializer block or the initializer code associated
+                // with a static variable on the log adapter class has thrown
+                // an exception.
+                //
+                // We treat this as meaning the adapter's underlying logging
+                // library could not be found.
+                String msg = e.getMessage();
+                logDiagnostic("The log adapter '" + logAdapterClassName +
+                              "' is unable to initialize itself when loaded via classloader " + objectId(currentCL) +
+                              ": " + msg.trim());
+                break;
+            } catch (LogConfigurationException e) {
+                // call to handleFlawedHierarchy above must have thrown
+                // a LogConfigurationException, so just throw it on
+                throw e;
+            } catch (Throwable t) {
+                handleThrowable(t); // may re-throw t
+                // handleFlawedDiscovery will determine whether this is a fatal
+                // problem or not. If it is fatal, then a LogConfigurationException
+                // will be thrown.
+                handleFlawedDiscovery(logAdapterClassName, currentCL, t);
+            }
+
+            if (currentCL == null) {
+                break;
+            }
+
+            // try the parent classloader
+            // currentCL = currentCL.getParent();
+            currentCL = getParentClassLoader(currentCL);
+        }
+
+        if (logAdapterClass != null && affectState) {
+            // We've succeeded, so set instance fields
+            this.logClassName   = logAdapterClassName;
+            this.logConstructor = constructor;
+
+            // Identify the <code>setLogFactory</code> method (if there is one)
+            try {
+                this.logMethod = logAdapterClass.getMethod("setLogFactory", logMethodSignature);
+                logDiagnostic("Found method setLogFactory(LogFactory) in '" + logAdapterClassName + "'");
+            } catch (Throwable t) {
+                handleThrowable(t); // may re-throw t
+                this.logMethod = null;
+                logDiagnostic("[INFO] '" + logAdapterClassName + "' from classloader " + objectId(currentCL) +
+                              " does not declare optional method " + "setLogFactory(LogFactory)");
+            }
+
+            logDiagnostic("Log adapter '" + logAdapterClassName + "' from classloader " +
+                          objectId(logAdapterClass.getClassLoader()) + " has been selected for use.");
+        }
+
+        return logAdapter;
+    }
+
+    /**
+     * Return the classloader from which we should try to load the logging
+     * adapter classes.
+     * <p>
+     * This method usually returns the context classloader. However if it
+     * is discovered that the classloader which loaded this class is a child
+     * of the context classloader <i>and</i> the allowFlawedContext option
+     * has been set then the classloader which loaded this class is returned
+     * instead.
+     * <p>
+     * The only time when the classloader which loaded this class is a
+     * descendant (rather than the same as or an ancestor of the context
+     * classloader) is when an app has created custom classloaders but
+     * failed to correctly set the context classloader. This is a bug in
+     * the calling application; however we provide the option for JCL to
+     * simply generate a warning rather than fail outright.
+     *
+     */
+    private ClassLoader getBaseClassLoader() throws LogConfigurationException {
+        ClassLoader thisClassLoader = getClassLoader(LogFactoryImpl.class);
+
+        if (!useTCCL) {
+            return thisClassLoader;
+        }
+
+        ClassLoader contextClassLoader = getContextClassLoaderInternal();
+
+        ClassLoader baseClassLoader = getLowestClassLoader(
+                contextClassLoader, thisClassLoader);
+
+        if (baseClassLoader == null) {
+           // The two classloaders are not part of a parent child relationship.
+           // In some classloading setups (e.g. JBoss with its
+           // UnifiedLoaderRepository) this can still work, so if user hasn't
+           // forbidden it, just return the contextClassLoader.
+           if (allowFlawedContext) {
+              if (isDiagnosticsEnabled()) {
+                   logDiagnostic("[WARNING] the context classloader is not part of a" +
+                                 " parent-child relationship with the classloader that" +
+                                 " loaded LogFactoryImpl.");
+              }
+              // If contextClassLoader were null, getLowestClassLoader() would
+              // have returned thisClassLoader.  The fact we are here means
+              // contextClassLoader is not null, so we can just return it.
+              return contextClassLoader;
+           }
+           else {
+            throw new LogConfigurationException("Bad classloader hierarchy; LogFactoryImpl was loaded via" +
+                                                " a classloader that is not related to the current context" +
+                                                " classloader.");
+           }
+        }
+
+        if (baseClassLoader != contextClassLoader) {
+            // We really should just use the contextClassLoader as the starting
+            // point for scanning for log adapter classes. However it is expected
+            // that there are a number of broken systems out there which create
+            // custom classloaders but fail to set the context classloader so
+            // we handle those flawed systems anyway.
+            if (allowFlawedContext) {
+                if (isDiagnosticsEnabled()) {
+                    logDiagnostic(
+                            "Warning: the context classloader is an ancestor of the" +
+                            " classloader that loaded LogFactoryImpl; it should be" +
+                            " the same or a descendant. The application using" +
+                            " commons-logging should ensure the context classloader" +
+                            " is used correctly.");
+                }
+            } else {
+                throw new LogConfigurationException(
+                        "Bad classloader hierarchy; LogFactoryImpl was loaded via" +
+                        " a classloader that is not related to the current context" +
+                        " classloader.");
+            }
+        }
+
+        return baseClassLoader;
+    }
+
+    /**
+     * Given two related classloaders, return the one which is a child of
+     * the other.
+     * <p>
+     * @param c1 is a classloader (including the null classloader)
+     * @param c2 is a classloader (including the null classloader)
+     *
+     * @return c1 if it has c2 as an ancestor, c2 if it has c1 as an ancestor,
+     * and null if neither is an ancestor of the other.
+     */
+    private ClassLoader getLowestClassLoader(ClassLoader c1, ClassLoader c2) {
+        // TODO: use AccessController when dealing with classloaders here
+
+        if (c1 == null) {
+            return c2;
+        }
+
+        if (c2 == null) {
+            return c1;
+        }
+
+        ClassLoader current;
+
+        // scan c1's ancestors to find c2
+        current = c1;
+        while (current != null) {
+            if (current == c2) {
+                return c1;
+            }
+            // current = current.getParent();
+            current = getParentClassLoader(current);
+        }
+
+        // scan c2's ancestors to find c1
+        current = c2;
+        while (current != null) {
+            if (current == c1) {
+                return c2;
+            }
+            // current = current.getParent();
+            current = getParentClassLoader(current);
+        }
+
+        return null;
+    }
+
+    /**
+     * Generates an internal diagnostic logging of the discovery failure and
+     * then throws a <code>LogConfigurationException</code> that wraps
+     * the passed <code>Throwable</code>.
+     *
+     * @param logAdapterClassName is the class name of the Log implementation
+     * that could not be instantiated. Cannot be <code>null</code>.
+     *
+     * @param classLoader is the classloader that we were trying to load the
+     * logAdapterClassName from when the exception occurred.
+     *
+     * @param discoveryFlaw is the Throwable created by the classloader
+     *
+     * @throws LogConfigurationException    ALWAYS
+     */
+    private void handleFlawedDiscovery(String logAdapterClassName,
+                                       ClassLoader classLoader, // USED?
+                                       Throwable discoveryFlaw) {
+
+        if (isDiagnosticsEnabled()) {
+            logDiagnostic("Could not instantiate Log '" +
+                      logAdapterClassName + "' -- " +
+                      discoveryFlaw.getClass().getName() + ": " +
+                      discoveryFlaw.getLocalizedMessage());
+
+            if (discoveryFlaw instanceof InvocationTargetException ) {
+                // Ok, the lib is there but while trying to create a real underlying
+                // logger something failed in the underlying lib; display info about
+                // that if possible.
+                InvocationTargetException ite = (InvocationTargetException)discoveryFlaw;
+                Throwable cause = ite.getTargetException();
+                if (cause != null) {
+                    logDiagnostic("... InvocationTargetException: " +
+                        cause.getClass().getName() + ": " +
+                        cause.getLocalizedMessage());
+
+                    if (cause instanceof ExceptionInInitializerError) {
+                        ExceptionInInitializerError eiie = (ExceptionInInitializerError)cause;
+                        Throwable cause2 = eiie.getException();
+                        if (cause2 != null) {
+                            final StringWriter sw = new StringWriter();
+                            cause2.printStackTrace(new PrintWriter(sw, true));
+                            logDiagnostic("... ExceptionInInitializerError: " + sw.toString());
+                        }
+                    }
+                }
+            }
+        }
+
+        if (!allowFlawedDiscovery) {
+            throw new LogConfigurationException(discoveryFlaw);
+        }
+    }
+
+    /**
+     * Report a problem loading the log adapter, then either return
+     * (if the situation is considered recoverable) or throw a
+     * LogConfigurationException.
+     * <p>
+     * There are two possible reasons why we successfully loaded the
+     * specified log adapter class then failed to cast it to a Log object:
+     * <ol>
+     * <li>the specific class just doesn't implement the Log interface
+     *     (user screwed up), or
+     * <li> the specified class has bound to a Log class loaded by some other
+     *      classloader; Log at classloaderX cannot be cast to Log at classloaderY.
+     * </ol>
+     * <p>
+     * Here we try to figure out which case has occurred so we can give the
+     * user some reasonable feedback.
+     *
+     * @param badClassLoader is the classloader we loaded the problem class from,
+     * ie it is equivalent to badClass.getClassLoader().
+     *
+     * @param badClass is a Class object with the desired name, but which
+     * does not implement Log correctly.
+     *
+     * @throws LogConfigurationException when the situation
+     * should not be recovered from.
+     */
+    private void handleFlawedHierarchy(ClassLoader badClassLoader, Class badClass)
+        throws LogConfigurationException {
+
+        boolean implementsLog = false;
+        String logInterfaceName = Log.class.getName();
+        Class interfaces[] = badClass.getInterfaces();
+        for (int i = 0; i < interfaces.length; i++) {
+            if (logInterfaceName.equals(interfaces[i].getName())) {
+                implementsLog = true;
+                break;
+            }
+        }
+
+        if (implementsLog) {
+            // the class does implement an interface called Log, but
+            // it is in the wrong classloader
+            if (isDiagnosticsEnabled()) {
+                try {
+                    ClassLoader logInterfaceClassLoader = getClassLoader(Log.class);
+                    logDiagnostic("Class '" + badClass.getName() + "' was found in classloader " +
+                                  objectId(badClassLoader) + ". It is bound to a Log interface which is not" +
+                                  " the one loaded from classloader " + objectId(logInterfaceClassLoader));
+                } catch (Throwable t) {
+                    handleThrowable(t); // may re-throw t
+                    logDiagnostic("Error while trying to output diagnostics about" + " bad class '" + badClass + "'");
+                }
+            }
+
+            if (!allowFlawedHierarchy) {
+                StringBuffer msg = new StringBuffer();
+                msg.append("Terminating logging for this context ");
+                msg.append("due to bad log hierarchy. ");
+                msg.append("You have more than one version of '");
+                msg.append(Log.class.getName());
+                msg.append("' visible.");
+                if (isDiagnosticsEnabled()) {
+                    logDiagnostic(msg.toString());
+                }
+                throw new LogConfigurationException(msg.toString());
+            }
+
+            if (isDiagnosticsEnabled()) {
+                StringBuffer msg = new StringBuffer();
+                msg.append("Warning: bad log hierarchy. ");
+                msg.append("You have more than one version of '");
+                msg.append(Log.class.getName());
+                msg.append("' visible.");
+                logDiagnostic(msg.toString());
+            }
+        } else {
+            // this is just a bad adapter class
+            if (!allowFlawedDiscovery) {
+                StringBuffer msg = new StringBuffer();
+                msg.append("Terminating logging for this context. ");
+                msg.append("Log class '");
+                msg.append(badClass.getName());
+                msg.append("' does not implement the Log interface.");
+                if (isDiagnosticsEnabled()) {
+                    logDiagnostic(msg.toString());
+                }
+
+                throw new LogConfigurationException(msg.toString());
+            }
+
+            if (isDiagnosticsEnabled()) {
+                StringBuffer msg = new StringBuffer();
+                msg.append("[WARNING] Log class '");
+                msg.append(badClass.getName());
+                msg.append("' does not implement the Log interface.");
+                logDiagnostic(msg.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
new file mode 100644
index 0000000..6feaa31
--- /dev/null
+++ b/src/main/java/org/apache/commons/logging/impl/LogKitLogger.java
@@ -0,0 +1,269 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.impl;
+
+import java.io.Serializable;
+import org.apache.log.Logger;
+import org.apache.log.Hierarchy;
+import org.apache.commons.logging.Log;
+
+/**
+ * Implementation of <code>org.apache.commons.logging.Log</code>
+ * that wraps the <a href="http://avalon.apache.org/logkit/">avalon-logkit</a>
+ * logging system. Configuration of <code>LogKit</code> is left to the user.
+ * <p>
+ * <code>LogKit</code> accepts only <code>String</code> messages.
+ * Therefore, this implementation converts object messages into strings
+ * by called their <code>toString()</code> method before logging them.
+ *
+ * @version $Id: LogKitLogger.java 1448119 2013-02-20 12:28:04Z tn $
+ */
+public class LogKitLogger implements Log, Serializable {
+
+    /** Serializable version identifier. */
+    private static final long serialVersionUID = 3768538055836059519L;
+
+    // ------------------------------------------------------------- Attributes
+
+    /** Logging goes to this <code>LogKit</code> logger */
+    protected transient volatile Logger logger = null;
+
+    /** Name of this logger */
+    protected String name = null;
+
+    // ------------------------------------------------------------ Constructor
+
+    /**
+     * Construct <code>LogKitLogger</code> which wraps the <code>LogKit</code>
+     * logger with given name.
+     *
+     * @param name log name
+     */
+    public LogKitLogger(String name) {
+        this.name = name;
+        this.logger = getLogger();
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * 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</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#trace(Object)
+    */
+    public void trace(Object message) {
+        debug(message);
+    }
+
+    /**
+     * Logs a message with <code>org.apache.log.Priority.DEBUG</code>.
+     *
+     * @param message to log
+     * @param t log this cause
+     * @see org.apache.commons.logging.Log#trace(Object, Throwable)
+     */
+    public void trace(Object message, Throwable t) {
+        debug(message, t);
+    }
+
+    /**
+     * Logs a message with <code>org.apache.log.Priority.DEBUG</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#debug(Object)
+     */
+    public void debug(Object message) {
+        if (message != null) {
+            getLogger().debug(String.valueOf(message));
+        }
+    }
+
+    /**
+     * Logs a message with <code>org.apache.log.Priority.DEBUG</code>.
+     *
+     * @param message to log
+     * @param t log this cause
+     * @see org.apache.commons.logging.Log#debug(Object, Throwable)
+     */
+    public void debug(Object message, Throwable t) {
+        if (message != null) {
+            getLogger().debug(String.valueOf(message), t);
+        }
+    }
+
+    /**
+     * Logs a message with <code>org.apache.log.Priority.INFO</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#info(Object)
+     */
+    public void info(Object message) {
+        if (message != null) {
+            getLogger().info(String.valueOf(message));
+        }
+    }
+
+    /**
+     * Logs a message with <code>org.apache.log.Priority.INFO</code>.
+     *
+     * @param message to log
+     * @param t log this cause
+     * @see org.apache.commons.logging.Log#info(Object, Throwable)
+     */
+    public void info(Object message, Throwable t) {
+        if (message != null) {
+            getLogger().info(String.valueOf(message), t);
+        }
+    }
+
+    /**
+     * Logs a message with <code>org.apache.log.Priority.WARN</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#warn(Object)
+     */
+    public void warn(Object message) {
+        if (message != null) {
+            getLogger().warn(String.valueOf(message));
+        }
+    }
+
+    /**
+     * Logs a message with <code>org.apache.log.Priority.WARN</code>.
+     *
+     * @param message to log
+     * @param t log this cause
+     * @see org.apache.commons.logging.Log#warn(Object, Throwable)
+     */
+    public void warn(Object message, Throwable t) {
+        if (message != null) {
+            getLogger().warn(String.valueOf(message), t);
+        }
+    }
+
+    /**
+     * Logs a message with <code>org.apache.log.Priority.ERROR</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#error(Object)
+     */
+    public void error(Object message) {
+        if (message != null) {
+            getLogger().error(String.valueOf(message));
+        }
+    }
+
+    /**
+     * Logs a message with <code>org.apache.log.Priority.ERROR</code>.
+     *
+     * @param message to log
+     * @param t log this cause
+     * @see org.apache.commons.logging.Log#error(Object, Throwable)
+     */
+    public void error(Object message, Throwable t) {
+        if (message != null) {
+            getLogger().error(String.valueOf(message), t);
+        }
+    }
+
+    /**
+     * Logs a message with <code>org.apache.log.Priority.FATAL_ERROR</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#fatal(Object)
+     */
+    public void fatal(Object message) {
+        if (message != null) {
+            getLogger().fatalError(String.valueOf(message));
+        }
+    }
+
+    /**
+     * Logs a message with <code>org.apache.log.Priority.FATAL_ERROR</code>.
+     *
+     * @param message to log
+     * @param t log this cause
+     * @see org.apache.commons.logging.Log#fatal(Object, Throwable)
+     */
+    public void fatal(Object message, Throwable t) {
+        if (message != null) {
+            getLogger().fatalError(String.valueOf(message), t);
+        }
+    }
+
+    /**
+     * Checks whether the <code>LogKit</code> logger will log messages of priority <code>DEBUG</code>.
+     */
+    public boolean isDebugEnabled() {
+        return getLogger().isDebugEnabled();
+    }
+
+    /**
+     * Checks whether the <code>LogKit</code> logger will log messages of priority <code>ERROR</code>.
+     */
+    public boolean isErrorEnabled() {
+        return getLogger().isErrorEnabled();
+    }
+
+    /**
+     * Checks whether the <code>LogKit</code> logger will log messages of priority <code>FATAL_ERROR</code>.
+     */
+    public boolean isFatalEnabled() {
+        return getLogger().isFatalErrorEnabled();
+    }
+
+    /**
+     * Checks whether the <code>LogKit</code> logger will log messages of priority <code>INFO</code>.
+     */
+    public boolean isInfoEnabled() {
+        return getLogger().isInfoEnabled();
+    }
+
+    /**
+     * Checks whether the <code>LogKit</code> logger will log messages of priority <code>DEBUG</code>.
+     */
+    public boolean isTraceEnabled() {
+        return getLogger().isDebugEnabled();
+    }
+
+    /**
+     * Checks whether the <code>LogKit</code> logger will log messages of priority <code>WARN</code>.
+     */
+    public boolean isWarnEnabled() {
+        return getLogger().isWarnEnabled();
+    }
+}
diff --git a/src/main/java/org/apache/commons/logging/impl/NoOpLog.java b/src/main/java/org/apache/commons/logging/impl/NoOpLog.java
new file mode 100644
index 0000000..ef0de24
--- /dev/null
+++ b/src/main/java/org/apache/commons/logging/impl/NoOpLog.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.impl;
+
+import java.io.Serializable;
+import org.apache.commons.logging.Log;
+
+/**
+ * Trivial implementation of Log that throws away all messages.  No
+ * configurable system properties are supported.
+ *
+ * @version $Id: NoOpLog.java 1432663 2013-01-13 17:24:18Z tn $
+ */
+public class NoOpLog implements Log, Serializable {
+
+    /** Serializable version identifier. */
+    private static final long serialVersionUID = 561423906191706148L;
+
+    /** Convenience constructor */
+    public NoOpLog() { }
+    /** Base constructor */
+    public NoOpLog(String name) { }
+    /** Do nothing */
+    public void trace(Object message) { }
+    /** Do nothing */
+    public void trace(Object message, Throwable t) { }
+    /** Do nothing */
+    public void debug(Object message) { }
+    /** Do nothing */
+    public void debug(Object message, Throwable t) { }
+    /** Do nothing */
+    public void info(Object message) { }
+    /** Do nothing */
+    public void info(Object message, Throwable t) { }
+    /** Do nothing */
+    public void warn(Object message) { }
+    /** Do nothing */
+    public void warn(Object message, Throwable t) { }
+    /** Do nothing */
+    public void error(Object message) { }
+    /** Do nothing */
+    public void error(Object message, Throwable t) { }
+    /** Do nothing */
+    public void fatal(Object message) { }
+    /** Do nothing */
+    public void fatal(Object message, Throwable t) { }
+
+    /**
+     * Debug is never enabled.
+     *
+     * @return false
+     */
+    public final boolean isDebugEnabled() { return false; }
+
+    /**
+     * Error is never enabled.
+     *
+     * @return false
+     */
+    public final boolean isErrorEnabled() { return false; }
+
+    /**
+     * Fatal is never enabled.
+     *
+     * @return false
+     */
+    public final boolean isFatalEnabled() { return false; }
+
+    /**
+     * Info is never enabled.
+     *
+     * @return false
+     */
+    public final boolean isInfoEnabled() { return false; }
+
+    /**
+     * Trace is never enabled.
+     *
+     * @return false
+     */
+    public final boolean isTraceEnabled() { return false; }
+
+    /**
+     * Warn is never enabled.
+     *
+     * @return false
+     */
+    public final boolean isWarnEnabled() { return false; }
+}
diff --git a/src/main/java/org/apache/commons/logging/impl/ServletContextCleaner.java b/src/main/java/org/apache/commons/logging/impl/ServletContextCleaner.java
new file mode 100644
index 0000000..c7c2162
--- /dev/null
+++ b/src/main/java/org/apache/commons/logging/impl/ServletContextCleaner.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.impl;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * This class is capable of receiving notifications about the undeployment of
+ * a webapp, and responds by ensuring that commons-logging releases all
+ * memory associated with the undeployed webapp.
+ * <p>
+ * In general, the WeakHashtable support added in commons-logging release 1.1
+ * ensures that logging classes do not hold references that prevent an
+ * undeployed webapp's memory from being garbage-collected even when multiple
+ * copies of commons-logging are deployed via multiple classloaders (a
+ * situation that earlier versions had problems with). However there are
+ * some rare cases where the WeakHashtable approach does not work; in these
+ * situations specifying this class as a listener for the web application will
+ * ensure that all references held by commons-logging are fully released.
+ * <p>
+ * To use this class, configure the webapp deployment descriptor to call
+ * this class on webapp undeploy; the contextDestroyed method will tell
+ * every accessible LogFactory class that the entry in its map for the
+ * current webapp's context classloader should be cleared.
+ *
+ * @version $Id: ServletContextCleaner.java 1432580 2013-01-13 10:41:05Z tn $
+ * @since 1.1
+ */
+public class ServletContextCleaner implements ServletContextListener {
+
+    private static final Class[] RELEASE_SIGNATURE = {ClassLoader.class};
+
+    /**
+     * Invoked when a webapp is undeployed, this tells the LogFactory
+     * class to release any logging information related to the current
+     * contextClassloader.
+     */
+    public void contextDestroyed(ServletContextEvent sce) {
+        ClassLoader tccl = Thread.currentThread().getContextClassLoader();
+
+        Object[] params = new Object[1];
+        params[0] = tccl;
+
+        // Walk up the tree of classloaders, finding all the available
+        // LogFactory classes and releasing any objects associated with
+        // the tccl (ie the webapp).
+        //
+        // When there is only one LogFactory in the classpath, and it
+        // is within the webapp being undeployed then there is no problem;
+        // garbage collection works fine.
+        //
+        // When there are multiple LogFactory classes in the classpath but
+        // parent-first classloading is used everywhere, this loop is really
+        // short. The first instance of LogFactory found will
+        // be the highest in the classpath, and then no more will be found.
+        // This is ok, as with this setup this will be the only LogFactory
+        // holding any data associated with the tccl being released.
+        //
+        // When there are multiple LogFactory classes in the classpath and
+        // child-first classloading is used in any classloader, then multiple
+        // LogFactory instances may hold info about this TCCL; whenever the
+        // webapp makes a call into a class loaded via an ancestor classloader
+        // and that class calls LogFactory the tccl gets registered in
+        // the LogFactory instance that is visible from the ancestor
+        // classloader. However the concrete logging library it points
+        // to is expected to have been loaded via the TCCL, so the
+        // underlying logging lib is only initialised/configured once.
+        // These references from ancestor LogFactory classes down to
+        // TCCL classloaders are held via weak references and so should
+        // be released but there are circumstances where they may not.
+        // Walking up the classloader ancestry ladder releasing
+        // the current tccl at each level tree, though, will definitely
+        // clear any problem references.
+        ClassLoader loader = tccl;
+        while (loader != null) {
+            // Load via the current loader. Note that if the class is not accessible
+            // via this loader, but is accessible via some ancestor then that class
+            // will be returned.
+            try {
+                Class logFactoryClass = loader.loadClass("org.apache.commons.logging.LogFactory");
+                Method releaseMethod = logFactoryClass.getMethod("release", RELEASE_SIGNATURE);
+                releaseMethod.invoke(null, params);
+                loader = logFactoryClass.getClassLoader().getParent();
+            } catch(ClassNotFoundException ex) {
+                // Neither the current classloader nor any of its ancestors could find
+                // the LogFactory class, so we can stop now.
+                loader = null;
+            } catch(NoSuchMethodException ex) {
+                // This is not expected; every version of JCL has this method
+                System.err.println("LogFactory instance found which does not support release method!");
+                loader = null;
+            } catch(IllegalAccessException ex) {
+                // This is not expected; every ancestor class should be accessible
+                System.err.println("LogFactory instance found which is not accessable!");
+                loader = null;
+            } catch(InvocationTargetException ex) {
+                // This is not expected
+                System.err.println("LogFactory instance release method failed!");
+                loader = null;
+            }
+        }
+
+        // Just to be sure, invoke release on the LogFactory that is visible from
+        // this ServletContextCleaner class too. This should already have been caught
+        // by the above loop but just in case...
+        LogFactory.release(tccl);
+    }
+
+    /**
+     * Invoked when a webapp is deployed. Nothing needs to be done here.
+     */
+    public void contextInitialized(ServletContextEvent sce) {
+        // do nothing
+    }
+}
diff --git a/src/main/java/org/apache/commons/logging/impl/SimpleLog.java b/src/main/java/org/apache/commons/logging/impl/SimpleLog.java
new file mode 100644
index 0000000..d4756af
--- /dev/null
+++ b/src/main/java/org/apache/commons/logging/impl/SimpleLog.java
@@ -0,0 +1,649 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.impl;
+
+import java.io.InputStream;
+import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Properties;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogConfigurationException;
+
+/**
+ * Simple implementation of Log that sends all enabled log messages,
+ * for all defined loggers, to System.err.  The following system properties
+ * are supported to configure the behavior of this logger:
+ * <ul>
+ * <li><code>org.apache.commons.logging.simplelog.defaultlog</code> -
+ *     Default logging detail level for all instances of SimpleLog.
+ *     Must be one of ("trace", "debug", "info", "warn", "error", or "fatal").
+ *     If not specified, defaults to "info". </li>
+ * <li><code>org.apache.commons.logging.simplelog.log.xxxxx</code> -
+ *     Logging detail level for a SimpleLog instance named "xxxxx".
+ *     Must be one of ("trace", "debug", "info", "warn", "error", or "fatal").
+ *     If not specified, the default logging detail level is used.</li>
+ * <li><code>org.apache.commons.logging.simplelog.showlogname</code> -
+ *     Set to <code>true</code> if you want the Log instance name to be
+ *     included in output messages. Defaults to <code>false</code>.</li>
+ * <li><code>org.apache.commons.logging.simplelog.showShortLogname</code> -
+ *     Set to <code>true</code> if you want the last component of the name to be
+ *     included in output messages. Defaults to <code>true</code>.</li>
+ * <li><code>org.apache.commons.logging.simplelog.showdatetime</code> -
+ *     Set to <code>true</code> if you want the current date and time
+ *     to be included in output messages. Default is <code>false</code>.</li>
+ * <li><code>org.apache.commons.logging.simplelog.dateTimeFormat</code> -
+ *     The date and time format to be used in the output messages.
+ *     The pattern describing the date and time format is the same that is
+ *     used in <code>java.text.SimpleDateFormat</code>. If the format is not
+ *     specified or is invalid, the default format is used.
+ *     The default format is <code>yyyy/MM/dd HH:mm:ss:SSS zzz</code>.</li>
+ * </ul>
+ * <p>
+ * In addition to looking for system properties with the names specified
+ * above, this implementation also checks for a class loader resource named
+ * <code>"simplelog.properties"</code>, and includes any matching definitions
+ * from this resource (if it exists).
+ *
+ * @version $Id: SimpleLog.java 1435115 2013-01-18 12:40:19Z tn $
+ */
+public class SimpleLog implements Log, Serializable {
+
+    /** Serializable version identifier. */
+    private static final long serialVersionUID = 136942970684951178L;
+
+    // ------------------------------------------------------- Class Attributes
+
+    /** All system properties used by <code>SimpleLog</code> start with this */
+    static protected final String systemPrefix = "org.apache.commons.logging.simplelog.";
+
+    /** Properties loaded from simplelog.properties */
+    static protected final Properties simpleLogProps = new Properties();
+
+    /** The default format to use when formating dates */
+    static protected final String DEFAULT_DATE_TIME_FORMAT = "yyyy/MM/dd HH:mm:ss:SSS zzz";
+
+    /** Include the instance name in the log message? */
+    static volatile protected boolean showLogName = false;
+
+    /** Include the short name ( last component ) of the logger in the log
+     *  message. Defaults to true - otherwise we'll be lost in a flood of
+     *  messages without knowing who sends them.
+     */
+    static volatile protected boolean showShortName = true;
+
+    /** Include the current time in the log message */
+    static volatile protected boolean showDateTime = false;
+
+    /** The date and time format to use in the log message */
+    static volatile protected String dateTimeFormat = DEFAULT_DATE_TIME_FORMAT;
+
+    /**
+     * Used to format times.
+     * <p>
+     * Any code that accesses this object should first obtain a lock on it,
+     * ie use synchronized(dateFormatter); this requirement was introduced
+     * in 1.1.1 to fix an existing thread safety bug (SimpleDateFormat.format
+     * is not thread-safe).
+     */
+    static protected DateFormat dateFormatter = null;
+
+    // ---------------------------------------------------- Log Level Constants
+
+    /** "Trace" level logging. */
+    public static final int LOG_LEVEL_TRACE  = 1;
+    /** "Debug" level logging. */
+    public static final int LOG_LEVEL_DEBUG  = 2;
+    /** "Info" level logging. */
+    public static final int LOG_LEVEL_INFO   = 3;
+    /** "Warn" level logging. */
+    public static final int LOG_LEVEL_WARN   = 4;
+    /** "Error" level logging. */
+    public static final int LOG_LEVEL_ERROR  = 5;
+    /** "Fatal" level logging. */
+    public static final int LOG_LEVEL_FATAL  = 6;
+
+    /** Enable all logging levels */
+    public static final int LOG_LEVEL_ALL    = LOG_LEVEL_TRACE - 1;
+
+    /** Enable no logging levels */
+    public static final int LOG_LEVEL_OFF    = LOG_LEVEL_FATAL + 1;
+
+    // ------------------------------------------------------------ Initializer
+
+    private static String getStringProperty(String name) {
+        String prop = null;
+        try {
+            prop = System.getProperty(name);
+        } catch (SecurityException e) {
+            // Ignore
+        }
+        return prop == null ? simpleLogProps.getProperty(name) : prop;
+    }
+
+    private static String getStringProperty(String name, String dephault) {
+        String prop = getStringProperty(name);
+        return prop == null ? dephault : prop;
+    }
+
+    private static boolean getBooleanProperty(String name, boolean dephault) {
+        String prop = getStringProperty(name);
+        return prop == null ? dephault : "true".equalsIgnoreCase(prop);
+    }
+
+    // Initialize class attributes.
+    // Load properties file, if found.
+    // Override with system properties.
+    static {
+        // Add props from the resource simplelog.properties
+        InputStream in = getResourceAsStream("simplelog.properties");
+        if(null != in) {
+            try {
+                simpleLogProps.load(in);
+                in.close();
+            } catch(java.io.IOException e) {
+                // ignored
+            }
+        }
+
+        showLogName = getBooleanProperty(systemPrefix + "showlogname", showLogName);
+        showShortName = getBooleanProperty(systemPrefix + "showShortLogname", showShortName);
+        showDateTime = getBooleanProperty(systemPrefix + "showdatetime", showDateTime);
+
+        if(showDateTime) {
+            dateTimeFormat = getStringProperty(systemPrefix + "dateTimeFormat",
+                                               dateTimeFormat);
+            try {
+                dateFormatter = new SimpleDateFormat(dateTimeFormat);
+            } catch(IllegalArgumentException e) {
+                // If the format pattern is invalid - use the default format
+                dateTimeFormat = DEFAULT_DATE_TIME_FORMAT;
+                dateFormatter = new SimpleDateFormat(dateTimeFormat);
+            }
+        }
+    }
+
+    // ------------------------------------------------------------- Attributes
+
+    /** The name of this simple log instance */
+    protected volatile String logName = null;
+    /** The current log level */
+    protected volatile int currentLogLevel;
+    /** The short name of this simple log instance */
+    private volatile String shortLogName = null;
+
+    // ------------------------------------------------------------ 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(int currentLogLevel) {
+        this.currentLogLevel = currentLogLevel;
+    }
+
+    /**
+     * Get logging level.
+     */
+    public int getLevel() {
+        return currentLogLevel;
+    }
+
+    // -------------------------------------------------------- Logging Methods
+
+    /**
+     * Do the actual logging.
+     * <p>
+     * This method assembles the message and then calls <code>write()</code>
+     * 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(int type, Object message, Throwable t) {
+        // Use a string buffer for better performance
+        final StringBuffer buf = new StringBuffer();
+
+        // 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 java.io.StringWriter sw = new java.io.StringWriter(1024);
+            final java.io.PrintWriter pw = new java.io.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</code> to the appropriate output destination.  The
+     * default implementation writes to <code>System.err</code>.
+     *
+     * @param buffer A <code>StringBuffer</code> containing the accumulated
+     *  text to be logged
+     */
+    protected void write(StringBuffer buffer) {
+        System.err.println(buffer.toString());
+    }
+
+    /**
+     * Is the given log level currently enabled?
+     *
+     * @param logLevel is this level enabled?
+     */
+    protected boolean isLevelEnabled(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</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#debug(Object)
+     */
+    public final void debug(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</code>.
+     *
+     * @param message to log
+     * @param t log this cause
+     * @see org.apache.commons.logging.Log#debug(Object, Throwable)
+     */
+    public final void debug(Object message, 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</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#trace(Object)
+     */
+    public final void trace(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</code>.
+     *
+     * @param message to log
+     * @param t log this cause
+     * @see org.apache.commons.logging.Log#trace(Object, Throwable)
+     */
+    public final void trace(Object message, 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</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#info(Object)
+     */
+    public final void info(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</code>.
+     *
+     * @param message to log
+     * @param t log this cause
+     * @see org.apache.commons.logging.Log#info(Object, Throwable)
+     */
+    public final void info(Object message, 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</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#warn(Object)
+     */
+    public final void warn(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</code>.
+     *
+     * @param message to log
+     * @param t log this cause
+     * @see org.apache.commons.logging.Log#warn(Object, Throwable)
+     */
+    public final void warn(Object message, 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</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#error(Object)
+     */
+    public final void error(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</code>.
+     *
+     * @param message to log
+     * @param t log this cause
+     * @see org.apache.commons.logging.Log#error(Object, Throwable)
+     */
+    public final void error(Object message, 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</code>.
+     *
+     * @param message to log
+     * @see org.apache.commons.logging.Log#fatal(Object)
+     */
+    public final void fatal(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</code>.
+     *
+     * @param message to log
+     * @param t log this cause
+     * @see org.apache.commons.logging.Log#fatal(Object, Throwable)
+     */
+    public final void fatal(Object message, Throwable t) {
+        if (isLevelEnabled(SimpleLog.LOG_LEVEL_FATAL)) {
+            log(SimpleLog.LOG_LEVEL_FATAL, message, t);
+        }
+    }
+
+    /**
+     * Are debug messages currently enabled?
+     * <p>
+     * This allows expensive operations such as <code>String</code>
+     * concatenation to be avoided when the message will be ignored by the
+     * logger.
+     */
+    public final boolean isDebugEnabled() {
+        return isLevelEnabled(SimpleLog.LOG_LEVEL_DEBUG);
+    }
+
+    /**
+     * Are error messages currently enabled?
+     * <p>
+     * This allows expensive operations such as <code>String</code>
+     * concatenation to be avoided when the message will be ignored by the
+     * logger.
+     */
+    public final boolean isErrorEnabled() {
+        return isLevelEnabled(SimpleLog.LOG_LEVEL_ERROR);
+    }
+
+    /**
+     * Are fatal messages currently enabled?
+     * <p>
+     * This allows expensive operations such as <code>String</code>
+     * concatenation to be avoided when the message will be ignored by the
+     * logger.
+     */
+    public final boolean isFatalEnabled() {
+        return isLevelEnabled(SimpleLog.LOG_LEVEL_FATAL);
+    }
+
+    /**
+     * Are info messages currently enabled?
+     * <p>
+     * This allows expensive operations such as <code>String</code>
+     * concatenation to be avoided when the message will be ignored by the
+     * logger.
+     */
+    public final boolean isInfoEnabled() {
+        return isLevelEnabled(SimpleLog.LOG_LEVEL_INFO);
+    }
+
+    /**
+     * Are trace messages currently enabled?
+     * <p>
+     * This allows expensive operations such as <code>String</code>
+     * concatenation to be avoided when the message will be ignored by the
+     * logger.
+     */
+    public final boolean isTraceEnabled() {
+        return isLevelEnabled(SimpleLog.LOG_LEVEL_TRACE);
+    }
+
+    /**
+     * Are warn messages currently enabled?
+     * <p>
+     * This allows expensive operations such as <code>String</code>
+     * concatenation to be avoided when the message will be ignored by the
+     * logger.
+     */
+    public final boolean isWarnEnabled() {
+        return isLevelEnabled(SimpleLog.LOG_LEVEL_WARN);
+    }
+
+    /**
+     * Return the thread context class loader if available.
+     * Otherwise return null.
+     *
+     * The thread context class loader is available for JDK 1.2
+     * or later, if certain security conditions are met.
+     *
+     * @exception LogConfigurationException if a suitable class loader
+     * cannot be identified.
+     */
+    private static ClassLoader getContextClassLoader() {
+        ClassLoader classLoader = null;
+
+        try {
+            // Are we running on a JDK 1.2 or later system?
+            final Method method = Thread.class.getMethod("getContextClassLoader", (Class[]) null);
+
+            // Get the thread context class loader (if there is one)
+            try {
+                classLoader = (ClassLoader)method.invoke(Thread.currentThread(), (Class[]) null);
+            } catch (IllegalAccessException e) {
+                // ignore
+            } catch (InvocationTargetException e) {
+                /**
+                 * InvocationTargetException is thrown by 'invoke' when
+                 * the method being invoked (getContextClassLoader) throws
+                 * an exception.
+                 *
+                 * 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.
+                 *
+                 * In the first case (not related), we want to ignore and
+                 * keep going.  We cannot help but also ignore the second
+                 * with the logic below, but other calls elsewhere (to
+                 * obtain a class loader) will trigger this exception where
+                 * we can make a distinction.
+                 */
+                if (e.getTargetException() instanceof SecurityException) {
+                    // ignore
+                } else {
+                    // Capture 'e.getTargetException()' exception for details
+                    // alternate: log 'e.getTargetException()', and pass back 'e'.
+                    throw new LogConfigurationException
+                        ("Unexpected InvocationTargetException", e.getTargetException());
+                }
+            }
+        } catch (NoSuchMethodException e) {
+            // Assume we are running on JDK 1.1
+            // ignore
+        }
+
+        if (classLoader == null) {
+            classLoader = SimpleLog.class.getClassLoader();
+        }
+
+        // Return the selected class loader
+        return classLoader;
+    }
+
+    private static InputStream getResourceAsStream(final String name) {
+        return (InputStream)AccessController.doPrivileged(
+            new PrivilegedAction() {
+                public Object run() {
+                    ClassLoader threadCL = getContextClassLoader();
+
+                    if (threadCL != null) {
+                        return threadCL.getResourceAsStream(name);
+                    } else {
+                        return ClassLoader.getSystemResourceAsStream(name);
+                    }
+                }
+            });
+    }
+}
+
diff --git a/src/main/java/org/apache/commons/logging/impl/WeakHashtable.java b/src/main/java/org/apache/commons/logging/impl/WeakHashtable.java
new file mode 100644
index 0000000..c5773f3
--- /dev/null
+++ b/src/main/java/org/apache/commons/logging/impl/WeakHashtable.java
@@ -0,0 +1,482 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.impl;
+
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Implementation of <code>Hashtable</code> that uses <code>WeakReference</code>'s
+ * to hold its keys thus allowing them to be reclaimed by the garbage collector.
+ * The associated values are retained using strong references.
+ * <p>
+ * This class follows the semantics of <code>Hashtable</code> as closely as
+ * possible. It therefore does not accept null values or keys.
+ * <p>
+ * <strong>Note:</strong>
+ * This is <em>not</em> intended to be a general purpose hash table replacement.
+ * This implementation is also tuned towards a particular purpose: for use as a replacement
+ * for <code>Hashtable</code> in <code>LogFactory</code>. This application requires
+ * good liveliness for <code>get</code> and <code>put</code>. Various tradeoffs
+ * have been made with this in mind.
+ * <p>
+ * <strong>Usage:</strong> typical use case is as a drop-in replacement
+ * for the <code>Hashtable</code> used in <code>LogFactory</code> for J2EE environments
+ * running 1.3+ JVMs. Use of this class <i>in most cases</i> (see below) will
+ * allow classloaders to be collected by the garbage collector without the need
+ * to call {@link org.apache.commons.logging.LogFactory#release(ClassLoader) LogFactory.release(ClassLoader)}.
+ * <p>
+ * <code>org.apache.commons.logging.LogFactory</code> checks whether this class
+ * can be supported by the current JVM, and if so then uses it to store
+ * references to the <code>LogFactory</code> implementation it loads
+ * (rather than using a standard Hashtable instance).
+ * Having this class used instead of <code>Hashtable</code> solves
+ * certain issues related to dynamic reloading of applications in J2EE-style
+ * environments. However this class requires java 1.3 or later (due to its use
+ * of <code>java.lang.ref.WeakReference</code> and associates).
+ * And by the way, this extends <code>Hashtable</code> rather than <code>HashMap</code>
+ * for backwards compatibility reasons. See the documentation
+ * for method <code>LogFactory.createFactoryStore</code> for more details.
+ * <p>
+ * The reason all this is necessary is due to a issue which
+ * arises during hot deploy in a J2EE-like containers.
+ * Each component running in the container owns one or more classloaders; when
+ * the component loads a LogFactory instance via the component classloader
+ * a reference to it gets stored in the static LogFactory.factories member,
+ * keyed by the component's classloader so different components don't
+ * stomp on each other. When the component is later unloaded, the container
+ * sets the component's classloader to null with the intent that all the
+ * component's classes get garbage-collected. However there's still a
+ * reference to the component's classloader from a key in the "global"
+ * <code>LogFactory</code>'s factories member! If <code>LogFactory.release()</code>
+ * is called whenever component is unloaded, the classloaders will be correctly
+ * garbage collected; this <i>should</i> be done by any container that
+ * bundles commons-logging by default. However, holding the classloader
+ * references weakly ensures that the classloader will be garbage collected
+ * without the container performing this step.
+ * <p>
+ * <strong>Limitations:</strong>
+ * There is still one (unusual) scenario in which a component will not
+ * be correctly unloaded without an explicit release. Though weak references
+ * are used for its keys, it is necessary to use strong references for its values.
+ * <p>
+ * If the abstract class <code>LogFactory</code> is
+ * loaded by the container classloader but a subclass of
+ * <code>LogFactory</code> [LogFactory1] is loaded by the component's
+ * classloader and an instance stored in the static map associated with the
+ * base LogFactory class, then there is a strong reference from the LogFactory
+ * class to the LogFactory1 instance (as normal) and a strong reference from
+ * the LogFactory1 instance to the component classloader via
+ * <code>getClass().getClassLoader()</code>. This chain of references will prevent
+ * collection of the child classloader.
+ * <p>
+ * Such a situation occurs when the commons-logging.jar is
+ * loaded by a parent classloader (e.g. a server level classloader in a
+ * servlet container) and a custom <code>LogFactory</code> implementation is
+ * loaded by a child classloader (e.g. a web app classloader).
+ * <p>
+ * To avoid this scenario, ensure
+ * that any custom LogFactory subclass is loaded by the same classloader as
+ * the base <code>LogFactory</code>. Creating custom LogFactory subclasses is,
+ * however, rare. The standard LogFactoryImpl class should be sufficient
+ * for most or all users.
+ *
+ * @version $Id: WeakHashtable.java 1435077 2013-01-18 10:51:35Z tn $
+ * @since 1.1
+ */
+public final class WeakHashtable extends Hashtable {
+
+    /** Serializable version identifier. */
+    private static final long serialVersionUID = -1546036869799732453L;
+
+    /**
+     * The maximum number of times put() or remove() can be called before
+     * the map will be purged of all cleared entries.
+     */
+    private static final int MAX_CHANGES_BEFORE_PURGE = 100;
+
+    /**
+     * The maximum number of times put() or remove() can be called before
+     * the map will be purged of one cleared entry.
+     */
+    private static final int PARTIAL_PURGE_COUNT     = 10;
+
+    /* ReferenceQueue we check for gc'd keys */
+    private final ReferenceQueue queue = new ReferenceQueue();
+    /* Counter used to control how often we purge gc'd entries */
+    private int changeCount = 0;
+
+    /**
+     * Constructs a WeakHashtable with the Hashtable default
+     * capacity and load factor.
+     */
+    public WeakHashtable() {}
+
+    /**
+     *@see Hashtable
+     */
+    public boolean containsKey(Object key) {
+        // purge should not be required
+        Referenced referenced = new Referenced(key);
+        return super.containsKey(referenced);
+    }
+
+    /**
+     *@see Hashtable
+     */
+    public Enumeration elements() {
+        purge();
+        return super.elements();
+    }
+
+    /**
+     *@see Hashtable
+     */
+    public Set entrySet() {
+        purge();
+        Set referencedEntries = super.entrySet();
+        Set unreferencedEntries = new HashSet();
+        for (Iterator it=referencedEntries.iterator(); it.hasNext();) {
+            Map.Entry entry = (Map.Entry) it.next();
+            Referenced referencedKey = (Referenced) entry.getKey();
+            Object key = referencedKey.getValue();
+            Object value = entry.getValue();
+            if (key != null) {
+                Entry dereferencedEntry = new Entry(key, value);
+                unreferencedEntries.add(dereferencedEntry);
+            }
+        }
+        return unreferencedEntries;
+    }
+
+    /**
+     *@see Hashtable
+     */
+    public Object get(Object key) {
+        // for performance reasons, no purge
+        Referenced referenceKey = new Referenced(key);
+        return super.get(referenceKey);
+    }
+
+    /**
+     *@see Hashtable
+     */
+    public Enumeration keys() {
+        purge();
+        final Enumeration enumer = super.keys();
+        return new Enumeration() {
+            public boolean hasMoreElements() {
+                return enumer.hasMoreElements();
+            }
+            public Object nextElement() {
+                 Referenced nextReference = (Referenced) enumer.nextElement();
+                 return nextReference.getValue();
+            }
+        };
+    }
+
+    /**
+     *@see Hashtable
+     */
+    public Set keySet() {
+        purge();
+        Set referencedKeys = super.keySet();
+        Set unreferencedKeys = new HashSet();
+        for (Iterator it=referencedKeys.iterator(); it.hasNext();) {
+            Referenced referenceKey = (Referenced) it.next();
+            Object keyValue = referenceKey.getValue();
+            if (keyValue != null) {
+                unreferencedKeys.add(keyValue);
+            }
+        }
+        return unreferencedKeys;
+    }
+
+    /**
+     *@see Hashtable
+     */
+    public synchronized Object put(Object key, Object value) {
+        // check for nulls, ensuring semantics match superclass
+        if (key == null) {
+            throw new NullPointerException("Null keys are not allowed");
+        }
+        if (value == null) {
+            throw new NullPointerException("Null values are not allowed");
+        }
+
+        // for performance reasons, only purge every
+        // MAX_CHANGES_BEFORE_PURGE times
+        if (changeCount++ > MAX_CHANGES_BEFORE_PURGE) {
+            purge();
+            changeCount = 0;
+        }
+        // do a partial purge more often
+        else if (changeCount % PARTIAL_PURGE_COUNT == 0) {
+            purgeOne();
+        }
+
+        Referenced keyRef = new Referenced(key, queue);
+        return super.put(keyRef, value);
+    }
+
+    /**
+     *@see Hashtable
+     */
+    public void putAll(Map t) {
+        if (t != null) {
+            Set entrySet = t.entrySet();
+            for (Iterator it=entrySet.iterator(); it.hasNext();) {
+                Map.Entry entry = (Map.Entry) it.next();
+                put(entry.getKey(), entry.getValue());
+            }
+        }
+    }
+
+    /**
+     *@see Hashtable
+     */
+    public Collection values() {
+        purge();
+        return super.values();
+    }
+
+    /**
+     *@see Hashtable
+     */
+    public synchronized Object remove(Object key) {
+        // for performance reasons, only purge every
+        // MAX_CHANGES_BEFORE_PURGE times
+        if (changeCount++ > MAX_CHANGES_BEFORE_PURGE) {
+            purge();
+            changeCount = 0;
+        }
+        // do a partial purge more often
+        else if (changeCount % PARTIAL_PURGE_COUNT == 0) {
+            purgeOne();
+        }
+        return super.remove(new Referenced(key));
+    }
+
+    /**
+     *@see Hashtable
+     */
+    public boolean isEmpty() {
+        purge();
+        return super.isEmpty();
+    }
+
+    /**
+     *@see Hashtable
+     */
+    public int size() {
+        purge();
+        return super.size();
+    }
+
+    /**
+     *@see Hashtable
+     */
+    public String toString() {
+        purge();
+        return super.toString();
+    }
+
+    /**
+     * @see Hashtable
+     */
+    protected void rehash() {
+        // purge here to save the effort of rehashing dead entries
+        purge();
+        super.rehash();
+    }
+
+    /**
+     * 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) {
+            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(Object key, Object value) {
+            this.key = key;
+            this.value = value;
+        }
+
+        public boolean equals(Object o) {
+            boolean result = false;
+            if (o != null && o instanceof Map.Entry) {
+                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;
+        }
+
+        public int hashCode() {
+            return (getKey()==null ? 0 : getKey().hashCode()) ^
+                (getValue()==null ? 0 : getValue().hashCode());
+        }
+
+        public Object setValue(Object value) {
+            throw new UnsupportedOperationException("Entry.setValue is not supported.");
+        }
+
+        public Object getValue() {
+            return value;
+        }
+
+        public Object getKey() {
+            return key;
+        }
+    }
+
+    /** Wrapper giving correct symantics for equals and hashcode */
+    private final static class Referenced {
+
+        private final WeakReference reference;
+        private final int           hashCode;
+
+        /**
+         *
+         * @throws NullPointerException if referant is <code>null</code>
+         */
+        private Referenced(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</code>
+         */
+        private Referenced(Object key, 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();
+
+        }
+
+        public int hashCode() {
+            return hashCode;
+        }
+
+        private Object getValue() {
+            return reference.get();
+        }
+
+        public boolean equals(Object o) {
+            boolean result = false;
+            if (o instanceof Referenced) {
+                Referenced otherKey = (Referenced) o;
+                Object thisKeyValue = getValue();
+                Object otherKeyValue = otherKey.getValue();
+                if (thisKeyValue == null) {
+                    result = otherKeyValue == null;
+
+                    // Since our hashcode was calculated from the original
+                    // non-null referant, the above check breaks the
+                    // hashcode/equals contract, as two cleared Referenced
+                    // objects could test equal but have different hashcodes.
+                    // We can reduce (not eliminate) the chance of this
+                    // happening by comparing hashcodes.
+                    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</code> and also makes accessible
+     * the Referenced object holding it.
+     */
+    private final static class WeakKey extends WeakReference {
+
+        private final Referenced referenced;
+
+        private WeakKey(Object key,
+                        ReferenceQueue queue,
+                        Referenced referenced) {
+            super(key, queue);
+            this.referenced = referenced;
+        }
+
+        private Referenced getReferenced() {
+            return referenced;
+        }
+     }
+}
diff --git a/src/main/java/org/apache/commons/logging/impl/package.html b/src/main/java/org/apache/commons/logging/impl/package.html
new file mode 100644
index 0000000..5344784
--- /dev/null
+++ b/src/main/java/org/apache/commons/logging/impl/package.html
@@ -0,0 +1,22 @@
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+ 
+      http://www.apache.org/licenses/LICENSE-2.0
+ 
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+
+<body>
+<p>Concrete implementations of commons-logging wrapper APIs.</p>
+</body>
diff --git a/src/main/java/org/apache/commons/logging/package.html b/src/main/java/org/apache/commons/logging/package.html
new file mode 100644
index 0000000..e9b32a5
--- /dev/null
+++ b/src/main/java/org/apache/commons/logging/package.html
@@ -0,0 +1,255 @@
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+ 
+      http://www.apache.org/licenses/LICENSE-2.0
+ 
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+
+<body>
+<p>Simple wrapper API around multiple logging APIs.</p>
+
+
+<h3>Overview</h3>
+
+<p>This package provides an API for logging in server-based applications that
+can be used around a variety of different logging implementations, including
+prebuilt support for the following:</p>
+<ul>
+<li><a href="http://logging.apache.org/log4j/">Log4J</a> (version 1.2 or later)
+    from Apache's Logging project.  Each named <a href="Log.html">Log</a>
+    instance is connected to a corresponding Log4J Logger.</li>
+<li><a href="http://java.sun.com/j2se/1.4/docs/guide/util/logging/index.html">
+    JDK Logging API</a>, included in JDK 1.4 or later systems.  Each named
+    <a href="Log.html">Log</a> instance is connected to a corresponding
+    <code>java.util.logging.Logger</code> instance.</li>
+<li><a href="http://avalon.apache.org/logkit/">LogKit</a> from Apache's
+    Avalon project.  Each named <a href="Log.html">Log</a> instance is
+    connected to a corresponding LogKit <code>Logger</code>.</li>
+<li><a href="impl/NoOpLog.html">NoOpLog</a> implementation that simply swallows
+    all log output, for all named <a href="Log.html">Log</a> instances.</li>
+<li><a href="impl/SimpleLog.html">SimpleLog</a> implementation that writes all
+    log output, for all named <a href="Log.html">Log</a> instances, to
+    System.err.</li>
+</ul>
+
+
+<h3>Quick Start Guide</h3>
+
+<p>For those impatient to just get on with it, the following example
+illustrates the typical declaration and use of a logger that is named (by
+convention) after the calling class:
+
+<pre>
+    import org.apache.commons.logging.Log;
+    import org.apache.commons.logging.LogFactory;
+
+    public class Foo {
+
+        private Log log = LogFactory.getLog(Foo.class);
+
+        public void foo() {
+            ...
+            try {
+                if (log.isDebugEnabled()) {
+                    log.debug("About to do something to object " + name);
+                }
+                name.bar();
+            } catch (IllegalStateException e) {
+                log.error("Something bad happened to " + name, e);
+            }
+            ...
+        }
+</pre>
+
+<p>Unless you configure things differently, all log output will be written
+to System.err.  Therefore, you really will want to review the remainder of
+this page in order to understand how to configure logging for your
+application.</p>
+
+
+<h3>Configuring the Commons Logging Package</h3>
+
+
+<h4>Choosing a <code>LogFactory</code> Implementation</h4>
+
+<p>From an application perspective, the first requirement is to retrieve an
+object reference to the <code>LogFactory</code> instance that will be used
+to create <code><a href="Log.html">Log</a></code> instances for this
+application.  This is normally accomplished by calling the static
+<code>getFactory()</code> method.  This method implements the following
+discovery algorithm to select the name of the <code>LogFactory</code>
+implementation class this application wants to use:</p>
+<ul>
+<li>Check for a system property named
+   <code>org.apache.commons.logging.LogFactory</code>.</li>
+<li>Use the JDK 1.3 JAR Services Discovery mechanism (see
+    <a href="http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html">
+    http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html</a> for
+    more information) to look for a resource named
+    <code>META-INF/services/org.apache.commons.logging.LogFactory</code>
+    whose first line is assumed to contain the desired class name.</li>
+<li>Look for a properties file named <code>commons-logging.properties</code>
+    visible in the application class path, with a property named
+    <code>org.apache.commons.logging.LogFactory</code> defining the
+    desired implementation class name.</li>
+<li>Fall back to a default implementation, which is described
+    further below.</li>
+</ul>
+
+<p>If a <code>commons-logging.properties</code> file is found, all of the
+properties defined there are also used to set configuration attributes on
+the instantiated <code>LogFactory</code> instance.</p>
+
+<p>Once an implementation class name is selected, the corresponding class is
+loaded from the current Thread context class loader (if there is one), or
+from the class loader that loaded the <code>LogFactory</code> class itself
+otherwise.  This allows a copy of <code>commons-logging.jar</code> to be
+shared in a multiple class loader environment (such as a servlet container),
+but still allow each web application to provide its own <code>LogFactory</code>
+implementation, if it so desires.  An instance of this class will then be
+created, and cached per class loader.
+
+
+<h4>The Default <code>LogFactory</code> Implementation</h4>
+
+<p>The Logging Package APIs include a default <code>LogFactory</code>
+implementation class (<a href="impl/LogFactoryImpl.html">
+org.apache.commons.logging.impl.LogFactoryImpl</a>) that is selected if no
+other implementation class name can be discovered.  Its primary purpose is
+to create (as necessary) and return <a href="Log.html">Log</a> instances
+in response to calls to the <code>getInstance()</code> method.  The default
+implementation uses the following rules:</p>
+<ul>
+<li>At most one <code>Log</code> instance of the same name will be created.
+    Subsequent <code>getInstance()</code> calls to the same
+    <code>LogFactory</code> instance, with the same name or <code>Class</code>
+    parameter, will return the same <code>Log</code> instance.</li>
+<li>When a new <code>Log</code> instance must be created, the default
+    <code>LogFactory</code> implementation uses the following discovery
+    process:
+    <ul>
+    <li>Look for a configuration attribute of this factory named
+        <code>org.apache.commons.logging.Log</code> (for backwards
+        compatibility to pre-1.0 versions of this API, an attribute
+        <code>org.apache.commons.logging.log</code> is also consulted).</li>
+    <li>Look for a system property named
+        <code>org.apache.commons.logging.Log</code> (for backwards
+        compatibility to pre-1.0 versions of this API, a system property
+        <code>org.apache.commons.logging.log</code> is also consulted).</li>
+    <li>If the Log4J logging system is available in the application
+        class path, use the corresponding wrapper class
+        (<a href="impl/Log4JLogger.html">Log4JLogger</a>).</li>
+    <li>If the application is executing on a JDK 1.4 system, use
+        the corresponding wrapper class
+        (<a href="impl/Jdk14Logger.html">Jdk14Logger</a>).</li>
+    <li>Fall back to the default simple logging implementation
+        (<a href="impl/SimpleLog.html">SimpleLog</a>).</li>
+    </ul></li>
+<li>Load the class of the specified name from the thread context class
+    loader (if any), or from the class loader that loaded the
+    <code>LogFactory</code> class otherwise.</li>
+<li>Instantiate an instance of the selected <code>Log</code>
+    implementation class, passing the specified name as the single
+    argument to its constructor.</li>
+</ul>
+
+<p>See the <a href="impl/SimpleLog.html">SimpleLog</a> JavaDocs for detailed
+configuration information for this default implementation.</p>
+
+
+<h4>Configuring the Underlying Logging System</h4>
+
+<p>The basic principle is that the user is totally responsible for the
+configuration of the underlying logging system.
+Commons-logging should not change the existing configuration.</p>
+
+<p>Each individual <a href="Log.html">Log</a> implementation may
+support its own configuration properties.  These will be documented in the
+class descriptions for the corresponding implementation class.</p>
+
+<p>Finally, some <code>Log</code> implementations (such as the one for Log4J)
+require an external configuration file for the entire logging environment.
+This file should be prepared in a manner that is specific to the actual logging
+technology being used.</p>
+
+
+<h3>Using the Logging Package APIs</h3>
+
+<p>Use of the Logging Package APIs, from the perspective of an application
+component, consists of the following steps:</p>
+<ol>
+<li>Acquire a reference to an instance of
+    <a href="Log.html">org.apache.commons.logging.Log</a>, by calling the
+    factory method
+    <a href="LogFactory.html#getInstance(java.lang.String)">
+    LogFactory.getInstance(String name)</a>.  Your application can contain
+    references to multiple loggers that are used for different
+    purposes.  A typical scenario for a server application is to have each
+    major component of the server use its own Log instance.</li>
+<li>Cause messages to be logged (if the corresponding detail level is enabled)
+    by calling appropriate methods (<code>trace()</code>, <code>debug()</code>,
+    <code>info()</code>, <code>warn()</code>, <code>error</code>, and
+    <code>fatal()</code>).</li>
+</ol>
+
+<p>For convenience, <code>LogFactory</code> also offers a static method
+<code>getLog()</code> that combines the typical two-step pattern:</p>
+<pre>
+  Log log = LogFactory.getFactory().getInstance(Foo.class);
+</pre>
+<p>into a single method call:</p>
+<pre>
+  Log log = LogFactory.getLog(Foo.class);
+</pre>
+
+<p>For example, you might use the following technique to initialize and
+use a <a href="Log.html">Log</a> instance in an application component:</p>
+<pre>
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+public class MyComponent {
+
+  protected Log log =
+    LogFactory.getLog(MyComponent.class);
+
+  // Called once at startup time
+  public void start() {
+    ...
+    log.info("MyComponent started");
+    ...
+  }
+
+  // Called once at shutdown time
+  public void stop() {
+    ...
+    log.info("MyComponent stopped");
+    ...
+  }
+
+  // Called repeatedly to process a particular argument value
+  // which you want logged if debugging is enabled
+  public void process(String value) {
+    ...
+    // Do the string concatenation only if logging is enabled
+    if (log.isDebugEnabled())
+      log.debug("MyComponent processing " + value);
+    ...
+  }
+
+}
+</pre>
+
+</body>
diff --git a/src/main/java/overview.html b/src/main/java/overview.html
new file mode 100644
index 0000000..4fc581b
--- /dev/null
+++ b/src/main/java/overview.html
@@ -0,0 +1,35 @@
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+ 
+      http://www.apache.org/licenses/LICENSE-2.0
+ 
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+
+<html>
+<head>
+<title>Overview Documentation for COMMONS-LOGGING</title>
+</head>
+<body bgcolor="white">
+<p>The <em>Logging Wrapper Library</em> component of the Apache Commons
+subproject offers wrappers around an extensible set of concrete logging
+implementations, so that application code based on it does not need to be
+modified in order to select a different logging implementation.</p>
+
+<p>See the
+<a href="org/apache/commons/logging/package-summary.html#package_description">
+Package Description</a> for the <code>org.apache.commons.logging</code>
+package for more information.</p>
+</body>
+</html>
diff --git a/src/media/logo.png b/src/media/logo.png
new file mode 100644
index 0000000..c4c43f8
Binary files /dev/null and b/src/media/logo.png differ
diff --git a/src/media/logo.xcf b/src/media/logo.xcf
new file mode 100644
index 0000000..40b5afa
Binary files /dev/null and b/src/media/logo.xcf differ
diff --git a/src/site/resources/images/logo.png b/src/site/resources/images/logo.png
new file mode 100644
index 0000000..c4c43f8
Binary files /dev/null and b/src/site/resources/images/logo.png differ
diff --git a/src/site/site.xml b/src/site/site.xml
new file mode 100644
index 0000000..73d424a
--- /dev/null
+++ b/src/site/site.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<project xmlns="http://maven.apache.org/DECORATION/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/DECORATION/1.1.0 http://maven.apache.org/xsd/decoration-1.1.0.xsd">
+  <bannerRight>
+    <name>Commons Logging</name>
+    <src>/images/logo.png</src>
+    <href>http://commons.apache.org/logging/</href>
+  </bannerRight>
+  <body>
+    <menu name="Commons Logging">
+      <item name="Overview"
+            href="/index.html"/>
+      <item name="Download"
+            href="/download_logging.cgi"/>
+      <item name="User Guide"
+            href="/guide.html"/>
+      <item name="Troubleshooting Guide"
+            href="/troubleshooting.html"/>
+      <item name="Release Notes"
+            href="/RELEASE-NOTES.txt"/>
+      <item name='JavaDoc (Latest release)'
+            href='/javadocs/api-release/index.html'/>
+      <item name='JavaDoc (v1.1.3)'
+            href='/javadocs/api-1.1.3/index.html'/>
+      <item name='JavaDoc (v1.0.4)'
+            href='/javadocs/api-1.0.4/index.html'/>
+    </menu>
+    <menu name="Development">
+      <item name="Building"
+            href="/building.html"/>
+      <item name="Mailing Lists"
+            href="/mail-lists.html"/>
+      <item name="Issue Tracking"
+            href="/issue-tracking.html"/>
+      <item name="Proposal"
+            href="/proposal.html"/>
+      <item name="Tech Guide"
+            href="/tech.html"/>
+      <item name="Wiki"
+            href="http://wiki.apache.org/commons/Logging"/>
+      <item name='JavaDoc (SVN latest)'
+            href='/apidocs/index.html'/>
+    </menu>    
+  </body>
+</project>
diff --git a/src/site/xdoc/building.xml b/src/site/xdoc/building.xml
new file mode 100644
index 0000000..7484ecd
--- /dev/null
+++ b/src/site/xdoc/building.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<document>
+ <properties>
+  <title>Building</title>
+  <author email="dev at commons.apache.org">Commons Documentation Team</author>
+ </properties>
+<body>
+<!-- ================================================== -->
+<section name="Overview">
+  <p>
+    Commons Logging uses <a href="http://maven.apache.org">Maven 2.0.x</a> as its
+    primary build system. <a href="http://ant.apache.org">Ant</a> can also be used.
+  </p>
+</section>
+<!-- ================================================== -->
+<section name="Maven">
+  <p>
+    To build the full website, run
+  </p>
+  <source>mvn site</source>
+  <p>
+    The result will be in the <code>target/site</code> folder.
+    You must be online and using JDK 1.4 or higher to successfully complete this target.
+  </p>
+  <p>
+    To build the jar files, run
+  </p>
+  <source>mvn package</source>
+  <p>
+    The resulting 4 jar files will be in the <code>target</code> folder.
+    You must use JDK 1.4 or higher to successfully complete this target.
+  </p>
+  <p>
+    To create a full distribution, run
+  </p>
+  <source>mvn clean site assembly:assembly</source>
+  <p>
+    The resulting .zip and .tar.gz files will be in the <code>target</code> folder.
+    You must use JDK 1.4 or higher to successfully complete this target.
+  </p>
+  <p>
+    Further details can be found in the
+    <a href="http://commons.apache.org/building.html">commons build instructions</a>.
+  </p>
+</section>
+<!-- ================================================== -->
+<section name="Ant">
+  <p>
+    We still use Ant to test the artifacts built my Maven.
+    Please follow the instructions in the file <code>build-testing.xml</code>.
+  </p>
+  <p>
+    <b>Note:</b> A 1.2 JDK is needed to run the tests.
+  </p>
+</section>
+<!-- ================================================== -->
+</body>
+</document>
diff --git a/src/site/xdoc/download_logging.xml b/src/site/xdoc/download_logging.xml
new file mode 100644
index 0000000..79ccd0d
--- /dev/null
+++ b/src/site/xdoc/download_logging.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<!--
+ +======================================================================+
+ |****                                                              ****|
+ |****      THIS FILE IS GENERATED BY THE COMMONS BUILD PLUGIN      ****|
+ |****                    DO NOT EDIT DIRECTLY                      ****|
+ |****                                                              ****|
+ +======================================================================+
+ | TEMPLATE FILE: download-page-template.xml                            |
+ | commons-build-plugin/trunk/src/main/resources/commons-xdoc-templates |
+ +======================================================================+
+ |                                                                      |
+ | 1) Re-generate using: mvn commons:download-page                      |
+ |                                                                      |
+ | 2) Set the following properties in the component's pom:              |
+ |    - commons.componentid (required, alphabetic, lower case)          |
+ |    - commons.release.version (required)                              |
+ |    - commons.binary.suffix (optional)                                |
+ |      (defaults to "-bin", set to "" for pre-maven2 releases)         |
+ |                                                                      |
+ | 3) Example Properties                                                |
+ |                                                                      |
+ |  <properties>                                                        |
+ |    <commons.componentid>math</commons.componentid>                   |
+ |    <commons.release.version>1.2</commons.release.version>            |
+ |  </properties>                                                       |
+ |                                                                      |
+ +======================================================================+
+-->
+<document>
+  <properties>
+    <title>Download Apache Commons Logging</title>
+    <author email="dev at commons.apache.org">Commons Documentation Team</author>
+  </properties>
+  <body>
+    <section name="Download Apache Commons Logging">
+    <subsection name="Using a Mirror">
+      <p>
+        We recommend you use a mirror to download our release
+        builds, but you <strong>must</strong> verify the integrity of
+        the downloaded files using signatures downloaded from our main
+        distribution directories. Recent releases (48 hours) may not yet
+        be available from the mirrors.
+      </p>
+
+      <p>
+        You are currently using <b>[preferred]</b>.  If you
+        encounter a problem with this mirror, please select another
+        mirror.  If all mirrors are failing, there are <i>backup</i>
+        mirrors (at the end of the mirrors list) that should be
+        available.
+        <br></br>
+        [if-any logo]<a href="[link]"><img align="right" src="[logo]" border="0"></img></a>[end]
+      </p>
+
+      <form action="[location]" method="get" id="SelectMirror">
+        <p>
+          Other mirrors:
+          <select name="Preferred">
+          [if-any http]
+            [for http]<option value="[http]">[http]</option>[end]
+          [end]
+          [if-any ftp]
+            [for ftp]<option value="[ftp]">[ftp]</option>[end]
+          [end]
+          [if-any backup]
+            [for backup]<option value="[backup]">[backup] (backup)</option>[end]
+          [end]
+          </select>
+          <input type="submit" value="Change"></input>
+        </p>
+      </form>
+
+      <p>
+        The <a href="http://www.apache.org/dist/commons/KEYS">KEYS</a>
+        link links to the code signing keys used to sign the product.
+        The <code>PGP</code> link downloads the OpenPGP compatible signature from our main site.
+        The <code>MD5</code> link downloads the checksum from the main site.
+      </p>
+    </subsection>
+    </section>
+    <section name="Apache Commons Logging 1.2 ">
+      <subsection name="Binaries">
+        <table>
+          <tr>
+              <td><a href="[preferred]/commons/logging/binaries/commons-logging-1.2-bin.tar.gz">commons-logging-1.2-bin.tar.gz</a></td>
+              <td><a href="http://www.apache.org/dist/commons/logging/binaries/commons-logging-1.2-bin.tar.gz.md5">md5</a></td>
+              <td><a href="http://www.apache.org/dist/commons/logging/binaries/commons-logging-1.2-bin.tar.gz.asc">pgp</a></td>
+          </tr>
+          <tr>
+              <td><a href="[preferred]/commons/logging/binaries/commons-logging-1.2-bin.zip">commons-logging-1.2-bin.zip</a></td>
+              <td><a href="http://www.apache.org/dist/commons/logging/binaries/commons-logging-1.2-bin.zip.md5">md5</a></td>
+              <td><a href="http://www.apache.org/dist/commons/logging/binaries/commons-logging-1.2-bin.zip.asc">pgp</a></td>
+          </tr>
+        </table>
+      </subsection>
+      <subsection name="Source">
+        <table>
+          <tr>
+              <td><a href="[preferred]/commons/logging/source/commons-logging-1.2-src.tar.gz">commons-logging-1.2-src.tar.gz</a></td>
+              <td><a href="http://www.apache.org/dist/commons/logging/source/commons-logging-1.2-src.tar.gz.md5">md5</a></td>
+              <td><a href="http://www.apache.org/dist/commons/logging/source/commons-logging-1.2-src.tar.gz.asc">pgp</a></td>
+          </tr>
+          <tr>
+              <td><a href="[preferred]/commons/logging/source/commons-logging-1.2-src.zip">commons-logging-1.2-src.zip</a></td>
+              <td><a href="http://www.apache.org/dist/commons/logging/source/commons-logging-1.2-src.zip.md5">md5</a></td>
+              <td><a href="http://www.apache.org/dist/commons/logging/source/commons-logging-1.2-src.zip.asc">pgp</a></td>
+          </tr>
+        </table>
+      </subsection>
+    </section>
+    <section name="Archives">
+        <p>
+          Older releases can be obtained from the archives.
+        </p>
+        <ul>
+          <li class="download"><a href="[preferred]/commons/logging/">browse download area</a></li>
+          <li><a href="http://archive.apache.org/dist/commons/logging/">archives...</a></li>
+        </ul>
+    </section>
+  </body>
+</document>
diff --git a/src/site/xdoc/guide.xml b/src/site/xdoc/guide.xml
new file mode 100644
index 0000000..4790aba
--- /dev/null
+++ b/src/site/xdoc/guide.xml
@@ -0,0 +1,850 @@
+<?xml version="1.0"?>
+
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+ 
+      http://www.apache.org/licenses/LICENSE-2.0
+ 
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+
+<document>
+
+ <properties>
+  <title>User Guide</title>
+  <author email="dev at commons.apache.org">Commons Documentation Team</author>
+ </properties>
+
+ <body>
+    <section name='Contents'>
+        <p>
+            <ol>
+                <li><a href='#Introduction'>Introduction</a></li>
+                <li><a href='#Quick Start'>Quick Start</a>
+                    <ol>
+                        <li><a href='#Configuration'>Configuration</a></li>
+                        <li>
+<a href='#Configuring The Underlying Logging System'>Configuring The Underlying Logging System</a>
+                        </li>
+                        <li>
+<a href='#Configuring Log4J'>Configuring Log4J</a>
+                        </li>
+                    </ol>
+                </li>
+                <li><a href='#Developing With JCL'>Developing With JCL</a>
+                     <ol>
+                        <li><a href='#Obtaining a Log Object'>Obtaining a Log Object</a></li>
+                        <li><a href='#Logging a Message'>Logging a Message</a></li>
+                        <li><a href='#Serialization Issues'>Serialization Issues</a></li>
+                     </ol>
+                </li>
+                <li><a href='#Jars Included in the Standard Distribution'>Jars Included in the Standard Distribution</a>
+                     <ol>
+                        <li><a href='#commons-logging.jar'>commons-logging.jar</a></li>
+                        <li><a href='#commons-logging-api.jar'>commons-logging-api.jar</a></li>
+                        <li><a href='#commons-logging-adapters.jar'>commons-logging-adapters.jar</a></li>
+                     </ol>
+                </li>
+                <li><a href='#JCL Best Practices'>JCL Best Practices</a></li>
+                <li><a href='#Best Practices (General)'>Best Practices (General)</a>
+                    <ol>
+                        <li><a href='#Code Guards'>Code Guards</a></li>
+                        <li><a href='#Message Priorities/Levels'>Message Priorities/Levels</a></li>
+                        <li><a href='#Default Message Priority/Level'>Default Message Priority/Level</a></li>
+                    </ol>
+                </li>
+                <li><a href='#Best Practices (Enterprise)'>Best Practices (Enterprise)</a>
+                    <ol>
+                        <li><a href='#Logging Exceptions'>Logging Exceptions</a></li>
+                        <li><a href='#When Info Level Instead of Debug?'>When Info Level Instead of Debug?</a></li>
+                        <li><a href='#More Control of Enterprise Exception Logging'>More Control of Enterprise Exception Logging</a></li>                    
+                        <li><a href='#National Language Support And Internationalization'>National Language Support And Internationalization</a></li>
+                        <li><a href='#Classloader and Memory Management'>Classloader and Memory Management</a></li>
+                    </ol>
+                </li>
+                <li><a href='#Extending Commons Logging'>Extending Commons Logging</a>
+                    <ol>
+                        <li><a href='#Contract'>Contract</a></li>
+                        <li><a href='#Creating a Log Implementation'>Creating a Log Implementation</a></li>
+                        <li><a href='#Creating A LogFactory Implementation'>Creating A LogFactory Implementation</a></li>
+                    </ol>
+                </li>
+                <li><a href='#A%20Quick%20Guide%20To%20Simple%20Log'>A Quick Guide To Simple Log</a>
+                </li>
+                <li><a href='#Frequently Asked Questions'>Frequently Asked Questions</a>
+                </li>
+            </ol>
+        </p>
+    </section>
+    <section name="Introduction">
+        <p>
+The Apache Commons Logging (JCL) provides a <code>Log</code> interface that
+is intended to be both light-weight and an independent abstraction of other logging toolkits.
+It provides the middleware/tooling developer with a simple
+logging abstraction, that allows the user (application developer) to plug in
+a specific logging implementation.
+    </p>
+<p>JCL provides thin-wrapper <code>Log</code> implementations for
+other logging tools, including
+<a href="http://logging.apache.org/log4j/docs/index.html">Log4J</a>,
+<a href="http://avalon.apache.org/logkit/index.html">Avalon LogKit</a>
+(the Avalon Framework's logging infrastructure),
+JDK 1.4, and an implementation of JDK 1.4 logging APIs (JSR-47) for pre-1.4
+systems.
+The interface maps closely to Log4J and LogKit.
+</p>
+        <p>
+Familiarity with high-level details of the relevant Logging implementations is presumed.
+    </p>
+</section>
+    <section name="Quick Start">
+        <p>
+As far as possible, JCL tries to be as unobtrusive as possible.
+In most cases, including the (full) <code>commons-logging.jar</code> in the classpath
+should result in JCL configuring itself in a reasonable manner.
+There's a good chance that it'll guess (discover) your preferred logging system and you won't
+need to do any configuration of JCL at all! 
+    </p><p>
+Note, however, that if you have a particular preference then providing a simple
+<code>commons-logging.properties</code> file which specifies the concrete logging library to be
+used is recommended, since (in this case) JCL will log only to that system
+and will report any configuration problems that prevent that system being used.
+    </p>
+    <p>
+When no particular logging library is specified then JCL will silently ignore any logging library
+that it finds but cannot initialise and continue to look for other alternatives. This is a deliberate
+design decision; no application should fail to run because a "guessed" logging library cannot be
+used. To ensure an exception is reported when a particular logging library cannot be used, use one
+of the available JCL configuration mechanisms to force that library to be selected (ie disable
+JCL's discovery process).
+    </p>
+        <subsection name='Configuration'>
+            <p>
+There are two base abstractions used by JCL: <code>Log</code>
+(the basic logger) and <code>LogFactory</code> (which knows how to create <code>Log</code>
+instances). Specifying a particular Log implementation is very useful (whether that is
+one provided by commons-logging or a user-defined one). Specifying a 
+<code>LogFactory</code> implementation other than the default is a subject for
+advanced users only, so will not be addressed here.
+        </p>
+            <p>
+The default <code>LogFactory</code> implementation uses the following discovery process
+to determine what type of <code>Log</code> implementation it should use
+(the process terminates when the first positive match - in order - is found):
+        </p>
+
+            <ol>
+                <li>
+Look for a configuration attribute of this factory named
+<code>org.apache.commons.logging.Log</code> (for backwards compatibility to
+pre-1.0 versions of this API, an attribute
+<code>org.apache.commons.logging.log</code> is also consulted).
+<p>
+Configuration attributes can be set explicitly by java code, but they are more
+commonly set by placing a file named commons-logging.properties in the classpath.
+When such a file exists, every entry in the properties file becomes an "attribute"
+of the LogFactory. When there is more than one such file in the classpath, releases
+of commons-logging prior to 1.1 simply use the first one found. From release 1.1,
+each file may define a <code>priority</code> key, and the file with
+the highest priority is used (no priority definition implies priority of zero).
+When multiple files have the same priority, the first one found is used.
+</p>
+<p>
+Defining this property in a commons-logging.properties file is the recommended
+way of explicitly selecting a Log implementation.
+</p>
+            </li>
+                <li>
+Look for a system property named
+<code>org.apache.commons.logging.Log</code> (for backwards
+compatibility to pre-1.0 versions of this API, a system property
+<code>org.apache.commons.logging.log</code> is also consulted).
+            </li>
+                <li>
+If the Log4J logging system is available in the application
+class path, use the corresponding wrapper class
+(<a href="http://commons.apache.org/logging/apidocs/org/apache/commons/logging/impl/Log4JLogger.html">Log4JLogger</a>).
+            </li>
+                <li>
+If the application is executing on a JDK 1.4 system, use
+the corresponding wrapper class
+(<a href="http://commons.apache.org/logging/apidocs/org/apache/commons/logging/impl/Jdk14Logger.html">Jdk14Logger</a>).
+            </li>
+                <li>
+Fall back to the default simple logging wrapper
+(<a href="http://commons.apache.org/logging/apidocs/org/apache/commons/logging/impl/SimpleLog.html">SimpleLog</a>).
+            </li>
+        </ol>
+            <p>
+Consult the JCL javadocs for details of the various <code>Log</code>
+implementations that ship with the component. (The discovery process is also covered in more
+detail there.)
+        </p>
+    </subsection>
+        <subsection name='Configuring The Underlying Logging System'>
+            <p>
+The JCL SPI
+can be configured to use different logging toolkits (see <a href='#Configuration'>above</a>).
+JCL provides only a bridge for writing log messages. It does not (and will not) support any
+sort of configuration API for the underlying logging system. 
+        </p>
+            <p>
+Configuration of the behavior of the JCL ultimately depends upon the
+logging toolkit being used. Please consult the documentation for the chosen logging system.
+        </p>
+        <p>
+JCL is NOT responsible for initialisation, configuration or shutdown of the underlying logging library.
+In many cases logging libraries will automatically initialise/configure themselves when first used, and
+need no explicit shutdown process. In these situations an application can simply use JCL and not depend
+directly on the API of the underlying logging system in any way. However if the logging library being used
+requires special initialisation, configuration or shutdown then some logging-library-specific code will
+be required in the application. JCL simply forwards logging method calls to the correct underlying
+implementation. When writing library code this issue is of course not relevant as the calling application
+is responsible for handling such issues.
+        </p>
+            <subsection name='Configuring Log4J'>
+                <p>
+Log4J is a very commonly used logging implementation (as well as being the JCL primary default), 
+so a <i>few</i> details are presented herein to get the developer/integrator going.
+Please see the <a href='http://logging.apache.org/log4j/docs/index.html'>Log4J Home</a> for more details
+on Log4J and it's configuration.
+            </p>
+                <p>
+Configure Log4J using system properties and/or a properties file:
+            </p>
+                <ul>
+                    <li>
+<strong>log4j.configuration=<em>log4j.properties</em></strong>
+Use this system property to specify the name of a Log4J configuration file.
+If not specified, the default configuration file is <i>log4j.properties</i>.
+                </li>
+                    <li>
+<strong>log4j.rootCategory=<i>priority</i> [, <i>appender</i>]*</strong>
+                </li>
+Set the default (root) logger priority.
+                    <li>
+<strong>log4j.logger.<i>logger.name</i>=<i>priority</i></strong>
+Set the priority for the named logger
+and all loggers hierarchically lower than, or below, the
+named logger.
+<i>logger.name</i> corresponds to the parameter of
+<code>LogFactory.getLog(<i>logger.name</i>)</code>,
+used to create the logger instance.  Priorities are:
+<code>DEBUG</code>,
+<code>INFO</code>,
+<code>WARN</code>,
+<code>ERROR</code>,
+or <code>FATAL</code>.
+<br/>
+Log4J understands hierarchical names,
+enabling control by package or high-level qualifiers:
+<code>log4j.logger.org.apache.component=DEBUG</code>
+will enable debug messages for all classes in both
+<code>org.apache.component</code>
+and
+<code>org.apache.component.sub</code>.
+Likewise, setting
+<code>log4j.logger.org.apache.component=DEBUG</code>
+will enable debug message for all 'component' classes,
+but not for other Apache projects.
+                </li>
+                    <li>
+<strong>log4j.appender.<i>appender</i>.Threshold=<i>priority</i></strong>
+                </li>
+Log4J <i>appenders</i> correspond to different output devices:
+console, files, sockets, and others.
+If appender's <i>threshold</i>
+is less than or equal to the message priority then
+the message is written by that appender.
+This allows different levels of detail to be appear
+at different log destinations.
+For example: one can capture DEBUG (and higher) level information in a logfile,
+while limiting console output to INFO (and higher).
+            </ul>
+        </subsection>
+    </subsection>
+</section>
+    <section name='Developing With JCL'>
+    <subsection name="Obtaining a Log Object">
+        <p>
+To use the JCL SPI from a Java class,
+include the following import statements:
+    </p>
+        <ul>
+            <code>
+import org.apache.commons.logging.Log;
+<br/>
+import org.apache.commons.logging.LogFactory;
+<br/>
+        </code>
+    </ul>
+        <p>
+Note that some components using JCL may
+either extend Log,
+or provide a component-specific LogFactory implementation.
+Review the component documentation for guidelines
+on how commons-logging should be used in such components.
+    </p>
+        <p>
+For each class definition, declare and initialize a
+<code>log</code> attribute as follows:
+    </p>
+        <ul>
+            <source>
+public class CLASS
+{
+    private Log log = LogFactory.getLog(CLASS.class);
+    ...
+    ;
+        </source>
+    </ul>
+    <p>
+Note that for application code, declaring the log member as "static" is more
+efficient as one Log object is created per class, and is recommended.
+However this is not safe to do for a class which may be deployed via a "shared"
+classloader in a servlet or j2ee container or similar environment. If the class
+may end up invoked with different thread-context-classloader values set then the
+member must <i>not</i> be declared static. The use of "static" should therefore
+be avoided in code within any "library" type project.
+    </p>
+    </subsection>
+    <subsection name="Logging a Message">
+        <p>
+Messages are logged to a <em>logger</em>, such as <code>log</code>
+by invoking a method corresponding to <em>priority</em>.
+The <code>org.apache.commons.logging.Log</code> interface defines the
+following methods for use
+in writing log/trace messages to the log:
+    </p>
+        <ul>
+            <source>
+    log.fatal(Object message);
+    log.fatal(Object message, Throwable t);
+    log.error(Object message);
+    log.error(Object message, Throwable t);
+    log.warn(Object message);
+    log.warn(Object message, Throwable t);
+    log.info(Object message);
+    log.info(Object message, Throwable t);
+    log.debug(Object message);
+    log.debug(Object message, Throwable t);
+    log.trace(Object message);
+    log.trace(Object message, Throwable t);
+        </source>
+    </ul>
+        <p>
+Semantics for these methods are such that it is expected
+that the severity, from highest to lowest, of messages is ordered as above.
+    </p>
+        <p>
+In addition to the logging methods, the following are provided for code guards:
+    </p>
+        <ul>
+            <source>
+    log.isFatalEnabled();
+    log.isErrorEnabled();
+    log.isWarnEnabled();
+    log.isInfoEnabled();
+    log.isDebugEnabled();
+    log.isTraceEnabled();
+        </source>
+    </ul>
+    </subsection>
+    <subsection name="Serialization Issues">
+    <p>Prior to release 1.0.4, none of the standard Log implementations were
+    Serializable. If you are using such a release and have a Serializable class
+    with a member that is of type Log then it is necessary to declare
+    that member to be transient and to ensure that the value is restored on
+    deserialization. The recommended approach is to define a custom
+    readObject method on the class which reinitializes that member.</p>
+    <p>In release 1.0.4, all standard Log implementations are Serializable. This
+    means that class members of type Log do <i>not</i> need to be declared transient;
+    on deserialization the Log object will "rebind" to the same category for the
+    same logging library. Note that the same underlying logging library will be
+    used on deserialization as was used in the original object, even if the
+    application the object was deserialized into is using a different logging
+    library. There is one exception; LogKitLogger (adapter for the Avalon LogKit
+    library) is not Serializable for technical reasons.</p>
+    <p>Custom Log implementations not distributed with commons-logging may
+    or may not be Serializable. If you wish your code to be compatible with
+    any arbitrary log adapter then you should follow the advice given above
+    for pre-1.0.4 releases.</p>
+    </subsection>
+</section>
+<section name="Jars Included in the Standard Distribution">
+    <subsection name="commons-logging.jar">
+      <p>
+The <code>commons-logging.jar</code> file includes the JCL API, the default 
+<code>LogFactory</code> implementation and thin-wrapper <code>Log</code> 
+implementations for
+<a href="http://logging.apache.org/log4j/docs/index.html">Log4J</a>,
+<a href="http://avalon.apache.org/logkit/index.html">Avalon LogKit</a>,
+the Avalon Framework's logging infrastructure,
+JDK 1.4, as well as an implementation of JDK 1.4 logging APIs (JSR-47) for 
+pre-1.4 systems.
+   </p>
+   <p>
+In most cases, including <code>commons-logging.jar</code> and your preferred 
+logging implementation in the classpath should be all that is required to 
+use JCL.
+   </p>
+    </subsection>
+    <subsection name="commons-logging-api.jar">
+<p>
+The <code>commons-logging-api.jar</code> file includes the JCL API and the 
+default <code>LogFactory</code> implementation as well as the built-in
+<code>Log</code> implementations SimpleLog and NoOpLog. However it does not
+include the wrapper <code>Log</code> implementations that require additional
+libraries such as <code>Log4j</code>, <code>Avalon</code> and
+<code>Lumberjack</code>. 
+</p>
+<p>
+This jar is intended for use by projects that recompile the commons-logging
+source using alternate java environments, and cannot compile against all of
+the optional libraries that the Apache release of commons-logging supports.
+Because of the reduced dependencies of this jarfile, such projects should be
+able to create an equivalent of this library with fewer difficulties.
+</p>
+<p>
+This jar is also useful for build environments that automatically track
+dependencies, and thus have difficulty with the concept that the main
+commons-logging.jar has "optional" dependencies on various logging
+implementations that can safely go unsatisfied at runtime.
+</p>
+    </subsection>
+    <subsection name="commons-logging-adapters.jar">
+<p>
+The <code>commons-logging-adapters.jar</code> file includes only adapters
+to third-party logging implementations, and none of the core commons-logging
+framework. As such, it cannot be used alone; either commons-logging.jar or
+commons-logging-api.jar must also be present in the classpath.
+</p>
+<p>
+This library will not often be used; it is only intended for situations where
+a container has deployed commons-logging-api.jar in a shared classpath but a
+webapp wants to bind logging to one of the external logging implementations
+that the api jar does not include. In this situation, deploying the
+commons-logging.jar file within the webapp can cause problems as this leads to
+duplicates of the core commons-logging classes (Log, LogFactory, etc) in
+the classpath which in turn can cause unpleasant ClassCastException exceptions
+to occur. Deploying only the adapters avoids this problem.
+</p>
+    </subsection>
+</section>
+        <section name='JCL Best Practices'>
+            <p>
+Best practices for JCL are presented in two categories:
+General and Enterprise.
+The general principles are fairly clear.Enterprise practices are a bit more involved
+and it is not always as clear as to why they are important.
+        </p>
+            <p>
+Enterprise best-practice principles apply to middleware components
+and tooling that is expected to execute in an "Enterprise" level
+environment.
+These issues relate to Logging as Internationalization,
+and fault detection.
+Enterprise requires more effort and planning, but are strongly encouraged (if not required)
+in production level systems.  Different corporate enterprises/environments have different
+requirements, so being flexible always helps.
+        </p>
+    </section>
+    <section name='Best Practices (General)'>
+        <subsection name='Code Guards'>
+            <p>
+Code guards are typically used to guard code that
+only needs to execute in support of logging,
+that otherwise introduces undesirable runtime overhead
+in the general case (logging disabled).
+Examples are multiple parameters, or expressions (e.g. string + " more") for parameters.
+Use the guard methods of the form <code>log.is<<i>Priority</i>>()</code> to verify
+that logging should be performed, before incurring the overhead of the logging method call.
+Yes, the logging methods will perform the same check, but only after resolving parameters.
+            </p>
+        </subsection>
+           <subsection name='Message Priorities/Levels'>
+                <p>
+It is important to ensure that log message are
+appropriate in content and severity.
+The following guidelines are suggested:
+        </p>
+            <ul>
+                <li>
+<b>fatal</b> - Severe errors that cause premature termination.
+Expect these to be immediately visible on a status console.
+See also <a HREF="#National%20Language%20Support%20And%20Internationalization">
+Internationalization</a>.
+            </li>
+                <li>
+<b>error</b> - Other runtime errors or unexpected conditions.
+Expect these to be immediately visible on a status console.
+See also <a HREF="#National%20Language%20Support%20And%20Internationalization">
+Internationalization</a>.
+            </li>
+                <li>
+<b>warn</b> - Use of deprecated APIs, poor use of API, 'almost' errors,
+other runtime situations that are undesirable or unexpected, but not
+necessarily "wrong".
+Expect these to be immediately visible on a status console.
+See also <a HREF="#National%20Language%20Support%20And%20Internationalization">
+Internationalization</a>.
+            </li>
+                <li>
+<b>info</b> - Interesting runtime events (startup/shutdown).
+Expect these to be immediately visible on a console,
+so be conservative and keep to a minimum.
+See also <a HREF="#National%20Language%20Support%20And%20Internationalization">
+Internationalization</a>.
+            </li>
+                <li>
+<b>debug</b> - detailed information on the flow through the system.
+Expect these to be written to logs only.
+            </li>
+                <li>
+<b>trace</b> - more detailed information.
+Expect these to be written to logs only.
+            </li>
+        </ul>
+        </subsection>
+            <subsection name='Default Message Priority/Level'>
+                <p>
+By default the message priority should be no lower than <b>info</b>.
+That is, by default <b>debug</b> message should not be seen in the logs.
+            </p>
+        </subsection>
+    </section>
+    <section name='Best Practices (Enterprise)'>
+          <subsection name='Logging Exceptions'>
+               <p>
+The general rule in dealing with exceptions is to assume that
+the user (developer using a tooling/middleware API) isn't going
+to follow the rules.
+Since any problems that result are going to be assigned to you,
+it's in your best interest to be prepared with the proactive
+tools necessary to demonstrate that your component works correctly,
+or at worst that the problem can be analyzed from your logs.
+For this discussion, we must make a distinction between different types of exceptions
+based on what kind of boundaries they cross:
+           </p>
+               <ul>
+                   <li>
+<b>External Boundaries - Expected Exceptions</b>.
+This classification includes exceptions such as <code>FileNotFoundException</code>
+that cross API/SPI boundaries, and are exposed to the user of a component/toolkit.
+These are listed in the 'throws' clause of a method signature.
+<br/>
+Appropriate handling of these exceptions depends upon the type
+of code you are developing.
+API's for utility functions and tools should log these at the <b>debug</b> level,
+if they are caught at all by internal code.
+<br/>
+For higher level frameworks and middleware components,
+these exceptions should be caught immediately prior to crossing
+the API/SPI interface back to user code-space,
+logged with full stack trace at <b>info</b> level,
+and rethrown.
+The assures that the log contains a record of the root cause for
+future analysis <i>in the event that the exception is not caught and resolved
+as expected by the user's code</i>.
+<br/>
+            </li>
+               <li>
+<b>External Boundaries - Unexpected Exceptions</b>.
+This classification includes exceptions such as <code>NullPointerException</code>
+that cross API/SPI boundaries, and are exposed to the user of a component/toolkit.
+These are runtime exceptions/error that are NOT
+listed in the 'throws' clause of a method signature.
+<br/>
+Appropriate handling of these exceptions depends upon the type
+of code you are developing.
+APIs for utility functions and tools should log these at the <b>debug</b> level,
+if they are caught at all.
+<br/>
+For higher level frameworks and middleware components,
+these exceptions should be caught immediately prior to crossing
+the API/SPI interface back to user code-space,
+logged with full stack trace at <b>info</b> level,
+and rethrown/wrapped as <code><i>Component</i>InternalError</code>.
+This ensures that the log contains a record of the root cause for
+future analysis <i>in the event that the exception is not caught and
+logged/reported as expected by the user's code</i>.
+            </li>
+                <li>
+<b>Internal Boundaries</b>.
+Exceptions that occur internally and are resolved internally.
+These should be logged when caught as <b>debug</b> or <b>info</b> messages,
+at the programmer's discretion.
+            </li>
+                <li>
+<b>Significant Internal Boundaries</b>.
+This typically only applies to middleware components that span networks or runtime processes.
+Exceptions that cross over significant internal component boundaries such as networks
+should be logged when caught as <b>info</b> messages.
+Do not assume that such a (process/network) boundary will deliver exceptions to the 'other side'.
+                </li>
+            </ul>
+    </subsection>
+        <subsection name='When Info Level Instead of Debug?'>
+                <p>
+You want to have exception/problem information available for
+first-pass problem determination in a production level
+enterprise application without turning on <b>debug</b>
+as a default log level.  There is simply too much information
+in <b>debug</b> to be appropriate for day-to-day operations.
+            </p>
+        </subsection>
+            <subsection name='More Control of Enterprise Exception Logging'>
+                <p>
+If more control is desired for the level of detail of these
+'enterprise' exceptions, then consider creating a special
+logger just for these exceptions:
+            </p>
+                <ul>
+<source>
+   Log log = LogFactory.getLog("org.apache.<i>component</i>.enterprise");
+</source>
+            </ul>
+                <p>
+This allows the 'enterprise' level information to be turned on/off explicitly
+by most logger implementations.
+            </p>
+        </subsection>
+        <subsection name='National Language Support And Internationalization'>
+            <p>
+NLS internationalization involves looking up messages from
+a message file by a message key, and using that message for logging.
+There are various tools in Java, and provided by other components,
+for working with NLS messages.
+                </p>
+            <p>
+NLS enabled components are particularly appreciated
+(that's an open-source-correct term for 'required by corporate end-users' :-)
+for <strong>tooling</strong> and <strong>middleware</strong> components.
+                </p>
+            <p>
+NLS internationalization SHOULD be strongly considered for used for
+<b>fatal</b>, <b>error</b>, <b>warn</b>, and <b>info</b> messages.
+It is generally considered optional for <b>debug</b> and <b>trace</b> messages.
+                </p>
+            <p>
+Perhaps more direct support for internationalizing log messages
+can be introduced in a future or alternate version of the <code>Log</code> interface.
+                </p>
+            </subsection>
+            <subsection name="Classloader and Memory Management">
+            <p>
+The <code>LogFactory</code> discovery process (see 
+<a href='#Configuration'>Configuration</a> above) is a fairly expensive 
+operation, so JCL certainly should not perform it each time user code 
+invokes: 
+</p>
+<source>LogFactory.getLog()</source> 
+<p>
+Instead JCL caches the 
+<code>LogFactory</code> implementation created as a result of the discovery 
+process and uses the cached factory to return <code>Log</code> objects.  
+Since in J2EE and similar multi-classloader environments, the result of the 
+discovery process can vary depending on the thread context classloader 
+(e.g. one webapp in a web container may be configured to use Log4j and 
+another to use JDK 1.4 logging), JCL internally caches the 
+<code>LogFactory</code> instances in a static hashtable, keyed by classloader.
+            </p>
+            <p>
+While this approach is efficient, it can lead to memory leaks if container
+implementors are not careful to call 
+</p>
+<source>LogFactory.release()</source> 
+<p>
+whenever a classloader that has utilized JCL is undeployed.  If 
+<code>release()</code> is not called, a reference to the undeployed 
+classloader (and thus to all the classes loaded by it) will be
+held in <code>LogFactory</code>'s static hashtable.
+            </p>
+            <p>
+Beginning with JCL 1.1, <code>LogFactory</code> caches factory implementations in a 
+"WeakHashtable". This class is similar to <code>java.util.WeakHashMap</code> in
+that it holds a <code>WeakReference</code> to each key (but a strong reference
+to each value), thus allowing classloaders to be GC'd even if
+<code>LogFactory.release()</code> is never invoked.
+            </p>
+            <p>
+Because <code>WeakHashtable</code> depends on JDK 1.3+ features, it is dynamically
+loaded depending on the JVM version; when commons-logging is run on java versions
+prior to 1.3 the code defaults to a standard Hashtable instead.
+            </p>
+            <p>
+If a custom LogFactory implementation is used, however, then a
+<code>WeakHashtable</code> alone can be insufficient to allow garbage collection
+of a classloader without a call to <code>release</code>.  If the abstract class
+<code>LogFactory</code> is loaded by a parent classloader and a concrete
+subclass implementation of <code>LogFactory</code> is loaded by a child
+classloader, the WeakHashtable's key is a weak reference to the TCCL (child
+classloader), but the value is a strong reference to the LogFactory instance,
+which in turn contains a strong reference to its class and thus loading
+classloader - the child classloader. This chain of strong references prevents
+the child loader from being garbage collected.
+           </p>
+           <p>
+If use of a custom <code>LogFactory</code> subclass is desired, ensuring that
+the custom subclass is loaded by the same classloader as <code>LogFactory</code>
+will prevent problems.  In normal deployments, the standard implementations 
+of <code>LogFactory</code> found in package <code>org.apache.commons.logging.impl</code> 
+will be loaded by the same classloader that loads <code>LogFactory</code> 
+itself, so use of the standard <code>LogFactory</code> implementation
+should not pose problems. Alternatively, use the provided ServletContextCleaner
+to ensure this reference is explicitly released on webapp unload.
+         </p>
+            </subsection>
+    </section>
+    <section name='Extending Commons Logging'>
+        <p>
+JCL is designed to encourage extensions to be created that add functionality. 
+Typically, extensions to JCL fall into two categories:
+    </p>
+        <ul>
+            <li>new <code>Log</code> implementations that provide new bridges to logging systems</li>
+            <li>
+new <code>LogFactory</code> implementations that provide alternative discovery strategies
+            </li>
+    </ul>
+        <subsection name='Contract'>
+            <p>
+When creating new implementations for <code>Log</code> and <code>LogFactory</code>,
+it is important to understand the implied contract between the factory 
+and the log implementations:
+                <ul>
+                    <li><b>Life cycle</b>
+                        <blockquote>
+The JCL LogFactory implementation must assume responsibility for
+either connecting/disconnecting to a logging toolkit,
+or instantiating/initializing/destroying a logging toolkit.
+                        </blockquote>
+                </li>
+                    <li><b>Exception handling</b>
+                        <blockquote>
+The JCL Log interface doesn't specify any exceptions to be handled,
+the implementation must catch any exceptions.
+                    </blockquote>
+                </li>
+                    <li><b>Multiple threads</b>
+                        <blockquote>
+The JCL Log and LogFactory implementations must ensure
+that any synchronization required by the logging toolkit
+is met.
+                    </blockquote>
+                </li>
+            </ul>
+        </p>
+    </subsection>
+    <subsection name='Creating a Log Implementation'>
+        <p>
+The minimum requirement to integrate with another logger
+is to provide an implementation of the
+<code>org.apache.commons.logging.Log</code> interface.
+In addition, an implementation of the
+<code>org.apache.commons.logging.LogFactory</code> interface
+can be provided to meet
+specific requirements for connecting to, or instantiating, a logger.
+    </p>
+            <p>
+The default <code>LogFactory</code> provided by JCL
+can be configured to instantiate a specific implementation of the
+<code>org.apache.commons.logging.Log</code> interface
+by setting the property of the same name (<code>org.apache.commons.logging.Log</code>).
+This property can be specified as a system property,
+or in the <code>commons-logging.properties</code> file,
+which must exist in the CLASSPATH.
+        </p>
+     </subsection>
+        <subsection name='Creating A LogFactory Implementation'>
+            <p>
+If desired, the default implementation of the
+<code>org.apache.commons.logging.LogFactory</code>
+interface can be overridden,
+allowing the JDK 1.3 Service Provider discovery process
+to locate and create a LogFactory specific to the needs of the application.
+Review the Javadoc for the <code>LogFactoryImpl.java</code>
+for details.
+        </p>
+    </subsection>
+</section>
+<section name='A Quick Guide To Simple Log'>
+  <p>
+JCL is distributed with a very simple <code>Log</code> implementation named 
+<code>org.apache.commons.logging.impl.SimpleLog</code>. This is intended to be a minimal
+implementation and those requiring a fully functional open source logging system are 
+directed to <a href='http://logging.apache.org/log4j'>Log4J</a>.
+  </p>
+  <p>
+  <code>SimpleLog</code> sends all (enabled) log messages,
+  for all defined loggers, to <code>System.err</code>.  The following system properties
+  are supported to configure the behavior of this logger:</p>
+  <ul>
+  <li><strong>org.apache.commons.logging.simplelog.defaultlog</strong> -
+      Default logging detail level for all instances of SimpleLog.
+      Must be one of:
+      <ul>
+          <li><code>trace</code></li>
+          <li><code>debug</code></li>
+          <li><code>info</code></li>
+          <li><code>warn</code></li>
+          <li><code>error</code></li>
+          <li><code>fatal</code></li>
+      </ul>
+      If not specified, defaults to <code>info</code>. </li>
+  <li><strong>org.apache.commons.logging.simplelog.log.xxxxx</strong> -
+      Logging detail level for a SimpleLog instance named "xxxxx".
+      Must be one of:
+      <ul>
+          <li><code>trace</code></li>
+          <li><code>debug</code></li>
+          <li><code>info</code></li>
+          <li><code>warn</code></li>
+          <li><code>error</code></li>
+          <li><code>fatal</code></li>
+      </ul>
+      If not specified, the default logging detail level is used.</li>
+  <li><strong>org.apache.commons.logging.simplelog.showlogname</strong> -
+      Set to <code>true</code> if you want the <code>Log</code> instance name to be
+      included in output messages. Defaults to <code>false</code>.</li>
+  <li><strong>org.apache.commons.logging.simplelog.showShortLogname</strong> -
+      Set to <code>true</code> if you want the last component of the name to be
+      included in output messages. Defaults to <code>true</code>.</li>
+  <li><strong>org.apache.commons.logging.simplelog.showdatetime</strong> -
+      Set to <code>true</code> if you want the current date and time
+      to be included in output messages. Default is <code>false</code>.</li>
+ <li><strong>org.apache.commons.logging.simplelog.dateTimeFormat</strong> -
+      The date and time format to be used in the output messages.
+      The pattern describing the date and time format is the same that is
+      used in <code>java.text.SimpleDateFormat</code>. If the format is not
+      specified or is invalid, the default format is used.
+      The default format is <code>yyyy/MM/dd HH:mm:ss:SSS zzz</code>.</li>
+  </ul>
+ 
+  <p>
+In addition to looking for system properties with the names specified
+above, this implementation also checks for a class loader resource named
+<code>"simplelog.properties"</code>, and includes any matching definitions
+from this resource (if it exists).
+  </p>
+</section>
+    <section name='Frequently Asked Questions'>
+<p>
+See the <a href="http://wiki.apache.org/commons/Logging/FrequentlyAskedQuestions">FAQ document</a>
+on the commons-logging wiki site
+</p>
+</section>
+
+</body>
+</document>
diff --git a/src/site/xdoc/index.xml b/src/site/xdoc/index.xml
new file mode 100644
index 0000000..4219096
--- /dev/null
+++ b/src/site/xdoc/index.xml
@@ -0,0 +1,144 @@
+<?xml version="1.0"?>
+
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+ 
+      http://www.apache.org/licenses/LICENSE-2.0
+ 
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+
+<document>
+
+ <properties>
+  <title>Overview</title>
+  <author email="dev at commons.apache.org">Commons Documentation Team</author>
+ </properties>
+
+ <body>
+
+<section name="The Logging Component">
+
+<p>When writing a library it is very useful to log information. However there
+are many logging implementations out there, and a library cannot impose the use
+of a particular one on the overall application that the library is a part of.</p>
+
+<p>The Logging package is an ultra-thin bridge between different logging
+implementations.  A library that uses the commons-logging API can be used with
+any logging implementation at runtime. Commons-logging comes with support for a
+number of popular logging implementations, and writing adapters for others is a
+reasonably simple task.</p>
+
+<p>Applications (rather than libraries) may also choose to use commons-logging.
+While logging-implementation independence is not as important for applications
+as it is for libraries, using commons-logging does allow the application to
+change to a different logging implementation without recompiling code. 
+</p><p>
+Note that commons-logging does not attempt to initialise or terminate the underlying
+logging implementation that is used at runtime; that is the responsibility of
+the application. However many popular logging implementations do automatically
+initialise themselves; in this case an application may be able to avoid
+containing any code that is specific to the logging implementation used.</p>
+
+</section>
+
+
+<section name="Documentation">
+
+<p>The <a href="RELEASE-NOTES.txt">
+Release Notes</a> document the new features and bug fixes that have been
+included in the latest release.</p>
+
+<p>The <a href="http://commons.apache.org/logging/apidocs/index.html">
+JavaDoc API documents</a> for the latest release are available online.
+In particular, you should read the package overview of the <code>org.apache.commons.logging</code>
+package. In addition, there is a (short)
+<a href="guide.html">User Guide</a>.</p>
+
+<p>The <a href="http://wiki.apache.org/commons/Logging">Wiki site</a> has
+the latest updates, an FAQ and much other useful information.</p>
+<p>
+Users needing to become experts or wanting to help develop JCL should
+(in addition) consult the <a href='tech.html'>Tech Guide</a>.
+This gives short introductions to topics such as advanced class loading.
+</p>
+</section>
+
+
+<section name="Releases">
+    <p>
+Binary and source distributions are available
+       <a href="http://commons.apache.org/logging/download_logging.cgi">here</a>.
+    </p>
+    <subsection name='1.2 Release - July 2014'>
+      <p>The main purpose of the 1.2 release is to drop support for Java 1.1.</p>
+      <p>For a full list of changes since the 1.1.3 release, please refer to the
+      <a href="changes-report.html">change-report</a>.</p> 
+    </subsection>
+    <subsection name='1.1.3 Release - May 2013'>
+      <p>The 1.1.3 release only updates the Bundle-SymbolicName in the manifest
+      to "org.apache.commons.logging".</p>
+      <p>For a full list of changes since the 1.1.1 release, please refer to the
+      <a href="changes-report.html">change-report</a>.</p> 
+    </subsection>
+    <subsection name='1.1.2 Release - March 2013'>
+      <p>The 1.1.2 release is a packaging of bug fixes since release 1.1.1.</p>
+      <p>For the full details, see the release notes for this version.</p> 
+    </subsection>
+    <subsection name='1.1.1 Release - November 2007'>
+      <p>
+       This release is a minor update to the 1.1 release that fixes a number of bugs, and 
+       resolves packaging issues for maven 1.x and maven 2.x users.
+      </p>
+      <p>For the full details, see the release notes for this version.</p> 
+    </subsection>
+    <subsection name='1.1 Release - 10 May 2006'>
+      <p>This release makes several changes that are intended to resolve issues that
+      have been encountered when using commons-logging in servlet containers or j2ee
+      containers where complex classpaths are present and multiple copies of
+      commons-logging libraries are present at different levels.</p>
+      <p>This release also adds support for the TRACE level added to log4j in the
+      1.2.12 release. In former commons-logging versions, the log.trace method
+      caused log4j to output the message at the DEBUG level (the lowest level
+      supported by log4j at that time).</p>
+      <p>For the full details, see the release notes for this version.</p> 
+    </subsection>
+    <subsection name='1.0.5 Release (Alpha)'>
+      <p>
+        <strong>Note:</strong> the 1.0.5 release was abandoned at alpha status.
+      </p>
+      <p>
+    The next JCL release will be designated 1.1 since we feel this more 
+    accurately reflects the improvements made to the codebase.</p>
+    </subsection>
+    <subsection name='1.0.4 Release - 16 Jun 2004'>
+      <p>The 1.0.4 release of commons-logging is a service release containing support
+      for both the 1.2.x and 1.3.x series of Log4J releases.</p>
+    </subsection>
+    <subsection name='1.0.3 Release - 7 Apr 2003'>
+      <p>The 1.0.3 release is primarily a maintenance and code cleanup release with minimal new features.</p>
+    </subsection>
+    <subsection name='1.0.2 Release - 27 September 2002'>
+      <p>The 1.0.2 release is a packaging of bug fixes since release 1.0.1.</p>
+    </subsection>
+    <subsection name='1.0.1 Release - 13 August 2002'>
+      <p>The 1.0.1 release is a packaging of bug fixes and minor enhancements since release 1.0.</p>
+    </subsection>
+</section>
+<section name="Development Builds">
+  <p>Regular builds of the current SVN HEAD code are made available. See the 
+  <a href="http://wiki.apache.org/commons/Logging">wiki</a> for details.</p>
+</section>
+</body>
+</document>
diff --git a/src/site/xdoc/issue-tracking.xml b/src/site/xdoc/issue-tracking.xml
new file mode 100644
index 0000000..829a90c
--- /dev/null
+++ b/src/site/xdoc/issue-tracking.xml
@@ -0,0 +1,102 @@
+<?xml version="1.0"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<!--
+ +======================================================================+
+ |****                                                              ****|
+ |****      THIS FILE IS GENERATED BY THE COMMONS BUILD PLUGIN      ****|
+ |****                    DO NOT EDIT DIRECTLY                      ****|
+ |****                                                              ****|
+ +======================================================================+
+ | TEMPLATE FILE: issue-tracking-template.xml                           |
+ | commons-build-plugin/trunk/src/main/resources/commons-xdoc-templates |
+ +======================================================================+
+ |                                                                      |
+ | 1) Re-generate using: mvn commons:jira-page                          |
+ |                                                                      |
+ | 2) Set the following properties in the component's pom:              |
+ |    - commons.jira.id  (required, alphabetic, upper case)             |
+ |    - commons.jira.pid (required, numeric)                            |
+ |                                                                      |
+ | 3) Example Properties                                                |
+ |                                                                      |
+ |  <properties>                                                        |
+ |    <commons.jira.id>MATH</commons.jira.id>                           |
+ |    <commons.jira.pid>12310485</commons.jira.pid>                     |
+ |  </properties>                                                       |
+ |                                                                      |
+ +======================================================================+
+-->
+<document>
+  <properties>
+    <title>Commons Logging Issue tracking</title>
+    <author email="dev at commons.apache.org">Commons Documentation Team</author>
+  </properties>
+  <body>
+
+    <section name="Commons Logging Issue tracking">
+      <p>
+      Commons Logging uses <a href="http://issues.apache.org/jira/">ASF JIRA</a> for tracking issues.
+      See the <a href="http://issues.apache.org/jira/browse/LOGGING">Commons Logging JIRA project page</a>.
+      </p>
+
+      <p>
+      To use JIRA you may need to <a href="http://issues.apache.org/jira/secure/Signup!default.jspa">create an account</a>
+      (if you have previously created/updated Commons issues using Bugzilla an account will have been automatically
+      created and you can use the <a href="http://issues.apache.org/jira/secure/ForgotPassword!default.jspa">Forgot Password</a>
+      page to get a new password).
+      </p>
+
+      <p>
+      If you would like to report a bug, or raise an enhancement request with
+      Commons Logging please do the following:
+      <ol>
+        <li><a href="http://issues.apache.org/jira/secure/IssueNavigator.jspa?reset=true&pid=12310484&sorter/field=issuekey&sorter/order=DESC&status=1&status=3&status=4">Search existing open bugs</a>.
+            If you find your issue listed then please add a comment with your details.</li>
+        <li><a href="mail-lists.html">Search the mailing list archive(s)</a>.
+            You may find your issue or idea has already been discussed.</li>
+        <li>Decide if your issue is a bug or an enhancement.</li>
+        <li>Submit either a <a href="http://issues.apache.org/jira/secure/CreateIssueDetails!init.jspa?pid=12310484&issuetype=1&priority=4&assignee=-1">bug report</a>
+            or <a href="http://issues.apache.org/jira/secure/CreateIssueDetails!init.jspa?pid=12310484&issuetype=4&priority=4&assignee=-1">enhancement request</a>.</li>
+      </ol>
+      </p>
+
+      <p>
+      Please also remember these points:
+      <ul>
+        <li>the more information you provide, the better we can help you</li>
+        <li>test cases are vital, particularly for any proposed enhancements</li>
+        <li>the developers of Commons Logging are all unpaid volunteers</li>
+      </ul>
+      </p>
+
+      <p>
+      For more information on subversion and creating patches see the
+      <a href="http://www.apache.org/dev/contributors.html">Apache Contributors Guide</a>.
+      </p>
+
+      <p>
+      You may also find these links useful:
+      <ul>
+        <li><a href="http://issues.apache.org/jira/secure/IssueNavigator.jspa?reset=true&pid=12310484&sorter/field=issuekey&sorter/order=DESC&status=1&status=3&status=4">All Open Commons Logging bugs</a></li>
+        <li><a href="http://issues.apache.org/jira/secure/IssueNavigator.jspa?reset=true&pid=12310484&sorter/field=issuekey&sorter/order=DESC&status=5&status=6">All Resolved Commons Logging bugs</a></li>
+        <li><a href="http://issues.apache.org/jira/secure/IssueNavigator.jspa?reset=true&pid=12310484&sorter/field=issuekey&sorter/order=DESC">All Commons Logging bugs</a></li>
+      </ul>
+      </p>
+    </section>
+  </body>
+</document>
diff --git a/src/site/xdoc/junit-report.xml b/src/site/xdoc/junit-report.xml
new file mode 100644
index 0000000..786c93c
--- /dev/null
+++ b/src/site/xdoc/junit-report.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<document>
+
+  <properties>
+    <title>JUnit Test Results</title>
+    <author email="dev at commons.apache.org">Commons Documentation Team</author>
+  </properties>
+  
+  <body>
+    <!-- ================================================== -->
+    <section name="Summary">
+      <p>
+        The Apache Commons Logging test cases make extensive use of
+        sophisticated classloader configurations in order to simulate the
+        behaviour of various containers. It is difficult to run these tests
+        under Maven in the default "test" phase. As a consequence the tests
+        are executed via the failsafe-plugin as part of the "integration-test"
+        phase. The reports are available <a href="failsafe-report.html">here</a>.
+      </p>
+    </section>
+    <!-- ================================================== -->
+  </body>
+</document>
diff --git a/src/site/xdoc/mail-lists.xml b/src/site/xdoc/mail-lists.xml
new file mode 100644
index 0000000..c5bf9b9
--- /dev/null
+++ b/src/site/xdoc/mail-lists.xml
@@ -0,0 +1,202 @@
+<?xml version="1.0"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<!--
+ +======================================================================+
+ |****                                                              ****|
+ |****      THIS FILE IS GENERATED BY THE COMMONS BUILD PLUGIN      ****|
+ |****                    DO NOT EDIT DIRECTLY                      ****|
+ |****                                                              ****|
+ +======================================================================+
+ | TEMPLATE FILE: mail-lists-template.xml                               |
+ | commons-build-plugin/trunk/src/main/resources/commons-xdoc-templates |
+ +======================================================================+
+ |                                                                      |
+ | 1) Re-generate using: mvn commons:mail-page                          |
+ |                                                                      |
+ | 2) Set the following properties in the component's pom:              |
+ |    - commons.componentid (required, alphabetic, lower case)          |
+ |                                                                      |
+ | 3) Example Properties                                                |
+ |                                                                      |
+ |  <properties>                                                        |
+ |    <commons.componentid>math</commons.componentid>                   |
+ |  </properties>                                                       |
+ |                                                                      |
+ +======================================================================+
+-->
+<document>
+  <properties>
+    <title>Commons Logging Mailing Lists</title>
+    <author email="dev at commons.apache.org">Commons Documentation Team</author>
+  </properties>
+  <body>
+
+    <section name="Overview">
+      <p>
+        <a href="index.html">Commons Logging</a> shares mailing lists with all the other 
+        <a href="http://commons.apache.org/components.html">Commons Components</a>.
+        To make it easier for people to only read messages related to components they are interested in,
+        the convention in Commons is to prefix the subject line of messages with the component's name,
+        for example:
+        <ul>
+          <li>[logging] Problem with the ...</li>
+        </ul>
+      </p>
+      <p>
+        Questions related to the usage of Commons Logging should be posted to the
+        <a href="http://mail-archives.apache.org/mod_mbox/commons-user/">User List</a>.
+        <br />
+        The <a href="http://mail-archives.apache.org/mod_mbox/commons-dev/">Developer List</a>
+        is for questions and discussion related to the development of Commons Logging.
+        <br />
+        Please do not cross-post; developers are also subscribed to the user list.
+      </p>
+      <p>
+        <strong>Note:</strong> please don't send patches or attachments to any of the mailing lists.
+        Patches are best handled via the <a href="issue-tracking.html">Issue Tracking</a> system. 
+        Otherwise, please upload the file to a public server and include the URL in the mail. 
+      </p>
+    </section>
+
+    <section name="Commons Logging Mailing Lists">
+      <p>
+        <strong>Please prefix the subject line of any messages for <a href="index.html">Commons Logging</a>
+        with <i>[logging]</i></strong> - <i>thanks!</i>
+        <br />
+        <br />
+      </p>
+
+      <table>
+        <tr>
+          <th>Name</th>
+          <th>Subscribe</th>
+          <th>Unsubscribe</th>
+          <th>Post</th>
+          <th>Archive</th>
+          <th>Other Archives</th>
+        </tr>
+
+
+        <tr>
+          <td>
+            <strong>Commons User List</strong>
+            <br /><br />
+            Questions on using Commons Logging.
+            <br /><br />
+          </td>
+          <td><a href="mailto:user-subscribe at commons.apache.org">Subscribe</a></td>
+          <td><a href="mailto:user-unsubscribe at commons.apache.org">Unsubscribe</a></td>
+          <td><a href="mailto:user at commons.apache.org?subject=[logging]">Post</a></td>
+          <td><a href="http://mail-archives.apache.org/mod_mbox/commons-user/">mail-archives.apache.org</a></td>
+          <td><a href="http://markmail.org/list/org.apache.commons.users/">markmail.org</a><br />
+              <a href="http://www.mail-archive.com/user@commons.apache.org/">www.mail-archive.com</a><br />
+              <a href="http://news.gmane.org/gmane.comp.jakarta.commons.devel">news.gmane.org</a>
+          </td>
+        </tr>
+
+
+        <tr>
+          <td>
+            <strong>Commons Developer List</strong>
+            <br /><br />
+            Discussion of development of Commons Logging.
+            <br /><br />
+          </td>
+          <td><a href="mailto:dev-subscribe at commons.apache.org">Subscribe</a></td>
+          <td><a href="mailto:dev-unsubscribe at commons.apache.org">Unsubscribe</a></td>
+          <td><a href="mailto:dev at commons.apache.org?subject=[logging]">Post</a></td>
+          <td><a href="http://mail-archives.apache.org/mod_mbox/commons-dev/">mail-archives.apache.org</a></td>
+          <td><a href="http://markmail.org/list/org.apache.commons.dev/">markmail.org</a><br />
+              <a href="http://www.mail-archive.com/dev@commons.apache.org/">www.mail-archive.com</a><br />
+              <a href="http://news.gmane.org/gmane.comp.jakarta.commons.devel">news.gmane.org</a>
+          </td>
+        </tr>
+
+
+        <tr>
+          <td>
+            <strong>Commons Issues List</strong>
+            <br /><br />
+            Only for e-mails automatically generated by the <a href="issue-tracking.html">issue tracking</a> system.
+            <br /><br />
+          </td>
+          <td><a href="mailto:issues-subscribe at commons.apache.org">Subscribe</a></td>
+          <td><a href="mailto:issues-unsubscribe at commons.apache.org">Unsubscribe</a></td>
+          <td><i>read only</i></td>
+          <td><a href="http://mail-archives.apache.org/mod_mbox/commons-issues/">mail-archives.apache.org</a></td>
+          <td><a href="http://markmail.org/list/org.apache.commons.issues/">markmail.org</a><br />
+              <a href="http://www.mail-archive.com/issues@commons.apache.org/">www.mail-archive.com</a>
+          </td>
+        </tr>
+
+
+        <tr>
+          <td>
+            <strong>Commons Commits List</strong>
+            <br /><br />
+            Only for e-mails automatically generated by the <a href="source-repository.html">source control</a> sytem.
+            <br /><br />
+          </td>
+          <td><a href="mailto:commits-subscribe at commons.apache.org">Subscribe</a></td>
+          <td><a href="mailto:commits-unsubscribe at commons.apache.org">Unsubscribe</a></td>
+          <td><i>read only</i></td>
+          <td><a href="http://mail-archives.apache.org/mod_mbox/commons-commits/">mail-archives.apache.org</a></td>
+          <td><a href="http://markmail.org/list/org.apache.commons.commits/">markmail.org</a><br />
+              <a href="http://www.mail-archive.com/commits@commons.apache.org/">www.mail-archive.com</a>
+          </td>
+        </tr>
+
+      </table>
+
+    </section>
+    <section name="Apache Mailing Lists">
+      <p>
+        Other mailing lists which you may find useful include:
+      </p>
+
+      <table>
+        <tr>
+          <th>Name</th>
+          <th>Subscribe</th>
+          <th>Unsubscribe</th>
+          <th>Post</th>
+          <th>Archive</th>
+          <th>Other Archives</th>
+        </tr>
+        <tr>
+          <td>
+            <strong>Apache Announce List</strong>
+            <br /><br />
+            General announcements of Apache project releases.
+            <br /><br />
+          </td>
+          <td><a class="externalLink" href="mailto:announce-subscribe at apache.org">Subscribe</a></td> 
+          <td><a class="externalLink" href="mailto:announce-unsubscribe at apache.org">Unsubscribe</a></td> 
+          <td><i>read only</i></td>
+          <td><a class="externalLink" href="http://mail-archives.apache.org/mod_mbox/www-announce/">mail-archives.apache.org</a></td> 
+          <td><a class="externalLink" href="http://markmail.org/list/org.apache.announce/">markmail.org</a><br />
+              <a class="externalLink" href="http://old.nabble.com/Apache-News-and-Announce-f109.html">old.nabble.com</a><br />
+              <a class="externalLink" href="http://www.mail-archive.com/announce@apache.org/">www.mail-archive.com</a><br />
+              <a class="externalLink" href="http://news.gmane.org/gmane.comp.apache.announce">news.gmane.org</a>
+          </td>
+        </tr>
+      </table>
+
+    </section>
+  </body>
+</document>
diff --git a/src/site/xdoc/proposal.xml b/src/site/xdoc/proposal.xml
new file mode 100644
index 0000000..bd97a59
--- /dev/null
+++ b/src/site/xdoc/proposal.xml
@@ -0,0 +1,127 @@
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+ 
+      http://www.apache.org/licenses/LICENSE-2.0
+ 
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+
+<document>
+<properties>
+<title>Proposal for Logging Library Package</title>
+</properties>
+<body>
+
+
+<section name="Proposal for Logging Package">
+
+
+
+<subsection name="(0) Rationale">
+
+<p>There is a great need for debugging and logging information inside of
+Commons components such as HTTPClient and dbcp.  However, there are many
+logging APIs out there and it is difficult to choose among them.
+</p>
+
+<p>The Logging package will be an ultra-thin bridge between different logging
+libraries.  Commons components may use the Logging JAR to remove
+compile-time/runtime dependencies on any particular logging package,
+and contributors may write Log implementations for the library of their choice.
+</p>
+
+</subsection>
+<subsection name="(1) Scope of the Package">
+
+<p>The package shall create and maintain a package that provides extremely
+basic logging functionality and bridges to other, more sophisticated logging
+implementations.
+</p>
+
+<p>
+The package should :
+<ul>
+<li>Have an API which should be as simple to use as possible</li>
+<li>Provide support for log4j</li>
+<li>Provide pluggable support for other logging APIs</li>
+</ul>
+</p>
+
+<p>
+Non-goals:
+<ul>
+<li>This package will not perform logging itself, except at the most basic
+    level.</li>
+<li>We do not seek to become a "standard" API.</li>
+</ul>
+</p>
+
+</subsection>
+<subsection name="(1.5) Interaction With Other Packages">
+
+<p><em>Logging</em> relies on:
+</p>
+
+<ul>
+  <li>Java Development Kit (Version 1.1 or later)</li>
+  <li>Avalon Framework (compile-time dependency only unless this Log
+      implementation is selected at runtime)</li>
+  <li>Avalon LogKit (compile-time dependency only unless this Log
+      implementation is selected at runtime)</li>
+  <li>JDK 1.4 (compile-time dependency only unless this log implementation
+      is selected at runtime).</li>
+  <li>Log4J (compile-time dependency only unless this Log
+      implementation is selected at runtime)</li>
+  <li><a href="http://sourceforge.net/projects/lumberjack/">Lumberjack</a>
+      (compile-time dependency only unless this Log
+      implementation is selected at runtime)</li>
+</ul>
+
+</subsection>
+<subsection name="(2) Required Jakarta-Commons Resources">
+
+<ul>
+<li>CVS Repository - New directory <code>logging</code> in the 
+<code>jakarta-commons</code> CVS repository.</li>
+
+<li>Initial Committers - The list is provided below. </li>
+
+<li>Mailing List - Discussions will take place on the general
+<em>dev at commons.apache.org</em> mailing list. To help list
+subscribers identify messages of interest, it is suggested that the
+message subject of messages about this component be prefixed with
+[Logging].</li>
+
+<li>Bugzilla - New component "Logging" under the "Commons" product
+category, with appropriate version identifiers as needed.</li>
+
+<li>Jyve FAQ - New category "commons-logging" (when available).</li>
+</ul>
+
+
+</subsection>
+<subsection name="(4) Initial Committers">
+
+<p>The initial committers on the Logging component shall be:</p>
+
+<ul>
+  <li>Morgan Delagrange</li>
+  <li>Rodney Waldhoff</li>
+  <li>Craig McClanahan</li>
+</ul>
+
+</subsection>
+</section>
+</body>
+</document>
diff --git a/src/site/xdoc/tech.xml b/src/site/xdoc/tech.xml
new file mode 100644
index 0000000..119bdf9
--- /dev/null
+++ b/src/site/xdoc/tech.xml
@@ -0,0 +1,653 @@
+<?xml version="1.0"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<document>
+
+ <properties>
+  <title>Technology Guide</title>
+  <author email="dev at commons.apache.org">Commons Documentation Team</author>
+ </properties>
+
+ <body>
+    <section name='Overview'>
+      <subsection name='Contents'>
+        <ul>
+          <li>
+          Overview
+            <ul>
+              <li>
+              Contents
+              </li>
+              <li>
+              <a href='#Introduction'>Introduction</a>
+              </li>
+            </ul>
+          </li>
+          <li>
+            <a href='#A Short Introduction to Class Loading and Class Loaders'>
+            A Short Introduction to Class Loading and Class Loaders
+            </a>
+              <ul>
+              <li>
+                <a href='#Preamble'>
+            Preamble
+                </a>
+              </li>
+                  <li>
+                    <a href='#Resolution Of Symbolic References'>
+                Resolution Of Symbolic References
+                    </a>
+                  </li>
+                <li>
+                <a href='#Loading'>
+            Loading
+                </a>
+              </li>
+              <li>
+                <a href='#Linking'>
+            Linking
+                </a>
+              </li>
+              <li>
+                <a href='#Loading Classes'>
+            Loading Classes
+                </a>
+              </li>
+                <li>
+                  <a href='#Bootstrap Classloader'>
+            Bootstrap Classloader
+                </a>
+              </li>
+              <li>
+                <a href='#Runtime Package'>
+            Runtime Package
+                </a>
+                </li>
+              <li>
+                <a href='#Loader Used To Resolve A Symbolic Reference'>
+            Loader Used To Resolve A Symbolic Reference
+                </a>
+              </li>
+              <li>
+                <a href='#Bibliography'>
+            Bibliography
+                </a>
+              </li>
+          </ul>
+        </li>
+                <li>
+                  <a href='#A Short Guide To Hierarchical Class Loading'>
+          A Short Guide To Hierarchical Class Loading
+                  </a>
+          <ul>
+            <li>
+              <a href='#Delegating Class Loaders'>
+          Delegating Class Loaders
+              </a>
+            </li>
+            <li>
+              <a href='#Parent-First And Child-First Class Loaders'>
+          Parent-First And Child-First Class Loaders
+              </a>
+            </li>
+            <li>
+              <a href='#Class ClassLoader'>
+          Class ClassLoader
+              </a>
+            </li>
+            <li>
+              <a href='#Context ClassLoader'>
+          Context ClassLoader
+              </a>
+            </li>
+            <li>
+              <a href='#The Context Classloader in Container Applications'>
+          The Context Classloader in Container Applications
+              </a>
+            </li>
+            <li>
+              <a href='#Issues with Context ClassLoaders'>
+          Issues with Context ClassLoaders
+              </a>
+            </li>
+            <li>
+              <a href='#Reflection And The Context ClassLoader'>
+          Reflection And The Context ClassLoader
+              </a>
+            </li>
+            <li>
+              <a href='#More Information'>
+          More Information
+              </a>
+            </li>
+          </ul>
+        </li>
+        <li>
+          <a href='#A Short Theory Guide To JCL'>
+          A Short Theory Guide To JCL
+          </a>
+          <ul>
+            <li>
+              <a href='#Isolation And The Context Class Loader'>
+          Isolation And The Context Class Loader
+              </a>
+            </li>
+            <li>
+              <a href='#Log And LogFactory'>
+          Log And LogFactory
+              </a>
+            </li>
+            <li>
+              <a href='#Log Implementations'>
+          Log Implementations
+              </a>
+            </li>
+              <li>
+                <a href='#Using Reflection To Load Log Implementations'>
+            Using Reflection To Load Log Implementations
+                </a>
+              </li>
+            </ul>
+          </li>
+        </ul>
+      </subsection>
+    <subsection name='Introduction'>
+      <p>
+  This guide is aimed at describing the technologies that JCL developers and expert users
+  (and users who need to become experts)
+  should be familiar with. The aim is to give an understanding whilst being precise but brief.
+  Details which are not relevant for JCL have been suppressed.
+  References have been included.
+      </p>
+      <p>
+  These topics are a little difficult and it's easy for even experienced developers to make
+  mistakes. We need you to help us get it right! Please submit corrections, comments, additional references
+  and requests for clarification
+  by either:
+      </p>
+      <ul>
+        <li>
+  posting to the <a href='http://commons.apache.org/mail-lists.html'>Apache Commons dev mailing list</a> or
+        </li>
+        <li>
+  creating an issue in <a href='http://issues.apache.org/jira/browse/LOGGING/'>JIRA</a>.
+        </li>
+      </ul>
+      <p>
+  TIA
+      </p>
+    </subsection>
+
+  </section>
+  <section name='A Short Introduction to Class Loading and Class Loaders'>
+    <subsection name='Preamble'>
+      <p>
+  This is intended to present a guide to the process by which Java bytecode uses bytecode in other classes
+  from the perspective of the language and virtual machine specifications. The focus will be on deciding
+  which bytecode will be used (rather than the mechanics of the usage). It focuses on facts and terminology.
+      </p>
+      <p>
+  The process is recursive: it is therefore difficult to pick a starting point.
+  Sun's documentation starts from the perspective of the startup of a new application.
+  This guide starts from the perspective of an executing application.
+      </p>
+      <p>
+  During this discussion, please assume that each time that <em>class</em> is mentioned,
+  the comments applied equally well to interfaces.
+      </p>
+      <p>
+  This document is targeted at Java 1.2 and above.
+      </p>
+      </subsection>
+       <subsection name='Resolution Of Symbolic References'>
+      <p>
+  (<a href='http://java.sun.com/docs/books/jls/second_edition/html/execution.doc.html#44524'>LangSpec 12.3.3</a>)
+  The bytecode representation of a class contains symbolic names for other classes referenced.
+      </p>
+      <p>
+        <em>
+  In practical development terms: If a class is imported (either explicitly in the list of imports at the top of
+  the source file or implicitly through a fully qualified name in the source code) it is referenced symbolically.
+        </em>
+      </p>
+      <p>
+  (<a href='http://java.sun.com/docs/books/vmspec/2nd-edition/html/ConstantPool.doc.html#73492'>VMSpec 5.4.3</a>)
+  Resolution of a symbolic reference occurs dynamically at runtime and is carried out by
+  the Java Virtual Machine. Resolution of a symbolic reference requires loading and linking of the new class.
+      </p>
+      <p>
+        <em>
+  Note: references are not statically resolved at compile time.
+        </em>
+      </p>
+      </subsection>
+    <subsection name='Loading'>
+      <p>
+  (<a href='http://java.sun.com/docs/books/vmspec/2nd-edition/html/Concepts.doc.html#19175'>VMSpec 2.17.2</a>)
+  Loading is the name given to the process by which a binary form of a class is obtained
+  by the Java Virtual Machine.
+  Java classes are always loaded and linked dynamically by the Java Virtual Machine
+  (rather than statically by the compiler).
+      </p>
+      <p>
+        <em>
+  In practical development terms:
+  This means that the developer has no certain knowledge about the actual
+  bytecode that will be used to execute any external call (one made outside the class). This is determined only
+  at execution time and is affected by the way that the code is deployed.
+        </em>
+      </p>
+      </subsection>
+    <subsection name='Linking'>
+      <p>
+  (<a href='http://java.sun.com/docs/books/vmspec/2nd-edition/html/Concepts.doc.html#22574'>VMSpec 2.17.3</a>)
+  Linking is the name used for combining the
+  binary form of a class into the Java Virtual Machine. This must happen before the class can be used.
+      </p>
+      <p>
+  (<a href='http://java.sun.com/docs/books/vmspec/2nd-edition/html/Concepts.doc.html#22574'>VMSpec 2.17.3</a>)
+  Linking is composed of verification, preparation and resolution (of symbolic references).
+  Flexibility is allowed over the timing of resolution. (Within limit) this may happen at any time after
+  preparation and before that reference is used.
+      </p>
+      <p>
+        <em>
+  In practical development terms: This means that different JVMs may realize that a reference cannot be
+  resolved at different times during execution. Consequently, the actual behaviour cannot be precisely predicted
+  without intimate knowledge of the JVM (on which the bytecode will be executed).
+  This makes it hard to give universal guidance to users.
+        </em>
+      </p>
+      </subsection>
+
+    <subsection name='Loading Classes'>
+      <p>
+  (<a href='http://java.sun.com/docs/books/vmspec/2nd-edition/html/Concepts.doc.html#19175'>VMSpec 2.17.2</a>)
+  The loading process is performed by a <code>ClassLoader</code>.
+      </p>
+      <p>
+  (<a href='http://java.sun.com/docs/books/vmspec/2nd-edition/html/ConstantPool.doc.html#72007'>VMSpec 5.3</a>)
+  A classloader may create a class either by delegation or by defining it directly.
+  The classloader that initiates loading of a class is known as the initiating loader.
+  The classloader that defines the class is known as the defining loader.
+      </p>
+      <p>
+        <em>
+  In practical terms: understanding and appreciating this distinction is crucial when debugging issues
+  concerning classloaders.
+        </em>
+      </p>
+      </subsection>
+
+    <subsection name='Bootstrap Classloader'>
+      <p>
+  (<a href='http://java.sun.com/docs/books/vmspec/2nd-edition/html/ConstantPool.doc.html#72007'>VMSPEC 5.3</a>)
+  The bootstrap is the base <code>ClassLoader</code> supplied by the Java Virtual Machine.
+  All others are user (also known as application) <code>ClassLoader</code> instances.
+      </p>
+      <p>
+        <em>
+  In practical development terms: The System classloader returned by <code>Classloader.getSystemClassLoader()</code>
+  will be either the bootstrap classloader or a direct descendant of the bootstrap classloader.
+  Only when debugging issues concerning the system classloader should there be any need to consider the detailed
+  differences between the bootstrap classloader and the system classloader.
+        </em>
+      </p>
+      </subsection>
+    <subsection name='Runtime Package'>
+      <p>
+  (<a href='http://java.sun.com/docs/books/vmspec/2nd-edition/html/ConstantPool.doc.html#72007'>VMSpec 5.3</a>)
+  At runtime, a class (or interface) is determined by its fully qualified name
+  and by the classloader that defines it. This is known as the class's runtime package.
+      </p>
+      <p>
+  (<a href='http://java.sun.com/docs/books/vmspec/2nd-edition/html/ConstantPool.doc.html#75929'>VMSpec 5.4.4</a>)
+  Only classes in the same runtime package are mutually accessible.
+      </p>
+      <p>
+        <em>
+  In practical development terms: two classes with the same symbolic name can only be used interchangeably
+  if they are defined by the same classloader. A classic symptom indicative of a classloader issue is that
+  two classes with the same fully qualified name are found to be incompatible during a method call.
+  This may happen when a member is expecting an interface which is (seemingly) implemented by a class
+  but the class is in a different runtime package after being defined by a different classloader. This is a
+  fundamental java language security feature.
+        </em>
+      </p>
+      </subsection>
+
+    <subsection name='Loader Used To Resolve A Symbolic Reference'>
+      <p>
+  (<a href='http://java.sun.com/docs/books/vmspec/2nd-edition/html/ConstantPool.doc.html#72007'>VMSpec 5.3</a>)
+  The classloader which defines the class (whose reference is being resolved) is the one
+  used to initiate loading of the class referred to.
+      </p>
+      <p>
+        <em>
+  In practical development terms: This is very important to bear in mind when trying to solve classloader issues.
+  A classic misunderstanding is this: suppose class A defined by classloader C has a symbolic reference to
+  class B and further that when C initiates loading of B, this is delegated to classloader D which defines B.
+  Class B can now only resolve symbols that can be loaded by D, rather than all those which can be loaded by C.
+  This is a classic recipe for classloader problems.
+        </em>
+      </p>
+      </subsection>
+
+    <subsection name='Bibliography'>
+      <ul>
+        <li>
+    <a href='http://java.sun.com/docs/books/vmspec/'>VMSpec</a> <em>The Java Virtual Machine Specification, Second Edition</em>
+        </li>
+        <li>
+    <a href='http://java.sun.com/docs/books/jls/'>LangSpec</a> <em>The Java Language Specification, Second Edition</em>
+        </li>
+      </ul>
+      </subsection>
+  </section>
+  <section name='A Short Guide To Hierarchical Class Loading'>
+    <subsection name='Delegating Class Loaders'>
+      <p>
+  When asked to load a class, a class loader may either define the class itself or delegate.
+  The base <code>ClassLoader</code> class insists that every implementation has a parent class loader.
+  This delegation model therefore naturally forms a tree structure rooted in the bootstrap classloader.
+      </p>
+      <p>
+  Containers (i.e. applications such as servlet engines or application servers
+  that manage and provide support services for a number of "contained" applications
+  that run inside of them) often use complex trees to allow isolation of different applications
+  running within the container. This is particularly true of J2EE containers.
+      </p>
+    </subsection>
+
+    <subsection name='Parent-First And Child-First Class Loaders'>
+      <p>
+  When a classloader is asked to load a class, a question presents itself: should it immediately
+  delegate the loading to its parent (and thus only define those classes not defined by its parent)
+  or should it try to define it first itself (and only delegate to its parent those classes it does
+  not itself define). Classloaders which universally adopt the first approach are termed parent-first
+  and the second child-first.
+      </p>
+      <p>
+   <strong>Note:</strong> the term child-first (though commonly used) is misleading.
+   A better term (and one which may be encountered on the mailing list) is parent-last.
+   This more accurately describes the actual process of classloading performed
+   by such a classloader.
+      </p>
+      <p>
+  Parent-first loading has been the standard mechanism in the JDK
+  class loader, at least since Java 1.2 introduced hierarchical classloaders.
+      </p>
+      <p>
+  Child-first classloading has the advantage of helping to improve isolation
+  between containers and the applications inside them.  If an application
+  uses a library jar that is also used by the container, but the version of
+  the jar used by the two is different, child-first classloading allows the
+  contained application to load its version of the jar without affecting the
+  container.
+      </p>
+      <p>
+  The ability for a servlet container to offer child-first classloading
+  is made available, as an option, by language in the servlet spec (Section
+  9.7.2) that allows a container to offer child-first loading with
+  certain restrictions, such as not allowing replacement of java.* or
+  javax.* classes, or the container's implementation classes.
+      </p>
+      <p>
+  Though child-first and parent-first are not the only strategies possible,
+  they are by far the most common.
+  All other strategies are rare.
+  However, it is not uncommon to be faced with a mixture of parent-first and child-first
+  classloaders within the same hierarchy.
+      </p>
+    </subsection>
+
+    <subsection name='Class ClassLoader'>
+      <p>
+  The class loader used to define a class is available programmatically by calling
+  the <code>getClassLoader</code> method
+  on the class in question. This is often known as the class classloader.
+      </p>
+    </subsection>
+
+    <subsection name='Context ClassLoader'>
+      <p>
+  Java 1.2 introduces a mechanism which allows code to access classloaders
+  which are not the class classloader or one of its parents.
+  A thread may have a class loader associated with it by its creator for use
+  by code running in the thread when loading resources and classes.
+  This classloader is accessed by the <code>getContextClassLoader</code>
+  method on <code>Thread</code>. It is therefore often known as the context classloader.
+      </p>
+      <p>
+  Note that the quality and appropriateness of the context classloader depends on the
+  care with which the thread's owner manages it.
+      </p>
+    </subsection>
+
+    <subsection name='The Context Classloader in Container Applications'>
+      <p>
+  The Javadoc for
+  <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Thread.html#setContextClassLoader(java.lang.ClassLoader)">
+  <code>Thread.setContextClassLoader</code></a> emphasizes the setting of the
+  context classloader as an aspect of thread creation.  However, in many
+  applications the context classloader is not fixed at thread creation but
+  rather is changed throughout the life of a thread as thread execution moves
+  from one context to another.  This usage of the context classloader is
+  particularly important in container applications.
+      </p>
+      <p>
+  For example, in a hypothetical servlet container, a pool of threads
+  is created to handle HTTP requests.  When created these threads have their
+  context classloader set to a classloader that loads container classes.
+  After the thread is assigned to handle a request, container code parses
+  the request and then determines which of the deployed web applications
+  should handle it. Only when the container is about to call code associated
+  with a particular web application (i.e. is about to cross an "application
+  boundary") is the context classloader set to the classloader used to load
+  the web app's classes.  When the web application finishes handling the
+  request and the call returns, the context classloader is set back to the
+  container classloader.
+      </p>
+      <p>
+  In a properly managed container, changes in the context classloader are
+  made when code execution crosses an application boundary.  When contained
+  application <code>A</code> is handling a request, the context classloader
+  should be the one used to load <code>A</code>'s resources. When application
+  <code>B</code> is handling a request, the context classloader should be
+  <code>B</code>'s.
+        </p>
+      <p>
+  While a contained application is handling a request, it is not
+  unusual for it to call system or library code loaded by the container.
+  For example, a contained application may wish to call a utility function
+  provided by a shared library.  This kind of call is considered to be
+  within the "application boundary", so the context classloader remains
+  the contained application's classloader.  If the system or library code
+  needs to load classes or other resources only visible to the contained
+  application's classloader, it can use the context classloader to access
+  these resources.
+      </p>
+      <p>
+  If the context classloader is properly managed, system and library code
+  that can be accessed by multiple applications can not only use it to load
+  application-specific resources, but also can use it to detect which
+  application is making a call and thereby provided services tailored to the
+  caller.
+      </p>
+    </subsection>
+
+    <subsection name='Issues with Context ClassLoaders'>
+      <p>
+  In practice, context classloaders vary in quality and issues sometimes arise
+  when using them.
+  The owner of the thread is responsible for setting the classloader.
+  If the context classloader is not set then it will default to the system
+  classloader.
+  Any container doing so will cause difficulties for any code using the context classloader.
+      </p>
+      <p>
+  The owner is also at liberty to set the classloader as they wish.
+  Containers may set the context classloader so that it is neither a child nor a parent
+  of the classloader that defines the class using that loader.
+  Again, this will cause difficulties.
+      </p>
+      <p>
+  Introduced in <a href='http://java.sun.com/j2ee/j2ee-1_3-fr-spec.pdf'>Java J2EE 1.3</a>
+  is a requirement for vendors to appropriately set the context classloader.
+  Section 6.2.4.8 (1.4 text):
+      </p>
+<source>
+This specification requires that J2EE containers provide a per thread
+context class loader for the use of system or library classes in
+dynamically loading classes provided by the application.  The EJB
+specification requires that all EJB client containers provide a per
+thread context class loader for dynamically loading system value classes.
+The per thread context class loader is accessed using the Thread method
+getContextClassLoader.
+
+The classes used by an application will typically be loaded by a
+hierarchy of class loaders. There may be a top level application class
+loader, an extension class loader, and so on, down to a system class
+loader.  The top level application class loader delegates to the lower
+class loaders as needed.  Classes loaded by lower class loaders, such as
+portable EJB system value classes, need to be able to discover the top
+level application class loader used to dynamically load application
+classes.
+
+We require that containers provide a per thread context class loader
+that can be used to load top level application classes as described
+above.
+</source>
+      <p>
+  This specification leaves quite a lot of freedom for vendors.
+  (As well as using unconventional terminology and containing the odd typo.)
+  It is a difficult passage (to say the least).
+      </p>
+    </subsection>
+
+    <subsection name='Reflection And The Context ClassLoader'>
+      <p>
+  Reflection cannot bypass restrictions imposed by the java language security model, but, by avoiding symbolic
+  references, reflection can be used to load classes which could not otherwise be loaded. Another <code>ClassLoader</code>
+  can be used to load a class and then reflection used to create an instance.
+      </p>
+      <p>
+  Recall that the runtime packaging is used to determine accessibility.
+  Reflection cannot be used to avoid basic java security.
+  Therefore, the runtime packaging becomes an issue when attempting to cast classes
+  created by reflection using other class loaders.
+  When using this strategy, various modes of failure are possible
+  when common class references are defined by the different class loaders.
+      </p>
+      <p>
+  Reflection is often used with the context classloader. In theory, this allows a class defined in
+  a parent classloader to load any class that is loadable by the application.
+  In practice, this only works well when the context classloader is set carefully.
+      </p>
+    </subsection>
+
+    <subsection name='More Information'>
+      <ul>
+        <li>
+          Articles On Class Loaders And Class Loading
+          <ul>
+            <li>
+              <a
+    href='http://www.onjava.com/pub/a/onjava/2001/07/25/ejb.html'>
+  Article on J2EE class loading
+              </a>
+            </li>
+            <li>
+              <a
+    href='http://www.onjava.com/pub/a/onjava/2003/11/12/classloader.html'>
+  Article on class loading
+              </a>
+            </li>
+            <li>
+              <a
+    href='http://www.javaworld.com/javaworld/javaqa/2003-06/01-qa-0606-load.html'>
+  Article on context class loaders
+              </a>
+            </li>
+          </ul>
+        </li>
+        <li>Specific Containers
+          <ul>
+            <li>
+              <a
+    href='http://tomcat.apache.org/tomcat-4.1-doc/class-loader-howto.html'>
+  Tomcat 4.1 ClassLoader Guide
+              </a>
+            </li>
+            <li>
+              <a
+    href='http://tomcat.apache.org/tomcat-5.0-doc/class-loader-howto.html'>
+  Tomcat 5.0 ClassLoader Guide
+              </a>
+            </li>
+            <li>
+              <a
+href='http://publib.boulder.ibm.com/infocenter/wasinfo/v6r0/index.jsp?topic=/com.ibm.websphere.express.doc/info/exp/ae/trun_classload_web.html'>
+  Classloading In WebSphere
+              </a>
+            </li>
+          </ul>
+        </li>
+      </ul>
+    </subsection>
+  </section>
+
+  <section name='A Short Theory Guide To JCL'>
+    <subsection name='Isolation And The Context Class Loader'>
+      <p>
+  JCL takes the view that different context class loader indicate boundaries between applications
+  running in a container environment. Isolation requires that JCL honours these boundaries
+  and therefore allows different isolated applications to configure their logging systems
+  independently.
+      </p>
+      </subsection>
+      <subsection name='Log And LogFactory'>
+      <p>
+  Performance dictates that symbolic references to these classes are present in the calling application code
+  (reflection would simply be too slow). Therefore, these classes must be loadable by the classloader
+  that loads the application code.
+      </p>
+      </subsection>
+      <subsection name='Log Implementations'>
+      <p>
+  Performance dictates that symbolic references to the logging systems are present in the implementation
+  classes (again, reflection would simply be too slow). So, for an implementation to be able to function,
+  it is necessary for the logging system to be loadable by the classloader that defines the implementing class.
+      </p>
+      </subsection>
+
+      <subsection name='Using Reflection To Load Log Implementations'>
+      <p>
+  However, there is actually no reason why <code>LogFactory</code> requires symbolic references to particular <code>Log</code>
+  implementations. Reflection can be used to load these from an appropriate classloader
+  without unacceptable performance degradation.
+  This is the strategy adopted by JCL.
+      </p>
+      <p>
+  JCL uses the context classloader to load the <code>Log</code> implementation.
+      </p>
+      </subsection>
+  </section>
+</body>
+</document>
diff --git a/src/site/xdoc/troubleshooting.xml b/src/site/xdoc/troubleshooting.xml
new file mode 100644
index 0000000..7ebe48a
--- /dev/null
+++ b/src/site/xdoc/troubleshooting.xml
@@ -0,0 +1,467 @@
+<?xml version="1.0"?>
+
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+ 
+      http://www.apache.org/licenses/LICENSE-2.0
+ 
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+
+<document>
+
+ <properties>
+  <title>Troubleshooting Guide</title>
+  <author email="dev at commons.apache.org">Commons Documentation Team</author>
+ </properties>
+
+ <body> 
+<section name="Contents">
+<ul>
+<li>
+<a href="#Contents">Contents</a>
+</li>
+<li>
+<a href="#Using JCL Diagnostics">Using JCL Diagnostics</a>
+<ul>
+<li>
+<a href="#When To Use Diagnostic Logging">When To Use Diagnostic Logging</a>
+<ul/>
+</li>
+<li>
+<a href="#How To Use Diagnostic logging">How To Use Diagnostic logging</a>
+</li>
+<li>
+<a href="#OIDs">OIDs</a>
+</li>
+<li>
+<a href="#Diagnostic Message Prefix">Diagnostic Message Prefix</a>
+</li>
+<li>
+<a href="#ClassLoader Hierarchy Tree">ClassLoader Hierarchy Tree</a>
+</li>
+<li>
+<a href="#LogFactory Class Bootstrap">LogFactory Class Bootstrap</a>
+</li>
+<li>
+<a href="#Construction Of LogFactoryImpl Instances">Construction Of LogFactoryImpl Instances</a>
+</li>
+<li>
+<a href="#Log Discovery Diagnostics">Log Discovery Diagnostics</a>
+</li>
+</ul>
+</li>
+<li>
+<a href="#Containers With Custom LogFactory Implementations">Containers With Custom LogFactory Implementations</a>
+<ul>
+<li>
+<a href="#The Incompatible LogFactory Issue">The Incompatible LogFactory Issue</a>
+<ul>
+<li>
+<a href="#Symptoms">Symptoms</a>
+</li>
+<li>
+<a href="#Explanation">Explanation</a>
+</li>
+<li>
+<a href="#Fixes">Fixes</a>
+</li>
+</ul>
+</li>
+</ul>
+</li>
+<li>
+<a href="#Containers With Custom ClassLoading Behaviour for Logging">Containers With Custom ClassLoading Behaviour for Logging</a>
+<ul>
+<li>
+<a href="#Apache Tomcat">Apache Tomcat</a>
+<ul/>
+</li>
+<li>
+<a href="#JBoss Application Server">JBoss Application Server</a>
+<ul/>
+</li>
+<li>
+<a href="#Other Containers">Other Containers</a>
+<ul/>
+</li>
+</ul>
+</li>
+</ul>
+</section>
+  <section name='Using JCL Diagnostics'>
+    <p>
+Diagnostics is a feature introduced in JCL 1.1 as an aid to debugging problems 
+with JCL configurations. When diagnostics are switched on, messages are logged 
+to a stream (specified by the user) by the two main classes involved in discovery
+in JCL (<code>LogFactory</code> and <code>LogFactoryImpl</code>).
+    </p>
+    <p>
+Diagnostics are intended to be used in conjunction with the source. The source
+contains numerous and lengthy comments. Often these are intended to help explain
+the meaning of the messages.
+    </p>
+    <subsection name='When To Use Diagnostic Logging'>
+      <p>
+Diagnostic logging is intended only to be used when debugging a problematic 
+configuration. It <em>should</em> be switched off for production. 
+      </p>
+    </subsection>
+    <subsection name='How To Use Diagnostic logging'>
+      <p>
+Diagnostic logging is controlled through the system property 
+<code>org.apache.commons.logging.diagnostics.dest</code>. Setting the property value
+to the special strings <code>STDOUT</code> or <code>STDERR</code> (case-sensitive) 
+will output messages to <code>System.out</code> and <code>System.err</code> respectively.
+Setting the property value to a valid file name will result in the messages being logged
+to that file.
+      </p>
+    </subsection> 
+    <subsection name='OIDs'>
+      <p>
+Diagnostics uses the concept of an Object ID (OID). This allows the identity of objects 
+to be tracked without relying on useful <code>toString</code> implementations. 
+These are of the form: 
+      </p>
+<code><pre>
+<em>classname</em>@<em>system identity hash code</em>
+</pre></code>
+      <p>
+The <em>system identity hash code</em> is found by calling <code>System.identityHashCode()</code> 
+which should uniquely identify a particular instance. The classname is usually the fully qualified
+class name though in a few cases, <code>org.apache.commons.logging.impl.LogFactoryImpl</code> may be
+shortened to <code>LogFactoryImpl</code> to increase ease of reading. For example:
+      </p>
+<code><pre>
+sun.misc.Launcher$AppClassLoader at 20120943
+LogFactoryImpl at 1671711
+</pre></code>
+      <p>
+OIDs are intended to be used to cross-reference. They allow particular instances of classloaders
+and JCL classes to be tracked in different contexts. This plays a vital role in building
+up the understanding of the classloader environment required to diagnose JCL problems. 
+      </p> 
+    </subsection>
+    <subsection name='Diagnostic Message Prefix'>
+      <p>
+Each diagnostic message is prefixed with details of the relevant class in a standard format.
+This takes the form:
+    </p>
+<code><pre>
+[<em>class-identifier</em> from <em>ClassLoader OID</em>]
+</pre></code>
+    <p>
+<em>ClassLoader OID</em> is the <a href='#OIDs'>OID</a> of a classloader which loaded 
+the class issuing the message.
+<em>class-identifier</em> identifies the object issuing the message. 
+    </p>
+    <p>
+In the case of 
+<code>LogFactory</code>, this is just <code>LogFactory</code>. For example (line split):
+    </p>
+<code><pre>
+[LogFactory 
+   from sun.misc.Launcher$AppClassLoader at 20120943] BOOTSTRAP COMPLETED
+</pre></code>
+    <p>
+In the case of 
+<code>LogFactoryImpl</code>, the prefix is the instance OID. This can be cross referenced
+to discover the details of the TCCL used to manage this instance. For example (line split):
+    </p>
+<code><pre>
+[LogFactoryImpl at 1671711 
+   from sun.misc.Launcher$AppClassLoader at 20120943] Instance created.
+</pre></code>
+    </subsection>
+    <subsection name='ClassLoader Hierarchy Tree'>
+      <p>
+Understanding the relationships between classloaders is vital when debugging JCL.
+At various points, JCL will print to the diagnostic log the hierarchy for important
+classloaders. This is obtained by walking the tree using <code>getParent</code>.
+Each classloader is represented (visually) by an OID (to allow cross referencing) 
+and the relationship indicated in <code><em>child</em> --> <em>parent</em></code> fashion.
+For example (line split for easy reading):
+      </p>
+<code><pre>
+ClassLoader tree:java.net.URLClassLoader at 3526198  
+      --> sun.misc.Launcher$AppClassLoader at 20120943 (SYSTEM) 
+      --> sun.misc.Launcher$ExtClassLoader at 11126876 
+      --> BOOT
+</pre></code>
+      <p>
+Represents a hierarchy with four elements ending in the boot classloader.
+      </p>
+    </subsection>
+    <subsection name='LogFactory Class Bootstrap'>
+      <p>
+Whenever the <code>LogFactory</code> class is initialized, diagnostic messages about 
+the classloader environment are logged. The content of each of these messages is prefixed by 
+<code>[ENV]</code> to help distinguish them. The extension directories, application classpath, 
+details of the classloader (including the <a href='#OIDs'>OID</a> and <code>toString</code> 
+value) used to load <code>LogFactory</code> and the 
+<a href='#ClassLoader%20Hierarchy%20Tree'>classloader tree</a> for that classloader 
+are logged.
+      </p>
+      <p>
+Many Sun classloaders have confusing <code>toString</code> values. For example, the OID may be
+      </p>
+<code><pre>
+sun.misc.Launcher$AppClassLoader at 20120943
+</pre></code>
+      <p>
+with a <code>toString</code> value of
+      </p>
+<code><pre>
+sun.misc.Launcher$AppClassLoader at 133056f
+</pre></code>
+      <p>
+Other classloader implementations may give very useful information (such as the local classpath).
+      </p>
+      <p>
+Finally, once initialization is complete a <code>BOOTSTRAP COMPLETED</code> message is issued.
+      </p>
+    </subsection>
+    <subsection name='Construction Of LogFactoryImpl Instances'>
+      <p>
+<code>LogFactoryImpl</code> is the standard and default <code>LogFactory</code> implementation.
+This section obviously only applies to configurations using this implementation.
+      </p>
+      <p>
+Before assigning a <code>Log</code> instance, <code>LogFactory</code> loads a 
+<code>LogFactory</code> implementation. The content is prefixed by <code>[LOOKUP]</code>
+for each diagnostic message logged by this process.
+      </p> 
+      <p>
+The implementation used can vary per Thread context classloader (TCCL). If this is the first time 
+that a Log has been requested for a particular TCCL a new instance will be created.
+      </p>
+      <p>
+Information of particular interest is logged at this stage. Details of the TCCL are logged
+allowing the <a href='#OIDs'>OID</a> later to be cross-referenced to the <code>toString</code> value 
+and the <a href='#ClassLoader%20Hierarchy%20Tree'>classloader tree</a>. For example, the 
+following log snippet details the TCCL (lines split):
+      </p>
+<code><pre>
+[LogFactory from sun.misc.Launcher$AppClassLoader at 20120943] 
+    [LOOKUP] LogFactory implementation requested for the first time for context 
+        classloader java.net.URLClassLoader at 3526198
+[LogFactory from sun.misc.Launcher$AppClassLoader at 20120943] 
+    [LOOKUP] java.net.URLClassLoader at 3526198 == 'java.net.URLClassLoader at 35ce36'
+[LogFactory from sun.misc.Launcher$AppClassLoader at 20120943] 
+    [LOOKUP] ClassLoader tree:java.net.URLClassLoader at 3526198 
+        --> sun.misc.Launcher$AppClassLoader at 20120943 (SYSTEM)  
+          --> sun.misc.Launcher$ExtClassLoader at 11126876 
+            --> BOOT
+</pre></code>
+    </subsection>
+    <subsection name='Log Discovery Diagnostics'>
+      <p>
+The standard <code>LogFactoryImpl</code> issues many diagnostic messages when discovering
+the <code>Log</code> implementation to be used.
+      </p>
+      <p>
+During discovery, environment variables are loaded and values set. This content is prefixed by 
+<code>[ENV]</code> to make it easier to distinguish this material.
+      </p>
+      <p>
+The possible messages issued during discovery are numerous. To understand them, the source
+should be consulted. Attention should be paid to the classloader hierarchy trees for the
+classloader used to load <code>LogFactory</code> and to the TCCL.
+      </p>
+    </subsection>
+  </section>
+  <section name='Containers With Custom LogFactory Implementations'>
+    <p>
+ Some containers use a custom <code>LogFactory</code> implementation to adapt JCL to their particular
+ logging system. This has some important consequences for the deployment of applications using JCL within 
+ these containers.
+    </p>
+    <p>
+ Containers known to use this mechanism:
+    </p>
+    <ul>
+      <li><a href='http://www.ibm.com/software/websphere/'>WebSphere Application Server</a> from
+      <a href='http://www.ibm.com/software/websphere/'>IBM</a> (versions 5 and 6).</li>
+    </ul>
+    <p>
+ Containers suspected to use this mechanism:
+    </p>
+    <ul>
+      <li>WebSphere Application Server (other versions).</li>
+    </ul>
+    <p>
+The Apache Commons team would be grateful if reports were posted to the development list
+of other containers using a custom implementation.
+    </p>
+    <subsection name='The Incompatible LogFactory Issue'>
+      <subsection name='Symptoms'>
+        <p>
+  An exception is thrown by JCL with a message similar to:
+        </p>
+  <code><pre>
+  The chosen LogFactory implementation does not extend LogFactory. Please check your configuration. 
+  (Caused by java.lang.ClassCastException: The application has specified that a custom LogFactory 
+  implementation should be used but Class 'com.ibm.ws.commons.logging.TrLogFactory' cannot be converted 
+  to 'org.apache.commons.logging.LogFactory'. The conflict is caused by the presence of multiple 
+  LogFactory classes in incompatible classloaders. Background can be found in 
+  http://commons.apache.org/logging/tech.html. If you have not explicitly specified a custom
+  LogFactory then it is likely that the container has set one without your knowledge. 
+  In this case, consider using the commons-logging-adapters.jar file or specifying the standard 
+  LogFactory from the command line. Help can be found @http://commons.apache.org/logging.
+  </pre></code>
+        <p>
+  This is a WebSphere example so the name of the custom LogFactory is 
+  <code>com.ibm.ws.commons.logging.TrLogFactory</code>. For other containers, this class name will
+  differ.
+        </p>
+      </subsection>
+      <subsection name='Explanation'>
+      <p>
+ A custom <code>LogFactory</code> implementation can only be used if the implementation class loaded 
+ dynamically at runtime can be cast to the <code>LogFactory</code> class that loaded it. There are 
+ several ways in which this cast can fail. The most obvious is that the source code may not actually 
+ extend <code>LogFactory</code>. The source may be compatible but if the <code>LogFactory</code> class
+ against which the source is compiled is not binary compatible then the cast will also fail.
+      </p>
+      <p>
+ There is also another more unusual way in which this cast can fail: even when the binary is compatible,
+ the implementation class loaded at runtime may be linked to a different instance of the 
+ <code>LogFactory</code> class. For more information, see the <a href='tech.html'>tech guide</a>.
+      </p>
+      <p>
+ This situation may be encountered in containers which use a custom <code>LogFactory</code> implementation.
+ The implementation will typically be provided in a shared, high level classloader together with JCL.
+ When an application classloader contains <code>LogFactory</code>, the implementation will be loaded 
+ from that higher level classloader. The implementation class will be linked to the <code>LogFactory</code>
+ class loaded by the higher level classloader. Even if the 
+ <code>LogFactory</code> implementations are binary compatible, since they are loaded by different classloaders
+ the two <code>LogFactory</code> Class instances are not equal and so the cast must fail.
+      </p>
+      <p>
+The policy adopted by JCL in this situation is to re-throw this exception. Additional information
+is included in the message to help diagnosis. The reasoning behind this choice is that a 
+particular <code>LogFactory</code> implementation has been actively specified and this
+choice should not be ignored. This policy has unfortunate consequences when running in
+containers which have custom implementations: the above runtime exception may be thrown
+under certain classloading policies without the user knowingly specifying a custom 
+implementation.
+      </p>
+      </subsection>
+      <subsection name='Fixes'>
+        <p>
+ There are various ways to fix this problem. Which fix is right depends on the circumstances.
+        </p>
+        <p>
+ If you are happy using another classloading policy for the application, select a 
+ classloading policy which ensures that <code>LogFactory</code> will be loaded from the
+ shared classloader containing the custom implementation.
+        </p>
+        <p>
+ If you want to bypass the container adaption mechanism then set the appropriate system property 
+ to the default value when the container is started:
+        </p>
+ <code><pre>
+ -Dorg.apache.commons.logging.LogFactory=org.apache.commons.logging.impl.LogFactoryImpl
+ </pre></code>
+      <p>
+ If you want to continue to use the default container mechanism then:
+      </p>
+      <ul>
+        <li>
+ Find and replace the commons-logging implementation used by the container with
+ the most modern release
+        </li>
+        <li>
+ Replace the commons-logging jar in the application with the commons-logging-adapters jar.
+ This will ensure that application classloader will delegate to it's parent when loading
+ <code>LogFactory</code>.
+        </li>
+      </ul>
+      <p>
+ If you encounter difficulties when applying the fixes recommended, please turn on
+ <a href='#Using JCL Diagnostics'>diagnostics</a> and consult the logs.
+      </p>
+      </subsection>
+    </subsection>
+  </section>
+  <section name='Containers With Custom ClassLoading Behaviour for Logging'>
+    <p>
+ Because commons-logging is such a fundamental library, some containers modify the way
+ in which classloading behaves for commons-logging classes.
+   </p>
+   <subsection name="Apache Tomcat">
+   <p>
+ At the current date, Tomcat 5.5.16 is the current release. All releases from version
+ 4.1.x through 5.5.16 have a startup process that places jarfile 
+ ${tomcat.home}/bin/commons-logging-api.jar in the system classpath and then
+ prevents any webapp from overriding the classes in that jarfile. Effectively, all
+ webapps behave as if "parent-first" classloading were enabled for those classes.
+   </p>
+   <p>
+ This has some benefits; in particular it means that there are no problems in
+ these Tomcat versions with having multiple copies of the commons-logging Log
+ interface in the classpath (which avoids the "Log does not implement Log"
+ problem described elsewhere).
+   </p>
+   <p>
+ However it also means that no webapp can override the core commons-logging
+ classes by including an updated commons-logging jarfile in WEB-INF/lib; any
+ class already loaded via the container takes priority. In particular, as
+ Tomcat bundles logging 1.0.4 only, the new diagnostics and memory-leak-prevention
+ features of the 1.1 release will not be available unless the container's
+ library version is updated.
+   </p>
+   <p>
+ Because the commons-logging-api.jar in the container does not contain any
+ log-library-adapter classes, updated behaviour for these <i>will</i> be
+ seen when logging 1.1 is bundled in WEB-INF/lib. In particular, the 
+ support for log4j's TRACE level will take effect without having to update
+ the container.
+   </p>
+   <p>
+ If you do wish to update Tomcat's version of commons-logging, then you
+ <i>must</i> use the commons-logging-1.1-api jar only, not the full jar.
+ Classes in the webapp cannot override classes loaded from the system
+ classpath set up during Tomcat's startup process, and logging adapters
+ can only see their matching concrete logging library if that library is
+ available in the same classpath. Bundling the full commons-logging jarfile
+ (with adapters) into the system classpath therefore means that logging
+ libraries (eg log4j) within WEB-INF/lib are not accessible.
+   </p>
+   <p>
+ Note that the behaviour described here only applies if the standard Tomcat
+ startup process is run. When Tomcat is embedded in a larger
+ framework (eg run embedded within an IDE) this may not apply.
+   </p>
+   </subsection>
+   <subsection name="JBoss Application Server">
+   <p>
+   The JBoss Application Server can be configured to prevent deployed
+   code from overriding classes higher in the hierarchy, effectively
+   forcing "parent-first" behaviour for selected classes. By default,
+   commons-logging is in this list (at least for some JBoss versions
+   starting with 4.0.2), and therefore including an updated version
+   of commons-logging in WEB-INF/lib or similar will have no effect.
+   See the JBoss classloading documentation for more details.
+   </p>
+   </subsection>
+   <subsection name="Other Containers">
+   <p>
+   As more information becomes available on this topic, it may be added
+   to the commons-logging wiki site.
+   </p>
+   </subsection>
+  </section>
+ </body>
+</document>
diff --git a/src/test/java/org/apache/commons/logging/AbstractLogTest.java b/src/test/java/org/apache/commons/logging/AbstractLogTest.java
new file mode 100644
index 0000000..1a72164
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/AbstractLogTest.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.logging;
+
+import junit.framework.TestCase;
+
+
+/**
+  * Generic tests that can be applied to any log adapter by
+  * subclassing this class and defining method getLogObject
+  * appropriately.
+  *
+  * @author Sean C. Sullivan
+  * @version $Revision: 1432587 $
+  */
+public abstract class AbstractLogTest extends TestCase {
+
+    public abstract Log getLogObject();
+
+    public void testLoggingWithNullParameters()
+    {
+        Log log = this.getLogObject();
+
+        assertNotNull(log);
+
+
+        log.debug(null);
+
+        log.debug(null, null);
+
+        log.debug(log.getClass().getName() + ": debug statement");
+
+        log.debug(log.getClass().getName() + ": debug statement w/ null exception", new RuntimeException());
+
+
+        log.error(null);
+
+        log.error(null, null);
+
+        log.error(log.getClass().getName() + ": error statement");
+
+        log.error(log.getClass().getName() + ": error statement w/ null exception", new RuntimeException());
+
+
+        log.fatal(null);
+
+        log.fatal(null, null);
+
+        log.fatal(log.getClass().getName() + ": fatal statement");
+
+        log.fatal(log.getClass().getName() + ": fatal statement w/ null exception", new RuntimeException());
+
+
+        log.info(null);
+
+        log.info(null, null);
+
+        log.info(log.getClass().getName() + ": info statement");
+
+        log.info(log.getClass().getName() + ": info statement w/ null exception", new RuntimeException());
+
+
+        log.trace(null);
+
+        log.trace(null, null);
+
+        log.trace(log.getClass().getName() + ": trace statement");
+
+        log.trace(log.getClass().getName() + ": trace statement w/ null exception", new RuntimeException());
+
+
+        log.warn(null);
+
+        log.warn(null, null);
+
+        log.warn(log.getClass().getName() + ": warn statement");
+
+        log.warn(log.getClass().getName() + ": warn statement w/ null exception", new RuntimeException());
+    }
+}
diff --git a/src/test/java/org/apache/commons/logging/AltHashtable.java b/src/test/java/org/apache/commons/logging/AltHashtable.java
new file mode 100644
index 0000000..231cda7
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/AltHashtable.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging;
+
+import java.util.Hashtable;
+
+public class AltHashtable extends Hashtable {
+
+    /**
+     * Generated serial version ID.
+     */
+    private static final long serialVersionUID = 8927996458633688095L;
+    
+    public static Object lastKey;
+    public static Object lastValue;
+
+    public Object put(Object key, Object value) {
+        lastKey = key;
+        lastValue = value;
+        return super.put(key, value);
+    }
+}
diff --git a/src/test/java/org/apache/commons/logging/AltHashtableTestCase.java b/src/test/java/org/apache/commons/logging/AltHashtableTestCase.java
new file mode 100644
index 0000000..8c8e01c
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/AltHashtableTestCase.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+/**
+ * Test the ability to force the LogFactory class to use some
+ * arbitrary Hashtable implementation to store its mapping from
+ * context-classloader -> LogFactory object.
+ */
+public class AltHashtableTestCase extends TestCase {
+
+    public static Test suite() throws Exception {
+        Class thisClass = AltHashtableTestCase.class;
+        ClassLoader thisClassLoader = thisClass.getClassLoader();
+
+        PathableClassLoader loader = new PathableClassLoader(null);
+        loader.useExplicitLoader("junit.", thisClassLoader);
+        loader.addLogicalLib("testclasses");
+        loader.addLogicalLib("commons-logging");
+
+        Class testClass = loader.loadClass(thisClass.getName());
+        return new PathableTestSuite(testClass, loader);
+    }
+
+    /**
+     * Set up before each test.
+     * <p>
+     * This method ensures that the appropriate system property is defined
+     * to force the LogFactory class to use the AltHashtable class as its
+     * Hashtable implementation for storing factories in.
+     * <p>
+     * This does make the assumption that whatever JVM we are running in
+     * doesn't initialise classes until they are actually referenced (ie the
+     * LogFactory class hasn't been initialised before this method is called).
+     * This is true of all JVMs I know of; and if it isn't then this test will
+     * fail and someone will tell us.
+     */
+    public void setUp() {
+        System.setProperty(
+                "org.apache.commons.logging.LogFactory.HashtableImpl",
+                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 initialised. 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 initialised, 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
+     * hashmap. In particular, this checks that this process works ok when
+     * a system property has been used to specify an alternative Hashtable
+     * implementation for LogFactory to use.
+     */
+    public void testPutCalled() throws Exception {
+        AltHashtable.lastKey = null;
+        AltHashtable.lastValue = null;
+
+        LogFactory.getLog(AltHashtableTestCase.class);
+        ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
+        assertEquals(contextLoader, AltHashtable.lastKey);
+        assertNotNull(AltHashtable.lastValue);
+    }
+}
diff --git a/src/test/java/org/apache/commons/logging/BadHashtablePropertyTestCase.java b/src/test/java/org/apache/commons/logging/BadHashtablePropertyTestCase.java
new file mode 100644
index 0000000..eda6e3f
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/BadHashtablePropertyTestCase.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging;
+
+import junit.framework.TestCase;
+import java.util.Hashtable;
+
+/**
+ * Tests behaviour when the property is misconfigured.
+ */
+public class BadHashtablePropertyTestCase extends TestCase {
+
+    public void testType()  {
+        assertTrue(LogFactory.factories instanceof Hashtable);
+    }
+
+    public void testPutCalled() throws Exception {
+        LogFactory.getLog(BadHashtablePropertyTestCase.class);
+    }
+}
diff --git a/src/test/java/org/apache/commons/logging/BasicOperationsTestCase.java b/src/test/java/org/apache/commons/logging/BasicOperationsTestCase.java
new file mode 100644
index 0000000..dfadab2
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/BasicOperationsTestCase.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.logging;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests the basic logging operations to ensure that they all function
+ * without exception failure. In other words, that they do no fail by
+ * throwing exceptions.
+ * This is the minimum requirement for any well behaved logger
+ * and so this test should be run for each kind.
+ */
+public class BasicOperationsTestCase extends TestCase
+{
+    public void testIsEnabledClassLog()
+    {
+        Log log = LogFactory.getLog(BasicOperationsTestCase.class);
+        executeIsEnabledTest(log);
+    }
+
+    public void testIsEnabledNamedLog()
+    {
+        Log log = LogFactory.getLog(BasicOperationsTestCase.class.getName());
+        executeIsEnabledTest(log);
+    }
+
+    public void executeIsEnabledTest(Log log)
+    {
+        try
+        {
+            log.isTraceEnabled();
+            log.isDebugEnabled();
+            log.isInfoEnabled();
+            log.isWarnEnabled();
+            log.isErrorEnabled();
+            log.isFatalEnabled();
+        }
+        catch (Throwable t)
+        {
+            t.printStackTrace();
+            fail("Exception thrown: " + t);
+        }
+    }
+
+    public void testMessageWithoutExceptionClassLog()
+    {
+        Log log = LogFactory.getLog(BasicOperationsTestCase.class);
+        executeMessageWithoutExceptionTest(log);
+    }
+
+    public void testMessageWithoutExceptionNamedLog()
+    {
+        Log log = LogFactory.getLog(BasicOperationsTestCase.class.getName());
+        executeMessageWithoutExceptionTest(log);
+    }
+
+    public void executeMessageWithoutExceptionTest(Log log)
+    {
+        try
+        {
+            log.trace("Hello, Mum");
+            log.debug("Hello, Mum");
+            log.info("Hello, Mum");
+            log.warn("Hello, Mum");
+            log.error("Hello, Mum");
+            log.fatal("Hello, Mum");
+        }
+        catch (Throwable t)
+        {
+            t.printStackTrace();
+            fail("Exception thrown: " + t);
+        }
+    }
+
+    public void testMessageWithExceptionClassLog()
+    {
+        Log log = LogFactory.getLog(BasicOperationsTestCase.class);
+        executeMessageWithExceptionTest(log);
+    }
+
+    public void testMessageWithExceptionNamedLog()
+    {
+        Log log = LogFactory.getLog(BasicOperationsTestCase.class.getName());
+        executeMessageWithExceptionTest(log);
+    }
+
+    public void executeMessageWithExceptionTest(Log 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 (Throwable t)
+        {
+            t.printStackTrace();
+            fail("Exception thrown: " + t);
+        }
+    }
+}
diff --git a/src/test/java/org/apache/commons/logging/DummyException.java b/src/test/java/org/apache/commons/logging/DummyException.java
new file mode 100644
index 0000000..2f5fdd4
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/DummyException.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.logging;
+
+/**
+ * Dummy exception that unit tests create instances of when they want to test
+ * logging of an Exception object.
+ */
+public class DummyException extends Exception {
+    private static final long serialVersionUID = 1L;
+    public DummyException() {
+        // super("Dummy Exception for unit testing");
+    }
+}
diff --git a/src/test/java/org/apache/commons/logging/LoadTestCase.java b/src/test/java/org/apache/commons/logging/LoadTestCase.java
new file mode 100644
index 0000000..ac3365b
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/LoadTestCase.java
@@ -0,0 +1,225 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.logging;
+
+import junit.framework.TestCase;
+
+/**
+ * testcase to emulate container and application isolated from container
+ * @author  baliuka
+ * @version $Id: LoadTestCase.java 1432587 2013-01-13 11:11:32Z tn $
+ */
+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.
+     * <p>
+     * When asked to load a class that is in one of the LOG_PCKG packages,
+     * it loads the class itself (child-first). This class doesn't need
+     * to be set up with a classpath, as it simply uses the same classpath
+     * as the classloader that loaded it.
+     */
+    static class AppClassLoader extends ClassLoader{
+        
+        java.util.Map classes = new java.util.HashMap();
+        
+        AppClassLoader(ClassLoader parent){
+            super(parent);
+        }
+        
+        private Class def(String name)throws ClassNotFoundException{
+            
+            Class result = (Class)classes.get(name);
+            if(result != null){
+                return result;
+            }
+            
+            try{
+                
+                ClassLoader cl = this.getClass().getClassLoader();
+                String classFileName = name.replace('.','/') + ".class";
+                java.io.InputStream is = cl.getResourceAsStream(classFileName);
+                java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream();
+                
+                while(is.available() > 0){
+                    out.write(is.read());
+                }
+                
+                byte data [] = out.toByteArray();
+                
+                result = super.defineClass(name, data, 0, data.length );
+                classes.put(name,result);
+                
+                return result;
+                
+            }catch(java.io.IOException ioe){
+                
+                throw new ClassNotFoundException( name + " caused by "
+                + ioe.getMessage() );
+            }
+            
+            
+        }
+        
+        // not very trivial to emulate we must implement "findClass",
+        // but it will delegete to junit class loder first
+        public Class loadClass(String name)throws ClassNotFoundException{
+            
+            //isolates all logging classes, application in the same classloader too.
+            //filters exeptions to simlify handling in test
+            for(int i = 0; i < LOG_PCKG.length; i++ ){
+                if( name.startsWith( LOG_PCKG[i] ) &&
+                name.indexOf("Exception") == -1   ){
+                    return def(name);
+                }
+            }
+            return super.loadClass(name);
+        }
+        
+    }
+    
+
+    /**
+     * Call the static setAllowFlawedContext method on the specified class
+     * (expected to be a UserClass loaded via a custom classloader), passing
+     * it the specified state parameter.
+     */
+    private void setAllowFlawedContext(Class c, String state) throws Exception {
+        Class[] params = {String.class};
+        java.lang.reflect.Method m = c.getDeclaredMethod("setAllowFlawedContext", params);
+        m.invoke(null, new Object[] {state});
+    }
+
+    /**
+     * Test what happens when we play various classloader tricks like those
+     * that happen in web and j2ee containers.
+     * <p>
+     * Note that this test assumes that commons-logging.jar and log4j.jar
+     * are available via the system classpath.
+     */
+    public void testInContainer()throws Exception{
+        
+        //problem can be in this step (broken app container or missconfiguration)
+        //1.  Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader());
+        //2.  Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
+        // we expect this :
+        // 1. Thread.currentThread().setContextClassLoader(appLoader);
+        // 2. Thread.currentThread().setContextClassLoader(null);
+        
+        // Context classloader is same as class calling into log
+        Class cls = reload();
+        Thread.currentThread().setContextClassLoader(cls.getClassLoader());
+        execute(cls);
+        
+        // Context classloader is the "bootclassloader". This is technically
+        // bad, but LogFactoryImpl.ALLOW_FLAWED_CONTEXT defaults to true so
+        // this test should pass.
+        cls = reload();
+        Thread.currentThread().setContextClassLoader(null);
+        execute(cls);
+        
+        // Context classloader is the "bootclassloader". This is same as above
+        // except that ALLOW_FLAWED_CONTEXT is set to false; an error should
+        // now be reported.
+        cls = reload();
+        Thread.currentThread().setContextClassLoader(null);
+        try {
+            setAllowFlawedContext(cls, "false");
+            execute(cls);
+            fail("Logging config succeeded when context classloader was null!");
+        } catch(LogConfigurationException ex) {
+            // expected; the boot classloader doesn't *have* JCL available
+        }
+        
+        // Context classloader is the system classloader.
+        //
+        // This is expected to cause problems, as LogFactoryImpl will attempt
+        // to use the system classloader to load the Log4JLogger class, which
+        // will then be unable to cast that object to the Log interface loaded
+        // via the child classloader. However as ALLOW_FLAWED_CONTEXT defaults
+        // to true this test should pass.
+        cls = reload();
+        Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader());
+        execute(cls);
+        
+        // Context classloader is the system classloader. This is the same
+        // as above except that ALLOW_FLAWED_CONTEXT is set to false; an error 
+        // should now be reported.
+        cls = reload();
+        Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader());
+        try {
+            setAllowFlawedContext(cls, "false");
+            execute(cls);
+            fail("Error: somehow downcast a Logger loaded via system classloader"
+                    + " to the Log interface loaded via a custom classloader");
+        } catch(LogConfigurationException ex) {
+            // 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;
+        
+        AppClassLoader appLoader = new AppClassLoader( 
+                this.getClass().getClassLoader());
+        try{
+            
+            testObjCls = appLoader.loadClass(UserClass.class.getName());
+            
+        }catch(ClassNotFoundException cnfe){
+            throw cnfe;
+        }catch(Throwable t){
+            t.printStackTrace();
+            fail("AppClassLoader failed ");
+        }
+        
+        assertTrue( "app isolated" ,testObjCls.getClassLoader() == appLoader );
+        
+        
+        return testObjCls;
+        
+        
+    }
+    
+    
+    private void execute(Class cls)throws Exception{
+            
+            cls.newInstance();
+        
+    }
+    
+    public void setUp() {
+        // save state before test starts so we can restore it when test ends
+        origContextClassLoader = Thread.currentThread().getContextClassLoader();
+    }
+    
+    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/LogTestCase.java b/src/test/java/org/apache/commons/logging/LogTestCase.java
new file mode 100644
index 0000000..0ac2a6f
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/LogTestCase.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.logging;
+
+
+public class LogTestCase extends AbstractLogTest
+{
+
+    public Log getLogObject()
+    {
+        /**
+         * Pickup whatever is found/configured!
+         */
+        return LogFactory.getLog(this.getClass().getName());
+    }
+
+}
diff --git a/src/test/java/org/apache/commons/logging/NullClassLoaderTestCase.java b/src/test/java/org/apache/commons/logging/NullClassLoaderTestCase.java
new file mode 100644
index 0000000..0e84c1c
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/NullClassLoaderTestCase.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.logging;
+
+import junit.framework.TestCase;
+
+/**
+ * Test cases for situations where getClassLoader or getContextClassLoader
+ * return null. This can happen when using JDK 1.1. It can also happen when
+ * JCL is deployed via the bootclassloader - something that could be done when
+ * using java in embedded systems.
+ */
+public class NullClassLoaderTestCase extends TestCase {
+
+    //---------------------- unit tests ---------------------------------    
+    
+    /**
+     * This tests that when getContextClassLoader returns null, the
+     * LogFactory.getLog(name) method still correctly returns the same
+     * log object when called multiple times with the same name.
+     */
+    public void testSameLogObject() throws Exception {
+        // unfortunately, there just isn't any way to emulate JCL being
+        // accessable via the null classloader in "standard" systems, so
+        // we can't include this test in our standard unit tests.
+    }
+}
diff --git a/src/test/java/org/apache/commons/logging/PathableClassLoader.java b/src/test/java/org/apache/commons/logging/PathableClassLoader.java
new file mode 100644
index 0000000..830f652
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/PathableClassLoader.java
@@ -0,0 +1,436 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.logging;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * A ClassLoader which sees only specified classes, and which can be
+ * set to do parent-first or child-first path lookup.
+ * <p>
+ * Note that this classloader is not "industrial strength"; users
+ * looking for such a class may wish to look at the Tomcat sourcecode
+ * instead. In particular, this class may not be threadsafe.
+ * <p>
+ * Note that the ClassLoader.getResources method isn't overloaded here.
+ * It would be nice to ensure that when child-first lookup is set the
+ * resources from the child are returned earlier in the list than the
+ * resources from the parent. However overriding this method isn't possible
+ * as the java 1.4 version of ClassLoader declares this method final
+ * (though the java 1.5 version has removed the final qualifier). As the
+ * ClassLoader javadoc doesn't specify the order in which resources
+ * are returned, it's valid to return the resources in any order (just
+ * untidy) so the inherited implementation is technically ok.
+ */
+
+public class PathableClassLoader extends URLClassLoader {
+    
+    private static final URL[] NO_URLS = new URL[0];
+    
+    /**
+     * A map of package-prefix to ClassLoader. Any class which is in
+     * this map is looked up via the specified classloader instead of
+     * the classpath associated with this classloader or its parents.
+     * <p>
+     * This is necessary in order for the rest of the world to communicate
+     * with classes loaded via a custom classloader. As an example, junit
+     * testcases which are loaded via a custom classloader needs to see
+     * the same junit classes as the code invoking the testcase, otherwise
+     * they can't pass result objects back. 
+     * <p>
+     * Normally, only a classloader created with a null parent needs to
+     * have any lookasides defined.
+     */
+    private HashMap lookasides = null;
+
+    /**
+     * See setParentFirst.
+     */
+    private boolean parentFirst = true;
+
+    /**
+     * Constructor.
+     * <p>
+     * Often, null is passed as the parent, ie the parent of the new
+     * instance is the bootloader. This ensures that the classpath is
+     * totally clean; nothing but the standard java library will be
+     * present.
+     * <p>
+     * When using a null parent classloader with a junit testcase, it *is*
+     * necessary for the junit library to also be visible. In this case, it
+     * is recommended that the following code be used:
+     * <pre>
+     * pathableLoader.useExplicitLoader(
+     *   "junit.",
+     *   junit.framework.Test.class.getClassLoader());
+     * </pre>
+     * Note that this works regardless of whether junit is on the system
+     * classpath, or whether it has been loaded by some test framework that
+     * creates its own classloader to run unit tests in (eg maven2's
+     * Surefire plugin).
+     */
+    public PathableClassLoader(ClassLoader parent) {
+        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.
+     */
+    public void addURL(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.
+     * <p> 
+     * 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.
+     * <p>
+     * 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.
+     * <p>
+     * This value defaults to true.
+     */
+    public void setParentFirst(boolean state) {
+        parentFirst = state;
+    }
+
+    /**
+     * For classes with the specified prefix, get them from the system
+     * classpath <i>which is active at the point this method is called</i>.
+     * <p>
+     * This method is just a shortcut for
+     * <pre>
+     * useExplicitLoader(prefix, ClassLoader.getSystemClassLoader());
+     * </pre>
+     * <p>
+     * Of course, this assumes that the classes of interest are already
+     * in the classpath of the system classloader.
+     */
+    public void useSystemLoader(String prefix) {
+        useExplicitLoader(prefix, ClassLoader.getSystemClassLoader());
+        
+    }
+
+    /**
+     * Specify a classloader to use for specific java packages.
+     * <p>
+     * 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).
+     * <p>
+     * The differences between using this method, and using
+     * addLogicalLib are:
+     * <ul>
+     * <li>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.
+     * <li>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.
+     * </ul>
+     */
+    public void useExplicitLoader(String prefix, ClassLoader loader) {
+        if (lookasides == null) {
+            lookasides = new HashMap();
+        }
+        lookasides.put(prefix, loader);
+    }
+
+    /**
+     * Specify a collection of logical libraries. See addLogicalLib.
+     */
+    public void addLogicalLib(String[] logicalLibs) {
+        for(int i=0; i<logicalLibs.length; ++i) {
+            addLogicalLib(logicalLibs[i]);
+        }
+    }
+
+    /**
+     * Specify a logical library to be included in the classpath used to
+     * locate classes. 
+     * <p>
+     * The specified lib name is used as a key into the system properties;
+     * there is expected to be a system property defined with that name
+     * whose value is a url that indicates where that logical library can
+     * be found. Typically this is the name of a jar file, or a directory
+     * containing class files.
+     * <p>
+     * If there is no system property, but the classloader that loaded
+     * this class is a URLClassLoader then the set of URLs that the
+     * classloader uses for its classpath is scanned; any jar in the
+     * URL set whose name starts with the specified string is added to
+     * the classpath managed by this instance. 
+     * <p>
+     * Using logical library names allows the calling code to specify its
+     * desired classpath without knowing the exact location of the necessary
+     * classes. 
+     */
+    public void addLogicalLib(String logicalLib) {
+        // first, check the system properties
+        String filename = System.getProperty(logicalLib);
+        if (filename != null) {
+            try {
+                URL libUrl = new File(filename).toURL();
+                addURL(libUrl);
+                return;
+            } catch(java.net.MalformedURLException e) {
+                throw new UnknownError(
+                    "Invalid file [" + filename + "] for logical lib [" + logicalLib + "]");
+            }
+        }
+
+        // now check the classpath for a similar-named lib
+        URL libUrl = libFromClasspath(logicalLib);
+        if (libUrl != null) {
+            addURL(libUrl);
+            return;
+        }
+
+        // lib not found
+        throw new UnknownError(
+            "Logical lib [" + logicalLib + "] is not defined"
+            + " as a System property.");
+    }
+
+    /**
+     * If the classloader that loaded this class has this logical lib in its
+     * path, then return the matching URL otherwise return null.
+     * <p>
+     * This only works when the classloader loading this class is an instance
+     * of URLClassLoader and thus has a getURLs method that returns the classpath
+     * it uses when loading classes. However in practice, the vast majority of the
+     * time this type is the classloader used.
+     * <p>
+     * The classpath of the classloader for this instance is scanned, and any
+     * jarfile in the path whose name starts with the logicalLib string is
+     * considered a match. For example, passing "foo" will match a url
+     * of <code>file:///some/where/foo-2.7.jar</code>.
+     * <p>
+     * When multiple classpath entries match the specified logicalLib string,
+     * the one with the shortest filename component is returned. This means that
+     * if "foo-1.1.jar" and "foobar-1.1.jar" are in the path, then a logicalLib
+     * name of "foo" will match the first entry above.
+     */
+    private URL libFromClasspath(String logicalLib) {
+        ClassLoader cl = this.getClass().getClassLoader();
+        if (cl instanceof URLClassLoader == false) {
+            return null;
+        }
+        
+        URLClassLoader ucl = (URLClassLoader) cl;
+        URL[] path = ucl.getURLs();
+        URL shortestMatch = null;
+        int shortestMatchLen = Integer.MAX_VALUE;
+        for(int i=0; i<path.length; ++i) {
+            URL u = path[i];
+            
+            // extract the filename bit on the end of the url
+            String filename = u.toString();
+            if (!filename.endsWith(".jar")) {
+                // not a jarfile, ignore it
+                continue;
+            }
+
+            int lastSlash = filename.lastIndexOf('/');
+            if (lastSlash >= 0) {
+                filename = filename.substring(lastSlash+1);
+            }
+            
+            if (filename.startsWith(logicalLib)) {
+                // ok, this is a candidate
+                if (filename.length() < shortestMatchLen) {
+                    shortestMatch = u;
+                    shortestMatchLen = filename.length();
+                }
+            }
+        }
+        
+        return shortestMatch;
+    }
+
+    /**
+     * Override ClassLoader method.
+     * <p>
+     * For each explicitly mapped package prefix, if the name matches the 
+     * prefix associated with that entry then attempt to load the class via 
+     * that entries' classloader.
+     */
+    protected Class loadClass(String name, boolean resolve) 
+    throws ClassNotFoundException {
+        // just for performance, check java and javax
+        if (name.startsWith("java.") || name.startsWith("javax.")) {
+            return super.loadClass(name, resolve);
+        }
+
+        if (lookasides != null) {
+            for(Iterator i = lookasides.entrySet().iterator(); i.hasNext(); ) {
+                Map.Entry entry = (Map.Entry) i.next();
+                String prefix = (String) entry.getKey();
+                if (name.startsWith(prefix) == true) {
+                    ClassLoader loader = (ClassLoader) entry.getValue();
+                    Class clazz = Class.forName(name, resolve, loader);
+                    return clazz;
+                }
+            }
+        }
+        
+        if (parentFirst) {
+            return super.loadClass(name, resolve);
+        } else {
+            // Implement child-first. 
+            //
+            // It appears that the findClass method doesn't check whether the
+            // class has already been loaded. This seems odd to me, but without
+            // first checking via findLoadedClass we can get java.lang.LinkageError
+            // with message "duplicate class definition" which isn't good.
+            
+            try {
+                Class clazz = findLoadedClass(name);
+                if (clazz == null) {
+                    clazz = super.findClass(name);
+                }
+                if (resolve) {
+                    resolveClass(clazz);
+                }
+                return clazz;
+            } catch(ClassNotFoundException e) {
+                return super.loadClass(name, resolve);
+            }
+        }
+    }
+    
+    /**
+     * 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.
+     */
+    public URL getResource(String name) {
+        if (parentFirst) {
+            return super.getResource(name);
+        } else {
+            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.
+     * <p>
+     * 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(String name) throws IOException {
+        if (parentFirst) {
+            return super.getResources(name);
+        } else {
+            Enumeration localUrls = super.findResources(name);
+            
+            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;
+            }
+            Enumeration parentUrls = parent.getResources(name);
+
+            ArrayList localItems = toList(localUrls);
+            ArrayList parentItems = toList(parentUrls);
+            localItems.addAll(parentItems);
+            return Collections.enumeration(localItems);
+        }
+    }
+    
+    /**
+     * 
+     * Clean implementation of list function of 
+     * {@link java.utils.Collection} added in JDK 1.4 
+     * @param en <code>Enumeration</code>, possibly null
+     * @return <code>ArrayList</code> containing the enumerated
+     * elements in the enumerated order, not null
+     */
+    private ArrayList toList(Enumeration en) {
+        ArrayList results = new ArrayList();
+        if (en != null) {
+            while (en.hasMoreElements()){
+                Object element = en.nextElement();
+                results.add(element);
+            }
+        }
+        return results;
+    }
+    
+    /**
+     * 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.
+     */
+    public InputStream getResourceAsStream(String name) {
+        if (parentFirst) {
+            return super.getResourceAsStream(name);
+        } else {
+            URL local = super.findResource(name);
+            if (local != null) {
+                try {
+                    return local.openStream();
+                } catch(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);
+        }
+    }
+}
diff --git a/src/test/java/org/apache/commons/logging/PathableTestSuite.java b/src/test/java/org/apache/commons/logging/PathableTestSuite.java
new file mode 100644
index 0000000..1a2ca81
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/PathableTestSuite.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.logging;
+
+import java.util.Properties;
+
+import junit.framework.Test;
+import junit.framework.TestResult;
+import junit.framework.TestSuite;
+
+/**
+ * Custom TestSuite class that can be used to control the context classloader
+ * in operation when a test runs.
+ * <p>
+ * For tests that need to control exactly what the classloader hierarchy is
+ * like when the test is run, something like the following is recommended:
+ * <pre>
+ * class SomeTestCase extends TestCase {
+ *  public static Test suite() throws Exception {
+ *   PathableClassLoader parent = new PathableClassLoader(null);
+ *   parent.useSystemLoader("junit.");
+ * 
+ *   PathableClassLoader child = new PathableClassLoader(parent);
+ *   child.addLogicalLib("testclasses");
+ *   child.addLogicalLib("log4j12");
+ *   child.addLogicalLib("commons-logging");
+ * 
+ *   Class testClass = child.loadClass(SomeTestCase.class.getName());
+ *   ClassLoader contextClassLoader = child;
+ * 
+ *   PathableTestSuite suite = new PathableTestSuite(testClass, child);
+ *   return suite;
+ *  }
+ * 
+ *  // test methods go here
+ * }
+ * </pre>
+ * Note that if the suite method throws an exception then this will be handled
+ * reasonable gracefully by junit; it will report that the suite method for 
+ * a test case failed with exception yyy.
+ * <p>
+ * The use of PathableClassLoader is not required to use this class, but it
+ * is expected that using the two classes together is common practice.
+ * <p>
+ * This class will run each test methods within the specified TestCase using
+ * the specified context classloader and system classloader. If different
+ * tests within the same class require different context classloaders,
+ * then the context classloader passed to the constructor should be the 
+ * "lowest" one available, and tests that need the context set to some parent
+ * of this "lowest" classloader can call
+ * <pre>
+ *  // NB: pseudo-code only
+ *  setContextClassLoader(getContextClassLoader().getParent());
+ * </pre>
+ * This class ensures that any context classloader changes applied by a test
+ * is undone after the test is run, so tests don't need to worry about
+ * restoring the context classloader on exit. This class also ensures that
+ * the system properties are restored to their original settings after each
+ * test, so tests that manipulate those don't need to worry about resetting them. 
+ * <p>
+ * This class does not provide facilities for manipulating system properties;
+ * tests that need specific system properties can simply set them in the
+ * fixture or at the start of a test method.
+ * <p>
+ * <b>Important!</b> When the test case is run, "this.getClass()" refers of
+ * course to the Class object passed to the constructor of this class - which 
+ * is different from the class whose suite() method was executed to determine
+ * the classpath. This means that the suite method cannot communicate with
+ * the test cases simply by setting static variables (for example to make the
+ * custom classloaders available to the test methods or setUp/tearDown fixtures).
+ * If this is really necessary then it is possible to use reflection to invoke
+ * static methods on the class object passed to the constructor of this class.
+ * <p>
+ * <h2>Limitations</h2>
+ * <p>
+ * This class cannot control the system classloader (ie what method 
+ * ClassLoader.getSystemClassLoader returns) because Java provides no
+ * mechanism for setting the system classloader. In this case, the only
+ * option is to invoke the unit test in a separate JVM with the appropriate
+ * settings.
+ * <p>
+ * The effect of using this approach in a system that uses junit's
+ * "reloading classloader" behaviour is unknown. This junit feature is
+ * intended for junit GUI apps where a test may be run multiple times
+ * within the same JVM - and in particular, when the .class file may
+ * be modified between runs of the test. How junit achieves this is
+ * actually rather weird (the whole junit code is rather weird in fact)
+ * and it is not clear whether this approach will work as expected in
+ * such situations.
+ */
+public class PathableTestSuite extends TestSuite {
+
+    /**
+     * The classloader that should be set as the context classloader
+     * before each test in the suite is run.
+     */
+    private final ClassLoader contextLoader;
+
+    /**
+     * Constructor.
+     * 
+     * @param testClass is the TestCase that is to be run, as loaded by
+     * the appropriate ClassLoader.
+     * 
+     * @param contextClassLoader is the loader that should be returned by
+     * calls to Thread.currentThread.getContextClassLoader from test methods
+     * (or any method called by test methods).
+     */
+    public PathableTestSuite(Class testClass, ClassLoader contextClassLoader) {
+        super(testClass);
+        contextLoader = contextClassLoader;
+    }
+
+    /**
+     * This method is invoked once for each Test in the current TestSuite.
+     * Note that a Test may itself be a TestSuite object (ie a collection
+     * of tests).
+     * <p>
+     * The context classloader and system properties are saved before each
+     * test, and restored after the test completes to better isolate tests.
+     */
+    public void runTest(Test test, TestResult result) {
+        ClassLoader origContext = Thread.currentThread().getContextClassLoader();
+        Properties oldSysProps = (Properties) System.getProperties().clone();
+        try {
+            Thread.currentThread().setContextClassLoader(contextLoader);
+            test.run(result);
+        } finally {
+            System.setProperties(oldSysProps);
+            Thread.currentThread().setContextClassLoader(origContext);
+        }
+    }
+}
diff --git a/src/test/java/org/apache/commons/logging/SimpleLogTestCase.java b/src/test/java/org/apache/commons/logging/SimpleLogTestCase.java
new file mode 100644
index 0000000..cc2fceb
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/SimpleLogTestCase.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.logging;
+
+import org.apache.commons.logging.impl.SimpleLog;
+
+public class SimpleLogTestCase extends AbstractLogTest
+{
+    public Log getLogObject()
+    {
+        return new SimpleLog(this.getClass().getName());
+    }
+}
diff --git a/src/test/java/org/apache/commons/logging/UserClass.java b/src/test/java/org/apache/commons/logging/UserClass.java
new file mode 100644
index 0000000..9013133
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/UserClass.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.logging;
+
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.logging.impl.LogFactoryImpl;
+
+public class UserClass {
+
+    /**
+     * Set the ALLOW_FLAWED_CONTEXT feature on the LogFactoryImpl object
+     * associated with this class' classloader.
+     * <p>
+     * Don't forget to set the context classloader to whatever it will be
+     * when an instance of this class is actually created <i>before</i> calling
+     * this method!
+     */
+    public static void setAllowFlawedContext(String state) {
+        LogFactory f = LogFactory.getFactory();
+        f.setAttribute(LogFactoryImpl.ALLOW_FLAWED_CONTEXT_PROPERTY, state);
+    }
+
+    public UserClass() {
+        Log log = LogFactory.getLog(LoadTestCase.class);
+      }
+
+}
diff --git a/src/test/java/org/apache/commons/logging/avalon/AvalonLoggerTestCase.java b/src/test/java/org/apache/commons/logging/avalon/AvalonLoggerTestCase.java
new file mode 100644
index 0000000..182624d
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/avalon/AvalonLoggerTestCase.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.logging.avalon;
+
+import org.apache.avalon.framework.logger.NullLogger;
+import org.apache.commons.logging.impl.AvalonLogger;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.AbstractLogTest;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * @author <a href="mailto:neeme at apache.org">Neeme Praks</a>
+ * @version $Revision: 1432587 $ $Date: 2013-01-13 12:11:32 +0100 (Sun, 13 Jan 2013) $
+ */
+public class AvalonLoggerTestCase extends AbstractLogTest {
+
+    public static Test suite() {
+        TestSuite suite = new TestSuite();
+        suite.addTestSuite(AvalonLoggerTestCase.class);
+        return suite;
+    }
+
+    public Log getLogObject() {
+        // Output does not seem to be used, so don't display it.
+        Log log = new AvalonLogger(new NullLogger());
+        return log;
+    }
+}
diff --git a/src/test/java/org/apache/commons/logging/config/FirstPriorityConfigTestCase.java b/src/test/java/org/apache/commons/logging/config/FirstPriorityConfigTestCase.java
new file mode 100644
index 0000000..4725dcd
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/config/FirstPriorityConfigTestCase.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.config;
+
+
+import java.net.URL;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.logging.PathableClassLoader;
+import org.apache.commons.logging.PathableTestSuite;
+
+
+/**
+ * Tests that verify that the process of configuring logging on startup
+ * works correctly by selecting the file with the highest priority.
+ * <p>
+ * This test sets up a classpath where:
+ * <ul>
+ * <li> first file found has priority=20
+ * <li> second file found has priority=10
+ * </ul>
+ * The result should be that the first file is used.
+ */
+public class FirstPriorityConfigTestCase extends TestCase {
+
+    // ------------------------------------------- JUnit Infrastructure Methods
+
+
+    /**
+     * Return the tests included in this test suite.
+     */
+    public static Test suite() throws Exception {
+        Class thisClass = FirstPriorityConfigTestCase.class;
+
+        // Determine the URL to this .class file, so that we can then
+        // append the priority dirs to it. For tidiness, load this
+        // class through a dummy loader though this is not absolutely
+        // necessary...
+        PathableClassLoader dummy = new PathableClassLoader(null);
+        dummy.useExplicitLoader("junit.", Test.class.getClassLoader());
+        dummy.addLogicalLib("testclasses");
+        dummy.addLogicalLib("commons-logging");
+
+        String thisClassPath = thisClass.getName().replace('.', '/') + ".class";
+        URL baseUrl = dummy.findResource(thisClassPath);
+
+        // Now set up the desired classloader hierarchy. We'll put JCL
+        // in the container path, the testcase in a webapp path, and
+        // both config files into the webapp path too.
+        PathableClassLoader containerLoader = new PathableClassLoader(null);
+        containerLoader.useExplicitLoader("junit.", Test.class.getClassLoader());
+        containerLoader.addLogicalLib("commons-logging");
+
+        PathableClassLoader webappLoader = new PathableClassLoader(containerLoader);
+        webappLoader.addLogicalLib("testclasses");
+
+        URL pri20URL = new URL(baseUrl, "priority20/");
+        webappLoader.addURL(pri20URL);
+
+        URL pri10URL = new URL(baseUrl, "priority10/");
+        webappLoader.addURL(pri10URL);
+
+        // load the test class via webapp loader, and use the webapp loader
+        // as the tccl loader too.
+        Class testClass = webappLoader.loadClass(thisClass.getName());
+        return new PathableTestSuite(testClass, webappLoader);
+    }
+
+    /**
+     * Set up instance variables required by this test case.
+     */
+    public void setUp() throws Exception {
+        LogFactory.releaseAll();
+    }
+
+    /**
+     * Tear down instance variables required by this test case.
+     */
+    public void tearDown() {
+        LogFactory.releaseAll();
+    }
+
+    // ----------------------------------------------------------- Test Methods
+
+    /**
+     * Verify that the config file being used is the one containing
+     * the desired configId value.
+     */
+    public void testPriority() throws Exception {
+        LogFactory instance = LogFactory.getFactory();
+
+        ClassLoader thisClassLoader = this.getClass().getClassLoader();
+        ClassLoader lfClassLoader = instance.getClass().getClassLoader();
+        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+
+        // context classloader should be thisClassLoader
+        assertEquals(thisClassLoader, contextClassLoader);
+
+        // lfClassLoader should be parent of this classloader
+        assertEquals(lfClassLoader, thisClassLoader.getParent());
+        assertEquals(PathableClassLoader.class.getName(),
+                lfClassLoader.getClass().getName());
+
+        String id = (String) instance.getAttribute("configId");
+        assertEquals("Correct config file loaded", "priority20", id );
+    }
+}
diff --git a/src/test/java/org/apache/commons/logging/config/PriorityConfigTestCase.java b/src/test/java/org/apache/commons/logging/config/PriorityConfigTestCase.java
new file mode 100644
index 0000000..831e255
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/config/PriorityConfigTestCase.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.config;
+
+
+import java.net.URL;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.logging.PathableClassLoader;
+import org.apache.commons.logging.PathableTestSuite;
+
+
+/**
+ * Tests that verify that the process of configuring logging on startup
+ * works correctly by selecting the file with the highest priority.
+ * <p>
+ * This test sets up a classpath where:
+ * <ul>
+ * <li> first file (in parent loader) has priority=10 (parentFirst=true)
+ * <li> second file found has no priority set
+ * <li> third file found has priority=20
+ * <li> fourth file found also has priority=20
+ * </ul>
+ * The result should be that the third file is used.
+ * <p>
+ * Note that parentFirst=true is used in this test because method
+ * <code>PathableClassLoader.getResources</code> always behaves as if
+ * parentFirst=true; see the PathableClassLoader javadoc for details.
+ */
+
+public class PriorityConfigTestCase extends TestCase {
+
+    // ------------------------------------------- JUnit Infrastructure Methods
+
+
+    /**
+     * Return the tests included in this test suite.
+     */
+    public static Test suite() throws Exception {
+        Class thisClass = PriorityConfigTestCase.class;
+
+        // Determine the URL to this .class file, so that we can then
+        // append the priority dirs to it. For tidiness, load this
+        // class through a dummy loader though this is not absolutely
+        // necessary...
+        PathableClassLoader dummy = new PathableClassLoader(null);
+        dummy.useExplicitLoader("junit.", Test.class.getClassLoader());
+        dummy.addLogicalLib("testclasses");
+        dummy.addLogicalLib("commons-logging");
+
+        String thisClassPath = thisClass.getName().replace('.', '/') + ".class";
+        URL baseUrl = dummy.findResource(thisClassPath);
+
+        // Now set up the desired classloader hierarchy. We'll put a config
+        // file of priority=10 in the container path, and ones of both
+        // "no priority" and priority=20 in the webapp path.
+        //
+        // A second properties file with priority=20 is also added,
+        // so we can check that the first one in the classpath is
+        // used.
+        PathableClassLoader containerLoader = new PathableClassLoader(null);
+        containerLoader.useExplicitLoader("junit.", Test.class.getClassLoader());
+        containerLoader.addLogicalLib("commons-logging");
+
+        URL pri10URL = new URL(baseUrl, "priority10/");
+        containerLoader.addURL(pri10URL);
+
+        PathableClassLoader webappLoader = new PathableClassLoader(containerLoader);
+        webappLoader.setParentFirst(true);
+        webappLoader.addLogicalLib("testclasses");
+
+        URL noPriorityURL = new URL(baseUrl, "nopriority/");
+        webappLoader.addURL(noPriorityURL);
+
+        URL pri20URL = new URL(baseUrl, "priority20/");
+        webappLoader.addURL(pri20URL);
+
+        URL pri20aURL = new URL(baseUrl, "priority20a/");
+        webappLoader.addURL(pri20aURL);
+
+        // load the test class via webapp loader, and use the webapp loader
+        // as the tccl loader too.
+        Class testClass = webappLoader.loadClass(thisClass.getName());
+        return new PathableTestSuite(testClass, webappLoader);
+    }
+
+    /**
+     * Set up instance variables required by this test case.
+     */
+    public void setUp() throws Exception {
+        LogFactory.releaseAll();
+    }
+
+    /**
+     * Tear down instance variables required by this test case.
+     */
+    public void tearDown() {
+        LogFactory.releaseAll();
+    }
+
+    // ----------------------------------------------------------- Test Methods
+
+    /**
+     * Verify that the config file being used is the one containing
+     * the desired configId value.
+     */
+    public void testPriority() throws Exception {
+        LogFactory instance = LogFactory.getFactory();
+        String id = (String) instance.getAttribute("configId");
+        assertEquals("Correct config file loaded", "priority20", id );
+    }
+}
diff --git a/src/test/java/org/apache/commons/logging/impl/WeakHashtableTestCase.java b/src/test/java/org/apache/commons/logging/impl/WeakHashtableTestCase.java
new file mode 100644
index 0000000..8a37068
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/impl/WeakHashtableTestCase.java
@@ -0,0 +1,313 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.commons.logging.impl;
+
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+public class WeakHashtableTestCase extends TestCase {
+
+    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(String testName) {
+        super(testName);
+    }
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        weakHashtable = new WeakHashtable();
+
+        keyOne = new Long(1);
+        keyTwo = new Long(2);
+        keyThree = new Long(3);
+        valueOne = new Long(100);
+        valueTwo = new Long(200);
+        valueThree = new Long(300);
+
+        weakHashtable.put(keyOne, valueOne);
+        weakHashtable.put(keyTwo, valueTwo);
+        weakHashtable.put(keyThree, valueThree);
+    }
+
+    /** Tests public boolean contains(Object value) */
+    public void testContains() throws Exception {
+        assertFalse(weakHashtable.contains(new Long(1)));
+        assertFalse(weakHashtable.contains(new Long(2)));
+        assertFalse(weakHashtable.contains(new Long(3)));
+        assertTrue(weakHashtable.contains(new Long(100)));
+        assertTrue(weakHashtable.contains(new Long(200)));
+        assertTrue(weakHashtable.contains(new Long(300)));
+        assertFalse(weakHashtable.contains(new Long(400)));
+    }
+
+    /** Tests public boolean containsKey(Object key) */
+    public void testContainsKey() throws Exception {
+        assertTrue(weakHashtable.containsKey(new Long(1)));
+        assertTrue(weakHashtable.containsKey(new Long(2)));
+        assertTrue(weakHashtable.containsKey(new Long(3)));
+        assertFalse(weakHashtable.containsKey(new Long(100)));
+        assertFalse(weakHashtable.containsKey(new Long(200)));
+        assertFalse(weakHashtable.containsKey(new Long(300)));
+        assertFalse(weakHashtable.containsKey(new Long(400)));
+    }
+
+    /** Tests public boolean containsValue(Object value) */
+    public void testContainsValue() throws Exception {
+        assertFalse(weakHashtable.containsValue(new Long(1)));
+        assertFalse(weakHashtable.containsValue(new Long(2)));
+        assertFalse(weakHashtable.containsValue(new Long(3)));
+        assertTrue(weakHashtable.containsValue(new Long(100)));
+        assertTrue(weakHashtable.containsValue(new Long(200)));
+        assertTrue(weakHashtable.containsValue(new Long(300)));
+        assertFalse(weakHashtable.containsValue(new Long(400)));
+    }
+
+    /** Tests public Enumeration elements() */
+    public void testElements() throws Exception {
+        ArrayList elements = new ArrayList();
+        for (Enumeration e = weakHashtable.elements(); e.hasMoreElements();) {
+            elements.add(e.nextElement());
+        }
+        assertEquals(3, elements.size());
+        assertTrue(elements.contains(valueOne));
+        assertTrue(elements.contains(valueTwo));
+        assertTrue(elements.contains(valueThree));
+    }
+
+    /** Tests public Set entrySet() */
+    public void testEntrySet() throws Exception {
+        Set entrySet = weakHashtable.entrySet();
+        for (Iterator it = entrySet.iterator(); it.hasNext();) {
+            Map.Entry entry = (Map.Entry) it.next();
+            Object key = entry.getKey();
+            if (keyOne.equals(key)) {
+                assertEquals(valueOne, entry.getValue());
+            } else if (keyTwo.equals(key)) {
+                assertEquals(valueTwo, entry.getValue());
+            } else if (keyThree.equals(key)) {
+                assertEquals(valueThree, entry.getValue());
+            } else {
+                fail("Unexpected key");
+            }
+        }
+    }
+
+    /** Tests public Object get(Object key) */
+    public void testGet() throws Exception {
+        assertEquals(valueOne, weakHashtable.get(keyOne));
+        assertEquals(valueTwo, weakHashtable.get(keyTwo));
+        assertEquals(valueThree, weakHashtable.get(keyThree));
+        assertNull(weakHashtable.get(new Long(50)));
+    }
+
+    /** Tests public Enumeration keys() */
+    public void testKeys() throws Exception {
+        ArrayList keys = new ArrayList();
+        for (Enumeration e = weakHashtable.keys(); e.hasMoreElements();) {
+            keys.add(e.nextElement());
+        }
+        assertEquals(3, keys.size());
+        assertTrue(keys.contains(keyOne));
+        assertTrue(keys.contains(keyTwo));
+        assertTrue(keys.contains(keyThree));
+    }
+
+    /** Tests public Set keySet() */
+    public void testKeySet() throws Exception {
+        Set keySet = weakHashtable.keySet();
+        assertEquals(3, keySet.size());
+        assertTrue(keySet.contains(keyOne));
+        assertTrue(keySet.contains(keyTwo));
+        assertTrue(keySet.contains(keyThree));
+    }
+
+    /** Tests public Object put(Object key, Object value) */
+    public void testPut() throws Exception {
+        Long anotherKey = new Long(2004);
+        weakHashtable.put(anotherKey, new Long(1066));
+
+        assertEquals(new Long(1066), weakHashtable.get(anotherKey));
+
+        // Test compliance with the hashtable API re nulls
+        Exception caught = null;
+        try {
+            weakHashtable.put(null, new Object());
+        }
+        catch (Exception e) {
+            caught = e;
+        }
+        assertNotNull("did not throw an exception adding a null key", caught);
+        caught = null;
+        try {
+            weakHashtable.put(new Object(), null);
+        }
+        catch (Exception e) {
+            caught = e;
+        }
+        assertNotNull("did not throw an exception adding a null value", caught);
+    }
+
+    /** Tests public void putAll(Map t) */
+    public void testPutAll() throws Exception {
+        Map newValues = new HashMap();
+        Long newKey = new Long(1066);
+        Long newValue = new Long(1415);
+        newValues.put(newKey, newValue);
+        Long anotherNewKey = new Long(1645);
+        Long anotherNewValue = new Long(1815);
+        newValues.put(anotherNewKey, anotherNewValue);
+        weakHashtable.putAll(newValues);
+
+        assertEquals(5, weakHashtable.size());
+        assertEquals(newValue, weakHashtable.get(newKey));
+        assertEquals(anotherNewValue, weakHashtable.get(anotherNewKey));
+    }
+
+    /** Tests public Object remove(Object key) */
+    public void testRemove() throws Exception {
+        weakHashtable.remove(keyOne);
+        assertEquals(2, weakHashtable.size());
+        assertNull(weakHashtable.get(keyOne));
+    }
+
+    /** Tests public Collection values() */
+    public void testValues() throws Exception {
+        Collection values = weakHashtable.values();
+        assertEquals(3, values.size());
+        assertTrue(values.contains(valueOne));
+        assertTrue(values.contains(valueTwo));
+        assertTrue(values.contains(valueThree));
+    }
+
+    /**
+     * Disabled this test as it makes wrong assumptions wrt the GC.
+     * This test especially fails with:
+     *
+     * Java(TM) SE Runtime Environment (build pxi3260sr12-20121025_01(SR12))
+     * IBM J9 VM (build 2.4, JRE 1.6.0 IBM J9 2.4 Linux x86-32 jvmxi3260sr12-20121024_1
+     */
+    public void xxxIgnoretestRelease() throws Exception {
+        assertNotNull(weakHashtable.get(new Long(1)));
+        ReferenceQueue testQueue = new ReferenceQueue();
+        WeakReference weakKeyOne = new WeakReference(keyOne, testQueue);
+
+        // lose our references
+        keyOne = null;
+        keyTwo = null;
+        keyThree = null;
+        valueOne = null;
+        valueTwo = null;
+        valueThree = null;
+
+        int iterations = 0;
+        int bytz = 2;
+        while(true) {
+            System.gc();
+            if(iterations++ > MAX_GC_ITERATIONS){
+                fail("Max iterations reached before resource released.");
+            }
+
+            if(weakHashtable.get(new Long(1)) == null) {
+                break;
+
+            } else {
+                // create garbage:
+                byte[] b =  new byte[bytz];
+                bytz = bytz * 2;
+            }
+        }
+
+        // some JVMs seem to take a little time to put references on
+        // the reference queue once the reference has been collected
+        // need to think about whether this is enough to justify
+        // stepping through the collection each time...
+        while(testQueue.poll() == null) {}
+
+        // 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(String name) {
+            super(name);
+        }
+
+        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 {
+        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 (int i = 0; i < t.length; i++) {
+                t[i].join(WAIT_FOR_THREAD_COMPLETION);
+                if (t[i].isAlive()) {
+                    break; // at least one thread is stuck
+                }
+            }
+            int active=0;
+            for (int i = 0; i < t.length; i++) {
+                if (t[i].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
new file mode 100644
index 0000000..afa04d6
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/jdk14/CustomConfigAPITestCase.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.jdk14;
+
+import junit.framework.Test;
+
+import org.apache.commons.logging.PathableClassLoader;
+import org.apache.commons.logging.PathableTestSuite;
+
+
+/**
+ * TestCase for Jdk14 logging when the commons-logging-api jar file is in
+ * the parent classpath and commons-logging.jar is in the child.
+ */
+
+public class CustomConfigAPITestCase extends CustomConfigTestCase {
+
+    public CustomConfigAPITestCase(String name) {
+        super(name);
+    }
+
+    /**
+     * Return the tests included in this test suite.
+     */
+    public static Test suite() throws Exception {
+        PathableClassLoader parent = new PathableClassLoader(null);
+        parent.useExplicitLoader("junit.", Test.class.getClassLoader());
+
+        // the TestHandler class must be accessable from the System classloader
+        // in order for java.util.logging.LogManager.readConfiguration to
+        // be able to instantiate it. And this test case must see the same
+        // class in order to be able to access its data. Yes this is ugly
+        // but the whole jdk14 API is a ******* mess anyway.
+        ClassLoader scl = ClassLoader.getSystemClassLoader();
+        loadTestHandler(HANDLER_NAME, scl);
+        parent.useExplicitLoader(HANDLER_NAME, scl);
+        parent.addLogicalLib("commons-logging-api");
+
+        PathableClassLoader child = new PathableClassLoader(parent);
+        child.addLogicalLib("testclasses");
+        child.addLogicalLib("commons-logging");
+
+        Class testClass = child.loadClass(CustomConfigAPITestCase.class.getName());
+        return new PathableTestSuite(testClass, child);
+    }
+}
diff --git a/src/test/java/org/apache/commons/logging/jdk14/CustomConfigFullTestCase.java b/src/test/java/org/apache/commons/logging/jdk14/CustomConfigFullTestCase.java
new file mode 100644
index 0000000..86a6232
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/jdk14/CustomConfigFullTestCase.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.jdk14;
+
+
+import junit.framework.Test;
+
+import org.apache.commons.logging.PathableTestSuite;
+import org.apache.commons.logging.PathableClassLoader;
+
+
+/**
+ * TestCase for Jdk14 logging when the commons-logging jar file is in
+ * the parent classpath.
+ */
+
+public class CustomConfigFullTestCase extends CustomConfigTestCase {
+
+
+    public CustomConfigFullTestCase(String name) {
+        super(name);
+    }
+
+
+    /**
+     * Return the tests included in this test suite.
+     */
+    public static Test suite() throws Exception {
+        PathableClassLoader parent = new PathableClassLoader(null);
+        parent.useExplicitLoader("junit.", Test.class.getClassLoader());
+
+        // the TestHandler class must be accessable from the System classloader
+        // in order for java.util.logging.LogManager.readConfiguration to
+        // be able to instantiate it. And this test case must see the same
+        // class in order to be able to access its data. Yes this is ugly
+        // but the whole jdk14 API is a ******* mess anyway.
+        ClassLoader scl = ClassLoader.getSystemClassLoader();
+        loadTestHandler(HANDLER_NAME, scl);
+        parent.useExplicitLoader(HANDLER_NAME, scl);
+        parent.addLogicalLib("commons-logging");
+
+        PathableClassLoader child = new PathableClassLoader(parent);
+        child.addLogicalLib("testclasses");
+
+        Class testClass = child.loadClass(CustomConfigFullTestCase.class.getName());
+        return new PathableTestSuite(testClass, child);
+    }
+}
diff --git a/src/test/java/org/apache/commons/logging/jdk14/CustomConfigTestCase.java b/src/test/java/org/apache/commons/logging/jdk14/CustomConfigTestCase.java
new file mode 100644
index 0000000..d970d76
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/jdk14/CustomConfigTestCase.java
@@ -0,0 +1,395 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.jdk14;
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import java.util.Iterator;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.LogManager;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+
+import junit.framework.Test;
+
+import org.apache.commons.logging.DummyException;
+import org.apache.commons.logging.PathableClassLoader;
+import org.apache.commons.logging.PathableTestSuite;
+
+
+/**
+ * <p>TestCase for JDK 1.4 logging when running on a JDK 1.4 system with
+ * custom configuration, so that JDK 1.4 should be selected and an appropriate
+ * logger configured per the configuration properties.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1448063 $ $Date: 2013-02-20 11:01:41 +0100 (Wed, 20 Feb 2013) $
+ */
+
+public class CustomConfigTestCase extends DefaultConfigTestCase {
+
+    protected static final String HANDLER_NAME = "org.apache.commons.logging.jdk14.TestHandler";
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * <p>Construct a new instance of this test case.</p>
+     *
+     * @param name Name of the test case
+     */
+    public CustomConfigTestCase(String name) {
+        super(name);
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * <p>The customized <code>Handler</code> we will be using.</p>
+     */
+    protected TestHandler handler = null;
+
+
+    /**
+     * <p>The underlying <code>Handler</code>s we will be using.</p>
+     */
+    protected Handler handlers[] = null;
+
+
+    /**
+     * <p>The underlying <code>Logger</code> we will be using.</p>
+     */
+    protected Logger logger = null;
+
+
+    /**
+     * <p>The underlying <code>LogManager</code> we will be using.</p>
+     */
+    protected LogManager manager = null;
+
+
+    /**
+     * <p>The message levels that should have been logged.</p>
+     */
+    protected Level testLevels[] =
+    { Level.FINE, Level.INFO, Level.WARNING, Level.SEVERE, Level.SEVERE };
+
+
+    /**
+     * <p>The message strings that should have been logged.</p>
+     */
+    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(String name, ClassLoader srcCL) throws Exception {
+        String resName = name.replace('.', '/') + ".class";
+        System.err.println("Trying to load resource [" + resName + "]");
+        InputStream is = srcCL.getResourceAsStream(resName);
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        System.err.println("Reading resource [" + resName + "]");
+        byte[] buf = new byte[1000];
+        for(;;) {
+            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
+     * works for classes for which all dependencies are already loaded in
+     * that classloader.
+     */
+    protected static void loadTestHandler(String className, ClassLoader targetCL) {
+        try {
+            targetCL.loadClass(className);
+            // fail("Class already in target classloader");
+            return;
+        } catch(ClassNotFoundException ex) {
+            // ok, go ahead and load it
+        }
+
+        try {
+            ClassLoader srcCL = CustomConfigAPITestCase.class.getClassLoader();
+            byte[] classData = readClass(className, srcCL);
+
+            Class[] params = new Class[] { String.class, classData.getClass(), Integer.TYPE, Integer.TYPE };
+            Method m = ClassLoader.class.getDeclaredMethod("defineClass", params);
+
+            Object[] args = new Object[4];
+            args[0] = className;
+            args[1] = classData;
+            args[2] = new Integer(0);
+            args[3] = new Integer(classData.length);
+            m.setAccessible(true);
+            m.invoke(targetCL, args);
+        } catch(Exception e) {
+            e.printStackTrace();
+            fail("Unable to load class " + className);
+        }
+    }
+
+    /**
+     * Set up instance variables required by this test case.
+     */
+    public void setUp() throws Exception {
+        setUpManager
+            ("org/apache/commons/logging/jdk14/CustomConfig.properties");
+        setUpLogger(this.getClass().getName());
+        setUpHandlers();
+        setUpFactory();
+        setUpLog(this.getClass().getName());
+    }
+
+
+    /**
+     * Return the tests included in this test suite.
+     */
+    public static Test suite() throws Exception {
+        PathableClassLoader cl = new PathableClassLoader(null);
+        cl.useExplicitLoader("junit.", Test.class.getClassLoader());
+
+        // the TestHandler class must be accessable from the System classloader
+        // in order for java.util.logging.LogManager.readConfiguration to
+        // be able to instantiate it. And this test case must see the same
+        // class in order to be able to access its data. Yes this is ugly
+        // but the whole jdk14 API is a ******* mess anyway.
+        ClassLoader scl = ClassLoader.getSystemClassLoader();
+        loadTestHandler(HANDLER_NAME, scl);
+        cl.useExplicitLoader(HANDLER_NAME, scl);
+        cl.addLogicalLib("commons-logging");
+        cl.addLogicalLib("testclasses");
+
+        Class testClass = cl.loadClass(CustomConfigTestCase.class.getName());
+        return new PathableTestSuite(testClass, cl);
+    }
+
+    /**
+     * Tear down instance variables required by this test case.
+     */
+    public void tearDown() {
+        super.tearDown();
+        handlers = null;
+        logger = null;
+        manager = null;
+    }
+
+
+    // ----------------------------------------------------------- Test Methods
+
+
+    // Test logging message strings with exceptions
+    public void testExceptionMessages() throws Exception {
+
+        logExceptionMessages();
+        checkLogRecords(true);
+
+    }
+
+
+    // Test logging plain message strings
+    public void testPlainMessages() throws Exception {
+
+        logPlainMessages();
+        checkLogRecords(false);
+
+    }
+
+
+    // Test pristine Handlers instances
+    public void testPristineHandlers() {
+
+        assertNotNull(handlers);
+        assertEquals(1, handlers.length);
+        assertTrue(handlers[0] instanceof TestHandler);
+        assertNotNull(handler);
+
+    }
+
+
+    // Test pristine Logger instance
+    public void testPristineLogger() {
+
+        assertNotNull("Logger exists", logger);
+        assertEquals("Logger name", this.getClass().getName(), logger.getName());
+
+        // Assert which logging levels have been enabled
+        assertTrue(logger.isLoggable(Level.SEVERE));
+        assertTrue(logger.isLoggable(Level.WARNING));
+        assertTrue(logger.isLoggable(Level.INFO));
+        assertTrue(logger.isLoggable(Level.CONFIG));
+        assertTrue(logger.isLoggable(Level.FINE));
+        assertTrue(!logger.isLoggable(Level.FINER));
+        assertTrue(!logger.isLoggable(Level.FINEST));
+
+    }
+
+
+    // Test Serializability of Log instance
+    public void testSerializable() throws Exception {
+
+        super.testSerializable();
+        testExceptionMessages();
+
+    }
+
+
+    // -------------------------------------------------------- Support Methods
+
+
+    // Check the log instance
+    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());
+        assertTrue(!log.isTraceEnabled());
+
+    }
+
+
+    // Check the recorded messages
+    protected void checkLogRecords(boolean thrown) {
+        Iterator records = handler.records();
+        for (int i = 0; i < testMessages.length; i++) {
+            assertTrue(records.hasNext());
+            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());
+            }
+        }
+        assertTrue(!records.hasNext());
+        handler.flush();
+    }
+
+
+    // Log the messages with exceptions
+    protected void logExceptionMessages() {
+        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(String name) throws Exception {
+        logger = Logger.getLogger(name);
+    }
+
+
+    // Set up LogManager instance
+    protected void setUpManager(String config) throws Exception {
+        manager = LogManager.getLogManager();
+        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
new file mode 100644
index 0000000..29f3992
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/jdk14/DefaultConfigTestCase.java
@@ -0,0 +1,191 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.jdk14;
+
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.logging.PathableClassLoader;
+import org.apache.commons.logging.PathableTestSuite;
+
+
+/**
+ * <p>TestCase for JDK 1.4 logging when running on a JDK 1.4 system with
+ * zero configuration, and with Log4J not present (so JDK 1.4 logging
+ * should be automatically configured.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1432587 $ $Date: 2013-01-13 12:11:32 +0100 (Sun, 13 Jan 2013) $
+ */
+
+public class DefaultConfigTestCase extends TestCase {
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * <p>Construct a new instance of this test case.</p>
+     *
+     * @param name Name of the test case
+     */
+    public DefaultConfigTestCase(String name) {
+        super(name);
+    }
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * <p>The {@link LogFactory} implementation we have selected.</p>
+     */
+    protected LogFactory factory = null;
+
+
+    /**
+     * <p>The {@link Log} implementation we have selected.</p>
+     */
+    protected Log log = null;
+
+
+    // ------------------------------------------- JUnit Infrastructure Methods
+
+
+    /**
+     * Set up instance variables required by this test case.
+     */
+    public void setUp() throws Exception {
+        setUpFactory();
+        setUpLog("TestLogger");
+    }
+
+
+    /**
+     * Return the tests included in this test suite.
+     */
+    public static Test suite() throws Exception {
+        PathableClassLoader loader = new PathableClassLoader(null);
+        loader.useExplicitLoader("junit.", Test.class.getClassLoader());
+        loader.addLogicalLib("testclasses");
+        loader.addLogicalLib("commons-logging");
+
+        Class testClass = loader.loadClass(DefaultConfigTestCase.class.getName());
+        return new PathableTestSuite(testClass, loader);
+    }
+
+    /**
+     * Tear down instance variables required by this test case.
+     */
+    public void tearDown() {
+        log = null;
+        factory = null;
+        LogFactory.releaseAll();
+    }
+
+
+    // ----------------------------------------------------------- Test Methods
+
+
+    // 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());
+
+        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
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ObjectOutputStream oos = new ObjectOutputStream(baos);
+        oos.writeObject(log);
+        oos.close();
+        ByteArrayInputStream bais =
+            new ByteArrayInputStream(baos.toByteArray());
+        ObjectInputStream ois = new ObjectInputStream(bais);
+        log = (Log) ois.readObject();
+        ois.close();
+
+        // Check the characteristics of the resulting object
+        checkLog();
+
+    }
+
+
+    // -------------------------------------------------------- Support Methods
+
+
+
+    // Check the log instance
+    protected void checkLog() {
+
+        assertNotNull("Log exists", log);
+        assertEquals("Log class",
+                     "org.apache.commons.logging.impl.Jdk14Logger",
+                     log.getClass().getName());
+
+        // Can we call level checkers with no exceptions?
+        log.isDebugEnabled();
+        log.isErrorEnabled();
+        log.isFatalEnabled();
+        log.isInfoEnabled();
+        log.isTraceEnabled();
+        log.isWarnEnabled();
+
+    }
+
+
+    // Set up factory instance
+    protected void setUpFactory() throws Exception {
+        factory = LogFactory.getFactory();
+    }
+
+
+    // Set up log instance
+    protected void setUpLog(String name) throws Exception {
+        log = LogFactory.getLog(name);
+    }
+
+
+}
diff --git a/src/test/java/org/apache/commons/logging/jdk14/TestHandler.java b/src/test/java/org/apache/commons/logging/jdk14/TestHandler.java
new file mode 100644
index 0000000..d60e53a
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/jdk14/TestHandler.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.logging.jdk14;
+
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+
+
+/**
+ * <p>Test implementation of <code>java.util.logging.Handler</code>.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1432587 $ $Date: 2013-01-13 12:11:32 +0100 (Sun, 13 Jan 2013) $
+ */
+
+public class TestHandler extends Handler {
+
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // The set of logged records for this handler
+    private final List records = new ArrayList();
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    public Iterator records() {
+        return records.iterator();
+    }
+
+
+    // -------------------------------------------------------- Handler Methods
+
+
+    public void close() {
+    }
+
+
+    public void flush() {
+        records.clear();
+    }
+
+
+    public void publish(LogRecord record) {
+        records.add(record);
+    }
+
+
+}
diff --git a/src/test/java/org/apache/commons/logging/log4j/StandardTests.java b/src/test/java/org/apache/commons/logging/log4j/StandardTests.java
new file mode 100644
index 0000000..2f86f74
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/log4j/StandardTests.java
@@ -0,0 +1,216 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.log4j;
+
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.logging.DummyException;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ * Abstract set of tests that can be executed with various classpaths set.
+ * <p>
+ * The tests verify that when running on a system with Log4J present,
+ * Log4J is selected and that the logger basically works.
+ */
+
+public abstract class StandardTests extends TestCase {
+
+    /**
+     * Simple structure to store information about messages that actually get
+     * logged by the underlying logging library.
+     */
+    public static class LogEvent {
+        public String msg;
+        public String level;
+        public Throwable throwable;
+    }
+
+    // -------------------------------------------------------------------
+    // JUnit Infrastructure Methods
+    // -------------------------------------------------------------------
+
+    /**
+     * Set up instance variables required by this test case.
+     */
+    public void setUp() throws Exception {
+        LogFactory.releaseAll();
+    }
+
+    /**
+     * Tear down instance variables required by this test case.
+     */
+    public void tearDown() {
+        LogFactory.releaseAll();
+    }
+
+    // -----------------------------------------------------------
+    // abstract methods
+    // -----------------------------------------------------------
+
+    /**
+     * Modify log4j's setup so that all messages actually logged get redirected
+     * into the specified list.
+     * <p>
+     * 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 Methods
+
+    /**
+     * Test that a LogFactory gets created as expected.
+     */
+    public void testCreateFactory() {
+        LogFactory factory = LogFactory.getFactory();
+        assertNotNull("LogFactory exists", factory);
+        assertEquals("LogFactory class",
+                     "org.apache.commons.logging.impl.LogFactoryImpl",
+                     factory.getClass().getName());
+
+        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 {
+        List logEvents = new ArrayList();
+        setUpTestAppender(logEvents);
+        Log log = LogFactory.getLog("test-category");
+        logPlainMessages(log);
+        checkLoggingEvents(logEvents, false);
+    }
+
+    /**
+     * Verify that we can log exception messages.
+     */
+    public void testExceptionMessages() throws Exception {
+        List logEvents = new ArrayList();
+        setUpTestAppender(logEvents);
+        Log log = LogFactory.getLog("test-category");
+        logExceptionMessages(log);
+        checkLoggingEvents(logEvents, true);
+    }
+
+    /**
+     * Test Serializability of Log instance
+     */
+    public void testSerializable() throws Exception {
+        List logEvents = new ArrayList();
+        setUpTestAppender(logEvents);
+        Log log = LogFactory.getLog("test-category");
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ObjectOutputStream oos = new ObjectOutputStream(baos);
+        oos.writeObject(log);
+        oos.close();
+        ByteArrayInputStream bais =
+            new ByteArrayInputStream(baos.toByteArray());
+        ObjectInputStream ois = new ObjectInputStream(bais);
+        Log newLog = (Log) ois.readObject();
+        ois.close();
+
+        // Check the characteristics of the resulting object
+        logExceptionMessages(newLog);
+        checkLoggingEvents(logEvents, true);
+    }
+
+    // -------------------------------------------------------- Support Methods
+
+    /**
+     * Verify that the TestAppender has received the expected
+     * number of messages. This assumes that:
+     * <ul>
+     * <li>setUpTestAppender has been called
+     * <li>logPlainMessages or logExceptionMessages has been
+     * called to log a known number of messages at known levels.
+     * </ul>
+     *
+     * @param logEvents is the list of log events received.
+     *
+     * @param thrown False if logPlainMessages was called
+     * (ie the TestAppender is expected to have received
+     * logevents with no associated exception info). True if
+     * logExceptionMessages was called.
+     */
+    private void checkLoggingEvents(List logEvents, boolean thrown) {
+        LogEvent ev;
+
+        assertEquals("Unexpected number of log events", 4, logEvents.size());
+
+        ev = (LogEvent) logEvents.get(0);
+        assertEquals("Info message expected", "info", ev.msg);
+        assertEquals("Info level expected", "INFO", ev.level);
+        assertEquals("Exception data incorrect", ev.throwable!=null, thrown);
+
+        ev = (LogEvent) logEvents.get(1);
+        assertEquals("Warn message expected", "warn", ev.msg);
+        assertEquals("Warn level expected", "WARN", ev.level);
+        assertEquals("Exception data incorrect", ev.throwable!=null, thrown);
+
+        ev = (LogEvent) logEvents.get(2);
+        assertEquals("Error message expected", "error", ev.msg);
+        assertEquals("Error level expected", "ERROR", ev.level);
+        assertEquals("Exception data incorrect", ev.throwable!=null, thrown);
+
+        ev = (LogEvent) logEvents.get(3);
+        assertEquals("Fatal message expected", "fatal", ev.msg);
+        assertEquals("Fatal level expected", "FATAL", ev.level);
+        assertEquals("Exception data incorrect", ev.throwable!=null, thrown);
+    }
+
+
+    /**
+     * Log plain messages.
+     */
+    private void logPlainMessages(Log log) {
+        log.trace("trace"); // Should not actually get logged
+        log.debug("debug"); // Should not actually get logged
+        log.info("info");
+        log.warn("warn");
+        log.error("error");
+        log.fatal("fatal");
+    }
+
+    /**
+     * Log messages with exceptions
+     */
+    private void logExceptionMessages(Log log) {
+        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);
+    }
+}
diff --git a/src/test/java/org/apache/commons/logging/log4j/log4j12/ApiClasspathStandardTestCase.java b/src/test/java/org/apache/commons/logging/log4j/log4j12/ApiClasspathStandardTestCase.java
new file mode 100644
index 0000000..1b7bace
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/log4j/log4j12/ApiClasspathStandardTestCase.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.log4j.log4j12;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+import org.apache.commons.logging.PathableClassLoader;
+import org.apache.commons.logging.PathableTestSuite;
+
+
+/**
+ * Tests for Log4J logging that emulate a webapp running within
+ * a container where the commons-logging-api jar file is in
+ * the parent classpath and commons-logging.jar is in the child.
+ */
+
+public class ApiClasspathStandardTestCase extends TestCase {
+
+    /**
+     * Return the tests included in this test suite.
+     */
+    public static Test suite() throws Exception {
+        PathableClassLoader parent = new PathableClassLoader(null);
+        parent.useExplicitLoader("junit.", Test.class.getClassLoader());
+        parent.addLogicalLib("commons-logging-api");
+
+        PathableClassLoader child = new PathableClassLoader(parent);
+        child.addLogicalLib("log4j12");
+        child.addLogicalLib("commons-logging");
+        child.addLogicalLib("testclasses");
+
+        Class testClass = child.loadClass(
+            "org.apache.commons.logging.log4j.log4j12.Log4j12StandardTests");
+        return new PathableTestSuite(testClass, child);
+    }
+}
diff --git a/src/test/java/org/apache/commons/logging/log4j/log4j12/AppClasspathStandardTestCase.java b/src/test/java/org/apache/commons/logging/log4j/log4j12/AppClasspathStandardTestCase.java
new file mode 100644
index 0000000..71da059
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/log4j/log4j12/AppClasspathStandardTestCase.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.log4j.log4j12;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+import org.apache.commons.logging.PathableClassLoader;
+import org.apache.commons.logging.PathableTestSuite;
+
+/**
+ * Tests for Log4J logging when there is only one classloader and everything
+ * is in it, as would be the situation for a standalone application.
+ */
+
+public class AppClasspathStandardTestCase extends TestCase {
+
+    /**
+     * Return the tests included in this test suite.
+     */
+    public static Test suite() throws Exception {
+        PathableClassLoader loader = new PathableClassLoader(null);
+        loader.useExplicitLoader("junit.", Test.class.getClassLoader());
+        loader.addLogicalLib("testclasses");
+        loader.addLogicalLib("log4j12");
+        loader.addLogicalLib("commons-logging");
+
+        Class testClass = loader.loadClass(
+            "org.apache.commons.logging.log4j.log4j12.Log4j12StandardTests");
+        return new PathableTestSuite(testClass, loader);
+    }
+}
diff --git a/src/test/java/org/apache/commons/logging/log4j/log4j12/ChildClasspathStandardTestCase.java b/src/test/java/org/apache/commons/logging/log4j/log4j12/ChildClasspathStandardTestCase.java
new file mode 100644
index 0000000..2620dbd
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/log4j/log4j12/ChildClasspathStandardTestCase.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.log4j.log4j12;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+import org.apache.commons.logging.PathableClassLoader;
+import org.apache.commons.logging.PathableTestSuite;
+
+
+/**
+ * Tests for Log4J logging that emulate a webapp running within
+ * a container where all the necessary libs are in the child.
+ */
+
+public class ChildClasspathStandardTestCase extends TestCase {
+
+    /**
+     * Return the tests included in this test suite.
+     */
+    public static Test suite() throws Exception {
+        PathableClassLoader parent = new PathableClassLoader(null);
+        parent.useExplicitLoader("junit.", Test.class.getClassLoader());
+
+        PathableClassLoader child = new PathableClassLoader(parent);
+        child.addLogicalLib("testclasses");
+        child.addLogicalLib("log4j12");
+        child.addLogicalLib("commons-logging");
+
+        Class testClass = child.loadClass(
+            "org.apache.commons.logging.log4j.log4j12.Log4j12StandardTests");
+        return new PathableTestSuite(testClass, child);
+    }
+}
diff --git a/src/test/java/org/apache/commons/logging/log4j/log4j12/Log4j12StandardTests.java b/src/test/java/org/apache/commons/logging/log4j/log4j12/Log4j12StandardTests.java
new file mode 100644
index 0000000..a03e74a
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/log4j/log4j12/Log4j12StandardTests.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.log4j.log4j12;
+
+import java.util.List;
+
+import org.apache.commons.logging.log4j.StandardTests;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+
+/**
+ * A concrete class that runs the standard tests, and is compiled
+ * specifically against log4j12. The parent class can't call any
+ * log4j methods at all as that would mean it has to be compiled
+ * against a particular version of log4j.
+ */
+
+public class Log4j12StandardTests extends StandardTests {
+
+    public void setUpTestAppender(List logEvents) {
+        TestAppender appender = new TestAppender(logEvents);
+        Logger rootLogger = Logger.getRootLogger();
+        rootLogger.removeAllAppenders();
+        rootLogger.addAppender(appender);
+        rootLogger.setLevel(Level.INFO);
+    }
+}
diff --git a/src/test/java/org/apache/commons/logging/log4j/log4j12/ParentClasspathStandardTestCase.java b/src/test/java/org/apache/commons/logging/log4j/log4j12/ParentClasspathStandardTestCase.java
new file mode 100644
index 0000000..3654d92
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/log4j/log4j12/ParentClasspathStandardTestCase.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.log4j.log4j12;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+import org.apache.commons.logging.PathableClassLoader;
+import org.apache.commons.logging.PathableTestSuite;
+
+/**
+ * Tests for Log4J logging that emulate a webapp running within
+ * a container where all the necessary libs are in the parent.
+ */
+
+public class ParentClasspathStandardTestCase extends TestCase {
+
+    /**
+     * Return the tests included in this test suite.
+     */
+    public static Test suite() throws Exception {
+        PathableClassLoader parent = new PathableClassLoader(null);
+        parent.useExplicitLoader("junit.", Test.class.getClassLoader());
+        parent.addLogicalLib("commons-logging");
+        parent.addLogicalLib("log4j12");
+
+        PathableClassLoader child = new PathableClassLoader(parent);
+        child.addLogicalLib("testclasses");
+
+        Class testClass = child.loadClass(
+            "org.apache.commons.logging.log4j.log4j12.Log4j12StandardTests");
+        return new PathableTestSuite(testClass, child);
+    }
+}
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
new file mode 100644
index 0000000..5ad71c8
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/log4j/log4j12/TestAppender.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.log4j.log4j12;
+
+
+import java.util.List;
+
+import org.apache.commons.logging.log4j.StandardTests;
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * A custom implementation of <code>org.apache.log4j.Appender</code> which
+ * converts the log4j-specific log event record into a representation that
+ * doesn't have a dependency on log4j and stores that new representation into
+ * an external list.
+ */
+
+public class TestAppender extends AppenderSkeleton {
+
+    /**
+     * Constructor.
+     */
+    public TestAppender(List logEvents) {
+        events = logEvents;
+    }
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    // The set of logged events for this appender
+    private final List events;
+
+
+    // ------------------------------------------------------- Appender Methods
+
+    protected void append(LoggingEvent event) {
+        StandardTests.LogEvent lev = new StandardTests.LogEvent();
+
+        lev.level = event.getLevel().toString();
+
+        if (event.getMessage() == null) {
+            lev.msg = null;
+        } else {
+            lev.msg = event.getMessage().toString();
+        }
+
+        if (event.getThrowableInformation() == null) {
+            lev.throwable = null;
+        } else {
+            lev.throwable = event.getThrowableInformation().getThrowable();
+        }
+
+        events.add(lev);
+    }
+
+
+    public void close() {
+    }
+
+
+    public boolean requiresLayout() {
+        return false;
+    }
+
+
+}
diff --git a/src/test/java/org/apache/commons/logging/logkit/StandardTestCase.java b/src/test/java/org/apache/commons/logging/logkit/StandardTestCase.java
new file mode 100644
index 0000000..4c1fbd8
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/logkit/StandardTestCase.java
@@ -0,0 +1,166 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.logkit;
+
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import junit.framework.Test;
+
+import org.apache.commons.logging.AbstractLogTest;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.logging.PathableClassLoader;
+import org.apache.commons.logging.PathableTestSuite;
+import org.apache.commons.logging.impl.LogKitLogger;
+
+/**
+ * Basic tests for Avalon LogKit logger adapter.
+ */
+
+public class StandardTestCase extends AbstractLogTest {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * <p>The {@link LogFactory} implementation we have selected.</p>
+     */
+    protected LogFactory factory = null;
+
+
+    /**
+     * <p>The {@link Log} implementation we have selected.</p>
+     */
+    protected Log log = null;
+
+
+    // ------------------------------------------- JUnit Infrastructure Methods
+
+
+    /**
+     * Return the tests included in this test suite.
+     */
+    public static Test suite() throws Exception {
+        Class thisClass = StandardTestCase.class;
+
+        PathableClassLoader loader = new PathableClassLoader(null);
+        loader.useExplicitLoader("junit.", Test.class.getClassLoader());
+        loader.addLogicalLib("testclasses");
+        loader.addLogicalLib("commons-logging");
+        loader.addLogicalLib("logkit");
+
+        Class testClass = loader.loadClass(thisClass.getName());
+        return new PathableTestSuite(testClass, loader);
+    }
+
+    /**
+     * Set up instance variables required by this test case.
+     */
+    public void setUp() throws Exception {
+        LogFactory.releaseAll();
+
+        System.setProperty(
+                "org.apache.commons.logging.Log",
+                "org.apache.commons.logging.impl.LogKitLogger");
+
+        factory = LogFactory.getFactory();
+        log = LogFactory.getLog("TestLogger");
+    }
+
+    /**
+     * Tear down instance variables required by this test case.
+     */
+    public void tearDown() {
+        log = null;
+        factory = null;
+        LogFactory.releaseAll();
+    }
+
+    // ----------------------------------------------------------- Test Methods
+
+    /**
+     * Override the abstract method from the parent class so that the
+     * inherited tests can access the right Log object type.
+     */
+    public Log getLogObject()
+    {
+        return new LogKitLogger(this.getClass().getName());
+    }
+
+    // Test pristine LogFactory instance
+    public void testPristineFactory() {
+
+        assertNotNull("LogFactory exists", factory);
+        assertEquals("LogFactory class",
+                     "org.apache.commons.logging.impl.LogFactoryImpl",
+                     factory.getClass().getName());
+
+        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 {
+        checkStandard();
+
+        // Serialize and deserialize the instance
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ObjectOutputStream oos = new ObjectOutputStream(baos);
+        oos.writeObject(log);
+        oos.close();
+        ByteArrayInputStream bais =
+            new ByteArrayInputStream(baos.toByteArray());
+        ObjectInputStream ois = new ObjectInputStream(bais);
+        log = (Log) ois.readObject();
+        ois.close();
+
+        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
new file mode 100644
index 0000000..fa54c82
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/noop/NoOpLogTestCase.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.noop;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.logging.impl.NoOpLog;
+import org.apache.commons.logging.AbstractLogTest;
+
+/**
+ * Tests for NoOpLog logging adapter.
+ * <p>
+ * This simply applies the tests defined in AbstractLogTest to this class.
+ */
+public class NoOpLogTestCase extends AbstractLogTest
+{
+    /**
+     * Set up instance variables required by this test case.
+     */
+    public void setUp() throws Exception {
+        LogFactory.releaseAll();
+
+        System.setProperty(
+                "org.apache.commons.logging.Log",
+                "org.apache.commons.logging.impl.NoOpLog");
+    }
+
+    /**
+     * Tear down instance variables required by this test case.
+     */
+    public void tearDown() {
+        LogFactory.releaseAll();
+        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.
+     */
+    public Log getLogObject()
+    {
+        return new NoOpLog(this.getClass().getName());
+    }
+
+    // Test Serializability of standard instance
+    public void testSerializable() throws Exception {
+        Log log = LogFactory.getLog(this.getClass().getName());
+        checkLog(log);
+
+        // Serialize and deserialize the instance
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ObjectOutputStream oos = new ObjectOutputStream(baos);
+        oos.writeObject(log);
+        oos.close();
+        ByteArrayInputStream bais =
+            new ByteArrayInputStream(baos.toByteArray());
+        ObjectInputStream ois = new ObjectInputStream(bais);
+        log = (Log) ois.readObject();
+        ois.close();
+
+        checkLog(log);
+    }
+
+
+    // -------------------------------------------------------- Support Methods
+
+    private void checkLog(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
new file mode 100644
index 0000000..1aeb12d
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/pathable/ChildFirstTestCase.java
@@ -0,0 +1,317 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.logging.pathable;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+import org.apache.commons.logging.PathableClassLoader;
+import org.apache.commons.logging.PathableTestSuite;
+
+/**
+ * Tests for the PathableTestSuite and PathableClassLoader functionality,
+ * where lookup order for the PathableClassLoader is child-first.
+ * <p>
+ * These tests assume:
+ * <ul>
+ * <li>junit is in system classpath
+ * <li>nothing else is in system classpath
+ * </ul>
+ */
+
+public class ChildFirstTestCase extends TestCase {
+
+    /**
+     * Set up a custom classloader hierarchy for this test case.
+     * The hierarchy is:
+     * <ul>
+     * <li> contextloader: child-first.
+     * <li> childloader: child-first, used to load test case.
+     * <li> parentloader: child-first, parent is the bootclassloader.
+     * </ul>
+     */
+    public static Test suite() throws Exception {
+        Class thisClass = ChildFirstTestCase.class;
+        ClassLoader thisClassLoader = thisClass.getClassLoader();
+
+        // Make the parent a direct child of the bootloader to hide all
+        // other classes in the system classpath
+        PathableClassLoader parent = new PathableClassLoader(null);
+        parent.setParentFirst(false);
+
+        // Make the junit classes visible as a special case, as junit
+        // won't be able to call this class at all without this. The
+        // junit classes must be visible from the classloader that loaded
+        // this class, so use that as the source for future access to classes
+        // from the junit package.
+        parent.useExplicitLoader("junit.", thisClassLoader);
+
+        // Make the commons-logging.jar classes visible via the parent
+        parent.addLogicalLib("commons-logging");
+
+        // Create a child classloader to load the test case through
+        PathableClassLoader child = new PathableClassLoader(parent);
+        child.setParentFirst(false);
+
+        // Obviously, the child classloader needs to have the test classes
+        // in its path!
+        child.addLogicalLib("testclasses");
+        child.addLogicalLib("commons-logging-adapters");
+
+        // Create a third classloader to be the context classloader.
+        PathableClassLoader context = new PathableClassLoader(child);
+        context.setParentFirst(false);
+
+        // reload this class via the child classloader
+        Class testClass = child.loadClass(thisClass.getName());
+
+        // and return our custom TestSuite class
+        return new PathableTestSuite(testClass, context);
+    }
+
+    /**
+     * Utility method to return the set of all classloaders in the
+     * parent chain starting from the one that loaded the class for
+     * this object instance.
+     */
+    private Set getAncestorCLs() {
+        Set s = new HashSet();
+        ClassLoader cl = this.getClass().getClassLoader();
+        while (cl != null) {
+            s.add(cl);
+            cl = cl.getParent();
+        }
+        return s;
+    }
+
+    /**
+     * Test that the classloader hierarchy is as expected, and that
+     * calling loadClass() on various classloaders works as expected.
+     * Note that for this test case, parent-first classloading is
+     * in effect.
+     */
+    public void testPaths() throws Exception {
+        // the context classloader is not expected to be null
+        ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
+        assertNotNull("Context classloader is null", contextLoader);
+        assertEquals("Context classloader has unexpected type",
+                PathableClassLoader.class.getName(),
+                contextLoader.getClass().getName());
+
+        // the classloader that loaded this class is obviously not null
+        ClassLoader thisLoader = this.getClass().getClassLoader();
+        assertNotNull("thisLoader is null", thisLoader);
+        assertEquals("thisLoader has unexpected type",
+                PathableClassLoader.class.getName(),
+                thisLoader.getClass().getName());
+
+        // the suite method specified that the context classloader's parent
+        // is the loader that loaded this test case.
+        assertSame("Context classloader is not child of thisLoader",
+                thisLoader, contextLoader.getParent());
+
+        // thisLoader's parent should be available
+        ClassLoader parentLoader = thisLoader.getParent();
+        assertNotNull("Parent classloader is null", parentLoader);
+        assertEquals("Parent classloader has unexpected type",
+                PathableClassLoader.class.getName(),
+                parentLoader.getClass().getName());
+
+        // parent should have a parent of null
+        assertNull("Parent classloader has non-null parent", parentLoader.getParent());
+
+        // getSystemClassloader is not a PathableClassLoader; it's of a
+        // built-in type. This also verifies that system classloader is none of
+        // (context, child, parent).
+        ClassLoader systemLoader = ClassLoader.getSystemClassLoader();
+        assertNotNull("System classloader is null", systemLoader);
+        assertFalse("System classloader has unexpected type",
+                PathableClassLoader.class.getName().equals(
+                        systemLoader.getClass().getName()));
+
+        // junit classes should be visible; their classloader is not
+        // in the hierarchy of parent classloaders for this class,
+        // though it is accessable due to trickery in the PathableClassLoader.
+        Class junitTest = contextLoader.loadClass("junit.framework.Test");
+        Set ancestorCLs = getAncestorCLs();
+        assertFalse("Junit not loaded by ancestor classloader",
+                ancestorCLs.contains(junitTest.getClassLoader()));
+
+        // jcl api classes should be visible only via the parent
+        Class logClass = contextLoader.loadClass("org.apache.commons.logging.Log");
+        assertSame("Log class not loaded via parent",
+                logClass.getClassLoader(), parentLoader);
+
+        // jcl adapter classes should be visible via both parent and child. However
+        // as the classloaders are child-first we should see the child one.
+        Class log4jClass = contextLoader.loadClass("org.apache.commons.logging.impl.Log4JLogger");
+        assertSame("Log4JLogger not loaded via child",
+                log4jClass.getClassLoader(), thisLoader);
+
+        // test classes should be visible via the child only
+        Class testClass = contextLoader.loadClass("org.apache.commons.logging.PathableTestSuite");
+        assertSame("PathableTestSuite not loaded via child",
+                testClass.getClassLoader(), thisLoader);
+
+        // test loading of class that is not available
+        try {
+            Class noSuchClass = contextLoader.loadClass("no.such.class");
+            fail("Class no.such.class is unexpectedly available");
+            assertNotNull(noSuchClass); // silence warning about unused var
+        } catch(ClassNotFoundException ex) {
+            // ok
+        }
+
+        // String class classloader is null
+        Class stringClass = contextLoader.loadClass("java.lang.String");
+        assertNull("String class classloader is not null!",
+                stringClass.getClassLoader());
+    }
+
+    /**
+     * Test that the various flavours of ClassLoader.getResource work as expected.
+     */
+    public void testResource() {
+        URL resource;
+
+        ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
+        ClassLoader childLoader = contextLoader.getParent();
+
+        // getResource where it doesn't exist
+        resource = childLoader.getResource("nosuchfile");
+        assertNull("Non-null URL returned for invalid resource name", resource);
+
+        // getResource where it is accessable only to parent classloader
+        resource = childLoader.getResource("org/apache/commons/logging/Log.class");
+        assertNotNull("Unable to locate Log.class resource", resource);
+
+        // getResource where it is accessable only to child classloader
+        resource = childLoader.getResource("org/apache/commons/logging/PathableTestSuite.class");
+        assertNotNull("Unable to locate PathableTestSuite.class resource", resource);
+
+        // getResource where it is accessable to both classloaders. The one visible
+        // to the child should be returned. The URL returned will be of form
+        //  jar:file:/x/y.jar!path/to/resource. The filename part should include the jarname
+        // of form commons-logging-adapters-nnnn.jar, not commons-logging-nnnn.jar
+        resource = childLoader.getResource("org/apache/commons/logging/impl/Log4JLogger.class");
+        assertNotNull("Unable to locate Log4JLogger.class resource", resource);
+        assertTrue("Incorrect source for Log4JLogger class",
+                resource.toString().indexOf("/commons-logging-adapters-1.") > 0);
+    }
+
+    /**
+     * Test that the various flavours of ClassLoader.getResources work as expected.
+     */
+    public void testResources() throws Exception {
+        Enumeration resources;
+        URL[] urls;
+
+        // verify the classloader hierarchy
+        ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
+        ClassLoader childLoader = contextLoader.getParent();
+        ClassLoader parentLoader = childLoader.getParent();
+        ClassLoader bootLoader = parentLoader.getParent();
+        assertNull("Unexpected classloader hierarchy", bootLoader);
+
+        // getResources where no instances exist
+        resources = childLoader.getResources("nosuchfile");
+        urls = toURLArray(resources);
+        assertEquals("Non-null URL returned for invalid resource name", 0, urls.length);
+
+        // getResources where the resource only exists in the parent
+        resources = childLoader.getResources("org/apache/commons/logging/Log.class");
+        urls = toURLArray(resources);
+        assertEquals("Unexpected number of Log.class resources found", 1, urls.length);
+
+        // getResources where the resource only exists in the child
+        resources = childLoader.getResources("org/apache/commons/logging/PathableTestSuite.class");
+        urls = toURLArray(resources);
+        assertEquals("Unexpected number of PathableTestSuite.class resources found", 1, urls.length);
+
+        // getResources where the resource exists in both.
+        // resources should be returned in order (child-resource, parent-resource).
+        //
+        // IMPORTANT: due to the fact that in java 1.4 and earlier method
+        // ClassLoader.getResources is final it isn't possible for PathableClassLoader
+        // to override this. So even when child-first is enabled the resource order
+        // is still (parent-resources, child-resources). This test verifies the expected
+        // behaviour - even though it's not the desired behaviour.
+
+        resources = childLoader.getResources("org/apache/commons/logging/impl/Log4JLogger.class");
+        urls = toURLArray(resources);
+        assertEquals("Unexpected number of Log4JLogger.class resources found", 2, urls.length);
+
+        // There is no guarantee about the ordering of results returned from getResources
+        // To make this test portable across JVMs, sort the string to give them a known order
+        String[] urlsToStrings = new String[2];
+        urlsToStrings[0] = urls[0].toString();
+        urlsToStrings[1] = urls[1].toString();
+        Arrays.sort(urlsToStrings);
+        assertTrue("Incorrect source for Log4JLogger class",
+                urlsToStrings[0].indexOf("/commons-logging-1.") > 0);
+        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(Enumeration e) {
+        ArrayList l = new ArrayList();
+        while (e.hasMoreElements()) {
+            URL u = (URL) e.nextElement();
+            l.add(u);
+        }
+        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
+        ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
+        ClassLoader childLoader = contextLoader.getParent();
+        ClassLoader parentLoader = childLoader.getParent();
+        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
new file mode 100644
index 0000000..86fd475
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/pathable/GeneralTestCase.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.logging.pathable;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+import org.apache.commons.logging.PathableClassLoader;
+import org.apache.commons.logging.PathableTestSuite;
+
+/**
+ * Tests for the PathableTestSuite class.
+ */
+
+public class GeneralTestCase extends TestCase {
+
+    /**
+     * Set up a custom classloader hierarchy for this test case.
+     */
+    public static Test suite() throws Exception {
+        Class thisClass = GeneralTestCase.class;
+        ClassLoader thisClassLoader = thisClass.getClassLoader();
+
+        PathableClassLoader loader = new PathableClassLoader(null);
+        loader.useExplicitLoader("junit.", thisClassLoader);
+        loader.addLogicalLib("testclasses");
+
+        // reload this class via the child classloader
+        Class testClass = loader.loadClass(thisClass.getName());
+
+        // and return our custom TestSuite class
+        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.
+     * <p>
+     * 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() {
+        ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
+        assertEquals("ContextLoader is of unexpected type",
+                contextLoader.getClass().getName(),
+                PathableClassLoader.class.getName());
+
+        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.
+     * <p>
+     * This method works in conjunction with testResetContext2. 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 contextClassloader.
+     * If the PathableTestSuite isn't resetting the contextClassLoader 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 testResetContext1() {
+        checkAndSetContext();
+    }
+
+    /**
+     * See testResetContext1.
+     */
+    public void testResetContext2() {
+        checkAndSetContext();
+    }
+}
diff --git a/src/test/java/org/apache/commons/logging/pathable/ParentFirstTestCase.java b/src/test/java/org/apache/commons/logging/pathable/ParentFirstTestCase.java
new file mode 100644
index 0000000..f9bf452
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/pathable/ParentFirstTestCase.java
@@ -0,0 +1,308 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.logging.pathable;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+import org.apache.commons.logging.PathableClassLoader;
+import org.apache.commons.logging.PathableTestSuite;
+
+/**
+ * Tests for the PathableTestSuite and PathableClassLoader functionality,
+ * where lookup order for the PathableClassLoader is parent-first.
+ * <p>
+ * These tests assume:
+ * <ul>
+ * <li>junit is in system classpath
+ * <li>nothing else is in system classpath
+ * </ul>
+ */
+
+public class ParentFirstTestCase extends TestCase {
+
+    /**
+     * Set up a custom classloader hierarchy for this test case.
+     * The hierarchy is:
+     * <ul>
+     * <li> contextloader: parent-first.
+     * <li> childloader: parent-first, used to load test case.
+     * <li> parentloader: parent-first, parent is the bootclassloader.
+     * </ul>
+     */
+    public static Test suite() throws Exception {
+        Class thisClass = ParentFirstTestCase.class;
+        ClassLoader thisClassLoader = thisClass.getClassLoader();
+
+        // Make the parent a direct child of the bootloader to hide all
+        // other classes in the system classpath
+        PathableClassLoader parent = new PathableClassLoader(null);
+
+        // Make the junit classes visible as a special case, as junit
+        // won't be able to call this class at all without this. The
+        // junit classes must be visible from the classloader that loaded
+        // this class, so use that as the source for future access to classes
+        // from the junit package.
+        parent.useExplicitLoader("junit.", thisClassLoader);
+        
+        // make the commons-logging.jar classes visible via the parent
+        parent.addLogicalLib("commons-logging");
+        
+        // create a child classloader to load the test case through
+        PathableClassLoader child = new PathableClassLoader(parent);
+        
+        // obviously, the child classloader needs to have the test classes
+        // in its path!
+        child.addLogicalLib("testclasses");
+        child.addLogicalLib("commons-logging-adapters");
+        
+        // create a third classloader to be the context classloader.
+        PathableClassLoader context = new PathableClassLoader(child);
+
+        // reload this class via the child classloader
+        Class testClass = child.loadClass(thisClass.getName());
+        
+        // and return our custom TestSuite class
+        return new PathableTestSuite(testClass, context);
+    }
+    
+    /**
+     * Utility method to return the set of all classloaders in the
+     * parent chain starting from the one that loaded the class for
+     * this object instance.
+     */
+    private Set getAncestorCLs() {
+        Set s = new HashSet();
+        ClassLoader cl = this.getClass().getClassLoader();
+        while (cl != null) {
+            s.add(cl);
+            cl = cl.getParent();
+        }
+        return s;
+    }
+
+    /**
+     * Test that the classloader hierarchy is as expected, and that
+     * calling loadClass() on various classloaders works as expected.
+     * Note that for this test case, parent-first classloading is
+     * in effect.
+     */
+    public void testPaths() throws Exception {
+        // the context classloader is not expected to be null
+        ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
+        assertNotNull("Context classloader is null", contextLoader);
+        assertEquals("Context classloader has unexpected type",
+                PathableClassLoader.class.getName(),
+                contextLoader.getClass().getName());
+        
+        // the classloader that loaded this class is obviously not null
+        ClassLoader thisLoader = this.getClass().getClassLoader();
+        assertNotNull("thisLoader is null", thisLoader);
+        assertEquals("thisLoader has unexpected type",
+                PathableClassLoader.class.getName(),
+                thisLoader.getClass().getName());
+        
+        // the suite method specified that the context classloader's parent
+        // is the loader that loaded this test case.
+        assertSame("Context classloader is not child of thisLoader",
+                thisLoader, contextLoader.getParent());
+
+        // thisLoader's parent should be available
+        ClassLoader parentLoader = thisLoader.getParent();
+        assertNotNull("Parent classloader is null", parentLoader);
+        assertEquals("Parent classloader has unexpected type",
+                PathableClassLoader.class.getName(),
+                parentLoader.getClass().getName());
+        
+        // parent should have a parent of null
+        assertNull("Parent classloader has non-null parent", parentLoader.getParent());
+
+        // getSystemClassloader is not a PathableClassLoader; it's of a
+        // built-in type. This also verifies that system classloader is none of
+        // (context, child, parent).
+        ClassLoader systemLoader = ClassLoader.getSystemClassLoader();
+        assertNotNull("System classloader is null", systemLoader);
+        assertFalse("System classloader has unexpected type",
+                PathableClassLoader.class.getName().equals(
+                        systemLoader.getClass().getName()));
+
+        // junit classes should be visible; their classloader is not
+        // in the hierarchy of parent classloaders for this class,
+        // though it is accessable due to trickery in the PathableClassLoader.
+        Class junitTest = contextLoader.loadClass("junit.framework.Test");
+        Set ancestorCLs = getAncestorCLs();
+        assertFalse("Junit not loaded by ancestor classloader", 
+                ancestorCLs.contains(junitTest.getClassLoader()));
+
+        // jcl api classes should be visible only via the parent
+        Class logClass = contextLoader.loadClass("org.apache.commons.logging.Log");
+        assertSame("Log class not loaded via parent",
+                logClass.getClassLoader(), parentLoader);
+
+        // jcl adapter classes should be visible via both parent and child. However
+        // as the classloaders are parent-first we should see the parent one.
+        Class log4jClass = contextLoader.loadClass("org.apache.commons.logging.impl.Log4JLogger");
+        assertSame("Log4JLogger not loaded via parent", 
+                log4jClass.getClassLoader(), parentLoader);
+        
+        // test classes should be visible via the child only
+        Class testClass = contextLoader.loadClass("org.apache.commons.logging.PathableTestSuite");
+        assertSame("PathableTestSuite not loaded via child", 
+                testClass.getClassLoader(), thisLoader);
+        
+        // test loading of class that is not available
+        try {
+            Class noSuchClass = contextLoader.loadClass("no.such.class");
+            fail("Class no.such.class is unexpectedly available");
+            assertNotNull(noSuchClass); // silence warning about unused var
+        } catch(ClassNotFoundException ex) {
+            // ok
+        }
+
+        // String class classloader is null
+        Class stringClass = contextLoader.loadClass("java.lang.String");
+        assertNull("String class classloader is not null!",
+                stringClass.getClassLoader());
+    }
+    
+    /**
+     * Test that the various flavours of ClassLoader.getResource work as expected.
+     */
+    public void testResource() {
+        URL resource;
+        
+        ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
+        ClassLoader childLoader = contextLoader.getParent();
+        
+        // getResource where it doesn't exist
+        resource = childLoader.getResource("nosuchfile");
+        assertNull("Non-null URL returned for invalid resource name", resource);
+
+        // getResource where it is accessable only to parent classloader
+        resource = childLoader.getResource("org/apache/commons/logging/Log.class");
+        assertNotNull("Unable to locate Log.class resource", resource);
+        
+        // getResource where it is accessable only to child classloader
+        resource = childLoader.getResource("org/apache/commons/logging/PathableTestSuite.class");
+        assertNotNull("Unable to locate PathableTestSuite.class resource", resource);
+
+        // getResource where it is accessable to both classloaders. The one visible
+        // to the parent should be returned. The URL returned will be of form
+        //  jar:file:/x/y.jar!path/to/resource. The filename part should include the jarname
+        // of form commons-logging-nnnn.jar, not commons-logging-adapters-nnnn.jar
+        resource = childLoader.getResource("org/apache/commons/logging/impl/Log4JLogger.class");
+        assertNotNull("Unable to locate Log4JLogger.class resource", resource);
+        assertTrue("Incorrect source for Log4JLogger class",
+                resource.toString().indexOf("/commons-logging-1.") > 0);
+    }
+    
+    /**
+     * Test that the various flavours of ClassLoader.getResources work as expected.
+     */
+    public void testResources() throws Exception {
+        Enumeration resources;
+        URL[] urls;
+        
+        // verify the classloader hierarchy
+        ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
+        ClassLoader childLoader = contextLoader.getParent();
+        ClassLoader parentLoader = childLoader.getParent();
+        ClassLoader bootLoader = parentLoader.getParent();
+        assertNull("Unexpected classloader hierarchy", bootLoader);
+        
+        // getResources where no instances exist
+        resources = childLoader.getResources("nosuchfile");
+        urls = toURLArray(resources);
+        assertEquals("Non-null URL returned for invalid resource name", 0, urls.length);
+        
+        // getResources where the resource only exists in the parent
+        resources = childLoader.getResources("org/apache/commons/logging/Log.class");
+        urls = toURLArray(resources);
+        assertEquals("Unexpected number of Log.class resources found", 1, urls.length);
+        
+        // getResources where the resource only exists in the child
+        resources = childLoader.getResources("org/apache/commons/logging/PathableTestSuite.class");
+        urls = toURLArray(resources);
+        assertEquals("Unexpected number of PathableTestSuite.class resources found", 1, urls.length);
+        
+        // getResources where the resource exists in both.
+        // resources should be returned in order (parent-resource, child-resource)
+        resources = childLoader.getResources("org/apache/commons/logging/impl/Log4JLogger.class");
+        urls = toURLArray(resources);
+        assertEquals("Unexpected number of Log4JLogger.class resources found", 2, urls.length);
+        
+        // There is no gaurantee about the ordering of results returned from getResources
+        // To make this test portable across JVMs, sort the string to give them a known order
+        String[] urlsToStrings = new String[2];
+        urlsToStrings[0] = urls[0].toString();
+        urlsToStrings[1] = urls[1].toString();
+        Arrays.sort(urlsToStrings);
+        assertTrue("Incorrect source for Log4JLogger class",
+                urlsToStrings[0].indexOf("/commons-logging-1.") > 0);
+        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(Enumeration e) {
+        ArrayList l = new ArrayList();
+        while (e.hasMoreElements()) {
+            URL u = (URL) e.nextElement();
+            l.add(u);
+        }
+        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
+        ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
+        ClassLoader childLoader = contextLoader.getParent();
+        ClassLoader parentLoader = childLoader.getParent();
+        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/DummyClass.java b/src/test/java/org/apache/commons/logging/security/DummyClass.java
new file mode 100644
index 0000000..ece0e7c
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/security/DummyClass.java
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE
+ * file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file
+ * to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
+ * applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+package org.apache.commons.logging.security;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+public class DummyClass {
+
+    public DummyClass() {
+        Log log = LogFactory.getLog(DummyClass.class);
+        log.info("Some log message");
+    }
+}
diff --git a/src/test/java/org/apache/commons/logging/security/MockSecurityManager.java b/src/test/java/org/apache/commons/logging/security/MockSecurityManager.java
new file mode 100644
index 0000000..0d9b4de
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/security/MockSecurityManager.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.security;
+
+import java.io.FilePermission;
+import java.security.Permission;
+import java.security.Permissions;
+
+
+/**
+ * Custom implementation of a security manager, so we can control the
+ * security environment for tests in this package.
+ */
+public class MockSecurityManager extends SecurityManager {
+
+    private final Permissions permissions = new Permissions();
+    private static final Permission setSecurityManagerPerm =
+        new RuntimePermission("setSecurityManager");
+
+    private int untrustedCodeCount = 0;
+
+    public MockSecurityManager() {
+        permissions.add(setSecurityManagerPerm);
+    }
+
+    /**
+     * Define the set of permissions to be granted to classes in the o.a.c.l package,
+     * but NOT to unit-test classes in o.a.c.l.security package.
+     */
+    public void addPermission(Permission p) {
+        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;
+    }
+
+    public void checkPermission(Permission p) throws SecurityException {
+        if (setSecurityManagerPerm.implies(p)) {
+            // ok, allow this; we don't want to block any calls to setSecurityManager
+            // otherwise this custom security manager cannot be reset to the original.
+            // System.out.println("setSecurityManager: granted");
+            return;
+        }
+
+        // Allow read-only access to files, as this is needed to load classes!
+        // Ideally, we would limit this to just .class and .jar files.
+        if (p instanceof FilePermission) {
+          FilePermission fp = (FilePermission) p;
+          if (fp.getActions().equals("read")) {
+            // System.out.println("Permit read of files");
+            return;
+          }
+        }
+
+        System.out.println("\n\ntesting permission:" + p.getClass() + ":"+ p);
+
+        Exception e = new Exception();
+        e.fillInStackTrace();
+        StackTraceElement[] stack = e.getStackTrace();
+
+        // scan the call stack from most recent to oldest.
+        // start at 1 to skip the entry in the stack for this method
+        for(int i=1; i<stack.length; ++i) {
+            String cname = stack[i].getClassName();
+            System.out.println("" + i + ":" + stack[i].getClassName() +
+              "." + stack[i].getMethodName() + ":" + stack[i].getLineNumber());
+
+            if (cname.equals("java.util.logging.Handler") && stack[i].getMethodName().equals("setLevel")) {
+                // LOGGING CODE CAUSES ACCESSCONTROLEXCEPTION
+                // http://www-01.ibm.com/support/docview.wss?uid=swg1IZ51152
+                return;
+            }
+
+            if (cname.equals("java.util.logging.Level") && stack[i].getMethodName().equals("getLocalizedLevelName")) {
+                // LOGGING-156: OpenJDK 1.7 JULI code (java.util.logging.Level#getLocalizedLevelName)
+                // calls ResourceBundle#getBundle() without using AccessController#doPrivileged()
+                // requiring RuntimePermission: "accessClassInPackage.sun.util.logging.resources"
+                return;
+            }
+            
+            if (cname.equals("java.security.AccessController")) {
+                // Presumably method name equals "doPrivileged"
+                //
+                // The previous iteration of this loop verified that the
+                // PrivilegedAction.run method associated with this
+                // doPrivileged method call had the right permissions,
+                // so we just return here. Effectively, the method invoking
+                // doPrivileged asserted that it checked the input params
+                // and found them safe, and that code is trusted, so we
+                // don't need to check the trust level of code higher in
+                // the call stack.
+                System.out.println("Access controller found: returning");
+                return;
+            } else if (cname.startsWith("java.")
+                || cname.startsWith("javax.")
+                || cname.startsWith("junit.")
+                || cname.startsWith("org.apache.tools.ant.")
+                || cname.startsWith("sun.")) {
+                // Code in these packages is trusted if the caller is trusted.
+                //
+                // TODO: maybe check class is loaded via system loader or similar rather
+                // than checking name? Trusted domains may be different in alternative
+                // jvms..
+            } else if (cname.startsWith("org.apache.commons.logging.security")) {
+                // this is the unit test code; treat this like an untrusted client
+                // app that is using JCL
+                ++untrustedCodeCount;
+                System.out.println("Untrusted code [testcase] found");
+                throw new SecurityException("Untrusted code [testcase] found");
+            } else if (cname.startsWith("org.apache.commons.logging.")) {
+                if (permissions.implies(p)) {
+                    // Code here is trusted if the caller is trusted
+                    System.out.println("Permission in allowed set for JCL class");
+                } else {
+                    System.out.println("Permission refused:" + p.getClass() + ":" + p);
+                    throw new SecurityException("Permission refused:" + p.getClass() + ":" + p);
+                }
+            } else {
+                // we found some code that is not trusted to perform this operation.
+                System.out.println("Unexpected code: permission refused:" + p.getClass() + ":" + p);
+                throw new SecurityException("Unexpected code: permission refused:" + p.getClass() + ":" + p);
+            }
+        }
+    }
+}
diff --git a/src/test/java/org/apache/commons/logging/security/SecurityAllowedTestCase.java b/src/test/java/org/apache/commons/logging/security/SecurityAllowedTestCase.java
new file mode 100644
index 0000000..b957237
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/security/SecurityAllowedTestCase.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.security;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.security.AllPermission;
+import java.util.Hashtable;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.logging.PathableClassLoader;
+import org.apache.commons.logging.PathableTestSuite;
+
+/**
+ * Tests for logging with a security policy that allows JCL access to everything.
+ * <p>
+ * This class has only one unit test, as we are (in part) checking behaviour in
+ * the static block of the LogFactory class. As that class cannot be unloaded after
+ * being loaded into a classloader, the only workaround is to use the
+ * PathableClassLoader approach to ensure each test is run in its own
+ * classloader, and use a separate testcase class for each test.
+ */
+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 {
+
+        /**
+         * Generated serial version ID.
+         */
+        private static final long serialVersionUID = 8941017300059246720L;
+    }
+
+    /**
+     * Return the tests included in this test suite.
+     */
+    public static Test suite() throws Exception {
+        PathableClassLoader parent = new PathableClassLoader(null);
+        parent.useExplicitLoader("junit.", Test.class.getClassLoader());
+        parent.addLogicalLib("commons-logging");
+        parent.addLogicalLib("testclasses");
+
+        Class testClass = parent.loadClass(
+            "org.apache.commons.logging.security.SecurityAllowedTestCase");
+        return new PathableTestSuite(testClass, parent);
+    }
+
+    public void setUp() {
+        // save security manager so it can be restored in tearDown
+        oldSecMgr = System.getSecurityManager();
+    }
+
+    public void tearDown() {
+        // Restore, so other tests don't get stuffed up if a test
+        // sets a custom security manager.
+        System.setSecurityManager(oldSecMgr);
+    }
+
+    /**
+     * Test what happens when JCL is run with all permissions enabled. Custom
+     * overrides should take effect.
+     */
+    public void testAllAllowed() {
+        System.setProperty(
+                LogFactory.HASHTABLE_IMPLEMENTATION_PROPERTY,
+                CustomHashtable.class.getName());
+        MockSecurityManager mySecurityManager = new MockSecurityManager();
+        mySecurityManager.addPermission(new AllPermission());
+        System.setSecurityManager(mySecurityManager);
+
+        try {
+            // Use reflection so that we can control exactly when the static
+            // initialiser for the LogFactory class is executed.
+            Class c = this.getClass().getClassLoader().loadClass(
+                    "org.apache.commons.logging.LogFactory");
+            Method m = c.getMethod("getLog", new Class[] {Class.class});
+            Log log = (Log) m.invoke(null, new Object[] {this.getClass()});
+
+            // Check whether we had any security exceptions so far (which were
+            // caught by the code). We should not, as every secure operation
+            // should be wrapped in an AccessController. Any security exceptions
+            // indicate a path that is missing an appropriate AccessController.
+            //
+            // We don't wait until after the log.info call to get this count
+            // because java.util.logging tries to load a resource bundle, which
+            // requires permission accessClassInPackage. JCL explicitly does not
+            // wrap calls to log methods in AccessControllers because writes to
+            // a log file *should* only be permitted if the original caller is
+            // trusted to access that file.
+            int untrustedCodeCount = mySecurityManager.getUntrustedCodeCount();
+            log.info("testing");
+
+            // check that the default map implementation was loaded, as JCL was
+            // forbidden from reading the HASHTABLE_IMPLEMENTATION_PROPERTY property.
+            System.setSecurityManager(null);
+            Field factoryField = c.getDeclaredField("factories");
+            factoryField.setAccessible(true);
+            Object factoryTable = factoryField.get(null);
+            assertNotNull(factoryTable);
+            assertEquals(CustomHashtable.class.getName(), factoryTable.getClass().getName());
+
+            // we better compare that we have no security exception during the call to log
+            // IBM JVM tries to load bundles during the invoke call, which increase the count
+            assertEquals("Untrusted code count", untrustedCodeCount, mySecurityManager.getUntrustedCodeCount());
+        } catch(Throwable t) {
+            // Restore original security manager so output can be generated; the
+            // PrintWriter constructor tries to read the line.separator
+            // system property.
+            System.setSecurityManager(oldSecMgr);
+            StringWriter sw = new StringWriter();
+            PrintWriter pw = new PrintWriter(sw);
+            t.printStackTrace(pw);
+            fail("Unexpected exception:" + t.getMessage() + ":" + sw.toString());
+        }
+    }
+}
diff --git a/src/test/java/org/apache/commons/logging/security/SecurityForbiddenTestCase.java b/src/test/java/org/apache/commons/logging/security/SecurityForbiddenTestCase.java
new file mode 100644
index 0000000..08c5af9
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/security/SecurityForbiddenTestCase.java
@@ -0,0 +1,191 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.security;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Hashtable;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.logging.PathableClassLoader;
+import org.apache.commons.logging.PathableTestSuite;
+
+/**
+ * Tests for logging with a security policy that forbids JCL access to anything.
+ * <p>
+ * Performing tests with security permissions disabled is tricky, as building error
+ * messages on failure requires certain security permissions. If the security manager
+ * blocks these, then the test can fail without the error messages being output.
+ * <p>
+ * This class has only one unit test, as we are (in part) checking behaviour in
+ * the static block of the LogFactory class. As that class cannot be unloaded after
+ * being loaded into a classloader, the only workaround is to use the
+ * PathableClassLoader approach to ensure each test is run in its own
+ * classloader, and use a separate testcase class for each test.
+ */
+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 {
+
+        /**
+         * Generated serial version ID.
+         */
+        private static final long serialVersionUID = 7224652794746236024L;
+    }
+
+    /**
+     * Return the tests included in this test suite.
+     */
+    public static Test suite() throws Exception {
+        PathableClassLoader parent = new PathableClassLoader(null);
+        parent.useExplicitLoader("junit.", Test.class.getClassLoader());
+        parent.addLogicalLib("commons-logging");
+        parent.addLogicalLib("testclasses");
+
+        Class testClass = parent.loadClass(
+            "org.apache.commons.logging.security.SecurityForbiddenTestCase");
+        return new PathableTestSuite(testClass, parent);
+    }
+
+    public void setUp() {
+        // save security manager so it can be restored in tearDown
+        oldSecMgr = System.getSecurityManager();
+        
+        PathableClassLoader classLoader = new PathableClassLoader(null);
+        classLoader.addLogicalLib("commons-logging");
+        classLoader.addLogicalLib("testclasses");
+        
+        otherClassLoader = classLoader;
+    }
+
+    public void tearDown() {
+        // Restore, so other tests don't get stuffed up if a test
+        // sets a custom security manager.
+        System.setSecurityManager(oldSecMgr);
+    }
+
+    /**
+     * Test what happens when JCL is run with absolutely no security
+     * privileges at all, including reading system properties. Everything
+     * should fall back to the built-in defaults.
+     */
+    public void testAllForbidden() {
+        System.setProperty(
+                LogFactory.HASHTABLE_IMPLEMENTATION_PROPERTY,
+                CustomHashtable.class.getName());
+        MockSecurityManager mySecurityManager = new MockSecurityManager();
+
+        System.setSecurityManager(mySecurityManager);
+
+        try {
+            // Use reflection so that we can control exactly when the static
+            // initialiser for the LogFactory class is executed.
+            Class c = this.getClass().getClassLoader().loadClass(
+                    "org.apache.commons.logging.LogFactory");
+            Method m = c.getMethod("getLog", new Class[] {Class.class});
+            Log log = (Log) m.invoke(null, new Object[] {this.getClass()});
+            log.info("testing");
+
+            // check that the default map implementation was loaded, as JCL was
+            // forbidden from reading the HASHTABLE_IMPLEMENTATION_PROPERTY property.
+            //
+            // The default is either the java Hashtable class (java < 1.2) or the
+            // JCL WeakHashtable (java >= 1.3).
+            System.setSecurityManager(oldSecMgr);
+            Field factoryField = c.getDeclaredField("factories");
+            factoryField.setAccessible(true);
+            Object factoryTable = factoryField.get(null);
+            assertNotNull(factoryTable);
+            String ftClassName = factoryTable.getClass().getName();
+            assertTrue("Custom hashtable unexpectedly used",
+                    !CustomHashtable.class.getName().equals(ftClassName));
+
+            assertEquals(0, mySecurityManager.getUntrustedCodeCount());
+        } catch(Throwable t) {
+            // Restore original security manager so output can be generated; the
+            // PrintWriter constructor tries to read the line.separator
+            // system property.
+            System.setSecurityManager(oldSecMgr);
+            StringWriter sw = new StringWriter();
+            PrintWriter pw = new PrintWriter(sw);
+            t.printStackTrace(pw);
+            fail("Unexpected exception:" + t.getMessage() + ":" + sw.toString());
+        }
+    }
+
+    /**
+     * Test what happens when JCL is run with absolutely no security
+     * privileges at all and a class loaded with a different classloader
+     * than the context classloader of the current thread tries to log something. 
+     */
+    public void testContextClassLoader() {
+        System.setProperty(
+                LogFactory.HASHTABLE_IMPLEMENTATION_PROPERTY,
+                CustomHashtable.class.getName());
+        MockSecurityManager mySecurityManager = new MockSecurityManager();
+
+        System.setSecurityManager(mySecurityManager);
+
+        try {
+            // load a dummy class with another classloader
+            // to force a SecurityException when the LogFactory calls
+            // Thread.getCurrentThread().getContextClassLoader()
+            loadClass("org.apache.commons.logging.security.DummyClass", otherClassLoader);
+
+            System.setSecurityManager(oldSecMgr);
+            assertEquals(0, mySecurityManager.getUntrustedCodeCount());
+        } catch(Throwable t) {
+            // Restore original security manager so output can be generated; the
+            // PrintWriter constructor tries to read the line.separator
+            // system property.
+            System.setSecurityManager(oldSecMgr);
+            StringWriter sw = new StringWriter();
+            PrintWriter pw = new PrintWriter(sw);
+            t.printStackTrace(pw);
+            fail("Unexpected exception:" + t.getMessage() + ":" + sw.toString());
+        }
+    }
+
+    /**
+     * Loads a class with the given classloader.
+     */
+    private Object loadClass(String name, ClassLoader classLoader) {
+        try {
+            Class clazz = classLoader.loadClass(name);
+            Object obj = clazz.newInstance();
+            return obj;
+        } catch ( Exception e ) {
+            StringWriter sw = new StringWriter();
+            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/servlet/BasicServletTestCase.java b/src/test/java/org/apache/commons/logging/servlet/BasicServletTestCase.java
new file mode 100644
index 0000000..e1ecdbb
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/servlet/BasicServletTestCase.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.servlet;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+import org.apache.commons.logging.PathableClassLoader;
+import org.apache.commons.logging.PathableTestSuite;
+import org.apache.commons.logging.impl.ServletContextCleaner;
+
+
+/**
+ * Tests for ServletContextCleaner utility class.
+ */
+
+public class BasicServletTestCase extends TestCase {
+
+    /**
+     * Return the tests included in this test suite.
+     */
+    public static Test suite() throws Exception {
+        // LogFactory in parent
+        // LogFactory in child (loads test)
+        // LogFactory in tccl
+        //
+        // Having the test loaded via a loader above the tccl emulates the situation
+        // where a web.xml file specifies ServletContextCleaner as a listener, and
+        // that class is deployed via a shared classloader.
+
+        PathableClassLoader parent = new PathableClassLoader(null);
+        parent.useExplicitLoader("junit.", Test.class.getClassLoader());
+        parent.addLogicalLib("commons-logging");
+        parent.addLogicalLib("servlet-api");
+
+        PathableClassLoader child = new PathableClassLoader(parent);
+        child.setParentFirst(false);
+        child.addLogicalLib("commons-logging");
+        child.addLogicalLib("testclasses");
+
+        PathableClassLoader tccl = new PathableClassLoader(child);
+        tccl.setParentFirst(false);
+        tccl.addLogicalLib("commons-logging");
+
+        Class testClass = child.loadClass(BasicServletTestCase.class.getName());
+        return new PathableTestSuite(testClass, tccl);
+    }
+
+    /**
+     * Test that calling ServletContextCleaner.contextDestroyed doesn't crash.
+     * Testing anything else is rather difficult...
+     */
+    public void testBasics() {
+        ServletContextCleaner scc = new ServletContextCleaner();
+        scc.contextDestroyed(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
new file mode 100644
index 0000000..c3a89b6
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/simple/CustomConfigTestCase.java
@@ -0,0 +1,277 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.simple;
+
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.apache.commons.logging.DummyException;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.logging.PathableClassLoader;
+import org.apache.commons.logging.PathableTestSuite;
+import org.apache.commons.logging.impl.SimpleLog;
+
+
+/**
+ * <p>TestCase for simple logging when running with custom configuration
+ * properties.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1432587 $ $Date: 2013-01-13 12:11:32 +0100 (Sun, 13 Jan 2013) $
+ */
+public class CustomConfigTestCase extends DefaultConfigTestCase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * <p>The expected log records.</p>
+     */
+    protected List expected;
+
+
+    /**
+     * <p>The message levels that should have been logged.</p>
+     */
+    /*
+    protected Level testLevels[] =
+    { Level.FINE, Level.INFO, Level.WARNING, Level.SEVERE, Level.SEVERE };
+    */
+
+
+    /**
+     * <p>The message strings that should have been logged.</p>
+     */
+    protected String testMessages[] =
+    { "debug", "info", "warn", "error", "fatal" };
+
+
+    // ------------------------------------------- JUnit Infrastructure Methods
+
+    /**
+     * 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.simple.DecoratedSimpleLog");
+        System.setProperty(
+            "org.apache.commons.logging.simplelog.defaultlog",
+            "debug");
+    }
+
+    /**
+     * Set up instance variables required by this test case.
+     */
+    public void setUp() throws Exception {
+        LogFactory.releaseAll();
+        setProperties();
+        expected = new ArrayList();
+        setUpFactory();
+        setUpLog("DecoratedLogger");
+    }
+
+
+    /**
+     * Return the tests included in this test suite.
+     * <p>
+     * We need to use a PathableClassLoader here because the SimpleLog class
+     * is a pile of junk and chock-full of static variables. Any other test
+     * (like simple.CustomConfigTestCase) that has used the SimpleLog class
+     * will already have caused it to do once-only initialisation that we
+     * can't reset, even by calling LogFactory.releaseAll, because of those
+     * ugly statics. The only clean solution is to load a clean copy of
+     * commons-logging including SimpleLog via a nice clean classloader.
+     * Or we could fix SimpleLog to be sane...
+     */
+    public static Test suite() throws Exception {
+        Class thisClass = CustomConfigTestCase.class;
+
+        PathableClassLoader loader = new PathableClassLoader(null);
+        loader.useExplicitLoader("junit.", Test.class.getClassLoader());
+        loader.addLogicalLib("testclasses");
+        loader.addLogicalLib("commons-logging");
+        
+        Class testClass = loader.loadClass(thisClass.getName());
+        return new PathableTestSuite(testClass, loader);
+    }
+
+    /**
+     * Tear down instance variables required by this test case.
+     */
+    public void tearDown() {
+        super.tearDown();
+        expected = null;
+    }
+
+
+    // ----------------------------------------------------------- Test Methods
+
+
+    // 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
+    public void testSerializable() throws Exception {
+
+        ((DecoratedSimpleLog) log).clearCache();
+        logPlainMessages();
+        super.testSerializable();
+        logExceptionMessages();
+        checkExpected();
+
+    }
+
+
+    // -------------------------------------------------------- Support Methods
+
+
+    // Check the decorated log instance
+    protected void checkDecorated() {
+
+        assertNotNull("Log exists", log);
+        assertEquals("Log class",
+                     "org.apache.commons.logging.simple.DecoratedSimpleLog",
+                     log.getClass().getName());
+
+        // Can we call level checkers with no exceptions?
+        assertTrue(log.isDebugEnabled());
+        assertTrue(log.isErrorEnabled());
+        assertTrue(log.isFatalEnabled());
+        assertTrue(log.isInfoEnabled());
+        assertTrue(!log.isTraceEnabled());
+        assertTrue(log.isWarnEnabled());
+
+        // Can we retrieve the current log level?
+        assertEquals(SimpleLog.LOG_LEVEL_DEBUG, ((SimpleLog) log).getLevel());
+
+        // Can we validate the extra exposed properties?
+        checkDecoratedDateTime();
+        assertEquals("DecoratedLogger",
+                     ((DecoratedSimpleLog) log).getLogName());
+        checkShowDateTime();
+        assertTrue(((DecoratedSimpleLog) log).getShowShortName());
+
+    }
+    
+    /** Hook for subclassses */
+    protected void checkShowDateTime() {
+        assertTrue(!((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() {
+
+        List acts = ((DecoratedSimpleLog) log).getCache();
+        Iterator exps = expected.iterator();
+        int n = 0;
+        while (exps.hasNext()) {
+            LogRecord exp = (LogRecord) exps.next();
+            LogRecord act = (LogRecord) acts.get(n++);
+            assertEquals("Row " + n + " type", exp.type, act.type);
+            assertEquals("Row " + n + " message", exp.message, act.message);
+            assertEquals("Row " + n + " throwable", exp.t, act.t);
+        }
+
+    }
+
+
+    // Check the standard log instance
+    protected void checkStandard() {
+
+        checkDecorated();
+
+    }
+
+
+    // Log the messages with exceptions
+    protected void logExceptionMessages() {
+
+        // Generate log records
+        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);
+
+        // Record the log records we expect
+        expected.add(new LogRecord(SimpleLog.LOG_LEVEL_DEBUG, "debug", t));
+        expected.add(new LogRecord(SimpleLog.LOG_LEVEL_INFO, "info", t));
+        expected.add(new LogRecord(SimpleLog.LOG_LEVEL_WARN, "warn", t));
+        expected.add(new LogRecord(SimpleLog.LOG_LEVEL_ERROR, "error", t));
+        expected.add(new LogRecord(SimpleLog.LOG_LEVEL_FATAL, "fatal", t));
+
+    }
+
+
+    // Log the plain messages
+    protected void logPlainMessages() {
+
+        // Generate log records
+        log.trace("trace"); // Should not actually get logged
+        log.debug("debug");
+        log.info("info");
+        log.warn("warn");
+        log.error("error");
+        log.fatal("fatal");
+
+        // Record the log records we expect
+        expected.add(new LogRecord(SimpleLog.LOG_LEVEL_DEBUG, "debug", null));
+        expected.add(new LogRecord(SimpleLog.LOG_LEVEL_INFO, "info", null));
+        expected.add(new LogRecord(SimpleLog.LOG_LEVEL_WARN, "warn", null));
+        expected.add(new LogRecord(SimpleLog.LOG_LEVEL_ERROR, "error", null));
+        expected.add(new LogRecord(SimpleLog.LOG_LEVEL_FATAL, "fatal", null));
+
+    }
+
+
+}
diff --git a/src/test/java/org/apache/commons/logging/simple/DateTimeCustomConfigTestCase.java b/src/test/java/org/apache/commons/logging/simple/DateTimeCustomConfigTestCase.java
new file mode 100644
index 0000000..5e72718
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/simple/DateTimeCustomConfigTestCase.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.simple;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import junit.framework.Test;
+
+import org.apache.commons.logging.PathableClassLoader;
+import org.apache.commons.logging.PathableTestSuite;
+
+
+/**
+ * Tests custom date time format configuration
+ */
+public class DateTimeCustomConfigTestCase extends CustomConfigTestCase {
+
+    // ----------------------------------------------------------- Constructors
+
+    /**
+     * Return the tests included in this test suite.
+     * <p>
+     * We need to use a PathableClassLoader here because the SimpleLog class
+     * is a pile of junk and chock-full of static variables. Any other test
+     * (like simple.CustomConfigTestCase) that has used the SimpleLog class
+     * will already have caused it to do once-only initialisation that we
+     * can't reset, even by calling LogFactory.releaseAll, because of those
+     * ugly statics. The only clean solution is to load a clean copy of
+     * commons-logging including SimpleLog via a nice clean classloader.
+     * Or we could fix SimpleLog to be sane...
+     */
+    public static Test suite() throws Exception {
+        Class thisClass = DateTimeCustomConfigTestCase.class;
+
+        PathableClassLoader loader = new PathableClassLoader(null);
+        loader.useExplicitLoader("junit.", Test.class.getClassLoader());
+        loader.addLogicalLib("testclasses");
+        loader.addLogicalLib("commons-logging");
+
+        Class testClass = loader.loadClass(thisClass.getName());
+        return new PathableTestSuite(testClass, loader);
+    }
+
+
+    /**
+     * Set up system properties required by this unit test. Here, we
+     * set up the props defined in the parent class setProperties method,
+     * and add a few to configure the SimpleLog class date/time output.
+     */
+    public void setProperties() {
+        super.setProperties();
+
+        System.setProperty(
+            "org.apache.commons.logging.simplelog.dateTimeFormat",
+            "dd.mm.yyyy");
+        System.setProperty(
+            "org.apache.commons.logging.simplelog.showdatetime",
+            "true");
+    }
+
+    /**
+     * Set up instance variables required by this test case.
+     */
+    public void setUp() throws Exception {
+        super.setUp();
+    }
+
+
+    // ----------------------------------------------------------- Methods
+
+    /** Checks that the date time format has been successfully set */
+    protected void checkDecoratedDateTime() {
+        assertEquals("Expected date format to be set", "dd.mm.yyyy",
+                     ((DecoratedSimpleLog) log).getDateTimeFormat());
+        // try the formatter
+        Date now = new Date();
+        DateFormat formatter = ((DecoratedSimpleLog) log).getDateTimeFormatter();
+        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 */
+    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
new file mode 100644
index 0000000..0129bcd
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/simple/DecoratedSimpleLog.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.simple;
+
+
+import java.util.ArrayList;
+import java.util.List;
+import java.text.DateFormat;
+import org.apache.commons.logging.impl.SimpleLog;
+
+
+/**
+ * <p>Decorated instance of SimpleLog to expose internal state and
+ * support buffered output.</p>
+ */
+
+public class DecoratedSimpleLog extends SimpleLog {
+
+
+    // ------------------------------------------------------------ Constructor
+
+
+    /**
+     * Generated serial version ID.
+     */
+    private static final long serialVersionUID = 196544280770017153L;
+
+    public DecoratedSimpleLog(String name) {
+        super(name);
+    }
+
+
+    // ------------------------------------------------------------- Properties
+
+    public DateFormat getDateTimeFormatter() {
+        return dateFormatter;
+    }
+
+
+    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
+    protected void log(int type, Object message, 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();
+    }
+
+
+    // Return cache
+    public List getCache() {
+        return this.cache;
+    }
+
+
+}
diff --git a/src/test/java/org/apache/commons/logging/simple/DefaultConfigTestCase.java b/src/test/java/org/apache/commons/logging/simple/DefaultConfigTestCase.java
new file mode 100644
index 0000000..37d3847
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/simple/DefaultConfigTestCase.java
@@ -0,0 +1,249 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.simple;
+
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.logging.PathableClassLoader;
+import org.apache.commons.logging.PathableTestSuite;
+import org.apache.commons.logging.impl.SimpleLog;
+
+
+/**
+ * <p>TestCase for simple logging when running with zero configuration
+ * other than selecting the SimpleLog implementation.</p>
+ *
+ * @author Craig R. McClanahan
+ * @version $Revision: 1432587 $ $Date: 2013-01-13 12:11:32 +0100 (Sun, 13 Jan 2013) $
+ */
+
+public class DefaultConfigTestCase extends TestCase {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * <p>The {@link LogFactory} implementation we have selected.</p>
+     */
+    protected LogFactory factory = null;
+
+
+    /**
+     * <p>The {@link Log} implementation we have selected.</p>
+     */
+    protected Log log = null;
+
+
+    // ------------------------------------------- JUnit Infrastructure Methods
+
+
+    /**
+     * Return the tests included in this test suite.
+     * <p>
+     * We need to use a PathableClassLoader here because the SimpleLog class
+     * is a pile of junk and chock-full of static variables. Any other test
+     * (like simple.CustomConfigTestCase) that has used the SimpleLog class
+     * will already have caused it to do once-only initialisation that we
+     * can't reset, even by calling LogFactory.releaseAll, because of those
+     * ugly statics. The only clean solution is to load a clean copy of
+     * commons-logging including SimpleLog via a nice clean classloader.
+     * Or we could fix SimpleLog to be sane...
+     */
+    public static Test suite() throws Exception {
+        Class thisClass = DefaultConfigTestCase.class;
+
+        PathableClassLoader loader = new PathableClassLoader(null);
+        loader.useExplicitLoader("junit.", Test.class.getClassLoader());
+        loader.addLogicalLib("testclasses");
+        loader.addLogicalLib("commons-logging");
+        
+        Class testClass = loader.loadClass(thisClass.getName());
+        return new PathableTestSuite(testClass, loader);
+    }
+
+    /**
+     * 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.
+     */
+    public void setUp() throws Exception {
+        LogFactory.releaseAll();
+        setProperties();
+        setUpFactory();
+        setUpLog("TestLogger");
+    }
+
+    /**
+     * Tear down instance variables required by this test case.
+     */
+    public void tearDown() {
+        log = null;
+        factory = null;
+        LogFactory.releaseAll();
+    }
+
+
+    // ----------------------------------------------------------- Test Methods
+
+
+    // 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());
+
+        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
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ObjectOutputStream oos = new ObjectOutputStream(baos);
+        oos.writeObject(log);
+        oos.close();
+        ByteArrayInputStream bais =
+            new ByteArrayInputStream(baos.toByteArray());
+        ObjectInputStream ois = new ObjectInputStream(bais);
+        log = (Log) ois.readObject();
+        ois.close();
+
+        // Check the characteristics of the resulting object
+        checkStandard();
+
+    }
+
+
+    // -------------------------------------------------------- Support Methods
+
+
+
+    // Check the decorated log instance
+    protected void checkDecorated() {
+
+        assertNotNull("Log exists", log);
+        assertEquals("Log class",
+                     "org.apache.commons.logging.simple.DecoratedSimpleLog",
+                     log.getClass().getName());
+
+        // Can we call level checkers with no exceptions?
+        assertTrue(!log.isDebugEnabled());
+        assertTrue(log.isErrorEnabled());
+        assertTrue(log.isFatalEnabled());
+        assertTrue(log.isInfoEnabled());
+        assertTrue(!log.isTraceEnabled());
+        assertTrue(log.isWarnEnabled());
+
+        // Can we retrieve the current log level?
+        assertEquals(SimpleLog.LOG_LEVEL_INFO, ((SimpleLog) log).getLevel());
+
+        // Can we validate the extra exposed properties?
+        assertEquals("yyyy/MM/dd HH:mm:ss:SSS zzz",
+                     ((DecoratedSimpleLog) log).getDateTimeFormat());
+        assertEquals("DecoratedLogger",
+                     ((DecoratedSimpleLog) log).getLogName());
+        assertTrue(!((DecoratedSimpleLog) log).getShowDateTime());
+        assertTrue(((DecoratedSimpleLog) log).getShowShortName());
+
+    }
+
+
+    // Check the standard log instance
+    protected void checkStandard() {
+
+        assertNotNull("Log exists", log);
+        assertEquals("Log class",
+                     "org.apache.commons.logging.impl.SimpleLog",
+                     log.getClass().getName());
+
+        // Can we call level checkers with no exceptions?
+        assertTrue(!log.isDebugEnabled());
+        assertTrue(log.isErrorEnabled());
+        assertTrue(log.isFatalEnabled());
+        assertTrue(log.isInfoEnabled());
+        assertTrue(!log.isTraceEnabled());
+        assertTrue(log.isWarnEnabled());
+
+        // Can we retrieve the current log level?
+        assertEquals(SimpleLog.LOG_LEVEL_INFO, ((SimpleLog) log).getLevel());
+
+    }
+
+
+    // Set up decorated log instance
+    protected void setUpDecorated(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(String name) throws Exception {
+        log = LogFactory.getLog(name);
+    }
+
+
+}
diff --git a/src/test/java/org/apache/commons/logging/simple/LogRecord.java b/src/test/java/org/apache/commons/logging/simple/LogRecord.java
new file mode 100644
index 0000000..0c6d784
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/simple/LogRecord.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.commons.logging.simple;
+
+
+import java.io.Serializable;
+
+
+public class LogRecord implements Serializable {
+
+
+    /**
+     * Generated serial version ID.
+     */
+    private static final long serialVersionUID = -5254831759209770665L;
+    
+    public LogRecord(int type, Object message, 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/BadTCCLTestCase.java b/src/test/java/org/apache/commons/logging/tccl/BadTCCLTestCase.java
new file mode 100644
index 0000000..7211156
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/tccl/BadTCCLTestCase.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.logging.tccl;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.logging.PathableClassLoader;
+import org.apache.commons.logging.PathableTestSuite;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+/**
+ * Simulates the case when TCCL is badly set and cannot load JCL.
+ */
+public class BadTCCLTestCase extends TestCase {
+
+    public static Test suite() throws Exception {
+        PathableClassLoader contextClassLoader = new PathableClassLoader(null);
+        contextClassLoader.useExplicitLoader("junit.", Test.class.getClassLoader());
+        PathableTestSuite suite = new PathableTestSuite(BadTCCLTestCase.class, contextClassLoader);
+        return suite;
+    }
+
+    // test methods
+    
+    /**
+     * This test just tests that a log implementation can be found
+     * by the LogFactory.
+     */
+    public void testGetLog() {
+         Log log = LogFactory.getLog(BadTCCLTestCase.class);
+         log.debug("Hello, Mum");
+    }
+}
diff --git a/src/test/java/org/apache/commons/logging/tccl/NullTCCLTestCase.java b/src/test/java/org/apache/commons/logging/tccl/NullTCCLTestCase.java
new file mode 100644
index 0000000..ae5ab4a
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/tccl/NullTCCLTestCase.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.logging.tccl;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.logging.PathableTestSuite;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+/**
+ * Simulates the case when TCCL is set to NULL.
+ */
+public class NullTCCLTestCase extends TestCase {
+
+    public static Test suite() throws Exception {
+        PathableTestSuite suite = new PathableTestSuite(NullTCCLTestCase.class, null);
+        return suite;
+    }
+
+    // test methods
+    
+    /**
+     * This test just tests that a log implementation can be found
+     * by the LogFactory.
+     */
+    public void testGetLog() {
+         Log log = LogFactory.getLog(NullTCCLTestCase.class);
+         log.debug("Hello, Mum");
+    }
+}
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
new file mode 100644
index 0000000..214f7b3
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/tccl/custom/MyLog.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.logging.tccl.custom;
+
+import org.apache.commons.logging.Log;
+
+public class MyLog implements Log {
+
+    public MyLog(String category) {}
+
+    public boolean isDebugEnabled() { return false; }
+    public boolean isErrorEnabled() { return false; }
+    public boolean isFatalEnabled() { return false; }
+    public boolean isInfoEnabled()  { return false; }
+    public boolean isTraceEnabled() { return false; }
+    public boolean isWarnEnabled()  { return false; }
+
+    public void trace(Object message) {}
+    public void trace(Object message, Throwable t) {}
+    public void debug(Object message) {}
+    public void debug(Object message, Throwable t) {}
+    public void info(Object message) {}
+    public void info(Object message, Throwable t) {}
+    public void warn(Object message) {}
+    public void warn(Object message, Throwable t) {}
+    public void error(Object message) {}
+    public void error(Object message, Throwable t) {}
+    public void fatal(Object message) {}
+    public void fatal(Object message, Throwable t) {}
+}
diff --git a/src/test/java/org/apache/commons/logging/tccl/custom/MyLogFactoryImpl.java b/src/test/java/org/apache/commons/logging/tccl/custom/MyLogFactoryImpl.java
new file mode 100644
index 0000000..63d5ac6
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/tccl/custom/MyLogFactoryImpl.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.tccl.custom;
+
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+public class MyLogFactoryImpl extends LogFactory {
+    public Object getAttribute(String name) { return null; }
+    public String[] getAttributeNames() { return null; }
+    public Log getInstance(Class clazz) { return null; }
+    public Log getInstance(String name) { return null; }
+    public void release() {}
+    public void removeAttribute(String name) {}
+    public void setAttribute(String name, Object value) {}
+}
diff --git a/src/test/java/org/apache/commons/logging/tccl/log/TcclDisabledTestCase.java b/src/test/java/org/apache/commons/logging/tccl/log/TcclDisabledTestCase.java
new file mode 100644
index 0000000..152af3c
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/tccl/log/TcclDisabledTestCase.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.tccl.log;
+
+
+import java.net.URL;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogConfigurationException;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.logging.PathableClassLoader;
+import org.apache.commons.logging.PathableTestSuite;
+
+
+/**
+ * Verify that by default LogFactoryImpl is loaded from the tccl classloader.
+ */
+
+public class TcclDisabledTestCase extends TestCase {
+
+    public static final String MY_LOG_PKG = 
+        "org.apache.commons.logging.tccl.custom";
+
+    public static final String MY_LOG_IMPL =
+        MY_LOG_PKG + ".MyLog";
+
+    // ------------------------------------------- JUnit Infrastructure Methods
+
+    /**
+     * Return the tests included in this test suite.
+     */
+    public static Test suite() throws Exception {
+        Class thisClass = TcclDisabledTestCase.class;
+
+        // Determine the URL to this .class file, so that we can then
+        // append the priority dirs to it. For tidiness, load this
+        // class through a dummy loader though this is not absolutely
+        // necessary...
+        PathableClassLoader dummy = new PathableClassLoader(null);
+        dummy.useExplicitLoader("junit.", Test.class.getClassLoader());
+        dummy.addLogicalLib("testclasses");
+        dummy.addLogicalLib("commons-logging");
+        
+        String thisClassPath = thisClass.getName().replace('.', '/') + ".class";
+        URL baseUrl = dummy.findResource(thisClassPath);
+
+        // Now set up the desired classloader hierarchy. Everything goes into
+        // the parent classpath, but we exclude the custom Log class.
+        //
+        // We then create a tccl classloader that can see the custom
+        // Log class. Therefore if that class can be found, then the
+        // TCCL must have been used to load it.
+        PathableClassLoader emptyLoader = new PathableClassLoader(null);
+        
+        PathableClassLoader parentLoader = new PathableClassLoader(null);
+        parentLoader.useExplicitLoader("junit.", Test.class.getClassLoader());
+        parentLoader.addLogicalLib("commons-logging");
+        parentLoader.addLogicalLib("testclasses");
+        // hack to ensure that the testcase classloader can't see
+        // the custom MyLog
+        parentLoader.useExplicitLoader(MY_LOG_PKG + ".", emptyLoader);
+        
+        URL propsEnableUrl = new URL(baseUrl, "props_disable_tccl/");
+        parentLoader.addURL(propsEnableUrl);
+
+        PathableClassLoader tcclLoader = new PathableClassLoader(parentLoader);
+        tcclLoader.addLogicalLib("testclasses");
+
+        Class testClass = parentLoader.loadClass(thisClass.getName());
+        return new PathableTestSuite(testClass, tcclLoader);
+    }
+
+    /**
+     * Set up instance variables required by this test case.
+     */
+    public void setUp() throws Exception {
+        LogFactory.releaseAll();
+    }
+
+    /**
+     * Tear down instance variables required by this test case.
+     */
+    public void tearDown() {
+        LogFactory.releaseAll();
+    }
+
+    // ----------------------------------------------------------- Test Methods
+
+    /**
+     * Verify that MyLog is only loadable via the tccl.
+     */
+    public void testLoader() throws Exception {
+        
+        ClassLoader thisClassLoader = this.getClass().getClassLoader();
+        ClassLoader tcclLoader = Thread.currentThread().getContextClassLoader();
+
+        // the tccl loader should NOT be the same as the loader that loaded this test class.
+        assertNotSame("tccl not same as test classloader", thisClassLoader, tcclLoader);
+
+        // MyLog should not be loadable via parent loader
+        try {
+            Class clazz = thisClassLoader.loadClass(MY_LOG_IMPL);
+            fail("Unexpectedly able to load MyLog via test class classloader");
+            assertNotNull(clazz); // silence warnings about unused var
+        } catch(ClassNotFoundException ex) {
+            // ok, expected
+        }
+        
+        // MyLog should be loadable via tccl loader
+        try {
+            Class clazz = tcclLoader.loadClass(MY_LOG_IMPL);
+            assertNotNull(clazz);
+        } catch(ClassNotFoundException ex) {
+            fail("Unexpectedly unable to load MyLog via tccl classloader");
+        }
+    }
+
+    /**
+     * Verify that the custom Log implementation which is only accessable
+     * via the TCCL has NOT been loaded. Because this is only accessable via the
+     * TCCL, and we've use a commons-logging.properties that disables TCCL loading,
+     * we should see the default Log rather than the custom one.
+     */
+    public void testTcclLoading() throws Exception {
+        LogFactory instance = LogFactory.getFactory();
+        assertEquals(
+                "Correct LogFactory loaded", 
+                "org.apache.commons.logging.impl.LogFactoryImpl",
+                instance.getClass().getName());
+
+        try {
+            Log log = instance.getInstance("test");
+            fail("Unexpectedly succeeded in loading a custom Log class"
+                + " that is only accessable via the tccl.");
+            assertNotNull(log); // silence compiler warning about unused var
+        } catch(LogConfigurationException ex) {
+            // ok, expected
+            int index = ex.getMessage().indexOf(MY_LOG_IMPL);
+            assertTrue("MyLog not found", index >= 0);
+        }
+    }
+}
diff --git a/src/test/java/org/apache/commons/logging/tccl/log/TcclEnabledTestCase.java b/src/test/java/org/apache/commons/logging/tccl/log/TcclEnabledTestCase.java
new file mode 100644
index 0000000..692b53e
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/tccl/log/TcclEnabledTestCase.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.tccl.log;
+
+
+import java.net.URL;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.logging.PathableClassLoader;
+import org.apache.commons.logging.PathableTestSuite;
+
+
+/**
+ * Verify that by default the standard LogFactoryImpl class loads a
+ * custom Log implementation via the TCCL.
+ */
+
+public class TcclEnabledTestCase extends TestCase {
+
+    public static final String MY_LOG_PKG = 
+        "org.apache.commons.logging.tccl.custom";
+
+    public static final String MY_LOG_IMPL =
+        MY_LOG_PKG + ".MyLog";
+
+    // ------------------------------------------- JUnit Infrastructure Methods
+
+    /**
+     * Return the tests included in this test suite.
+     */
+    public static Test suite() throws Exception {
+        Class thisClass = TcclEnabledTestCase.class;
+
+        // Determine the URL to this .class file, so that we can then
+        // append the priority dirs to it. For tidiness, load this
+        // class through a dummy loader though this is not absolutely
+        // necessary...
+        PathableClassLoader dummy = new PathableClassLoader(null);
+        dummy.useExplicitLoader("junit.", Test.class.getClassLoader());
+        dummy.addLogicalLib("testclasses");
+        dummy.addLogicalLib("commons-logging");
+        
+        String thisClassPath = thisClass.getName().replace('.', '/') + ".class";
+        URL baseUrl = dummy.findResource(thisClassPath);
+
+        // Now set up the desired classloader hierarchy. Everything goes into
+        // the parent classpath, but we exclude the custom Log class.
+        //
+        // We then create a tccl classloader that can see the custom
+        // Log class. Therefore if that class can be found, then the
+        // TCCL must have been used to load it.
+        PathableClassLoader emptyLoader = new PathableClassLoader(null);
+        
+        PathableClassLoader parentLoader = new PathableClassLoader(null);
+        parentLoader.useExplicitLoader("junit.", Test.class.getClassLoader());
+        parentLoader.addLogicalLib("commons-logging");
+        parentLoader.addLogicalLib("testclasses");
+        // hack to ensure that the testcase classloader can't see
+        // the custom MyLogFactoryImpl
+        parentLoader.useExplicitLoader(MY_LOG_PKG + ".", emptyLoader);
+        
+        URL propsEnableUrl = new URL(baseUrl, "props_enable_tccl/");
+        parentLoader.addURL(propsEnableUrl);
+
+        PathableClassLoader tcclLoader = new PathableClassLoader(parentLoader);
+        tcclLoader.addLogicalLib("testclasses");
+
+        Class testClass = parentLoader.loadClass(thisClass.getName());
+        return new PathableTestSuite(testClass, tcclLoader);
+    }
+
+    /**
+     * Set up instance variables required by this test case.
+     */
+    public void setUp() throws Exception {
+        LogFactory.releaseAll();
+    }
+
+    /**
+     * Tear down instance variables required by this test case.
+     */
+    public void tearDown() {
+        LogFactory.releaseAll();
+    }
+
+    // ----------------------------------------------------------- Test Methods
+
+    /**
+     * Verify that MyLogFactoryImpl is only loadable via the tccl.
+     */
+    public void testLoader() throws Exception {
+        
+        ClassLoader thisClassLoader = this.getClass().getClassLoader();
+        ClassLoader tcclLoader = Thread.currentThread().getContextClassLoader();
+        
+        // the tccl loader should NOT be the same as the loader that loaded this test class.
+        assertNotSame("tccl not same as test classloader", thisClassLoader, tcclLoader);
+
+        // MyLog should not be loadable via parent loader
+        try {
+            Class clazz = thisClassLoader.loadClass(MY_LOG_IMPL);
+            fail("Unexpectedly able to load MyLog via test class classloader");
+            assertNotNull(clazz); // silence warnings about unused var
+        } catch(ClassNotFoundException ex) {
+            // ok, expected
+        }
+        
+        // MyLog should be loadable via tccl loader
+        try {
+            Class clazz = tcclLoader.loadClass(MY_LOG_IMPL);
+            assertNotNull(clazz);
+        } catch(ClassNotFoundException ex) {
+            fail("Unexpectedly unable to load MyLog via tccl classloader");
+        }
+    }
+
+    /**
+     * Verify that the custom Log implementation which is only accessable
+     * via the TCCL has successfully been loaded as specified in the config file.
+     * This proves that the TCCL was used to load that class.
+     */
+    public void testTcclLoading() throws Exception {
+        LogFactory instance = LogFactory.getFactory();
+        
+        assertEquals(
+            "Correct LogFactory loaded", 
+            "org.apache.commons.logging.impl.LogFactoryImpl",
+            instance.getClass().getName());
+
+        Log log = instance.getInstance("test");
+        assertEquals(
+            "Correct Log loaded",
+            MY_LOG_IMPL,
+            log.getClass().getName());
+    }
+}
diff --git a/src/test/java/org/apache/commons/logging/tccl/logfactory/TcclDisabledTestCase.java b/src/test/java/org/apache/commons/logging/tccl/logfactory/TcclDisabledTestCase.java
new file mode 100644
index 0000000..fc577e3
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/tccl/logfactory/TcclDisabledTestCase.java
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.tccl.logfactory;
+
+
+import java.net.URL;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.logging.PathableClassLoader;
+import org.apache.commons.logging.PathableTestSuite;
+
+
+/**
+ * Verify that a commons-logging.properties file can prevent a custom
+ * LogFactoryImpl being loaded from the tccl classloader.
+ */
+
+public class TcclDisabledTestCase extends TestCase {
+
+    public static final String MY_LOG_FACTORY_PKG = 
+        "org.apache.commons.logging.tccl.custom";
+
+    public static final String MY_LOG_FACTORY_IMPL =
+        MY_LOG_FACTORY_PKG + ".MyLogFactoryImpl";
+
+    // ------------------------------------------- JUnit Infrastructure Methods
+
+
+    /**
+     * Return the tests included in this test suite.
+     */
+    public static Test suite() throws Exception {
+        Class thisClass = TcclDisabledTestCase.class;
+
+        // Determine the URL to this .class file, so that we can then
+        // append the priority dirs to it. For tidiness, load this
+        // class through a dummy loader though this is not absolutely
+        // necessary...
+        PathableClassLoader dummy = new PathableClassLoader(null);
+        dummy.useExplicitLoader("junit.", Test.class.getClassLoader());
+        dummy.addLogicalLib("testclasses");
+        dummy.addLogicalLib("commons-logging");
+        
+        String thisClassPath = thisClass.getName().replace('.', '/') + ".class";
+        URL baseUrl = dummy.findResource(thisClassPath);
+
+        // Now set up the desired classloader hierarchy. Everything goes into
+        // the parent classpath, but we exclude the custom LogFactoryImpl
+        // class.
+        //
+        // We then create a tccl classloader that can see the custom
+        // LogFactory class. Therefore if that class can be found, then the
+        // TCCL must have been used to load it.
+        PathableClassLoader emptyLoader = new PathableClassLoader(null);
+        
+        PathableClassLoader parentLoader = new PathableClassLoader(null);
+        parentLoader.useExplicitLoader("junit.", Test.class.getClassLoader());
+        parentLoader.addLogicalLib("commons-logging");
+        parentLoader.addLogicalLib("testclasses");
+        // hack to ensure that the testcase classloader can't see
+        // the custom MyLogFactoryImpl
+        parentLoader.useExplicitLoader(
+            MY_LOG_FACTORY_PKG + ".", emptyLoader);
+        
+        URL propsEnableUrl = new URL(baseUrl, "props_disable_tccl/");
+        parentLoader.addURL(propsEnableUrl);
+
+        PathableClassLoader tcclLoader = new PathableClassLoader(parentLoader);
+        tcclLoader.addLogicalLib("testclasses");
+
+        Class testClass = parentLoader.loadClass(thisClass.getName());
+        return new PathableTestSuite(testClass, tcclLoader);
+    }
+
+    /**
+     * Set up instance variables required by this test case.
+     */
+    public void setUp() throws Exception {
+        LogFactory.releaseAll();
+    }
+
+    /**
+     * Tear down instance variables required by this test case.
+     */
+    public void tearDown() {
+        LogFactory.releaseAll();
+    }
+
+    // ----------------------------------------------------------- Test Methods
+
+    /**
+     * Verify that MyLogFactoryImpl is only loadable via the tccl.
+     */
+    public void testLoader() throws Exception {
+        
+        ClassLoader thisClassLoader = this.getClass().getClassLoader();
+        ClassLoader tcclLoader = Thread.currentThread().getContextClassLoader();
+
+        // the tccl loader should NOT be the same as the loader that loaded this test class.
+        assertNotSame("tccl not same as test classloader", thisClassLoader, tcclLoader);
+
+        // MyLogFactoryImpl should not be loadable via parent loader
+        try {
+            Class clazz = thisClassLoader.loadClass(MY_LOG_FACTORY_IMPL);
+            fail("Unexpectedly able to load MyLogFactoryImpl via test class classloader");
+            assertNotNull(clazz); // silence warning about unused var
+        } catch(ClassNotFoundException ex) {
+            // ok, expected
+        }
+        
+        // MyLogFactoryImpl should be loadable via tccl loader
+        try {
+            Class clazz = tcclLoader.loadClass(MY_LOG_FACTORY_IMPL);
+            assertNotNull(clazz);
+        } catch(ClassNotFoundException ex) {
+            fail("Unexpectedly unable to load MyLogFactoryImpl via tccl classloader");
+        }
+    }
+
+    /**
+     * Verify that the custom LogFactory implementation which is only accessable
+     * via the TCCL has NOT been loaded. Because this is only accessable via the
+     * TCCL, and we've use a commons-logging.properties that disables TCCL loading,
+     * we should see the default LogFactoryImpl rather than the custom one.
+     */
+    public void testTcclLoading() throws Exception {
+        try {
+            LogFactory instance = LogFactory.getFactory();
+            fail("Unexpectedly succeeded in loading custom factory, though TCCL disabled.");
+            assertNotNull(instance); // silence warning about unused var
+        } catch(org.apache.commons.logging.LogConfigurationException ex) {
+            // ok, custom MyLogFactoryImpl as specified in props_disable_tccl
+            // could not be found.
+            int index = ex.getMessage().indexOf(MY_LOG_FACTORY_IMPL);
+            assertTrue("MylogFactoryImpl not found", index >= 0);
+        }
+    }
+}
diff --git a/src/test/java/org/apache/commons/logging/tccl/logfactory/TcclEnabledTestCase.java b/src/test/java/org/apache/commons/logging/tccl/logfactory/TcclEnabledTestCase.java
new file mode 100644
index 0000000..58d746b
--- /dev/null
+++ b/src/test/java/org/apache/commons/logging/tccl/logfactory/TcclEnabledTestCase.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.logging.tccl.logfactory;
+
+
+import java.net.URL;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.logging.PathableClassLoader;
+import org.apache.commons.logging.PathableTestSuite;
+
+
+/**
+ * Verify that by default a custom LogFactoryImpl is loaded from the
+ * tccl classloader.
+ */
+
+public class TcclEnabledTestCase extends TestCase {
+
+    // ------------------------------------------- JUnit Infrastructure Methods
+
+
+    /**
+     * Return the tests included in this test suite.
+     */
+    public static Test suite() throws Exception {
+        Class thisClass = TcclEnabledTestCase.class;
+
+        // Determine the URL to this .class file, so that we can then
+        // append the priority dirs to it. For tidiness, load this
+        // class through a dummy loader though this is not absolutely
+        // necessary...
+        PathableClassLoader dummy = new PathableClassLoader(null);
+        dummy.useExplicitLoader("junit.", Test.class.getClassLoader());
+        dummy.addLogicalLib("testclasses");
+        dummy.addLogicalLib("commons-logging");
+        
+        String thisClassPath = thisClass.getName().replace('.', '/') + ".class";
+        URL baseUrl = dummy.findResource(thisClassPath);
+
+        // Now set up the desired classloader hierarchy. Everything goes into
+        // the parent classpath, but we exclude the custom LogFactoryImpl
+        // class.
+        //
+        // We then create a tccl classloader that can see the custom
+        // LogFactory class. Therefore if that class can be found, then the
+        // TCCL must have been used to load it.
+        PathableClassLoader emptyLoader = new PathableClassLoader(null);
+        
+        PathableClassLoader parentLoader = new PathableClassLoader(null);
+        parentLoader.useExplicitLoader("junit.", Test.class.getClassLoader());
+        parentLoader.addLogicalLib("commons-logging");
+        parentLoader.addLogicalLib("testclasses");
+        // hack to ensure that the testcase classloader can't see
+        // the cust MyLogFactoryImpl
+        parentLoader.useExplicitLoader(
+            "org.apache.commons.logging.tccl.custom.", emptyLoader);
+        
+        URL propsEnableUrl = new URL(baseUrl, "props_enable_tccl/");
+        parentLoader.addURL(propsEnableUrl);
+
+        PathableClassLoader tcclLoader = new PathableClassLoader(parentLoader);
+        tcclLoader.addLogicalLib("testclasses");
+
+        Class testClass = parentLoader.loadClass(thisClass.getName());
+        return new PathableTestSuite(testClass, tcclLoader);
+    }
+
+    /**
+     * Set up instance variables required by this test case.
+     */
+    public void setUp() throws Exception {
+        LogFactory.releaseAll();
+    }
+
+    /**
+     * Tear down instance variables required by this test case.
+     */
+    public void tearDown() {
+        LogFactory.releaseAll();
+    }
+
+    // ----------------------------------------------------------- Test Methods
+
+    /**
+     * Verify that MyLogFactoryImpl is only loadable via the tccl.
+     */
+    public void testLoader() throws Exception {
+        
+        ClassLoader thisClassLoader = this.getClass().getClassLoader();
+        ClassLoader tcclLoader = Thread.currentThread().getContextClassLoader();
+
+        // the tccl loader should NOT be the same as the loader that loaded this test class.
+        assertNotSame("tccl not same as test classloader", thisClassLoader, tcclLoader);
+
+        // MyLogFactoryImpl should not be loadable via parent loader
+        try {
+            Class clazz = thisClassLoader.loadClass(
+                "org.apache.commons.logging.tccl.custom.MyLogFactoryImpl");
+            fail("Unexpectedly able to load MyLogFactoryImpl via test class classloader");
+            assertNotNull(clazz); // silence warning about unused var
+        } catch(ClassNotFoundException ex) {
+            // ok, expected
+        }
+        
+        // MyLogFactoryImpl should be loadable via tccl loader
+        try {
+            Class clazz = tcclLoader.loadClass(
+                "org.apache.commons.logging.tccl.custom.MyLogFactoryImpl");
+            assertNotNull(clazz);
+        } catch(ClassNotFoundException ex) {
+            fail("Unexpectedly unable to load MyLogFactoryImpl via tccl classloader");
+        }
+    }
+
+    /**
+     * Verify that the custom LogFactory implementation which is only accessable
+     * via the TCCL has successfully been loaded as specified in the config file.
+     * This proves that the TCCL was used to load that class.
+     */
+    public void testTcclLoading() throws Exception {
+        LogFactory instance = LogFactory.getFactory();
+        
+        assertEquals(
+            "Correct LogFactory loaded", 
+            "org.apache.commons.logging.tccl.custom.MyLogFactoryImpl",
+            instance.getClass().getName());
+    }
+}
diff --git a/src/test/resources/org/apache/commons/logging/config/nopriority/commons-logging.properties b/src/test/resources/org/apache/commons/logging/config/nopriority/commons-logging.properties
new file mode 100644
index 0000000..98d1177
--- /dev/null
+++ b/src/test/resources/org/apache/commons/logging/config/nopriority/commons-logging.properties
@@ -0,0 +1,18 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+configId=nopriority
diff --git a/src/test/resources/org/apache/commons/logging/config/priority10/commons-logging.properties b/src/test/resources/org/apache/commons/logging/config/priority10/commons-logging.properties
new file mode 100644
index 0000000..fa10f1a
--- /dev/null
+++ b/src/test/resources/org/apache/commons/logging/config/priority10/commons-logging.properties
@@ -0,0 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+priority=10.5
+configId=priority10
diff --git a/src/test/resources/org/apache/commons/logging/config/priority20/commons-logging.properties b/src/test/resources/org/apache/commons/logging/config/priority20/commons-logging.properties
new file mode 100644
index 0000000..4ead91f
--- /dev/null
+++ b/src/test/resources/org/apache/commons/logging/config/priority20/commons-logging.properties
@@ -0,0 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+priority=20.6
+configId=priority20
diff --git a/src/test/resources/org/apache/commons/logging/config/priority20a/commons-logging.properties b/src/test/resources/org/apache/commons/logging/config/priority20a/commons-logging.properties
new file mode 100644
index 0000000..6a91d83
--- /dev/null
+++ b/src/test/resources/org/apache/commons/logging/config/priority20a/commons-logging.properties
@@ -0,0 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+priority=20.6
+configId=priority20a
diff --git a/src/test/resources/org/apache/commons/logging/jdk14/CustomConfig.properties b/src/test/resources/org/apache/commons/logging/jdk14/CustomConfig.properties
new file mode 100644
index 0000000..2a5e0e7
--- /dev/null
+++ b/src/test/resources/org/apache/commons/logging/jdk14/CustomConfig.properties
@@ -0,0 +1,25 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+# 
+#      http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+# This is the custom configuration properties for the JDK 1.4 logger tests
+# in CustomConfigTestCase.
+
+# Configure the Handler so we can examine the logged messages
+handlers = org.apache.commons.logging.jdk14.TestHandler
+
+# Configre the default logging level to be FINE so we should get
+# everything except trace messages
+.level = FINE
diff --git a/src/test/resources/org/apache/commons/logging/tccl/log/props_disable_tccl/commons-logging.properties b/src/test/resources/org/apache/commons/logging/tccl/log/props_disable_tccl/commons-logging.properties
new file mode 100644
index 0000000..8f55b92
--- /dev/null
+++ b/src/test/resources/org/apache/commons/logging/tccl/log/props_disable_tccl/commons-logging.properties
@@ -0,0 +1,20 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+use_tccl=false
+org.apache.commons.logging.Log=org.apache.commons.logging.tccl.custom.MyLog
+org.apache.commons.logging.diagnostics.dest=STDERR
\ No newline at end of file
diff --git a/src/test/resources/org/apache/commons/logging/tccl/log/props_enable_tccl/commons-logging.properties b/src/test/resources/org/apache/commons/logging/tccl/log/props_enable_tccl/commons-logging.properties
new file mode 100644
index 0000000..440c764
--- /dev/null
+++ b/src/test/resources/org/apache/commons/logging/tccl/log/props_enable_tccl/commons-logging.properties
@@ -0,0 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+org.apache.commons.logging.Log=org.apache.commons.logging.tccl.custom.MyLog
+org.apache.commons.logging.diagnostics.dest=STDERR
\ No newline at end of file
diff --git a/src/test/resources/org/apache/commons/logging/tccl/logfactory/props_disable_tccl/commons-logging.properties b/src/test/resources/org/apache/commons/logging/tccl/logfactory/props_disable_tccl/commons-logging.properties
new file mode 100644
index 0000000..9d21a8c
--- /dev/null
+++ b/src/test/resources/org/apache/commons/logging/tccl/logfactory/props_disable_tccl/commons-logging.properties
@@ -0,0 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+use_tccl=false
+org.apache.commons.logging.LogFactory=org.apache.commons.logging.tccl.custom.MyLogFactoryImpl
diff --git a/src/test/resources/org/apache/commons/logging/tccl/logfactory/props_enable_tccl/commons-logging.properties b/src/test/resources/org/apache/commons/logging/tccl/logfactory/props_enable_tccl/commons-logging.properties
new file mode 100644
index 0000000..29bc41a
--- /dev/null
+++ b/src/test/resources/org/apache/commons/logging/tccl/logfactory/props_enable_tccl/commons-logging.properties
@@ -0,0 +1,18 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+org.apache.commons.logging.LogFactory=org.apache.commons.logging.tccl.custom.MyLogFactoryImpl

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/libcommons-logging-java.git



More information about the pkg-java-commits mailing list