Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

problem mit doppelter Definition einer Klasse

1 view
Skip to first unread message

Jochen Theodorou

unread,
Nov 15, 2007, 5:00:44 PM11/15/07
to
hi alle,

ja, die lieben ClassLoader sind mal wieder schwer für Kopfschmerzen auf
unserer Seite verantwortlich. Jetzt wollte ich mal fragen ob da jemand
eine Idee hat. Den mir gehen die Ideen langsam aus oder werden doch
etwas... waghalsig.

die Fehlermeldung ist:
> Error executing script ListPlugins: loader constraint violation: loader (instanc
> e of org/codehaus/groovy/tools/RootLoader) previously initiated loading for a di
> fferent type with name "org/w3c/dom/UserDataHandler"
> java.lang.LinkageError: loader constraint violation: loader (instance of org/cod
> ehaus/groovy/tools/RootLoader) previously initiated loading for a different type
> with name "org/w3c/dom/UserDataHandler"
> at java.lang.ClassLoader.defineClass1(Native Method)
> at java.lang.ClassLoader.defineClass(ClassLoader.java:620)
> at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124)
> at java.net.URLClassLoader.defineClass(URLClassLoader.java:260)
> at java.net.URLClassLoader.access$000(URLClassLoader.java:56)
> at java.net.URLClassLoader$1.run(URLClassLoader.java:195)
> at java.security.AccessController.doPrivileged(Native Method)
> at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
> at org.codehaus.groovy.tools.RootLoader.oldFindClass(RootLoader.java:142)
> at org.codehaus.groovy.tools.RootLoader.loadClass(RootLoader.java:114)

und noch einiges mehr.

was ich bisher feststellen konnte ist, dass es kein
Multithreadingproblem sein kann, da nur ein Thread aktiv ist. es gibt
nur einen RootLoader in der Hirarchie, es ist also der gleiche hier
gemeint. Das Debugging war nicht sehr aussagekräftig bisher, aber
scheinbar wird die Klasse vorher über eine native Methode definiert ( da
bin ich mir nch nicht sicher) und nicht erst über loadClass angefragt.
wie man am trace sieht wird das zweite Mal über loadClass angefragt,
allerdings behauptet this.findLoadedClass, dass die Klasse bisher nicht
geladen ist.

Meine Frage wäre jetzt... tja... wie kann ich dem Problem auf die
Schliche kommen? die Klasse hatte bisher immer funktioniert, aber es
scheint jetzt irgendwie Zufall zu sein dass das bisher keine Fehler gab.
Kann es passieren dass die VM direkt, das heisst ohne loadClass
nochmal zu bemühen, Klassen definiert? gibt es bei der VM einen switch
oder sowas der anzeigt welche Klassen definiert werden, das könnte
eventuell ein wenig helfen. Auch jede andere Idee wäre willkommen... ich
bin ein bischen ratlos

Gruss theo

--
Jochen "blackdrag" Theodorou
The Groovy Project Tech Lead (http://groovy.codehaus.org)
http://blackdragsview.blogspot.com/
http://www.g2one.com/

Bernd Eckenfels

unread,
Nov 15, 2007, 5:05:32 PM11/15/07
to
Jochen Theodorou <blac...@uni.de> wrote:
> was ich bisher feststellen konnte ist, dass es kein
> Multithreadingproblem sein kann, da nur ein Thread aktiv ist. es gibt
> nur einen RootLoader in der Hirarchie, es ist also der gleiche hier
> gemeint.

Wenn es kein MT PRoblem ist (sieht aber nach einem aus), dann kann es
entweder ein Rekursions-Problem sein oder ganz spontan würde ich auch mal
die Stacks grösser machen (ein java oder nativer stack der vollaeuft fuehrt
auch immer zu merkwuerdigen class loader exceptions)

> nochmal zu bemühen, Klassen definiert? gibt es bei der VM einen switch
> oder sowas der anzeigt welche Klassen definiert werden, das könnte
> eventuell ein wenig helfen. Auch jede andere Idee wäre willkommen... ich
> bin ein bischen ratlos

ich weiss nicht ob -verbose:class was bringt.

Gruss
Bernd

Jochen Theodorou

unread,
Nov 15, 2007, 6:24:47 PM11/15/07
to
Bernd Eckenfels schrieb:

> Jochen Theodorou <blac...@uni.de> wrote:
>> was ich bisher feststellen konnte ist, dass es kein
>> Multithreadingproblem sein kann, da nur ein Thread aktiv ist. es gibt
>> nur einen RootLoader in der Hirarchie, es ist also der gleiche hier
>> gemeint.
>
> Wenn es kein MT PRoblem ist (sieht aber nach einem aus),

das habe ich auch zuerst gedacht, aber der Fehler lässt sich im Debugger
nachvollziehen und es ist nur ein Thread am laufen.

> dann kann es
> entweder ein Rekursions-Problem sein oder ganz spontan würde ich auch mal
> die Stacks grösser machen (ein java oder nativer stack der vollaeuft fuehrt
> auch immer zu merkwuerdigen class loader exceptions)

ein Stackproblem... der trace ist relativ klein.


>> nochmal zu bemühen, Klassen definiert? gibt es bei der VM einen switch
>> oder sowas der anzeigt welche Klassen definiert werden, das könnte
>> eventuell ein wenig helfen. Auch jede andere Idee wäre willkommen... ich
>> bin ein bischen ratlos
>
> ich weiss nicht ob -verbose:class was bringt.

hmm, werde ich morgen mal probieren

Ralf Ullrich

unread,
Nov 15, 2007, 7:23:32 PM11/15/07
to
Jochen Theodorou wrote:

>Auch jede andere Idee wäre willkommen...

Na dann hoff' ich mal das wenigstens eine genaue Analyse willkommen ist,
wenn ich dir auch nicht sagen kann, woran es konkret scheitert:

Siehe Bug 4972409 [1] ganz unten Punkt 6: »The loader has previously
initiated loading for a different type with the same name« das ist genau
deine Fehlersituation, und der entsprechende Abschnitt in der VM Spec ist
5.3.5 [2] Punkt 1: »First, the Java virtual machine determines whether it
has already recorded that L is an initiating loader of a class or
interface denoted by N. If so, this creation attempt is invalid and
loading throws a LinkageError.«

Es gibt zwei Stellen, an denen dein ClassLoader zuvor als »initiating
loader« registriert werden konnte:5.3.2 [3] und 5.3.5 [2] Punkt 5. Somit
bleiben zwei Szenarien:

a) Auf deinem ClassLoader wurde vorher loadClass(N) aufgerufen, was er
dann aber an einen anderen ClassLoader delegiert hat. Bei einem erneuten
Aufruf von loadClass(N) hat dein ClassLoader dann aber plötzlich nicht
mehr delegiert, sondern selbst defineClass(N, bc(N)) aufgerufen, was dann
fehlschlagen muss.

