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

Dynamic Class Reloading

5 views
Skip to first unread message

Dijital Maniak

unread,
Jul 29, 2002, 11:49:44 PM7/29/02
to
Hey all,

As part of a project I am working on, we need to dynamically reload
classes (much the same way that some webservers reload servlets and JSPs)
without shutting down the JVM. I wrote the attached ClassLoader, but now
when I try to use it with this code (ChangingClass is also attached):

ClassLoader loader = new ClassReloader();
Class clazz = loader.loadClass("ChangingClass");
ChangingClass changer = (ChangingClass)clazz.newInstance();

I get the following error on the third line:

Exception in thread "main" java.lang.IllegalAccessException: Class
ReloadTest can not access a member of class
ChangingClass with modifiers "public"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:57)
at java.lang.Class.newInstance0(Class.java:290)
at java.lang.Class.newInstance(Class.java:249)
at ReloadTest.main(ReloadTest.java:14)

Any ideas?? Please keep in mind that I would like to be able to reload
*ANY* class from the classpath, however, if I have to place all reloadable
classes in a certain place on the filesystem, I am not totally adverse to
that.

Thanks it advance for your help on this (remember to take the stuff that is
obviously not part of my e-mail address out before e-mailing me directly).

- Dijital

Here are my classes....I can't do a normal attachment:


class ChangingClass
{
public ChangingClass()
{

}

public void print()
{
System.out.println("Version 2");
}
}


import java.io.*;
import java.util.Hashtable;
import java.util.Properties;
import java.util.Enumeration;
import java.security.*;
import java.net.*;

/**
* Class that can be used to dynamically load a class, and then reload an
updated version
* This is set up to only load classes whose names end with "Special", but
you can change
* that easy enough
*
* You may use this class as you wish, either as a basis for your own code
* or just drop it into your project
* However, I will not be held responisble if your program doesn't work, or
* your computer blows up
* @author Simon Macneall macn...@iinet.net.au
*/
public class ClassReloader extends ClassLoader
{
private Hashtable classes_hash = new Hashtable();

public ClassReloader()
{
}

protected synchronized Class loadClass(String name, boolean resolve)
throws ClassNotFoundException
{

// First, check if the class has already been loaded
Class c = null; //findLoadedClass(name);

if ( name.startsWith("java."))
{
c = findSystemClass(name);
}
else
{
c = findClass(name);
}


if (resolve)
{
resolveClass(c);
}

return c;

}

protected synchronized Class findClass(String name) throws
ClassNotFoundException
{

//read the class into a byte array
byte[] classData = loadClassData(name);

if (classData == null)
{
throw new ClassNotFoundException();
}

//convert the byte array into an actual class
Class result = defineClass(null, classData, 0, classData.length);

if (result == null)
{
throw new ClassFormatError();
}

return result;
}

private byte[] loadClassData(String name) throws ClassNotFoundException
{
String newName = name.replace('.', '/') + ".class";
InputStream is = getResourceAsStream( newName);
if (is == null)
{
return null;
}

//convert the stream to an array of bytes

BufferedInputStream bis = new BufferedInputStream(is);
ByteArrayOutputStream out = new ByteArrayOutputStream();

try
{
int c = bis.read();
while (c != -1)
{
out.write(c);
c = bis.read();
}
} catch (IOException e)
{
return null;
}
return out.toByteArray();

}
}

Chris Smith

unread,
Jul 30, 2002, 12:20:47 AM7/30/02
to
Dijital Maniak wrote ...

> As part of a project I am working on, we need to dynamically reload
> classes (much the same way that some webservers reload servlets and JSPs)
> without shutting down the JVM. I wrote the attached ClassLoader, but now
> when I try to use it with this code (ChangingClass is also attached):
>
> ClassLoader loader = new ClassReloader();
> Class clazz = loader.loadClass("ChangingClass");
> ChangingClass changer = (ChangingClass)clazz.newInstance();
>
> I get the following error on the third line:
>
> Exception in thread "main" java.lang.IllegalAccessException: Class
> ReloadTest can not access a member of class
> ChangingClass with modifiers "public"
> at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:57)
> at java.lang.Class.newInstance0(Class.java:290)
> at java.lang.Class.newInstance(Class.java:249)
> at ReloadTest.main(ReloadTest.java:14)

You'll notice from studying the above stack trace that the classloader is
not the problem... you're finding a valid class and loading it, but it's
instantiating that class that blows up. My guess is that the class is
not public, so newInstance() can't build an instance from outside the
package.

> Any ideas?? Please keep in mind that I would like to be able to reload
> *ANY* class from the classpath, however, if I have to place all reloadable
> classes in a certain place on the filesystem, I am not totally adverse to
> that.

You will have to settle for the latter, and remember that you really
can't ever "reload" a class. You can reload a class *file*, and get a
new class that represents the new contents of that file. To do that, you
need to instantiate a *new* classloader. The class you get back will be
a new class, and entirely separate from the old one except that it
happens to share the same name.

This action (creating a new classloader and loading the new class) will
*not* change the behaviors of any existing objects of the old class, and
the old class will continue to exist alongside the new one until it goes
away on its own (that is, all objects of that class die, and no one holds
any references to the class or the old classloader that loaded it). It's
best if you just don't think of this as "reloading" individual classes at
all.

One comment on your classloader itself; don't override either of the
loadClass methods if you can avoid it. Overriding findClass should be
enough unless you have strange needs. Looks like your classloader would
have issues with delegating properly to a parent, which is likely to
cause problems down the road.

Chris Smith

Dijital Maniak

