Hi Bernhard,
hmm - if the name of your property file is the same X, but only the
content is different for both classloading scenarios, then - as you have
already found out - the solution a just proposed in the previous reply
by Bryan / User batsatt will not work:
As the resource file X will already have been loaded from the common
classloader, the EJB classloader will delegate any further requests to
load resource X to its parent loaders and not load the different
incarnation of X from its own EJB-JAR.
So it indeed looks like the only way to overcome this issue is to set up
a custom non-delegating classloader within the EJB container, which is
quite a demanding, but interesting programming task... :-)
Firstly, I'd like to point you to One-JAR:
http://one-jar.sourceforge.net
where basically nested JARs are implemented in a similar way by defining
an application-specific classloader that does a very custom job.
I have used the One-JAR library at some point in the past and
modified/extended its com.simontuffs.onejar.JarClassLoader in a way that
it becomes a non-delegating classloader - which is what you will need
from the EJB container, though not for nested JAR files. But you will
get the basic idea of how a non-delegating classloader should look like:
package com.blabla.util;
import com.simontuffs.onejar.JarClassLoader;
/**
* NonDelegatingClassLoader is a custom, non-delegating classloader for the
* Blabla framework.
* <p>
* It freshly loads all Blabla-related classes from this classloader,
thereby
* overriding original Hubba classes that come with Foobar App Server and
* therefore enabling Blaba to be used from a Foobar Doodle.
* <p>
* Classes not explicitly included in the non-delegation list as
contained in
* method {_at_link loadClass()} will be loaded from the parent classloader as
* usual.
*/
public class NonDelegatingClassLoader extends JarClassLoader {
/**
* NonDelegatingClassLoader debug system property name: If set to
true, will
* write debug output to System.err.
*/
public static final String DEBUG_PROPERTY =
"com.blaba.util.NonDelegatingClassLoader.debug";
/**
* Debug flag. Default is <code>false</code>.
*/
protected boolean debug = false;
/**
* Nested exception class thrown and immediately caught to create stack
* traces on demand.
*/
public static class ForDebuggingOnlyNoRealException extends Exception {
private static final long serialVersionUID = 1L;
}
/**
* Construct a new NonDelegatingClassLoader instance.
*
* @param parent the parent classloader
*/
public NonDelegatingClassLoader(ClassLoader parent) {
super(parent);
debug = false;
if (System.getProperty(DEBUG_PROPERTY,
"false").equalsIgnoreCase("true")) {
debug = true;
}
if (debug) {
System.err.println("Parent classloader of
NonDelegatingClassLoader is " + parent);
}
}
/**
* {_at_inheritDoc}
*/
protected synchronized Class<?> loadClass(String name, boolean
resolve) throws ClassNotFoundException {
// is the class already loaded/available from this classloader?
Class<?> c = findLoadedClass(name);
if (c == null) {
// is the class name to be loaded included in the
non-delegation list?
if (!(name.startsWith("com.hubba."))) {
// no, it is not - therefore delegate to the parent classloader
if (debug) {
try {
throw new ForDebuggingOnlyNoRealException();
} catch (ForDebuggingOnlyNoRealException e) {
System.err.println("Loading class " + name + " from parent
classloader");
e.printStackTrace();
}
}
try {
c = getParent().loadClass(name);
} catch (ClassNotFoundException e) {
if (debug) {
System.err.println("Class " + name + " not found in parent
classloader");
}
}
}
if (c == null) {
// load class from this NonDelegatingClassLoader
try {
if (debug) {
try {
throw new ForDebuggingOnlyNoRealException();
} catch (ForDebuggingOnlyNoRealException e) {
System.err.println("Loading class " + name + " from
NonDelegatingClassLoader");
e.printStackTrace();
}
}
c = findClass(name);
} catch (ClassNotFoundException cnfe) {
if (debug) {
System.err.println("Class " + name + " not found in
NonDelegatingClassLoader");
}
}
// if not available from this NonDelegatingClassLoader, only
then try its parent
if (c == null) {
if (debug) {
try {
throw new ForDebuggingOnlyNoRealException();
} catch (ForDebuggingOnlyNoRealException cnfe) {
System.err.println("Loading class " + name + " from
parent classloader (2)");
cnfe.printStackTrace();
}
}
try {
c = getParent().loadClass(name);
} catch (ClassNotFoundException cnfe) {
if (debug) {
System.err.println("Class " + name + " not found in
parent classloader (2)");
cnfe.printStackTrace();
}
throw cnfe;
}
}
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
Any use of the custom classloader needs to be coded similar to the
following then:
// determine and store away current Java EE context classloader
ClassLoader oldcl = Thread.currentThread().getContextClassLoader();
LOG.debug("saved old context class loader " + oldcl);
try {
// load the true ("delegate") implementation class from the
custom non-delegating classloader
Class<?> cls =
NON_DELEGATING_CLASSLOADER.loadClass(DELEGATE_CLASS_NAME);
// and make the custom classloader the current Java EE context
classloader
Thread.currentThread().setContextClassLoader(NON_DELEGATING_CLASSLOADER);
>>> here you need to code access to your properties file instead by
using NON_DELEGATING_CLASSLOADER./getResourceAsStream/(name)
// create the delegate instance
Blabla myBlablaImpl = (Blabla) cls.newInstance();
// and forward the inbound call to the delegate object
return myBlablaImpl.blabla();
<<< here you need to code access to your properties file instead by
using NON_DELEGATING_CLASSLOADER./getResourceAsStream/(name)
} catch (Exception ex) {
(...)
} finally {
// restore the previous context class loader
Thread.currentThread().setContextClassLoader(oldcl);
LOG.debug("restored old context class loader " + oldcl);
}
You can also look here:
http://www.xinotes.org/notes/note/444/
for another sample of a non-delegating clasloader for a specific scenario...
Hope this helps & best regards,
Andreas
P.S.:
If you have any further questions, don't hesitate to contact me directly ;-)
I am unfortunately still on sick leave, but will hopefully be back at
work early next year...
--
Andreas Loew | Senior Java Architect
Oracle Advanced Customer Services
ORACLE Deutschland B.V. & Co. KG