b) Auf deinem ClassLoader wurde vorher defineClass(N,bc(N)) aufgerufen und
nun nochmals loadClass(N) was bei deiner Implementation offenbar zu einem
parent.findClass(N) führt und damit implizit zu einem erneuten
defineClass(N,bc(N)), der dann fehlschlagen muss.

In beiden Szenarien sollte aber findLoadedClass() kurz vor dem
fehlschlagenden defineClass() einen Wert ungleich »null« zurückgeben. Da
ich keine Ahnung habe wie du zu deiner Aussage kommst, dass dies »null«
liefert, möchte ich einen Fehler in deiner loadClass()-Implementierung,
die dies ja prüfen müsste, noch nicht ausschließen.

Die VM verwendet auf »user defined class loadern« stets loadClass und nie
direkt defineClass, daher kann Szenario b) nur auftreten, wenn du selbst
irgendwie außer der Reihe (d.h. ohne zugehörigen vorherigen
loadClass()-Aufruf) defineClass() aufrufst. Wenn du solche
defineClass-Aufrufe hast, dann mache direkt davor eine Logging-Anweisung,
die die Parameter mit denen defineClass-Aufrufen wird protokolliert.

Für Szenario a) reicht es wenn du protokollierst, mit welchen Parametern
loadClass an deinem ClassLoader aufgerufen wurde.

Achja, beide Protokollstellen sollten noch festhalten, ob findLoadedClass
null zurückgibt.

Mit diesen Protokollen sollte sich dir ein vollständiges Bild aufzeigen.

cu

[1] http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4972409

[2]
http://java.sun.com/docs/books/jvms/second_edition/html/ConstantPool.doc.html#79388

[3]
http://java.sun.com/docs/books/jvms/second_edition/html/ConstantPool.doc.html#79441

Jochen Theodorou

unread,
Nov 16, 2007, 7:40:03 AM11/16/07
to
Ralf Ullrich schrieb:

> Jochen Theodorou wrote:
>
>> Auch jede andere Idee wäre willkommen...
>
> Na dann hoff' ich mal das wenigstens eine genaue Analyse willkommen ist,
> wenn ich dir auch nicht sagen kann, woran es konkret scheitert:
>
> Siehe Bug 4972409 [1] ganz unten Punkt 6: »The loader has previously
> initiated loading for a different type with the same name« das ist genau
> deine Fehlersituation, und der entsprechende Abschnitt in der VM Spec
> ist 5.3.5 [2] Punkt 1: »First, the Java virtual machine determines
> whether it has already recorded that L is an initiating loader of a
> class or interface denoted by N. If so, this creation attempt is invalid
> and loading throws a LinkageError.«

genau... im Klartext, doppeldefinition im selben ClassLoader ist verboten.

> Es gibt zwei Stellen, an denen dein ClassLoader zuvor als »initiating
> loader« registriert werden konnte:5.3.2 [3] und 5.3.5 [2] Punkt 5. Somit
> bleiben zwei Szenarien:
>
> a) Auf deinem ClassLoader wurde vorher loadClass(N) aufgerufen, was er
> dann aber an einen anderen ClassLoader delegiert hat. Bei einem erneuten
> Aufruf von loadClass(N) hat dein ClassLoader dann aber plötzlich nicht
> mehr delegiert, sondern selbst defineClass(N, bc(N)) aufgerufen, was
> dann fehlschlagen muss.

das liest du falsch. Dass muss fehlschlagen wenn dieser ClassLoader
derjenige war, welcher die Klasse auch zuvor definiert hat, aber es muss
nicht Fehlschlagen wenn die Klasse von einem anderen (zum Beispiel vom
parent) definiert war. Man *soll* es nur nicht so machen.

ich habe zum Beispiel mal folgendes gehackt:

> class Loader extends URLClassLoader {
> boolean ignoreStartClass = false;
> public Loader(ClassLoader parent) {
> super(new URL[0], parent);
> }
> @Override
> public void addURL(URL url) {
> super.addURL(url);
> }
> @Override
> protected synchronized Class<?> loadClass(String name, boolean resolve)
> throws ClassNotFoundException {
> if (ignoreStartClass && name.equals("Start")) {
> throw new ClassNotFoundException();
> } else {
> return super.loadClass(name, resolve);
> }
> }
> }
>
> Loader l1 = new Loader(ClassLoader.getSystemClassLoader());
> Loader l2 = new Loader(l1);
> l1.addURL(new File(".").toURI().toURL());
> Class c1 = l2.loadClass("Start");
> l1.ignoreStartClass = true;
> l2.addURL(new File(".").toURI().toURL());
> Class c2 = l2.loadClass("Start");
> System.out.println(c1);
> System.out.println(c2);
> System.out.println(c1==c2);

