| <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ülcü. |
| </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->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->java.util<br/> |
| static FAIL |
| </td> |
| <td> |
| JCL->java.util<br/> |
| static FAIL |
| </td> |
| <td> |
| JCL->java.util <br/> |
| static FAIL |
| </td> |
| <td> |
| JCL->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->log4j <br/> |
| static OK |
| </td> |
| <td>JCL->log4j <br/> |
| static OK |
| </td> |
| <td> |
| JCL->log4j<br/> |
| static OK |
| </td> |
| <td> |
| JCL->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->java.util<br/> |
| static FAIL |
| </td> |
| <td>JCL->java.util<br/> |
| static FAIL |
| </td> |
| <td>JCL->log4j<br/> |
| static FAIL |
| </td> |
| <td>JCL->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->java.util<br/> |
| static OK</td> |
| <td>JCL->java.util<br/> |
| static OK |
| </td> |
| <td>JCL->log4j<br/> |
| static OK |
| </td> |
| <td>JCL->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->Log4j <br/> |
| static OK |
| </td> |
| <td>JCL->java.util <br/> |
| static FAIL |
| </td> |
| <td>JCL->Log4j<br/> |
| static OK |
| </td> |
| <td>JCL->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->log4j <br/> |
| static OK |
| </td> |
| <td>JCL->log4j <br/> |
| static OK |
| </td> |
| <td>JCL->log4j <br/> |
| static OK |
| </td> |
| <td>JCL->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->log4j <br/> |
| static OK |
| </td> |
| <td>JCL->java.util <br/> |
| static FAIL |
| </td> |
| <td>JCL->log4j <br/> |
| static OK |
| </td> |
| <td>JCL->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->log4j <br/> |
| static OK |
| </td> |
| <td>JCL->java.util <br/> |
| static OK |
| </td> |
| <td>JCL->log4j <br/> |
| static OK |
| </td> |
| <td>JCL->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> |