Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsWhite Papers
Discussion GroupsFirst AidDatabasesJavaBeansGUIJava 3DVirtual MachineCORBASecurityToolsGeneral
Java DirectoryOpen Source ProjectsSample Book ChaptersUser GroupsWeb Resources
Related Topics
Databases.NETMore Topics ...

Java Forum / General / October 2007

Tip: Looking for answers? Try searching our database.

ClassLoader not loading recompiled classes

Thread view: 
Aryeh M. Friedman - 02 Oct 2007 06:53 GMT
ClassLoader does not update class on recompile:

Script started on Tue Oct  2 01:45:20 200
> cat Main.java
public class Main
{
    public static void main(String[] args)
        throws Throwable
    {
        while(true) {
            ClassLoader loader=ClassLoader.getSystemClassLoader();
            Class klass=loader.loadClass("MyClass");
            MyClass mc=(MyClass) klass.newInstance();

            System.out.println("hit any key to reload/rerun MyClass");
            System.in.read();
        }
    }
}
> cat MyClass.java
public class MyClass
{
    public MyClass()
    {
        System.out.println("not hi there");
    }
}
> javac *.java
> java Main
not hi there
hit any key to reload/rerun MyClass

not hi there
hit any key to reload/rerun MyClass
^Z
Suspended
> cat foo
public class MyClass
{
    public MyClass()
    {
        System.out.println("foo on you");
    }
}
> mv foo MyClass.java
> javac MyClass.java
> fg
java Main

not hi there
hit any key to reload/rerun MyClass
^C
> exit
Script done on Tue Oct  2 01:47:02 200
Daniel Pitts - 02 Oct 2007 07:02 GMT
On Oct 1, 10:53 pm, "Aryeh M. Friedman" <Aryeh.Fried...@gmail.com>
wrote:
> ClassLoader does not update class on recompile:
>
[quoted text clipped - 50 lines]
>
> Script done on Tue Oct  2 01:47:02 200

Right, a ClassLoader will not re-load a class.  You will have to
instantiate a new class loader to do so.
ClassLoader.loadClass will first look for already loaded classes. It
will not re-load the class into the JVM.

Its generally difficult to get dynamic class behavior from Java.  You
aren't able to unload a class, and load a different version of it.
Also, and already loaded classes that refer to that other class will
only be able to refer to one instance of it, not one from one class
loader, and then another from another class loader.
Aryeh M. Friedman - 02 Oct 2007 07:07 GMT
> On Oct 1, 10:53 pm, "Aryeh M. Friedman" <Aryeh.Fried...@gmail.com>
> wrote:
[quoted text clipped - 64 lines]
> only be able to refer to one instance of it, not one from one class
> loader, and then another from another class loader.

Since I am relativally naive with class loaders how do I create a new
instance of the system class loader?
Silvio Bierman - 02 Oct 2007 08:56 GMT
>> On Oct 1, 10:53 pm, "Aryeh M. Friedman" <Aryeh.Fried...@gmail.com>
>> wrote:
[quoted text clipped - 56 lines]
> Since I am relativally naive with class loaders how do I create a new
> instance of the system class loader?

You don't. You can write your own class loaders if you want and can
implement any loading behavior you think is suitable.

Beware that class-reloading introduces all kinds of behavior that is
counter-intuitive. Multiple instances of what appears to be the same
static variable thereby also breaking singleton patterns in your code
(which are broken already, but that is a completely different matter) is
only one example.

Please start by telling us what the real problem is that you need to
solve. Going the class-loader way is probably among the worst solutions
for your problem.

Regards,

Silvio Bierman
Aryeh M. Friedman - 02 Oct 2007 10:04 GMT
> >> On Oct 1, 10:53 pm, "Aryeh M. Friedman" <Aryeh.Fried...@gmail.com>
> >> wrote:
[quoted text clipped - 69 lines]
> solve. Going the class-loader way is probably among the worst solutions
> for your problem.

I wrote a gui based unit testing framework and it loads the top level
test suites from a text box with there names in it (initially
populated from command line)... I do not want to have to close/reopen
the app when I rewrite/recompile some code under test.

--Aryeh
Aryeh M. Friedman - 02 Oct 2007 10:16 GMT
On Oct 2, 9:04 am, "Aryeh M. Friedman" <Aryeh.Fried...@gmail.com>
wrote:

> > >> On Oct 1, 10:53 pm, "Aryeh M. Friedman" <Aryeh.Fried...@gmail.com>
> > >> wrote:
[quoted text clipped - 74 lines]
> populated from command line)... I do not want to have to close/reopen
> the app when I rewrite/recompile some code under test.

If you want to see the actual thing in action you can download it from
http://www.flosoft-systems.com/order.php (sorry but due to legal
restrictions I can not give out the direct download link... for
"purpose" just put in "classloader issue from c.l.j.p" and the rest of
the data I can careless about [unless your of course going to buy it
after the trial peroid is over ;=)]
Silvio Bierman - 02 Oct 2007 10:28 GMT
>>>> On Oct 1, 10:53 pm, "Aryeh M. Friedman" <Aryeh.Fried...@gmail.com>
>>>> wrote:
[quoted text clipped - 72 lines]
>
> --Aryeh