c1 und c2 haben den gleichen Namen, aber c1 ist nicht c2. Das hier
funktioniert soweit ohne Probleme. Insbesondere bekomme ich nicht die
Exception.

> b) Auf deinem ClassLoader wurde vorher defineClass(N,bc(N)) aufgerufen
> und nun nochmals loadClass(N) was bei deiner Implementation offenbar zu
> einem parent.findClass(N) führt und damit implizit zu einem erneuten
> defineClass(N,bc(N)), der dann fehlschlagen muss.

das scheint mir wahrscheinlicher.

> In beiden Szenarien sollte aber findLoadedClass() kurz vor dem
> fehlschlagenden defineClass() einen Wert ungleich »null« zurückgeben. Da
> ich keine Ahnung habe wie du zu deiner Aussage kommst, dass dies »null«
> liefert, möchte ich einen Fehler in deiner loadClass()-Implementierung,
> die dies ja prüfen müsste, noch nicht ausschließen.

der Code sieht so aus:
> Class c = this.findLoadedClass(name);
> if (c != null) return c;
>
> try {
> c = oldFindClass(name);
> } catch (ClassNotFoundException cnfe) {
> // IGNORE
> }
> if (c == null) c = super.loadClass(name, resolve);
>
> if (resolve) resolveClass(c);
>
> return c;

der Aufruf von oldFundClass, welcher findClass von UrlClassLoader
aufruft ist dann der Auslöser der Exception.

> Die VM verwendet auf »user defined class loadern« stets loadClass und
> nie direkt defineClass, daher kann Szenario b) nur auftreten, wenn du
> selbst irgendwie außer der Reihe (d.h. ohne zugehörigen vorherigen
> loadClass()-Aufruf) defineClass() aufrufst. Wenn du solche
> defineClass-Aufrufe hast, dann mache direkt davor eine
> Logging-Anweisung, die die Parameter mit denen defineClass-Aufrufen wird
> protokolliert.

solche Aufrufe habe ich nicht drin. Ich werde mal noch ein paar
Debugsachen einfügen... hoffentlich kommt dann was neues bei raus.

Ralf Ullrich

unread,
Nov 16, 2007, 8:58:29 AM11/16/07
to
Jochen Theodorou wrote:

>>Es gibt zwei Stellen, an denen dein ClassLoader zuvor als »initiating
>>loader« registriert werden konnte:5.3.2 [3] und 5.3.5 [2] Punkt 5. Somit
>>bleiben zwei Szenarien:
>>
>>a) Auf deinem ClassLoader wurde vorher loadClass(N) aufgerufen, was er
>>dann aber an einen anderen ClassLoader delegiert hat. Bei einem erneuten
>>Aufruf von loadClass(N) hat dein ClassLoader dann aber plötzlich nicht
>>mehr delegiert, sondern selbst defineClass(N, bc(N)) aufgerufen, was dann
>>fehlschlagen muss.
>
>das liest du falsch. Dass muss fehlschlagen wenn dieser ClassLoader
>derjenige war, welcher die Klasse auch zuvor definiert hat, aber es muss
>nicht Fehlschlagen wenn die Klasse von einem anderen (zum Beispiel vom

>parent) definiert war. Man soll es nur nicht so machen.

Nein, ich habe schon richtig gelesen, dein Hack ging nur nicht weit genug.
Das Laden der Klasse, die doppelt definiert wird, muss allerdings beim
ersten Mal von der VM ausgelöst werden, also zum Beispiel dadurch, dass es
eine Superklasse S zu einer Klasse N ist, die mit cl.loadClass(N) geladen
wird.

Anbei ein Beispiel, das den LinkageError nach 5.3.2 erzeugt. Allerdings
ist die Meldung bei mir unter Java 1.6u2 schon mal anders betextet als bei
dir zitiert, womit dann wohl bewiesen ist, dass es dieses Szenario nicht
ist.

Ich erhalte folgende Ausgabe vom angehängten Code:

A: is init ldr L1=false L2=false
[L2]lC:ENTER: de.jnana.dclj.people.jt.clsldr.ClassLoaderDemo$Derived
1 java.lang.String::charAt (33 bytes)
[L2]lC:ENTER: de.jnana.dclj.people.jt.clsldr.ClassLoaderDemo$Base
[L1]lC:ENTER: de.jnana.dclj.people.jt.clsldr.ClassLoaderDemo$Base
[L1]lC:ENTER: java.lang.Object
[L1]lC:EXIT2: java.lang.Object fa7e74
[L1]lC:EXIT2: de.jnana.dclj.people.jt.clsldr.ClassLoaderDemo$Base e102dc
[L2]lC:EXIT1: de.jnana.dclj.people.jt.clsldr.ClassLoaderDemo$Base e102dc
[L2]lC:EXIT2: de.jnana.dclj.people.jt.clsldr.ClassLoaderDemo$Derived 133796
B: is init ldr L1=true L2=true
[L2]lC:ENTER: de.jnana.dclj.people.jt.clsldr.ClassLoaderDemo$Base
[L2]lC:ENTER: java.lang.Object
[L2]lC:EXIT2: java.lang.Object fa7e74
Exception in thread "main" java.lang.LinkageError: loader (instance of
de/jnana/dclj/people/jt/clsldr/ClassLoaderDemo$Loader): attempted
duplicate class definition for name:
"de/jnana/dclj/people/jt/clsldr/ClassLoaderDemo$Base"


at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:620)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:260)
at java.net.URLClassLoader.access$000(URLClassLoader.java:56)
at java.net.URLClassLoader$1.run(URLClassLoader.java:195)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)