unread,
Jul 30, 2002, 12:37:36 AM7/30/02
to
Thanks for your insight.....what i am trying to do is reload the class file
and obtain an new instance from that class file which is why the code is
written the way it is. What I want to do is leave all of the existing
instances alone (in actuality they are singletons, so they will get
de-referenced when I get a new instance, but I haven't got that far yet).

If you look at the ChangingClass definition at the bottom of my first note,
you'll notice that everything uses the public modifier and I can load this
class using the System ClassLoader without a problem, so I don't believe
that is the problem.

Perhaps you can explain something for me a little further. You mention that
I shouldn't override loadClass() method on my own, but if I don't won't the
parent ClassLoaders find the old Class template (an instance fo the Class
class) in memory for me and use that one, instead of the newly compile one
from the filesystem?? If this is the case, I will never reload the class
unless I look in a place NOT in the classpath....

However, if I just overrode the findClass() method to make it look someplace
NOT in the classpath, and made sure my classes to be reloaded were NOT in
the classpath, might that work?? I guess the only stipulation is that I
re-instantiate my ClassLoader each time so it won't find any pre-cached
Class templates (Class classes) in memory....does this seem correct??

- Dijital

"Chris Smith" <cds...@twu.net> wrote in message
news:MPG.17afc20db...@news.altopia.com...

Jim White

unread,
Jul 30, 2002, 12:50:34 AM7/30/02
to
Dijital Maniak wrote:
> As part of a project I am working on, we need to dynamically reload
> classes (much the same way that some webservers reload servlets and JSPs)
> without shutting down the JVM. I wrote the attached ClassLoader, but now
> when I try to use it with this code (ChangingClass is also attached):
>
> ClassLoader loader = new ClassReloader();
> Class clazz = loader.loadClass("ChangingClass");
> ChangingClass changer = (ChangingClass)clazz.newInstance();
>
> I get the following error on the third line:
>
> Exception in thread "main" java.lang.IllegalAccessException: Class
> ReloadTest can not access a member of class
> ChangingClass with modifiers "public"
> at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:57)
> at java.lang.Class.newInstance0(Class.java:290)
> at java.lang.Class.newInstance(Class.java:249)
> at ReloadTest.main(ReloadTest.java:14)
>
> Any ideas?? Please keep in mind that I would like to be able to reload
> *ANY* class from the classpath, however, if I have to place all reloadable
> classes in a certain place on the filesystem, I am not totally adverse to
> that.

The error message may not be extremely clear, but it is fairly accurate.
The problem is that ChangingClass is not public (it is not sufficient
for the constructor to be public, see the doc on Class.newInstance).

Because ReloadTest is not loaded from the same package (as in
Class.getPackage(), not simply having the same name, which is the
default package in this case) it fails because ChangingClass has package
scope.

Changing ChangingClass to 'public class ChangingClass ...' will then
lead to an obvious error which is that there is absolutely no way that
the resulting class from Class.newInstance() will be an instance of
ChangingClass from the system classloader because they are from
different class loaders.

jim

Chris Smith

unread,
Jul 30, 2002, 12:57:31 AM7/30/02
to
Dijital Maniak wrote ...

> If you look at the ChangingClass definition at the bottom of my first note,
> you'll notice that everything uses the public modifier and I can load this
> class using the System ClassLoader without a problem, so I don't believe
> that is the problem.

I didn't realize you'd provided that code. It actually confirms my
suspicions as to what the problem may be. The code reads:

class ChangingClass
{
public ChangingClass()
{

}

public void print()
{
System.out.println("Version 2");
}
}

Obviously, that's not a public class... it's got two public members, but
the class itself is left at the default package access. To use this
class from outside the package, you'll need to declare the class like
this instead:

public class ChangingClass
{
...
}

As for this working fine with the system classloader, I think you've
missed the point. Since you aren't using named packages at all (that's a
bad idea in the long run, by the way, but you'll get away with it for
now), if there's only once class loader in the application it doesn't
MATTER whether the class is public or not. It's when you've got two
different packages involved (in this case, the default package in
classloader A versus the default package in classloader B) that you'll
have problems with class access specifiers. I'm now certain that this is
your problem, and fortunately the fix is only one word.

> However, if I just overrode the findClass() method to make it look someplace
> NOT in the classpath, and made sure my classes to be reloaded were NOT in
> the classpath, might that work?? I guess the only stipulation is that I
> re-instantiate my ClassLoader each time so it won't find any pre-cached
> Class templates (Class classes) in memory....does this seem correct??

Exactly! What you just said is the only reasonable way I can think of to
implement what you want. Otherwise you're breaking several API spec
contracts and there's no guarantee that your code will do anything even
close to right. Changing the contents of the classpath at runtime is a
very bad idea.

Chris Smith

Dijital Maniak

unread,
Jul 30, 2002, 1:36:41 AM7/30/02
to
Chris,

Your suggestions got me on the right track and I have now got it working
properly...albeit I have to reload from a location not in the classpath (it
would be really nice if the Sun would provide functioanlity to reload
classes from the classpath, but I'll keep dreaming).....BTW, you were right
about the access thing....I noticed my ChangingClass error right after I
replied to you the first time!!! Silly me.....

Thanks again for your help!!!

- Dijital

"Chris Smith" <cds...@twu.net> wrote in message

news:MPG.17afcaa51...@news.altopia.com...

Dijital Maniak

unread,
Jul 30, 2002, 1:38:24 AM7/30/02
to
Yeah....I noticed that shortly after the first reply.....thanks!! I guess I
wasn't really paying attention to what I was doing!!

- Dijital


"Jim White" <j...@pagesmiths.com> wrote in message
news:3D461B1A...@pagesmiths.com...

0 new messages