Is there any need to run the tests in-process? You could spawn an
external java process to run the tests and have it store the test
results in a place where the GUI can find it.

Gr.

Silvio
Aryeh M. Friedman - 02 Oct 2007 10:44 GMT
> >>>> On Oct 1, 10:53 pm, "Aryeh M. Friedman" <Aryeh.Fried...@gmail.com>
> >>>> wrote:
[quoted text clipped - 80 lines]
>
> Silvio

In theory no but it sounds a little kludgist and perhaps not portable
(I don't see how it wouldn't be but once you ask the OS for anything I
get nervious)
Silvio Bierman - 02 Oct 2007 12:29 GMT
>>>>>> On Oct 1, 10:53 pm, "Aryeh M. Friedman" <Aryeh.Fried...@gmail.com>
>>>>>> wrote:
[quoted text clipped - 79 lines]
> (I don't see how it wouldn't be but once you ask the OS for anything I
> get nervious)

For this kind of application the ClassLoader woes I mentioned are
probably not relevant. However, you must remember that when you run such
completely generic test code in-process you may (and probably will)
compromise the state of your VM and risk bringing down the application
itself.
I would go the out-of-process way. I do not see why this would not work
in any reasonable Java Runtime env.

Regards,

Silvio
Arne Vajhøj - 02 Oct 2007 15:27 GMT
>>>> Right, a ClassLoader will not re-load a class.  You will have to
>>>> instantiate a new class loader to do so.
[quoted text clipped - 14 lines]
> populated from command line)... I do not want to have to close/reopen
> the app when I rewrite/recompile some code under test.

Try look at the super simple example attached below.

Arne

=============================================

import java.io.*;
import java.net.*;

public class DoubleDynmaic {
   private static void dynno(int n) {
      (new File("test")).mkdir();
      try {
         OutputStream os = new FileOutputStream("test/Test.java");
         PrintStream ps = new PrintStream(os);
         ps.println("public class Test {");
         ps.println("   public Test() {");
         ps.println("      System.out.println(" + n + ");");
         ps.println("   }");
         ps.println("}");
         ps.close();
         os.close();
         Runtime.getRuntime().exec("javac -d test
test/Test.java").waitFor();
         URL[] url = new URL[1];
         url[0] = new URL("file:test/");
         URLClassLoader cl = new URLClassLoader(url);
         Class.forName("Test", true, cl).newInstance();
      } catch (Exception e) {
         e.printStackTrace();
      }
   }
   public static void main(String[] args) {
      for(int i = 0; i < 10; i++) {
         dynno(i);
      }
   }
}
Aryeh M. Friedman - 03 Oct 2007 03:59 GMT
> Try look at the super simple example attached below.

Works fine *IF* the code is compiled from within the class but if it
is externally compliled it does the same thing as just straight class
loading.

> Arne
>
[quoted text clipped - 33 lines]
>
> }
Daniel Pitts - 03 Oct 2007 07:03 GMT
>> Try look at the super simple example attached below.
>
[quoted text clipped - 38 lines]
>
>> }
Please don't top post, it makes it hard to understand whats going on...

Anyway, my guess is that you're casting the class (MyClass).  Doing this
actually loads the class with the same class loader.  You can't know
about a class at the Java level without that class being loaded.  You're
going to have to use reflection all the way through.  While your
application may be a good candidate, there are dangers of using reflection:

<http://virtualinfinity.net/wordpress/program-design/2007/01/11/the-dangers-of-re
flection-or-put-down-that-mirror/
>

Hope this helps,
Daniel.
Signature

Daniel Pitts' Tech Blog: <http://virtualinfinity.net/wordpress/>

Mark Rafn - 03 Oct 2007 22:03 GMT
>> Try look at the super simple example attached below.

Aryeh M. Friedman <Aryeh.Friedman@gmail.com> wrote:

>Works fine *IF* the code is compiled from within the class but if it
>is externally compliled it does the same thing as just straight class
>loading.

Then you'll have to add timestamp checking or some other notification that
the class needs to be reloaded.  Then reload the class as shown, by
throwing away the classloader that has the old version, and creating a
new one to load the new version.

>Anyway, my guess is that you're casting the class (MyClass).  Doing this
>actually loads the class with the same class loader.  You can't know
>about a class at the Java level without that class being loaded.  You're
>going to have to use reflection all the way through.

A common pattern is to have an interface in the parent classloader,
and load an implementation from the child.  You use reflection to load and
instantiate the implementation from the child classloader, as shown in
the example, but you can cast it to the interface and then use it normally.  

What you might be seeing is that it's easy to accidentally load the class into
the parent classloader rather than the child.  You have to make sure that the
classes to be reloaded aren't in the classpath of the parent classloader.