at de.jnana.dclj.people.jt.clsldr.ClassLoaderDemo$Loader.loadClass(ClassLoaderDemo.java:44)
at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
at de.jnana.dclj.people.jt.clsldr.ClassLoaderDemo.main(ClassLoaderDemo.java:68)

Beachte, dass bei B: auch L2 initiating loader für Base ist, das wäre
nicht so, hätte ich Base direkt über loadClass geladen.

cu

package de.jnana.dclj.people.jt.clsldr;

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;

public class ClassLoaderDemo {

public static class Base {
}

public static class Derived extends Base {
}
static final String BASE_NAME = ClassLoaderDemo.class.getName() + "$Base";
static final String DERIVED_NAME = ClassLoaderDemo.class.getName() + "$Derived";

static class Loader extends URLClassLoader {

final String ldrName;

ClassLoader delegate;
boolean delegateFirstTime;

public Loader(String name, URL[] urls, ClassLoader delegate) {
super(urls, null);
ldrName = name;
this.delegate = delegate;
delegateFirstTime = (delegate != null);
}

@Override
protected synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {

System.out.printf("[%s]lC:ENTER: %s%n", ldrName, name);

if (delegateFirstTime && BASE_NAME.equals(name)) {
delegateFirstTime = false;
Class c = delegate.loadClass(name);
System.out.printf("[%s]lC:EXIT1: %s %x%n", ldrName, name, System.identityHashCode(c));
return c;
}
Class c = null;
try {
c = findClass(name);
} catch (ClassNotFoundException e) {
c = ClassLoader.getSystemClassLoader().loadClass(name);
}
if (resolve) {
resolveClass(c);
}
System.out.printf("[%s]lC:EXIT2: %s %x%n", ldrName, name, System.identityHashCode(c));
return c;
}

private boolean isInitiatingCLForName(String name) {
return null != findLoadedClass(name);
}
}

public static void main(String[] args) throws Exception {
try {
URL[] urls = ((URLClassLoader) ClassLoader.getSystemClassLoader()).getURLs();
Loader l1 = new Loader("L1",urls, null);
Loader l2 = new Loader("L2",urls, l1);
System.out.printf("A: is init ldr L1=%s L2=%s%n",l1.isInitiatingCLForName(BASE_NAME),l2.isInitiatingCLForName(BASE_NAME));
Class c1 = l2.loadClass(DERIVED_NAME).getSuperclass();
System.out.printf("B: is init ldr L1=%s L2=%s%n",l1.isInitiatingCLForName(BASE_NAME),l2.isInitiatingCLForName(BASE_NAME));
Class c2 = l2.loadClass(BASE_NAME);
System.out.printf("C: is init ldr L1=%s L2=%s%n",l1.isInitiatingCLForName(BASE_NAME),l2.isInitiatingCLForName(BASE_NAME));
System.out.println(c1 + "@" + System.identityHashCode(c1));
System.out.println(c2 + "@" + System.identityHashCode(c2));
System.out.println(c1 == c2);
System.out.println(c1.getClassLoader());
System.out.println(c2.getClassLoader());
} finally {
System.out.flush();
}
}
}

Ralf Ullrich

unread,
Nov 16, 2007, 10:08:19 AM11/16/07
to
Jochen Theodorou wrote:

>>java.lang.LinkageError: loader constraint violation: loader (instance of
>>org/cod
>>ehaus/groovy/tools/RootLoader) previously initiated loading for a
>>different type
>>with name "org/w3c/dom/UserDataHandler"

OK, es hat 'ne Weile gebraucht, aber ich habe deinen Fehler mal
redproduzieren können, und zwar sogar mit dem genau gleichen Stacktrace.

Ich lasse mal den Code sprechen, wenn dir was nicht klar ist bitte
nachfragen. Hier meine Ausgabe, danach der Code:

A: is init ldr L1=false L2=false L3=false
[L2]lC:ENTER: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Derived
[L2]lC:ENTER: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Actor
[L1]lC:ENTER: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Actor
[L1]lC:ENTER: java.lang.Object
[L1]lC:SUPER: java.lang.Object 1a679b7
[L1]lC:EXIT_: java.lang.Object 1a679b7
[L1]lC:FOUND: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Actor
4741d6
[L1]lC:EXIT_: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Actor
4741d6
[L2]lC:SUPER: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Actor
4741d6
[L2]lC:EXIT_: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Actor
4741d6
[L2]lC:FOUND: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Derived
578ceb
[L2]lC:EXIT_: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Derived
578ceb
B: is init ldr L1=false L2=false L3=false
[L3]lC:ENTER: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Derived
[L3]lC:ENTER: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Actor
[L1]lC:ENTER: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Actor
[L1]lC:LODED: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Actor
4741d6
[L1]lC:EXIT_: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Actor
4741d6
[L3]lC:SUPER: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Actor
4741d6
[L3]lC:EXIT_: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Actor
4741d6
[L3]lC:FOUND: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Derived
1fdc96c
[L3]lC:EXIT_: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Derived
1fdc96c
C: is init ldr L1=false L2=false L3=false
c1: class
de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Derived@578ceb
c2: class
de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Derived@1fdc96c
[L2]lC:ENTER: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Value
[L2]lC:ENTER: java.lang.Object
[L1]lC:ENTER: java.lang.Object
[L1]lC:LODED: java.lang.Object 1a679b7
[L1]lC:EXIT_: java.lang.Object 1a679b7
[L2]lC:SUPER: java.lang.Object 1a679b7
[L2]lC:EXIT_: java.lang.Object 1a679b7
[L2]lC:FOUND: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Value
b2fd8f
[L2]lC:EXIT_: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Value
b2fd8f
[L3]lC:ENTER: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Value
[L3]lC:ENTER: java.lang.Object
[L1]lC:ENTER: java.lang.Object
[L1]lC:LODED: java.lang.Object 1a679b7
[L1]lC:EXIT_: java.lang.Object 1a679b7
[L3]lC:SUPER: java.lang.Object 1a679b7
[L3]lC:EXIT_: java.lang.Object 1a679b7
Exception in thread "main" java.lang.LinkageError: loader constraint
violation: loader (instance of
de/jnana/dclj/people/jt/clsldr/LoadingConstraintDemo$Loader) previously

