blob: 9ae10f0f7ae7d02d1f049ef90d5c80ef086d60e7 [file] [log] [blame]
<html>
<!-- $Id: overview.html 132718 2004-09-09 20:38:21Z rdonkin $
Copyright 2005 The Apache Software Foundation.
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.
-->
<head>
<meta http-equiv="CONTENT-TYPE" content="text/html; charset=iso-8859-1">
<title>Overview Documentation for JCL Proof Of Concept Demonstrations</title>
</head>
<body>
<h1>Demonstrates Jakarta Commons Logging (JCL) concepts.</h1>
<h3>Introduction</h3>
<p>This document contains analysis of various JCL use cases. It works
both as an educational document and as a specification (of sorts).
It's intended audience are (potential) JCL developers and (potential)
expert users. The code that accompanies
this document demonstrates the cases analysed in the text.
</p>
<p>Familiarity with (advanced) class loading
concepts and terminology is assumed. Please digest the
<a HREF="http://jakarta.apache.org/commons/logging/tech.html">JCL
Technology Guide</a> before starting.
</p>
<p>This approach was inspired by a
<a href='http://www.qos.ch/logging/classloader.jsp'>JCL critique</a>
written by Ceki G&uuml;lc&uuml;.
</p>
<p>These demonstrations focus on discovery in general and discovery
of Log4J in particular. Not only is Log4J the most important target
but these are some of the most difficult and contentious cases.
Lessons learnt from these cases should easily and safely extrapolate
to other cases.
</p>
<p>It is important that this document is as accurate as possible both
in facts and analysis. Readers are encouraged to contribute
corrections and improvements. Please either:
</p>
<ul>
<li>submit a <A HREF="http://issues.apache.org/bugzilla">bug
report</a> for the JCL component</li>
<li>or post to the Jakarta Commons Developer <a href="http://jakarta.apache.org/site/mail.html">mailing
list</a></li>
</p>
</ul>
<h3>Overview</h3>
<p>The basic set up for these demonstrations is intentionally simple.
There are no tricks that might obscure a clear view of the concepts.
The source code used to run these demonstrations is simple and should
be easy to understand.
</p>
<p>Each demonstration is initiated by a runner. The runner loads the
other code components into appropriate ClassLoaders and then calls the caller.
</p>
<p>The caller is used to simulate a class that needs to log
something. It is isolated from the code that runs the demonstration
in order to allow a greater variety of ClassLoader situations to be simulated.
The calling code is split
into one class that presents the formatted results and another that
actually performs the calls. Calls are made to JCL and to a static
logger.
</p>
<p>The static logger simply contains a call directly to Log4J. This
is useful for comparison.
</p>
<p>Now would be a good idea to study the javadocs.
</p>
<p>Results are printed (with a little formatting) to System.out. The
run target in the ant build scripts runs all cases. It may be
difficult to run these demonstrations from an IDE (a clean system
ClassLoader is required).
</p>
<h4>Conventional And Unconventional Context ClassLoaders</h4>
<p>This analysis will start by making a division between conventional
context classloaders and unconventional ones. Conventionally, the
context classloader for a thread executing code in a particular class
should either be the classloader used to load the class, an ancestor
of that classloader or a descendent of it. The conventional context
classloader cases will focus on context classloaders which obey these
rules.
</p>
<h3>Conventional Classloader Cases</h3>
<p>The aim of the set up will be isolate the essentials. Only three
classloaders will be considered:
</p>
<ul>
<li>the system classloader,</li>
<li>a parent classloader (whose parent
is the system classloader) </li>
<li>and a child classloader of the parent.</li>
</ul>
<p>This situation is commonly encountered in containers (where the
child classloader is the application classloader). In practical
situations, the hierarchy is likely to be contain more classloaders
but it should be possible either to extrapolate from these simple
cases or reduce the more complex ones to these.
</p>
<p>The analysis will proceed by considering two cases separately:
</p>
<ul>
<li>parent-first classloading </li>
<li>and child-first classloading.</li>
</ul>
<p>In the parent first cases, when the child classloader initiates
loading, it starts by delegating to the parent classloader. Only if
this classloader does not define the class will the child classloader
attempt to define the class. In the child first cases, the child
classloader will begin by attempting to define the class itself. Only
when it cannot define a class will it delegate to it's parents.
Further discussion of each of these cases will be contained in the
appropriate section.
</p>
<p>The other variable is the setting of the context classloader. In
these cases, it will be set to either the system classloader (the
default) or the child classloader (the application classloader).
Perhaps the cases for setting to the parent classloader
should be added (but this would be unusual: conventionally the context
classloader should be either unset or set to the application
classloader). Contributions of these extra cases would be welcomed.
</p>
<p>The cases will be presented in a matrix. This contains details of
the classloader set ups including which
which classes are definable by which loaders and how the context
classloader is set. It also contains the results of analysis about
the way that both the static logging and JCL (ideally) should behave.
</p>
<p>For example
</p>
<table border='1'>
<tr>
<th>Context ClassLoader</th>
<td>System</td>
</tr>
<tr>
<th>Parent</th>
<td>JCL+Static</td>
</tr>
<tr>
<th>Child</th>
<td>
JCL+Static<br/>
Log4J<br/>
Caller
</td>
</tr>
<tr>
<th>Expected Result</th>
<td>JCL-&gt;java.util<br/>
static FAIL
</td>
</tr>
</table>
<p>This describes a case where the context classloader is unset (and
so defaults to the system), the static.jar and commons-logging.jar
are definable by the parent classloader, the static.jar,
commons-logging.jar and log4j are definable by the child. The
caller.jar is also definable by the child classloader. The expected
result in this case is that JCL will discover java.util.logging and
that logging statically to Log4j will fail.
</p>
<p>For purposes of reference, an indication is given in those cases
that correspond to examples used by Ceki in his critique.
</p>
<p>Analytical notes explaining how these results were obtained are
included below each table. As the cases proceed, the notes will grow
more terse.
</p>
<h4>Parent First ClassLoader Cases</h4>
<h5>Log4J Defined By Child, JCL By Parent</h5>
<table border='1' width='90%'>
<tr>
<th>Case</th>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
</tr>
<tr>
<th>Ceki Example</th>
<td>-</td>
<td>1</td>
<td>2A</td>
<td>3</td>
</tr>
<tr>
<th>Context ClassLoader</th>
<td>System</td>
<td>System</td>
<td>Child</td>
<td>Child</td>
</tr>
<tr>
<th>Parent</th>
<td>JCL+Static</td>
<td>JCL+Static Caller</td>
<td>JCL+Static</td>
<td>JCL+Static Caller</td>
</tr>
<tr>
<th>Child</th>
<td>
JCL+Static<br/>
Log4J<br/>
Caller</td>
<td>
JCL+Static<br/>
Log4J
</td>
<td>
JCL+Static<br/>
Log4J<br/>
Caller
</td>
<td>
JCL+Static <br/>
Log4J
</td>
</tr>
<tr>
<th>Expected Result</th>
<td>
JCL-&gt;java.util<br/>
static FAIL
</td>
<td>
JCL-&gt;java.util<br/>
static FAIL
</td>
<td>
JCL-&gt;java.util <br/>
static FAIL
</td>
<td>
JCL-&gt;java.util<br/>
static FAIL
</td>
</tr>
</table>
<p>One important point to bear in mind when analysing parent-first
classloaders is that the presence or absence in the child classloader
of classes definable by the parent classloader produces no difference
in behaviour: the parent classloader will always load the class in
question.
</p>
<p>In the cases above, Log4J is defined (only) by the child and all
JCL classes by the parent classloader. The symbolic references from
Log4JLogger to Log4J classes therefore cannot be resolved as Log4j is
not definable by the parent classloader. For the same reason, the
static call should also always fail.
</p>
<p>The context classloader can be of no use (whether set or not)
since Log4JLogger will always be defined by the parent classloader
whether loading is initiated by the child or by the parent loader.
</p>
<p>Therefore, in these cases, the appropriate behaviour is for JCL to
discover that java.util.logging is the only available logging system.
</p>
<h5>Log4J Defined By Parent, JCL By Parent</h5>
<table border='1' width='90%'>
<tr>
<th>Case</th>
<td>5</td>
<td>6</td>
<td>7</td>
<td>8</td>
</tr>
<tr>
<th>Ceki Example</th>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
</tr>
<tr>
<th>Context ClassLoader</th>
<td>System</td>
<td>System</td>
<td>Child</td>
<td>Child</td>
</tr>
<tr>
<th>Parent</th>
<td>Log4J <br/>
JCL+Static
<td>Log4J <br/>
JCL+Static <br/>
Caller
<td>Log4J <br/>
JCL+Static
</td>
<td>Log4J <br/>
JCL+Static <br/>
Caller
</td>
</tr>
<tr>
<th>Child</th>
<td>JCL+Static<br/>
Log4J <br/>
Caller <br/>
</td>
<td>JCL+Static<br/>
Log4J <br/>
</td>
<td>JCL+Static <br/>
Log4J<br/>
Caller
</td>
<td>JCL+Static<br/>
Log4J
</td>
</tr>
<tr>
<th>Expected Result
</th>
<td>JCL-&gt;log4j <br/>
static OK
</td>
<td>JCL-&gt;log4j <br/>
static OK
</td>
<td>
JCL-&gt;log4j<br/>
static OK
</td>
<td>
JCL-&gt;log4j<br/>
static OK
</td>
</tr>
</table>
<p>This demonstrates the usual way to fix the problems encountered
when Log4J is not definable by the loader that defines JCL: just add
the Log4j.jar into the classloader that defines JCL.
</p>
<p>This is a very simple set of cases with JCL and static being able
to resolve the appropriate symbolic references to Log4j directly.
Whether the context classloader is set or not in these cases should
make no difference.
</p>
<h5>Log4J Defined By Child, JCL API By Parent, JCL By Child</h5>
<table border='1' width='90%'>
<tr>
<th>Case</th>
<td>9</td>
<td>10</td>
<td>11</td>
<td>12</td>
</tr>
<tr>
<th>Ceki Example</th>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
</tr>
<tr>
<th>Context ClassLoader</th>
<td>System</td>
<td>System</td>
<td>Child</td>
<td>Child</td>
</tr>
<tr>
<th>Parent</th>
<td>API+Static</td>
<td>API+Static <br/>
Caller
</td>
<td>API+Static
</td>
<td>API+Static <br/>
Caller
</td>
</tr>
<tr>
<th>Child</th>
<td>JCL+Static <br/>
Log4J <br/>
Caller
</td>
<td>JCL+Static<br/>
Log4J
</td>
<td>JCL+Static <br/>
Log4J <br/>
Caller
</td>
<td>JCL+Static <br/>
Log4J
</td>
</tr>
<tr>
<th>Expected Result</th>
<td>JCL-&gt;java.util<br/>
static FAIL
</td>
<td>JCL-&gt;java.util<br/>
static FAIL
</td>
<td>JCL-&gt;log4j<br/>
static FAIL
</td>
<td>JCL-&gt;log4j <br/>
static FAIL</p>
</td>
</tr>
</table>
<p>These demonstrate variations on the first series of cases where
Log4J is defined by the child. The placement of the static jar does
not vary (from the previous set) and again is expected to
consistently fail.
</p>
<p>This time the API jar is placed in the parent classloader and the
full JCL jar in the child. This means that the symbolic reference
from the Log4JLogger class (contained in the full jar but not the
API) to Log4J classes can be resolved.
</p>
<p>In these cases, whether JCL can succeed depends on the context
classloader. The delegation model employed by Java ClassLoaders
allows child classloaders to know about parents but not vice versa.
Therefore, the context classloader is the only means available to
attempt to load Log4JLogger. When the context classloader is set to
the child, this classloader defines Log4JLogger and Log4J. Therefore,
JCL should be able to discover Log4J (only) when the context
classloader is set to the child.
</p>
<h5>Log4J Defined By Parent, JCL API By Parent, JCL By Child</h5>
<table border='1' width='90%'>
<tr>
<th>Case</th>
<td>13</td>
<td>14</td>
<td>15</td>
<td>16</td>
</tr>
<tr>
<th>Ceki Example</th>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
</tr>
<tr>
<th>Context ClassLoader</th>
<td>System</td>
<td>System</td>
<td>Child</td>
<td>Child</td>
</tr>
<tr>
<th>Parent</th>
<td>Log4J <br/>
API+Static
</td>
<td>Log4J <br/>
API+Static <br/>
Caller
</td>
<td>Log4J<br/>
API+Static
</td>
<td>Log4J<br/>
API+Static<br/>
Caller
</td>
</tr>
<tr>
<th>Child</th>
<td>JCL+Static<br/>
Log4J <br/>
Caller</td>
<td>JCL+Static <br/>
Log4J</td>
<td>JCL+Static <br/>
Log4J <br/>
Caller
</td>
<td>JCL+Static <br/>
Log4J</td>
</tr>
<tr>
<th>Expected Result</th>
<td>JCL-&gt;java.util<br/>
static OK</td>
<td>JCL-&gt;java.util<br/>
static OK
</td>
<td>JCL-&gt;log4j<br/>
static OK
</td>
<td>JCL-&gt;log4j<br/>
static OK
</td>
</tr>
</table>
<p>Trivial variations on the second series. Now API and JCL are
definable only by parent and child respectively (as in the last
series). When the context classloader is set to system,
Log4JLogger cannot be loaded and so java.util.logging is the only viable
logger.
</p>
<h4>Child First ClassLoader Cases</h4>
<p>The same classloader configuration can be repeated with (this
time) child-first classloading.
</p>
<h5>Log4J Defined By Child, JCL By Parent</h5>
<table border='1' width='90%'>
<tr>
<th>Case</th>
<td>17</td>
<td>18</td>
<td>19</td>
<td>20</td>
</tr>
<tr>
<th>Ceki Example</th>
<td>-</td>
<td>-</td>
<td>5</td>
<td>6</td>
</tr>
<tr>
<th>Context ClassLoader</th>
<td>System</td>
<td>System</td>
<td>Child</td>
<td>Child</td>
</tr>
<tr>
<th>Parent</th>
<td>JCL+Static</td>
<td>JCL+Static <br/>
Caller</td>
<td>JCL+Static</td>
<td>JCL+Static <br/>
Caller
</td>
</tr>
<tr>
<th>Child</th>
<td>JCL+Static <br/>
Log4J <br/>
Caller
</td>
<td>JCL+Static <br/>
Log4J
</td>
<td>JCL+Static <br/>
Log4J <br/>
Caller
</td>
<td>JCL+Static <br/>
Log4J
</td>
</tr>
<tr>
<th>Expected Result</th>
<td>JCL-&gt;Log4j <br/>
static OK
</td>
<td>JCL-&gt;java.util <br/>
static FAIL
</td>
<td>JCL-&gt;Log4j<br/>
static OK
</td>
<td>JCL-&gt;java.util<br/>
static FAIL
</td>
</tr>
</table>
<p>In child-first cases, the classloader which defines the caller
plays a more important role. When the caller is defined by the child
classloader, both static and JCL should succeed whether the context
classloader is set or not.
</p>
<p>When the caller is defined by the parent classloader, this means
that the parent classloader will define the JCL and static classes
(rather than the child). Log4J is not defined by the parent loader
and so the static call must fail in both cases.
</p>
<p>With a friendly context classloader, JCL can load an instance of
Log4JLogger whose symbolic references to Log4J can be resolved.
However, the child classloader which defines this class will resolve
the symbolic reference to Log to the class defined by the child
classloader rather than the Log class defined by the parent
classloader. They are not mutually accessible and so JCL must
discover java.util.logging.
</p>
<h5>Log4J Defined By Parent, JCL By Parent</h5>
<table border='1' width='90%'>
<tr>
<th>Case
</th>
<td>21</td>
<td>22</td>
<td>23</td>
<td>24</td>
</tr>
<tr>
<th>Ceki Example</th>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
</tr>
<tr>
<th>Context ClassLoader</th>
<td>System</td>
<td>System</td>
<td>Child</td>
<td>Child</td>
</tr>
<tr>
<th>Parent</th>
<td>Log4J <br/>
JCL+Static
</td>
<td>Log4J <br/>
JCL+Static <br/>
Caller
</td>
<td>Log4J <br/>
JCL+Static
</td>
<td>Log4J <br/>
JCL+Static <br/>
Caller
</td>
</tr>
<tr>
<th>Child</th>
<td>JCL+Static <br/>
Log4J <br/>
Caller
</td>
<td>JCL+Static <br/>
Log4J
</td>
<td>JCL+Static <br/>
Log4J <br/>
Caller
</td>
<td>JCL+Static <br/>
Log4J
</td>
</tr>
<tr>
<th>Expected Result</th>
<td>JCL-&gt;log4j <br/>
static OK
</td>
<td>JCL-&gt;log4j <br/>
static OK
</td>
<td>JCL-&gt;log4j <br/>
static OK
</td>
<td>JCL-&gt;log4j <br/>
static OK
</td>
</tr>
</table>
<p>Trivial case where jar's are definable by both loaders.
</p>
<h5>Log4J Defined By Child, JCL API By Parent, JCL By Child</h5>
<table border='1' width='90%'>
<tr>
<th>Case</th>
<td>25</td>
<td>26</td>
<td>27</td>
<td>28</td>
</tr>
<tr>
<th>Ceki Example</th>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
</tr>
<tr>
<th>Context ClassLoader</th>
<td>System</td>
<td>System</td>
<td>Child</td>
<td>Child</td>
</tr>
<tr>
<th>Parent</th>
<td>API+Static</td>
<td>API+Static <br/>
Caller
</td>
<td>API+Static</td>
<td>API+Static <br/>
Caller
</td>
</tr>
<tr>
<th>Child</th>
<td>JCL+Static <br/>
Log4J <br/>
Caller
</td>
<td>JCL+Static <br/>
Log4J
</td>
<td>JCL+Static <br/>
Log4J <br/>
Caller
</td>
<td>JCL+Static <br/>
Log4J
</td>
</tr>
<tr>
<th>Expected Result</th>
<td>JCL-&gt;log4j <br/>
static OK
</td>
<td>JCL-&gt;java.util <br/>
static FAIL
</td>
<td>JCL-&gt;log4j <br/>
static OK
</td>
<td>JCL-&gt;java.util <br/>
static FAIL
</td>
</tr>
</table>
<p>(As above) to succeed, the static call requires that Log4J is
definable by the loader which defines the callers. JCL should
discover Log4J in the cases where the static call succeeds.
</p>
<p>Even with a friendly context classloader, the Log class referenced
by the Log4JLogger defined by the child classloader will be
inaccessible to the caller (loader by the parent classloader).
</p>
<h5>Log4J Defined By Parent, JCL API By Parent, JCL By Child</h5>
<table border='1' width='90%'>
<tr>
<th>Case</th>
<td>29</td>
<td>30</td>
<td>31</td>
<td>32</td>
</tr>
<tr>
<th>Ceki Example</th>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
</tr>
<tr>
<th>Context ClassLoader</th>
<td>System</td>
<td>System</td>
<td>Child</td>
<td>Child</td>
</tr>
<tr>
<th>Parent</th>
<td>Log4J <br/>
API+Static
</td>
<td>Log4J <br/>
API+Static <br/>
Caller
</td>
<td>Log4J<br/>
API+Static
</td>
<td>Log4J<br/>
API+Static <br/>
Caller
</td>
</tr>
<tr>
<th>Child</th>
<td>JCL+Static <br/>
Log4J <br/>
Caller
</td>
<td>JCL+Static <br/>
Log4J
</td>
<td>JCL+Static <br/>
Log4J <br/>
Caller
</td>
<td>JCL+Static <br/>
Log4J
</td>
</tr>
<tr>
<th>Expected Result</th>
<td>JCL-&gt;log4j <br/>
static OK
</td>
<td>JCL-&gt;java.util <br/>
static OK
</td>
<td>JCL-&gt;log4j <br/>
static OK
</td>
<td>JCL-&gt;java.util <br/>
static OK
</td>
</tr>
</table>
<p>These results at first glance may seem a little confusing. The
issue for JCL is that classes needed to log to Log4J are only
definable by the child classloader.
</p>
<p>Even with a friendly context classloader, JCL runs into the same
difficulties with accessibility that
occurred above.
</p>
</body>
</html>