Example (using two directories to keep the impl from being loaded by the
parent classloader before the child gets a chance):
 create these two files in two directories named reloader and generator
 cd reloader; javac Reloader.java
 cd ../generator; javac -cp ../reloader MessageGeneratorImpl.java
 cd ../reloader; java -cp . Reloader
 (on another window/screen)
     change MessageGeneratorImpl.java to have a new message.
     recompile it as above.
 (on original window/screen) watch the output of Reloader change.

==== file reloader/Reloader.java:
import java.io.*;
import java.net.*;

public class Reloader {
   public interface MessageGenerator {
       public String getMessage();
   }

   /**
    * Load "MessageGeneratorImpl.class", and print it's message.  Repeat
    * every few seconds whenever the classfile changes.
    */
   public static void main(String[] args) throws Exception {
       long classTimestamp = 0;
       File classFile = new File("../generator/MessageGeneratorImpl.class");
       MessageGenerator generator = null;

       while (true) {
           long lastMod = classFile.lastModified();
           if (lastMod > classTimestamp) {
               // if classfile is new, load it.
               System.out.println("(re)loading MessageGeneratorImpl");
               Object o = new URLClassLoader(
                       new URL[] {new File("../generator").toURL()},
                       Reloader.class.getClassLoader()).
                           loadClass("MessageGeneratorImpl").newInstance();
               generator = (MessageGenerator)o;
               classTimestamp = lastMod;
           }

           System.out.println("message: " + generator.getMessage());
           Thread.currentThread().sleep(10 * 1000);
       }
   }
}

==== file generator/MessageGeneratorImpl.java:
public class MessageGeneratorImpl implements Reloader.MessageGenerator {
   public String getMessage() {
       return "Hello there!";
   }
}

--
Mark Rafn    dagon@dagon.net    <http://www.dagon.net/>
Aryeh M. Friedman - 03 Oct 2007 23:42 GMT
>         while (true) {
>             long lastMod = classFile.lastModified();
[quoted text clipped - 8 lines]
>                 classTimestamp = lastMod;
>             }

That is almost identical (and functionally so) to my test case:

while(true)
{
     loadClass();//handles all the nasty details of loading the class
including new URLCloassLoader
Aryeh M. Friedman - 03 Oct 2007 23:46 GMT
>         while (true) {
>             long lastMod = classFile.lastModified();
[quoted text clipped - 12 lines]
>             Thread.currentThread().sleep(10 * 1000);
>         }

This is functionally identical to the following (which is the test
code that lead me to make the comment about externally compiled
classes):

while(true) {
   testClassLoader();  // create a new class loader and load class

   System.out.println("hnit any key to reload");
   System.in();
}

new timestamps *DO NOT* help.... the only solution I have found is to
read the binary class file and then link it to symbol table with
ClassLoader.defineClass (btw this is the solution JUnit uses)).
Mark Rafn - 04 Oct 2007 00:23 GMT
>>             if (lastMod > classTimestamp) {
>>                 // if classfile is new, load it.
[quoted text clipped - 9 lines]
>>             System.out.println("message: " + generator.getMessage());
>>             Thread.currentThread().sleep(10 * 1000);

>This is functionally identical to the following (which is the test
>code that lead me to make the comment about externally compiled
>classes):

Sure, there's lots of ways to do it.  However, if you're having trouble with
classes not getting reloaded, my example shows that it has NOTHING to do with
casting before use, or "external" compilation.  

>new timestamps *DO NOT* help.

They're just one possible mechanism for determining that the class has
changed.  I used them because someone said they were having problems with
reloading classes and wanted to detect when a file changed.

>.. the only solution I have found is to
>read the binary class file and then link it to symbol table with
>ClassLoader.defineClass (btw this is the solution JUnit uses)).

JUnit may do that for it's own convenience, and perhaps for bytecode
manipulation as it loads stuff.  Or maybe so it can force loading classes that
have the same package and classname as something the parent classloader can
see.  It's absolutely not necessary for normal class loading.  
--
Mark Rafn    dagon@dagon.net    <http://www.dagon.net/>
Piotr Kobzda - 03 Oct 2007 07:26 GMT
>> Try look at the super simple example attached below.
>
> Works fine *IF* the code is compiled from within the class but if it
> is externally compliled it does the same thing as just straight class
> loading.

The point is to prevent from loading your externally compiled "things"
with your application's class loaders.

In the Arne's example each Test class is being loaded again because only
the URLClassLoader knows where to find it (its delegation parent loader
-- in the case, the system class loader -- can't reach it).  Each try
creates a new instance of the loader, thus there is no connection with
the previously used loader (and a Test class loaded previously) in each
Class.forName() lookup.

To see the difference run the Arne's example with 'test' directory
reachable from the system class loader's class-path, e.g. that way:

java -Xbootclasspath/a:test DoubleDynamic

piotr
Arne Vajhøj - 04 Oct 2007 01:23 GMT
>> Try look at the super simple example attached below.
>
> Works fine *IF* the code is compiled from within the class but if it
> is externally compliled it does the same thing as just straight class
> loading.

It does not matter how the code compiled. as long as it in the
classpath of the new classloader and not in the classpath of the
standard ones.

And no it does not do the same as straight class
loading (you can unload when using your own classloader).

Arne


Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.