initiated loading for a different type with name

"de/jnana/dclj/people/jt/clsldr/LoadingConstraintDemo$Value"


at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:620)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:260)
at java.net.URLClassLoader.access$000(URLClassLoader.java:56)
at java.net.URLClassLoader$1.run(URLClassLoader.java:195)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)

at de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Loader.loadClass(LoadingConstraintDemo.java:53)
at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319)
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2427)
at java.lang.Class.getMethod0(Class.java:2670)
at java.lang.Class.getMethod(Class.java:1603)
at de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo.main(LoadingConstraintDemo.java:105)

cu

package de.jnana.dclj.people.jt.clsldr;

import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.List;

public class LoadingConstraintDemo {

public static class Value {
}

public static class Actor {

public Value m() {
return null;
}
}

public static class Derived extends Actor {
// Override is important, because only then a loading constraint is placed. see 5.4.2

@Override
public Value m() {
return null;
}
}
static final String VALUE_NAME = LoadingConstraintDemo.class.getName() + "$Value";
static final String ACTOR_NAME = LoadingConstraintDemo.class.getName() + "$Actor";
static final String DERIVED_NAME = LoadingConstraintDemo.class.getName() + "$Derived";

static class Loader extends URLClassLoader {

final String ldrName;
final List<String> knownClasses;

public Loader(String name, URL[] urls, ClassLoader parent, String... knownClasses) {
super(urls, parent);
ldrName = name;
this.knownClasses = Arrays.asList(knownClasses.clone());
}

@Override
protected synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
System.out.printf("[%s]lC:ENTER: %s%n", ldrName, name);

Class c = findLoadedClass(name);
if (c == null) {
if (knownClasses.contains(name)) {
c = findClass(name);
System.out.printf("[%s]lC:FOUND: %s %x%n", ldrName, name, System.identityHashCode(c));
} else {
c = super.loadClass(name, resolve);
System.out.printf("[%s]lC:SUPER: %s %x%n", ldrName, name, System.identityHashCode(c));
}
} else {
System.out.printf("[%s]lC:LODED: %s %x%n", ldrName, name, System.identityHashCode(c));
}

if (resolve) {
resolveClass(c);
}
System.out.printf("[%s]lC:EXIT_: %s %x%n", ldrName, name, System.identityHashCode(c));
return c;
}

private boolean isInitiatingCLForName(String name) {
return null != findLoadedClass(name);
}
}

private static void dumpIsInitLdr(String msg, Loader... ldrs) {
StringBuilder sb = new StringBuilder(msg);
sb.append(": is init ldr");
for (Loader l : ldrs) {
sb.append(String.format(" %s=%s", l.ldrName, l.isInitiatingCLForName(VALUE_NAME)));
}
System.out.println(sb.toString());

}

@SuppressWarnings("unchecked")


public static void main(String[] args) throws Exception {
try {

Class[] params = new Class[0];


URL[] urls = ((URLClassLoader) ClassLoader.getSystemClassLoader()).getURLs();
Loader l1 = new Loader("L1",

urls, ClassLoader.getSystemClassLoader(), ACTOR_NAME);
Loader l2 = new Loader("L2", urls, l1, DERIVED_NAME, VALUE_NAME);
Loader l3 = new Loader("L3", urls, l1, DERIVED_NAME, VALUE_NAME);
Loader[] ldrs = {l1, l2, l3};
dumpIsInitLdr("A", ldrs);
Class c1 = l2.loadClass(DERIVED_NAME);
dumpIsInitLdr("B", ldrs);
Class c2 = l3.loadClass(DERIVED_NAME);
dumpIsInitLdr("C", ldrs);
System.out.printf("c1: %s@%x%n", c1, System.identityHashCode(c1));
System.out.printf("c2: %s@%x%n", c2, System.identityHashCode(c2));
Object o1 = c1.newInstance();
Method m1 = c1.getMethod("m", params);
Object o2 = c2.newInstance();
Method m2 = c2.getMethod("m", params);
Class v1 = m1.getReturnType();
Class v2 = m2.getReturnType();
System.out.printf("v1: %s@%x%n",
v1, System.identityHashCode(v1));
System.out.printf("v2: %s@%x%n", v2, System.identityHashCode(v2));


System.out.println(c1 == c2);
System.out.println(c1.getClassLoader());
System.out.println(c2.getClassLoader());
} finally {
System.out.flush();

try {
Thread.sleep(300);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}

Ralf Ullrich

unread,
Nov 16, 2007, 11:44:42 AM11/16/07
to
So, noch ein wenig mehr.

Meine vorige Reproduktion des Fehlers betraf wie im Code angedeutet
Loading Constraints nach VM Spec 5.4.2, also aus dem Preparation Schritt.

Neben diesem gibt es laut VM Spec 5.3.4 noch drei weitere Möglichkeiten
Loading Constraints aufzubauen, beschrieben jeweils unter 5.4.3.2, 5.4.3.3
und 5.4.3.4. Da diese alle drei in der Phase der Resolution auftreten muss
man den Test-Code noch um das Aufrufen der Methode ergänzen, also hier

Object o1 = c1.newInstance();
Method m1 = c1.getMethod("m", params);

m1.invoke(o1, args);


Object o2 = c2.newInstance();
Method m2 = c2.getMethod("m", params);

m2.invoke(o2, args);

die beiden invoke-Aufrufe einfügen, mit zuvor deklariertem:

Object[] args = new Object[0];

Danach kann man die drei Test-Klassen Value, Actor und Derived für die
weiteren Situationen anpassen.

Für 5.4.3.2 Field Resolution:

public static class Value {
}

public static class Actor {

public Value f;

}

public static class Derived extends Actor {

public Value m() {
return f; // symbolic field reference, see 5.4.3.2 for loading constraints
}
}

Für 5.4.3.3 Method Resolution:

public static class Value {
}

public static class Actor {

public Value foo() {
return null;
}
}

public static class Derived extends Actor {

public Value m() {
return foo(); // symbolic method reference, see 5.4.3.3 for loading constraints
}
}

Für 5.4.3.4 Interface Method Reslution:

public static class Value implements Actor {

public Value foo() {
return this;
}
}

public interface Actor {

public Value foo();
}

public static class Derived {

Actor a = new Value();

public Value m() {
return a.foo(); // symbolic interface method reference, see 5.4.3.4 for loading constraints
}
}

Alle drei Situationen haben deutlich andere LinkageError Beschreibungen.

Gemäß dem Bug 4972409 soll es ja sechs mögliche Situationen geben, von
denen ich dann wohl bislang
6) via 5.4.2
5) via 5.4.3.2
1) via 5.4.3.3
und
4) via 5.4.3.4

