[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