[DGD] Java or LPC (DGD)?

Felix A. Croes felix at dworkin.nl
Sat Aug 16 21:20:30 CEST 2003


"Albert Deinbeck" <albert-deinbeck at albert-deinbeck.de> wrote:

>[...]
> Every time a class is referenced, the classloader is asked to retrieve the
> class. If you write
> class Outer {
>  public MyClass myInstance = new MyClass();
> ...
> then the compiler will insert a logic reference. As soon as Outer class is
> loaded, the classloader
> checks all logic references and also loads the referenced classes, in this
> case MyClass. Then the logic
> reference is replaced by a physical reference (a position on the heap).
> So as long as Outer class exists, it points to the version of MyClass which
> was present when Outer was loaded.
> You can, however, delay the resolution of logical references.
>
> class Outer {
>  public MyInterface myInstance =
> (MyInterface)Class.forName('MyClass').newInstance();
> ...
> will do the same, but the class MyClass is loaded exactly at the time and
> EVERY time the line above is executed.
> The classloader loading Outer has no chance to load MyClass in advance.
> Every time you call this line the classloader will be run.
> All you have to do then is to write a CompilingClassLoader which first
> checks for a new MyClass.java before he looks if there is
> already a class object loaded.
> The classloader calls findLoadedClass() to retrieve an already loaded class
> object. all you have to do is:
>
> class CompilingClassLoader extends ClassLoader {
> public Class loadClass(String name, boolean resolve)
>     Class cls = null;
>     cls = findLoadedClass(name);
>     filename = name.replace('.','/'); // java.lang.String =>
> java/lang/String
>     classname = filename+".class";
>     javaname = filename+".java";
>     File classFile = new File(classname);
>     File javaFile = new File(javaname);
>     if(javaFile.exists() && !classFile.exists() || javaFile.lastModified() >
> classFile.lastModified()) {
>         ... compile the class and load the new class into a byte array ...
>        cls = defineClass(name,bytes,0,bytes.length);
>     }
> }

I assume you want to use Class.forName() in its three-argument version,
or else the default ClassLoader would be used.

Unfortunately, this doesn't solve anything.  The CompilingClassLoader
will fail to redefine MyClass, because it is already loaded -- even if
it is no longer referenced.  We've been through this before, so I have
prepared a small Java program to demonstrate the problem:

---start of test.java--

public class test extends ClassLoader {
    public static void main(String args[])
	throws java.io.UnsupportedEncodingException
    {
	test loader = new test();
	String s1, s2;
	Class foo;

	s1 = "\312\376\272\276\000\000\000\056\000\031\012\000\006\000\014\011"
	   + "\000\015\000\016\010\000\017\012\000\020\000\021\007\000\017\007"
	   + "\000\022\001\000\006\074\151\156\151\164\076\001\000\003\050\051"
	   + "\126\001\000\004\103\157\144\145\001\000\004\155\141\151\156\001"
	   + "\000\026\050\133\114\152\141\166\141\057\154\141\156\147\057\123"
	   + "\164\162\151\156\147\073\051\126\014\000\007\000\010\007\000\023"
	   + "\014\000\024\000\025\001\000\003\146\157\157\007\000\026\014\000"
	   + "\027\000\030\001\000\020\152\141\166\141\057\154\141\156\147\057"
	   + "\117\142\152\145\143\164\001\000\020\152\141\166\141\057\154\141"
	   + "\156\147\057\123\171\163\164\145\155\001\000\003\157\165\164\001"
	   + "\000\025\114\152\141\166\141\057\151\157\057\120\162\151\156\164"
	   + "\123\164\162\145\141\155\073\001\000\023\152\141\166\141\057\151"
	   + "\157\057\120\162\151\156\164\123\164\162\145\141\155\001\000\007"
	   + "\160\162\151\156\164\154\156\001\000\025\050\114\152\141\166\141"
	   + "\057\154\141\156\147\057\123\164\162\151\156\147\073\051\126\000"
	   + "\041\000\005\000\006\000\000\000\000\000\002\000\001\000\007\000"
	   + "\010\000\001\000\011\000\000\000\021\000\001\000\001\000\000\000"
	   + "\005\052\267\000\001\261\000\000\000\000\000\011\000\012\000\013"
	   + "\000\001\000\011\000\000\000\025\000\002\000\001\000\000\000\011"
	   + "\262\000\002\022\003\266\000\004\261\000\000\000\000\000\000";

	s2 = "\312\376\272\276\000\000\000\056\000\032\012\000\006\000\014\011"
	   + "\000\015\000\016\010\000\017\012\000\020\000\021\007\000\022\007"
	   + "\000\023\001\000\006\074\151\156\151\164\076\001\000\003\050\051"
	   + "\126\001\000\004\103\157\144\145\001\000\004\155\141\151\156\001"
	   + "\000\026\050\133\114\152\141\166\141\057\154\141\156\147\057\123"
	   + "\164\162\151\156\147\073\051\126\014\000\007\000\010\007\000\024"
	   + "\014\000\025\000\026\001\000\003\142\141\162\007\000\027\014\000"
	   + "\030\000\031\001\000\003\146\157\157\001\000\020\152\141\166\141"
	   + "\057\154\141\156\147\057\117\142\152\145\143\164\001\000\020\152"
	   + "\141\166\141\057\154\141\156\147\057\123\171\163\164\145\155\001"
	   + "\000\003\157\165\164\001\000\025\114\152\141\166\141\057\151\157"
	   + "\057\120\162\151\156\164\123\164\162\145\141\155\073\001\000\023"
	   + "\152\141\166\141\057\151\157\057\120\162\151\156\164\123\164\162"
	   + "\145\141\155\001\000\007\160\162\151\156\164\154\156\001\000\025"
	   + "\050\114\152\141\166\141\057\154\141\156\147\057\123\164\162\151"
	   + "\156\147\073\051\126\000\041\000\005\000\006\000\000\000\000\000"
	   + "\002\000\001\000\007\000\010\000\001\000\011\000\000\000\021\000"
	   + "\001\000\001\000\000\000\005\052\267\000\001\261\000\000\000\000"
	   + "\000\011\000\012\000\013\000\001\000\011\000\000\000\025\000\002"
	   + "\000\001\000\000\000\011\262\000\002\022\003\266\000\004\261\000"
	   + "\000\000\000\000\000";

	foo = loader.defineClass("foo", s1.getBytes(null), 0, s1.length());
	foo = null;
	// the following will fail
	foo = loader.defineClass("foo", s2.getBytes(null), 0, s2.length());
    }
}

---end of test.java---

This ClassLoader loads the class foo, unreferences it, and attempts to
reload a different version of it, resulting in the following error:

Exception in thread "main" java.lang.LinkageError: duplicate class definition: foo
        at java.lang.ClassLoader.defineClass0(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:502)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:431)
	at test.main(test.java:55)

Going through the forName() method changes nothing when you still wind up
in the same ClassLoader instance.


>[...]
> Interfaces should not and cannot be as mutable, as they are what others rely
> on.

I think you are confusing the general intended use of interfaces, and
the particular use that you yourself advocate here.  True, Java-style
interfaces are not supposed to be changed, and that is exactly why no
interfaces should be used in this case.  You are suggesting the use of
an interface for <every reloadable class>, not just for those that have
to interact with the classes of others.

I suggest the following as an exercise: write a medium-sized program in
Java by first creating interfaces for <every> class without writing any
of the class code itself; when this is done, you are allowed to write
the class code, but without making any changes to the interface or
adding any methods which are not in the interface.

Surely you will see that this is unworkable.  All you have done is set
your design mistakes in stone.  And that is just for a medium-sized
program that does not even have to be persistent.

Regards,
Dworkin
_________________________________________________________________
List config page:  http://list.imaginary.com/mailman/listinfo/dgd



More information about the DGD mailing list