reproduzieren konnte. Wie ich zu

2) Resolving the types in an interface method
3) Resolving the types in an overriding method

komme weiß ich nicht, weil ich eigentlich gedacht hätte das wären die
Fälle 5.4.3.4 und 5.4.2 gewesen, die aber von der Fehlermeldung her eben
besser zu 4) und 6) passen.

Also Jochen es sieht wohl so aus, dass mein Test-Code aus dem vorigen
Post, die einzige Situation nachstellt, in der der von dir beobachtete
Fehler auftauchen kann. Nur dumm, dass es selbst von dieser Situation noch
tausende Varianten gibt, von denen eine bei dir auftritt. BTW, schonmal
probiert in diesem Method-Override-Fall die Überschreibung dadurch
aufzuheben, dass du m entweder in Actor oder in Derived umbenennst? Dann
läuft der Code nämlich durch mit einem interessanten Unterschied in den
v1:v2:-Zeilen. Dies könnte zeigen warum es bei dir bislang immer
problemlos ging, denn allein das Überschreiben einer Methode an zwei
(beinahe) unbeteiligten Klassen, kann den Fehler auftreten lassen, wo er
vorher nicht auftreten würde.

cu

Jochen Theodorou

unread,
Nov 16, 2007, 1:06:20 PM11/16/07
to
Ralf Ullrich schrieb:

> So, noch ein wenig mehr.

also erstmal nachvollziehen... Ich habe deinen Code ein wenig geändert,
weil ich lieber sehen möchte wer die Klasse geladen hat, statt den
hascode the Klass zu sehen. Demnach sind L2+L3 Kinder von L1 und bilden
einen Baum.

bis vor dem Teil mit der Resolution kann ich folgendes feststellen:

L1 hat Actor geladen
L2 hat Derived geladen und kennt Actor von L1
L3 hat ein anderes Derived geladen und kennt Actor von L1

Value ist bisher nicht geladem. Wenn die Resolution startet, dann wird
Value von L2 geladen. L2 hat jetzt Derived und Value...

was mich stutzig macht ist das Actor, welches ja in L1 ist doch
eigentlich auch Value kennen müsste, das aber nicht kann, da L1 Value
garnicht kennt. Das müsste doch eigentlich schon einen Fehler
produzieren. Und tatsächlich, das produziert schon einen Fehler... und
der ist mir auch klar

aber vergessen wir das mal für einen kurzen Moment... in deinem Programm
schlägt ja die Resulotion durch L3 fehl.. was ich aber jetzt nicht ganz
verstehe.... L3 hat doch ein eigenes Derived, ich würde jetzt erwarten
dass es sich gleich wie L2 verhält und seine eigene Version von Value
lädt... tut es aber nicht, es gibt einen Fehler. Und den Fehler verstehe
ich nicht.

was ich dem ganzen entnehme ist, dass wenn ich Actor lade, sollte ich
Value über den gleichen Loader laden und auch wenn ich derived von einem
anderen Loader lade, dass ich Value trotzdem vom gleichen Loader wie
Actor laden muss. Dass würde für mich bedeuten ich habe eine Bibliohek,
die Klassen aus dem Systemloader dupliziert und diese Klassen werden
jetzt mal über den Systemloader und mal über meinen Loader geladen...
was den Fehler ergibt. Also entweder ist in der Bibliothek ziemlicher
Mist, oder die Klassen werden geladen noch bevor diese Bibliothek zur
Verfügung steht. Weil, wenn alle notwendigen Klassen dupliziert werden,
sollte es ja kein Problem geben, dann gibt es ja nur einen Loader der
die Klassen lädt. Ich habe jaxen in verdacht.. Ich weiss das der Fehler
nicht auftritt wenn diese Bibliothek nicht vorhanden ist, aber mir war
bisher nicht klar warum. Auch habe ich festgestellt, das jaxen wohl
Klassen aus Java dupliziert.

[andere Varianten]


> reproduzieren konnte. Wie ich zu
>
> 2) Resolving the types in an interface method
> 3) Resolving the types in an overriding method

eventuell über getSuperClass().getMethod usw.

aber danke für deine Arbeit... eventuell hast du mich auf eine Spur
gebracht. Wenn du mir das mit dem L3 noch erklären kannst dann wäre ich
echt zufrieden ;) Das Problem ist dann noch nicht gelöst, aber das Wesen
des Problems ist besser verstanden ;)

