Login  Register

Problem: Multiple uses of Compile and Run. [was: Strange ClassCastException...]

Posted by Bill Mohler on May 04, 2009; 7:20pm
URL: http://imagej.273.s1.nabble.com/Strange-ClassCastException-A-puzzle-for-Java-ImageJ-experts-to-help-me-wit-tp3692688p3692690.html

Dear ImageJers (especially Wayne, Curtis, and Dscho)

I ultimately confirmed that my trouble with ClassCastExceptions
arises when I try to have two different plugins, each individually
launched using compile and run, address the same plugin-based class
extending ImageStack.

I get the same problem with either of two versions of 1.42o that
Wayne sent me. And the problem goes away if the plugins are compiled
and installed from a jar file. Interestingly, the order in which they
are opened using Compile & Run matters as to whether I see the
problem or not:

StackOpener first, pluginFrame controller second => exception. (but
not if installed from jar or from precompiled class containing _ in
name.)

pluginFrame controller first, StackOpener second => no exception,
everything's fine.

Each use of Compile and Run creates a new class loader, and this can
cause conflicts due to multiple instances of the loaded class.

** Compile and Run of the StackOpener always builds a window which
fails to be controlled by the pluginFrame, whether the pluginFrame
was precompiled or loaded by Compile & Run.

If both are precompiled and installed, the order in which they were
instantiated does not matter. Everything works fine.

FYI I have been ping-ponging between the code for both classes and
moving methods from one to the other as I develop how they interact.
It's for this reason that I tend to load them sequentially with C&R
during the same ImageJ session. Maybe I should grow up and setup the
project in an IDE.

Perhaps Wayne or someone can give a better idea of why this happens
with Compile and Run and whether it can be fixed in any way. It's
easy enough to avoid now that I know it's a hazard. But I'm not sure
if others might be experiencing class loader problems with Compile
and Run but not knowing it.

Bill

