From 2aa2ad57f608985c41773dd2e098d52982d28bc6 Mon Sep 17 00:00:00 2001
From: Robert Burrell Donkin Invokes logging
+and takes the role of calling application code in these demonstrations.
+This separation allows a greater variety of defining classloaders for
+the calling application code to be simulated.
+ Runs the demonstrations.
+Also contained is utility code that efficiently sets up the
+various classloader hierarchies.
+ Calls Log4j. Uses a symbolic reference.
+
+This allows practical demonstrations of the behaviour of code
+that calls Log4J statically when placed in similar
+classloader circumstances of JCL.
+ 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.
+ Familiarity with (advanced) class loading
+concepts and terminology is assumed. Please digest the
+JCL
+Technology Guide before starting.
+ This approach was inspired by a JCL critique written by Ceki
+Guici.
+ 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.
+ 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:
+System.out.
+ */
+ public void runJCL() {
+ try {
+ SomeObject someObject = new SomeObject();
+ System.out.println("--------------------");
+ System.out.println("Logging to JCL:");
+ someObject.logToJCL();
+ System.out.println("JCL Logging OK");
+ } catch (Throwable t) {
+ System.out.println("JCL Logging FAILED: " + t.getClass());
+ System.out.println(t.getMessage());
+ System.out.println("");
+ }
+ }
+
+ /**
+ * Runs a test that logs to Log4J via static calls
+ * and outputs the results to System.out.
+ */
+ public void runStatic() {
+ try {
+ SomeObject someObject = new SomeObject();
+ System.out.println("--------------------");
+ System.out.println("Logging to Static:");
+ someObject.logToStaticLog4J();
+ System.out.println("Static Logging: OK");
+ } catch (Throwable t) {
+ System.out.println("Static Logging FAILED: " + t.getClass());
+ System.out.println(t.getMessage());
+ System.out.println("");
+ }
+ }
+}
diff --git a/demonstration/src/java/org/apache/commons/logging/proofofconcept/caller/SomeObject.java b/demonstration/src/java/org/apache/commons/logging/proofofconcept/caller/SomeObject.java
new file mode 100644
index 0000000..ca0ec93
--- /dev/null
+++ b/demonstration/src/java/org/apache/commons/logging/proofofconcept/caller/SomeObject.java
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+package org.apache.commons.logging.proofofconcept.caller;
+
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.logging.proofofconcept.staticlogger.StaticLog4JLogger;
+
+/**
+ * This simulates some application or library code
+ * that uses logging.
+ * This separation allows tests to be run
+ * where this class is defined by either the parent
+ * or the child classloader.
+ */
+public class SomeObject {
+
+ /**
+ * Logs a message to Jakarta Commons Logging.
+ */
+ public void logToJCL() {
+ LogFactory.getLog("a log").info("A message");
+ }
+
+ /**
+ * Logs a message to Log4j via a class
+ * which makes a static call.
+ */
+ public void logToStaticLog4J() {
+ StaticLog4JLogger.info("A message");
+ }
+}
diff --git a/demonstration/src/java/org/apache/commons/logging/proofofconcept/caller/package.html b/demonstration/src/java/org/apache/commons/logging/proofofconcept/caller/package.html
new file mode 100644
index 0000000..54aa603
--- /dev/null
+++ b/demonstration/src/java/org/apache/commons/logging/proofofconcept/caller/package.html
@@ -0,0 +1,24 @@
+
+
+URL array, not null possibly empty
+ */
+ private URL[] urlsForJars(int jars, String humanLoaderName) {
+ List urls = new ArrayList();;
+ if ((LOG4J_JAR & jars) > 0) {
+ urls.add(log4jUrl);
+ }
+ if ((STATIC_JAR & jars) > 0) {
+ urls.add(staticUrl);
+ }
+ if ((JCL_JAR & jars) > 0) {
+ urls.add(jclUrl);
+ }
+ if ((API_JAR & jars) > 0) {
+ urls.add(apiUrl);
+ }
+ if ((CALLER_JAR & jars) > 0) {
+ urls.add(callerUrl);
+ }
+ System.out.println(humanLoaderName + " " + urls);
+ URL[] results = (URL[]) urls.toArray(EMPTY_URLS);
+ return results;
+ }
+
+ /**
+ * Runs a demonstration.
+ * @param testName the human name for this test
+ * @param parentClassloaderUrls the URL's which should
+ * be definable by the parent classloader, not null
+ * @param childClassloaderUrls the URL's which should
+ * be definable by the child classloader, not null
+ * @param setContextClassloader true if the context
+ * classloader should be set to the child classloader,
+ * false if the default context classloader should
+ * be maintained
+ * @param childFirst true if the child classloader
+ * should delegate only when it cannot define the class,
+ * false otherwise
+ */
+ public void run (String testName,
+ URL[] parentClassloaderUrls,
+ URL[] childClassloaderUrls,
+ boolean setContextClassloader,
+ boolean childFirst) {
+
+ URLClassLoader parent = new URLClassLoader(parentClassloaderUrls);
+ URLClassLoader child = null;
+ if (childFirst) {
+ child = new ChildFirstClassLoader(childClassloaderUrls, parent);
+ } else {
+ child = new URLClassLoader(childClassloaderUrls, parent);
+ }
+
+ if (setContextClassloader) {
+ Thread.currentThread().setContextClassLoader(child);
+ }
+
+ logDefiningLoaders(child, parent);
+
+ try {
+
+ Class callerClass = child.loadClass(testName);
+ Method runMethod = callerClass.getDeclaredMethod("run", EMPTY_CLASSES);
+ Object caller = callerClass.newInstance();
+ runMethod.invoke(caller, EMPTY_OBJECTS);
+
+ } catch (Exception e) {
+ System.out.println("Cannot execute test: " + e.getMessage());
+ e.printStackTrace();
+ }
+
+ }
+
+ /**
+ * Logs the classloaders which define important classes
+ * @param child child ClassLoader, not null
+ * @param parent parent ClassLoader, not null
+ */
+ private void logDefiningLoaders(ClassLoader child, ClassLoader parent)
+ {
+ System.out.println("");
+ logDefiningLoaders(child, parent, "org.apache.commons.logging.LogFactory", "JCL ");
+ logDefiningLoaders(child, parent, "org.apache.log4j.Logger", "Log4j ");
+ logDefiningLoaders(child, parent, "org.apache.commons.logging.proofofconcept.staticlogger.StaticLog4JLogger", "Static Logger");
+ logDefiningLoaders(child, parent, "org.apache.commons.logging.proofofconcept.caller.SomeObject", "Caller ");
+ System.out.println("");
+ }
+
+ /**
+ * Logs the classloader which defines the class with the given name whose
+ * loading is initiated by the child classloader.
+ * @param child child ClassLoader, not null
+ * @param parent parent ClassLoader, not null
+ * @param className name of the class to be loaded
+ * @param humanName the human name for the class
+ */
+ private void logDefiningLoaders(ClassLoader child, ClassLoader parent, String className, String humanName) {
+ try {
+ Class clazz = child.loadClass(className);
+ ClassLoader definingLoader = clazz.getClassLoader();
+ if (definingLoader == null)
+ {
+ System.out.println(humanName + " defined by SYSTEM class loader");
+ }
+ else if (definingLoader.equals(child))
+ {
+ System.out.println(humanName + " defined by CHILD class loader");
+ }
+ else if (definingLoader.equals(parent))
+ {
+ System.out.println(humanName + " defined by PARENT class loader");
+ }
+ else
+ {
+ System.out.println(humanName + " defined by OTHER class loader");
+ }
+ } catch (Exception e) {
+ System.out.println(humanName + " NOT LOADABLE by application classloader");
+ }
+ }
+
+}
diff --git a/demonstration/src/java/org/apache/commons/logging/proofofconcept/runner/ParentFirstRunner.java b/demonstration/src/java/org/apache/commons/logging/proofofconcept/runner/ParentFirstRunner.java
new file mode 100644
index 0000000..5c102ae
--- /dev/null
+++ b/demonstration/src/java/org/apache/commons/logging/proofofconcept/runner/ParentFirstRunner.java
@@ -0,0 +1,165 @@
+/*
+ * 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.
+ */
+package org.apache.commons.logging.proofofconcept.runner;
+
+import java.net.MalformedURLException;
+
+
+/**
+ * Runs parent first demonstrations.
+ * @see #main(String[])
+ */
+public class ParentFirstRunner extends ClassLoaderRunner {
+
+
+ public ParentFirstRunner() throws MalformedURLException {
+ }
+
+ public void testCase1() {
+ int parentUrls = JCL_JAR + STATIC_JAR;
+ int childUrls = JCL_JAR + STATIC_JAR + CALLER_JAR + LOG4J_JAR;
+ run("1", parentUrls, childUrls, false, false);
+ }
+
+ public void testCase2() {
+ int parentUrls = JCL_JAR + STATIC_JAR + CALLER_JAR;
+ int childUrls = JCL_JAR + STATIC_JAR + LOG4J_JAR;
+ run("2", parentUrls, childUrls, false, false);
+ }
+
+ public void testCase3() {
+ int parentUrls = JCL_JAR + STATIC_JAR;
+ int childUrls = JCL_JAR + STATIC_JAR + CALLER_JAR + LOG4J_JAR;
+ run("3", parentUrls, childUrls, true, false);
+ }
+
+ public void testCase4() {
+ int parentUrls = JCL_JAR + STATIC_JAR + CALLER_JAR;
+ int childUrls = JCL_JAR + STATIC_JAR + LOG4J_JAR;
+ run("4", parentUrls, childUrls, true, false);
+ }
+
+ public void testCase5() {
+ int parentUrls = JCL_JAR + STATIC_JAR + LOG4J_JAR;
+ int childUrls = JCL_JAR + STATIC_JAR + CALLER_JAR + LOG4J_JAR;
+ run("5", parentUrls, childUrls, false, false);
+ }
+
+ public void testCase6() {
+ int parentUrls = JCL_JAR + STATIC_JAR + CALLER_JAR + LOG4J_JAR;
+ int childUrls = JCL_JAR + STATIC_JAR + LOG4J_JAR;
+ run("6", parentUrls, childUrls, false, false);
+ }
+
+ public void testCase7() {
+ int parentUrls = JCL_JAR + STATIC_JAR + LOG4J_JAR;
+ int childUrls = JCL_JAR + STATIC_JAR + CALLER_JAR + LOG4J_JAR;
+ run("7", parentUrls, childUrls, true, false);
+ }
+
+ public void testCase8() {
+ int parentUrls = JCL_JAR + STATIC_JAR + CALLER_JAR + LOG4J_JAR;
+ int childUrls = JCL_JAR + STATIC_JAR + LOG4J_JAR;
+ run("8", parentUrls, childUrls, true, false);
+ }
+
+
+ public void testCase9() {
+ int parentUrls = API_JAR + STATIC_JAR;
+ int childUrls = JCL_JAR + STATIC_JAR + CALLER_JAR + LOG4J_JAR;
+ run("9", parentUrls, childUrls, false, false);
+ }
+
+ public void testCase10() {
+ int parentUrls = API_JAR + STATIC_JAR + CALLER_JAR;
+ int childUrls = JCL_JAR + STATIC_JAR + LOG4J_JAR;
+ run("10", parentUrls, childUrls, false, false);
+ }
+
+ public void testCase11() {
+ int parentUrls = API_JAR + STATIC_JAR;
+ int childUrls = JCL_JAR + STATIC_JAR + CALLER_JAR + LOG4J_JAR;
+ run("11", parentUrls, childUrls, true, false);
+ }
+
+ public void testCase12() {
+ int parentUrls = API_JAR + STATIC_JAR + CALLER_JAR;
+ int childUrls = JCL_JAR + STATIC_JAR + LOG4J_JAR;
+ run("12", parentUrls, childUrls, true, false);
+ }
+
+ public void testCase13() {
+ int parentUrls = API_JAR + STATIC_JAR + LOG4J_JAR;
+ int childUrls = JCL_JAR + STATIC_JAR + CALLER_JAR + LOG4J_JAR;
+ run("13", parentUrls, childUrls, false, false);
+ }
+
+ public void testCase14() {
+ int parentUrls = API_JAR + STATIC_JAR + CALLER_JAR + LOG4J_JAR;
+ int childUrls = JCL_JAR + STATIC_JAR + LOG4J_JAR;
+ run("14", parentUrls, childUrls, false, false);
+ }
+
+ public void testCase15() {
+ int parentUrls = API_JAR + STATIC_JAR + LOG4J_JAR;
+ int childUrls = JCL_JAR + STATIC_JAR + CALLER_JAR + LOG4J_JAR;
+ run("15", parentUrls, childUrls, true, false);
+ }
+
+ public void testCase16() {
+ int parentUrls = API_JAR + STATIC_JAR + CALLER_JAR + LOG4J_JAR;
+ int childUrls = JCL_JAR + STATIC_JAR + LOG4J_JAR;
+ run("16", parentUrls, childUrls, true, false);
+ }
+
+ /**
+ * Runs all parent first cases.
+ * @param args
+ */
+ public static void main(String[] args)
+ {
+ ParentFirstRunner runner;
+ try {
+ runner = new ParentFirstRunner();
+
+ System.out.println("");
+ System.out.println("");
+ System.out.println("Running Parent First Cases...");
+ System.out.println("");
+ System.out.println("");
+ runner.testCase1();
+ runner.testCase2();
+ runner.testCase3();
+ runner.testCase4();
+ runner.testCase5();
+ runner.testCase6();
+ runner.testCase7();
+ runner.testCase8();
+ runner.testCase9();
+ runner.testCase10();
+ runner.testCase11();
+ runner.testCase12();
+ runner.testCase13();
+ runner.testCase14();
+ runner.testCase15();
+ runner.testCase16();
+ } catch (MalformedURLException e) {
+ System.out.println("Cannot find required jars");
+ e.printStackTrace();
+ }
+
+ }
+}
diff --git a/demonstration/src/java/org/apache/commons/logging/proofofconcept/runner/package.html b/demonstration/src/java/org/apache/commons/logging/proofofconcept/runner/package.html
new file mode 100644
index 0000000..94723ea
--- /dev/null
+++ b/demonstration/src/java/org/apache/commons/logging/proofofconcept/runner/package.html
@@ -0,0 +1,23 @@
+
+
+Demonstrates Jakarta Commons Logging (JCL) concepts.
+Introduction
+
+
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. +
+Each demonstration is initiated by a runner. The runner loads the +other code components into appropriate ClassLoaders and then calls the caller. +
+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. +
+The static logger simply contains a call directly to Log4J. This +is useful for comparison. +
+Now would be a good idea to study the javadocs. +
+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). +
+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. +
+The aim of the set up will be isolate the essentials. Only three +classloaders will be considered: +
+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. +
+The analysis will proceed by considering two cases separately: +
+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. +
+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. +
+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. +
+For example +
+| Context ClassLoader | +System | +
|---|---|
| Parent | +JCL+Static | +
| Child | +
+ JCL+Static + Log4J + Caller + |
+
| Expected Result | +JCL->java.util + static FAIL + |
+
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. +
+For purposes of reference, an indication is given in those cases +that correspond to examples used by Ceki in his critique. +
+Analytical notes explaining how these results were obtained are +included below each table. As the cases proceed, the notes will grow +more terse. +
+| Case | +1 | +2 | +3 | +4 | +
|---|---|---|---|---|
| Ceki Example | +- | +1 | +2A | +3 | +
| Context ClassLoader | +System | +System | +Child | +Child | +
| Parent | +JCL+Static | +JCL+Static Caller | +JCL+Static | +JCL+Static Caller | +
| Child | +
+ JCL+Static + Log4J + Caller |
+
+ JCL+Static + Log4J + |
+
+ JCL+Static + Log4J + Caller + |
+
+ JCL+Static + Log4J + |
+
| Expected Result | +
+ JCL->java.util + static FAIL + |
+
+ JCL->java.util + static FAIL + |
+
+ JCL->java.util + static FAIL + |
+
+ JCL->java.util + static FAIL + |
+
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. +
+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. +
+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. +
+Therefore, in these cases, the appropriate behaviour is for JCL to +discover that java.util.logging is the only available logging system. +
+| Case | +5 | +6 | +7 | +8 | +
|---|---|---|---|---|
| Ceki Example | +- | +- | +- | +- | +
| Context ClassLoader | +System | +System | +Child | +Child | +
| Parent | +Log4J + JCL+Static + | Log4J + JCL+Static + Caller + | Log4J + JCL+Static + |
+ Log4J + JCL+Static + Caller + |
+
| Child | +JCL+Static + Log4J + Caller + |
+ JCL+Static + Log4J + |
+ JCL+Static + Log4J + Caller + |
+ JCL+Static + Log4J + |
+
| Expected Result + | +JCL->log4j + static OK + |
+ JCL->log4j + static OK + |
+
+ JCL->log4j + static OK + |
+
+ JCL->log4j + static OK + |
+
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. +
+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. +
+| Case | +9 | +10 | +11 | +12 | +
|---|---|---|---|---|
| Ceki Example | +- | +- | +- | +- | +
| Context ClassLoader | +System | +System | +Child | +Child | +
| Parent | +API+Static | +API+Static + Caller + |
+ API+Static + | +API+Static + Caller + |
+
| Child | +JCL+Static + Log4J + Caller + |
+ JCL+Static + Log4J + |
+ JCL+Static + Log4J + Caller + |
+ JCL+Static + Log4J + |
+
| Expected Result | +JCL->java.util + static FAIL + |
+ JCL->java.util + static FAIL + |
+ JCL->log4j + static FAIL + |
+ JCL->log4j + static FAIL + |
+
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. +
+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. +
+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. +
+| Case | +13 | +14 | +15 | +16 | +
|---|---|---|---|---|
| Ceki Example | +- | +- | +- | +- | +
| Context ClassLoader | +System | +System | +Child | +Child | +
| Parent | +Log4J + API+Static + |
+ Log4J + API+Static + Caller + |
+ Log4J + API+Static + |
+ Log4J + API+Static + Caller + |
+
| Child | +JCL+Static + Log4J + Caller |
+ JCL+Static + Log4J |
+ JCL+Static + Log4J + Caller + |
+ JCL+Static + Log4J |
+
| Expected Result | +JCL->log4j + static OK |
+ JCL->log4j + static OK + |
+ JCL->log4j + static OK + |
+ JCL->log4j + static OK + |
+
Trivial variations on the second series. Now API and JCL are +definable only by parent and child respectively (as in the last +series). +
+The same classloader configuration can be repeated with (this +time) child-first classloading. +
+| Case | +17 | +18 | +19 | +20 | +
|---|---|---|---|---|
| Ceki Example | +- | +- | +5 | +6 | +
| Context ClassLoader | +System | +System | +Child | +Child | +
| Parent | +JCL+Static | +JCL+Static + Caller |
+ JCL+Static | +JCL+Static + Caller + |
+
| Child | +JCL+Static + Log4J + Caller + |
+ JCL+Static + Log4J + |
+ JCL+Static + Log4J + Caller + |
+ JCL+Static + Log4J + |
+
| Expected Result | +JCL->Log4j + static OK + |
+ JCL->java.util + static FAIL + |
+ JCL->Log4j + static OK + |
+ JCL->java.util + static FAIL + |
+
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. +
+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. +
+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. +
+| Case + | +21 | +22 | +23 | +24 | +
|---|---|---|---|---|
| Ceki Example | +- | +- | +- | +- | +
| Context ClassLoader | +System | +System | +Child | +Child | +
| Parent | +Log4J + JCL+Static + |
+ Log4J + JCL+Static + Caller + |
+ Log4J + JCL+Static + |
+ Log4J + JCL+Static + Caller + |
+
| Child | +JCL+Static + Log4J + Caller + |
+ JCL+Static + Log4J + |
+ JCL+Static + Log4J + Caller + |
+ JCL+Static + Log4J + |
+
| Expected Result | +JCL->log4j + static OK + |
+ JCL->log4j + static OK + |
+ JCL->log4j + static OK + |
+ JCL->log4j + static OK + |
+
Trivial case where jar's are definable by both loaders. +
+| Case | +25 | +26 | +27 | +28 | +
|---|---|---|---|---|
| Ceki Example | +- | +- | +- | +- | +
| Context ClassLoader | +System | +System | +Child | +Child | +
| Parent | +API+Static | +API+Static + Caller + |
+ API+Static | +API+Static + Caller + |
+
| Child | +JCL+Static + Log4J + Caller + |
+ JCL+Static + Log4J + |
+ JCL+Static + Log4J + Caller + |
+ JCL+Static + Log4J + |
+
| Expected Result | +JCL->log4j + static OK + |
+ JCL->java.util + static FAIL + |
+ JCL->log4j + static OK + |
+ JCL->java.util + static FAIL + |
+
(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. +
+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). +
+| Case | +29 | +30 | +31 | +32 | +
|---|---|---|---|---|
| Ceki Example | +- | +- | +- | +- | +
| Context ClassLoader | +System | +System | +Child | +Child | +
| Parent | +Log4J + API+Static + |
+ Log4J + API+Static + Caller + |
+ Log4J + API+Static + |
+ Log4J + API+Static + Caller + |
+
| Child | +JCL+Static + Log4J + Caller + |
+ JCL+Static + Log4J + |
+ JCL+Static + Log4J + Caller + |
+ JCL+Static + Log4J + |
+
| Expected Result | +JCL->log4j + static OK + |
+ JCL->java.util + static OK + |
+ JCL->log4j + static OK + |
+ JCL->java.util + static OK + |
+
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. +
+Even with a friendly context classloader, JCL runs into the same +difficulties with accessibility that +occurred above. +
+ +