> Also Jochen es sieht wohl so aus, dass mein Test-Code aus dem vorigen
> Post, die einzige Situation nachstellt, in der der von dir beobachtete
> Fehler auftauchen kann. Nur dumm, dass es selbst von dieser Situation
> noch tausende Varianten gibt, von denen eine bei dir auftritt. BTW,
> schonmal probiert in diesem Method-Override-Fall die Überschreibung
> dadurch aufzuheben, dass du m entweder in Actor oder in Derived
> umbenennst?

Das Problem ist... die fehlerauslösende Klasse ist nicht von mir. Ich
kann an dem Loader schrauben, aber nicht an der Bibliothek... nicht
wirklich jedenfalls.

Ralf Ullrich

unread,
Nov 16, 2007, 1:51:09 PM11/16/07
to
Jochen Theodorou wrote:

>Ralf Ullrich schrieb:
>>So, noch ein wenig mehr.
>
>also erstmal nachvollziehen... Ich habe deinen Code ein wenig geändert,
>weil ich lieber sehen möchte wer die Klasse geladen hat, statt den hascode
>the Klass zu sehen. Demnach sind L2+L3 Kinder von L1 und bilden einen Baum.
>
>bis vor dem Teil mit der Resolution kann ich folgendes feststellen:
>
>L1 hat Actor geladen
>L2 hat Derived geladen und kennt Actor von L1
>L3 hat ein anderes Derived geladen und kennt Actor von L1
>
>Value ist bisher nicht geladem. Wenn die Resolution startet, dann wird
>Value von L2 geladen. L2 hat jetzt Derived und Value...

>aber vergessen wir das mal für einen kurzen Moment... in deinem Programm
>schlägt ja die Resulotion durch L3 fehl.. was ich aber jetzt nicht ganz
>verstehe.... L3 hat doch ein eigenes Derived, ich würde jetzt erwarten
>dass es sich gleich wie L2 verhält und seine eigene Version von Value
>lädt... tut es aber nicht, es gibt einen Fehler. Und den Fehler verstehe
>ich nicht.

Also, ich kommentiere mal die Ausgabe. Wobei ich folgende Schreibweisen
verwende:

A für Actor, V für Value, D für Derived, O für Object
LS für SystemClassLoader
L1,L2,L3 für die drei ClassLoader

A/L1 ==> Actor Klasse mit Defining Loader L1 wurde definiert

+"A"/L2 == "A"/L1 ==> Es wurde ein Loading Constraint erzeugt, welches bestimmt dass der Name "A" bei den Loadern L2 und L1 auf dieselbe Klasse aufgelöst werden muss.

L2<A/L1 ==> L2 wurde als Initiating Loader der Actor Klasse mit Defining Loader L1 markiert. Dies bedeutet außerdem, dass an dieser Stelle nun die »transitive closure« (dt. transitive Hülle??) der Constraints zu "A"/L2 gebildet wird und überprüft wird.


>A: is init ldr L1=false L2=false L3=false
>[L2]lC:ENTER: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Derived
>[L2]lC:ENTER: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Actor
>[L1]lC:ENTER: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Actor
>[L1]lC:ENTER: java.lang.Object
>[L1]lC:SUPER: java.lang.Object 1a679b7
>[L1]lC:EXIT_: java.lang.Object 1a679b7

L1<O/LS

>[L1]lC:FOUND: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Actor
>4741d6

A/L1

>[L1]lC:EXIT_: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Actor
>4741d6

L1<A/L1

>[L2]lC:SUPER: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Actor
>4741d6
>[L2]lC:EXIT_: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Actor
>4741d6

L2<A/L1

>[L2]lC:FOUND: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Derived
>578ceb

D/L2

>[L2]lC:EXIT_: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Derived
>578ceb

L2<D/L2
und wegen m() aus D/L2 überschreibt m() aus A/L1:
+"V"/L2 == "V"/L1

>B: is init ldr L1=false L2=false L3=false
>[L3]lC:ENTER: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Derived
>[L3]lC:ENTER: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Actor
>[L1]lC:ENTER: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Actor
>[L1]lC:LODED: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Actor
>4741d6
>[L1]lC:EXIT_: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Actor
>4741d6
>[L3]lC:SUPER: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Actor
>4741d6
>[L3]lC:EXIT_: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Actor
>4741d6

L3<A/L1

>[L3]lC:FOUND: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Derived
>1fdc96c

D/L3

>[L3]lC:EXIT_: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Derived
>1fdc96c

L3<D/L3
und wegen m() aus D/L3 überschreibt m() aus A/L1:
+"V"/L3 == "V"/L1

>C: is init ldr L1=false L2=false L3=false
>c1: class
>de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Derived@578ceb
>c2: class
>de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Derived@1fdc96c
>[L2]lC:ENTER: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Value
>[L2]lC:ENTER: java.lang.Object
>[L1]lC:ENTER: java.lang.Object
>[L1]lC:LODED: java.lang.Object 1a679b7
>[L1]lC:EXIT_: java.lang.Object 1a679b7
>[L2]lC:SUPER: java.lang.Object 1a679b7
>[L2]lC:EXIT_: java.lang.Object 1a679b7

L2<O/LS

>[L2]lC:FOUND: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Value
>b2fd8f

V/L2

>[L2]lC:EXIT_: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Value
>b2fd8f

L2<V/L2
es wird die »transitive closure« von "V"/L2 gebildet, diese lautet
"V"/L2 == "V"/L1 == "V"/L3
Da L1 und L3 die Klasse V noch nicht kennen, bleiben die Contraints unverletzt. (Es gibt nur ein V bis jetzt.)

>[L3]lC:ENTER: de.jnana.dclj.people.jt.clsldr.LoadingConstraintDemo$Value
>[L3]lC:ENTER: java.lang.Object
>[L1]lC:ENTER: java.lang.Object
>[L1]lC:LODED: java.lang.Object 1a679b7
>[L1]lC:EXIT_: java.lang.Object 1a679b7
>[L3]lC:SUPER: java.lang.Object 1a679b7
>[L3]lC:EXIT_: java.lang.Object 1a679b7