>A little bit more info. I read up on class loaders, and decided to
>look at their parentage.
>
>It looks life all of the class loaders that pop up are direct
>descendents of the system class loader. I don't know if this gives
>me any further ability to control what's going on.
>
>Bill
>
>I put the class loader finding code block in 3 different places
>
>First, in the "opener" plugin that gets a list of movies from the
>user and then builds the MQTVS as an object called vstack:
>
>From QT_M_OMM.cSTHS
>vstack loader = ij.io.PluginClassLoader@4eabb
>vstack loader parent= sun.misc.Launcher$AppClassLoader@a39137
>mqtvs loader = ij.io.PluginClassLoader@4eabb
>mqtvs loader parent = sun.misc.Launcher$AppClassLoader@a39137
>Loaders equal? true/true
>system loader = sun.misc.Launcher$AppClassLoader@a39137
>system loader parent = sun.misc.Launcher$ExtClassLoader@858610
>ImageJ loader = ij.io.PluginClassLoader@4eabb
>ImageJ loader parent = sun.misc.Launcher$AppClassLoader@a39137
>
>Then in the MQTVS class's getProcessor() method that figures out
>which images to display:
>
>From MQTVS.gP
>stack loader = sun.misc.Launcher$AppClassLoader@a39137
>stack loader parent= sun.misc.Launcher$ExtClassLoader@858610
>mqtvs loader = ij.io.PluginClassLoader@4eabb
>mqtvs loader parent = sun.misc.Launcher$AppClassLoader@a39137
>Loaders equal? false/false
>system loader = sun.misc.Launcher$AppClassLoader@a39137
>system loader parent = sun.misc.Launcher$ExtClassLoader@858610
>ImageJ loader = ij.io.PluginClassLoader@4eabb
>ImageJ loader parent = sun.misc.Launcher$AppClassLoader@a39137
>
>**Interesting note: this getProcessor() gets called once even BEFORE
>the method far above gets called.  Then this  is also called many
>times later.  In both the first and then the subsequent calls, it
>always reports the loaders as shown here.
>
>Then in the PluginFrame adjustmentValueChanged() method that I've
>been bashing about all afternoon:
>
>From MAFMOD.aVC
>stack loader = ij.io.PluginClassLoader@4eabb
>stack loader parent= sun.misc.Launcher$AppClassLoader@a39137
>mqtvs loader = ij.io.PluginClassLoader@c68b6f
>mqtvs loader parent = sun.misc.Launcher$AppClassLoader@a39137
>Loaders equal? false/false
>system loader = sun.misc.Launcher$AppClassLoader@a39137
>system loader parent = sun.misc.Launcher$ExtClassLoader@858610
>ImageJ loader = ij.io.PluginClassLoader@c68b6f
>ImageJ loader parent = sun.misc.Launcher$AppClassLoader@a39137
>
>
>
>Somehow, it looks like this object gets handled first by the ImageJ
>loader in the "opener", then it (or a copy/clone?) gets handed off
>to the system loader in MQTVS, then it ends up in the hands of a
>third loader (neither system not ImageJ) in the PluginFrame. It
>looks life all of the class loaders that pop up are direct
>descendents of the system class loader.
>
>Fascinated, but more perplexed than ever.
>
>Thanks to Dscho and Curtis,
>Bill
>
>>Hi Bill,
>>
>>I agree with Dscho that it must be two ClassLoaders, each with their own
>>version of the class. I have never heard of this happening before, but it is
>>conceivable, since ImageJ uses its own ClassLoader, and maybe QTJava does
>>too (not sure).
>>
>>Try this code:
>>
>>ClassLoader stackLoader = stack.getClass().getClassLoader();
>>ClassLoader mqtvsLoader = MultiQTVirtualStack.class.getClassLoader();
>>IJ.log("stack loader = " + stackLoader);
>>IJ.log("mqtvs loader = " + mqtvsLoader);
>>IJ.log("Loaders equal? " + (stackLoader == mqtvsLoader) + "/" +
>>stackLoader.equals(mqtvsLoader));
>>ClassLoader systemLoader = ClassLoader.getSystemClassLoader();
>>ClassLoader ijLoader = ij.IJ.getClassLoader();
>>IJ.log("system loader = " + systemLoader);
>>IJ.log("ImageJ loader = " + ijLoader);
>>
>>Check whether the numerical codes for the stack & mqtvs loaders line up with
>>the system and/or ImageJ loaders. Then you'll know which class loader is
>>loading which version of the class. But ultimately, this bug may not be
>>something easily fixed within your code -- it may require a change to
>>ImageJ. Not sure without further messing around...
>>
>>-Curtis
>>
>>On Wed, Apr 29, 2009 at 11:06 AM, Johannes Schindelin <
>>[hidden email]> wrote:
>>
>>>  Hi,
>>>
>>>  On Wed, 29 Apr 2009, Bill Mohler wrote:
>>>
>>>  > Thanks, Curtis.  Here's what I get
>>>  >
>>>  > MultiQTVirtualStack1
>>>  > stack class = MultiQTVirtualStack
>>>  > MultiQTVirtualStack class = MultiQTVirtualStack
>>>  > Classes equal? false/false
>>>  > path to MultiQTVirtualStack class =
>>>  >
>>>
>>>file:/Applications/ImageJ/plugins/QuickTime_Plugins_MMRD041109/MultiQTVirtualStack.class
>>>  > path to stack class =
>>>  >
>>>
>>>file:/Applications/ImageJ/plugins/QuickTime_Plugins_MMRD041109/MultiQTVirtualStack.class
>>>  > beforetheloop imp[3-Movie Overlay #1 : see Log window for details
>>>  > 600x411x38961]
>>>  > stack[600x411x38961]
>>>  > intheloop j= 0 imp[3-Movie Overlay #1 : see Log window for details
>>>  > 600x411x38961]
>>>  > stack[600x411x38961]
>>>  > MultiQTVirtualStack2
>>>  > MultiQTVirtualStack3
>>>  >
>>>  >
>>>  > So, mysteriously to me, the code you sent reports that they are NOT equal
>>>  > classes.  However both paths point to the same class file!
>>  >
>>>  Then I am pretty certain that two different class loaders are competing
>>>  for the class.
>>>
>>>  Could you print ...getClass().getClassLoader(), too?
>>>
>>>  Ciao,
>>>  Dscho