L3<O/LS

Wir kriegen jetzt schon die Meldungen FOUND und EXIT für Value nicht mehr
angezeigt, weil in Wirklichkeit sich alles schon in
findClass-->defineClass abspielt und nicht wie oben etwas anschaulicher
dargestellt, nach den entsprechenden Meldungen. Es passiert also folgendes:

V/L3

L3<V/L3
es wird die »transitive closure« von "V"/L2 gebildet, diese lautet
"V"/L2 == "V"/L1 == "V"/L3
Und jetzt wird festgestellt, dass
V/L2 != V/L3
was den constraints widerspricht --> LinkageError.

>Exception in thread "main" java.lang.LinkageError: loader constraint
>violation: loader (instance of
>de/jnana/dclj/people/jt/clsldr/LoadingConstraintDemo$Loader) previously
>initiated loading for a different type with name
>"de/jnana/dclj/people/jt/clsldr/LoadingConstraintDemo$Value"


>


>was ich dem ganzen entnehme ist, dass wenn ich Actor lade, sollte ich
>Value über den gleichen Loader laden und auch wenn ich derived von einem
>anderen Loader lade, dass ich Value trotzdem vom gleichen Loader wie Actor
>laden muss.

Ja, so sehe ich das auch.

Grundsätzliche Ursache ist, dass die »Parent befragen vor eigenen
Auflösungsversuchen«-Regel in deiner loadClass-Implementierung verletzt
wird. Nun nehme ich an, du hast es aus gutem Grund gemacht, du musst dann
aber auf andere Weise sicherstellen, dass dein nicht-konformer ClassLoader
keine Klassen findet, die auch seine Parents finden würden.

Normalerweise reicht es dazu, wenn der nicht-konforme ClassLoader nur
Klassen sieht, die die Parents nicht sehen. Wenn dann natürlich
Bibliotheken über den nicht-konformen ClassLoader geladen werden, die APIs
aus Bequemlichkeit (z.B. für ältere JREs) mit eingebunden haben, die nun
(in neueren JREs) auch in rt.jar schon drin sind, und die W3C Sachen sind
da gute Kandidaten, dann bekommst du Probleme.

Eine Lösung in der Situation könnte sein, deinen nicht-konformen
ClassLoader mit einer Liste zu ignorierender Packages zu konfigurieren. So
könnte dein konkretes Problem beispielsweise verschwinden, wenn dein
RootLoader das Package org.w3c.dom grundsätzlich nur über super.loadClass
abwickelt und nichts eigenes dafür macht.

HTH

cu

Ralf Ullrich

unread,
Nov 16, 2007, 1:55:00 PM11/16/07
to
Ralf Ullrich wrote:

> L3<V/L3
> es wird die »transitive closure« von "V"/L2 gebildet, diese lautet

Hätte an der Stelle natürlich "V"/L3 lauten sollen. Blödes Copy&Paste.

cu

Jochen Theodorou

unread,
Nov 16, 2007, 2:48:16 PM11/16/07
to
Ralf Ullrich schrieb:
> Jochen Theodorou wrote:
[...]

> Grundsätzliche Ursache ist, dass die »Parent befragen vor eigenen
> Auflösungsversuchen«-Regel in deiner loadClass-Implementierung verletzt
> wird. Nun nehme ich an, du hast es aus gutem Grund gemacht, du musst
> dann aber auf andere Weise sicherstellen, dass dein nicht-konformer
> ClassLoader keine Klassen findet, die auch seine Parents finden würden.

ich denke es reicht schon aus wenn der parent niemals gefragt werden
würde für solch eine Klasse. Wenn L1, L2 und L3 zum Beispiel alle
Klassen kennen, dann gibt es auch kein Problem, obwohl L2 und L3 die
Klassen duplizieren. Nur das rumgemische ist schlecht und leider sehr
schlecht automatisch in den Griff zu bekommen.

> Normalerweise reicht es dazu, wenn der nicht-konforme ClassLoader nur
> Klassen sieht, die die Parents nicht sehen.

ja gut... dann kann ich auch einfach parent fragen ;) Ne, aufgrund von
Versionskonflikten will ich parent garnicht fragen, außer es geht nicht
mehr anders.

> Wenn dann natürlich
> Bibliotheken über den nicht-konformen ClassLoader geladen werden, die
> APIs aus Bequemlichkeit (z.B. für ältere JREs) mit eingebunden haben,
> die nun (in neueren JREs) auch in rt.jar schon drin sind, und die W3C
> Sachen sind da gute Kandidaten, dann bekommst du Probleme.

es kommt darauf an... Wenn du A extends B hast und A,B sind im rt.jar,
aber eine Bib definiert ausserdem B, dann ist das schlecht. Definiert
die Bib nur A, dann ist das eher kein Problem... ein prima Beispiel sind
wahrscheinlich die XML Parser die es so für 1.4 gibt. setzt man die mit
meinem RootLoader in 1.5 ein, dann funktionieren die Parser im Prinzip
ohne Probleme... nur die Factories aus dem 1.5 Teil sorgen plötzlich für
duplizierte Klassen usw. Aber prinzipiell geht es schon.

> Eine Lösung in der Situation könnte sein, deinen nicht-konformen
> ClassLoader mit einer Liste zu ignorierender Packages zu konfigurieren.
> So könnte dein konkretes Problem beispielsweise verschwinden, wenn dein
> RootLoader das Package org.w3c.dom grundsätzlich nur über
> super.loadClass abwickelt und nichts eigenes dafür macht.

das ist allerdings richtig... muss ich mir noch überlegen... mx beans
wären da auch ein Kandidat für

0 new messages