Phantasmal MUD Lib for DGD

Phantasmal Site > DGD > Java vs DGD

Java vs DGD

This is archived straight off the DGD (and to some extent, MUD-Dev) mailing list. For further details, check the MUD-Dev archives.

From: DGD Mailing List (Ben Chambers)
Date: Sun Aug 10 00:19:01 2003
Subject: [DGD] Java or LPC (DGD)?

This is a multi-part message in MIME format.

------=_NextPart_000_0001_01C35EDD.5B07D850
Content-Type: text/plain;
	charset="us-ascii"
Content-Transfer-Encoding: 7bit

I know both well, and have arguments both ways.  Which language would be
better to use when developing a MUD from scratch?

 

Java

+ Open Source Tools available (especially JUnit, for testing)

+ Common Language with more easily available support

-  Less support available explicitly for MUDs

+ Library already includes Web Server and a Random Number Generator that
supports normally distributed numbers (something I would have to add to LPC
one way or another)

+ Better syntax for objects (in my opinion, the C++/Java object system is
much cleaner than the one used in LPC)

- Possibly slower?

 

LPC

+ Designed explicitly for a MUD Server

+ Gives persistence for free

-  Not as supported in general, although more resources are available for
MUD design.

+ Possibly Faster?

 

Are there any points for either of the two that I missed?  Which of the
points that I put in there aren't really significant?  Which language would
you recommend?

 

-- Ben



From DGD Mailing List  Sun Aug 10 00:40:02 2003
From: DGD Mailing List (Kris Van Hees)
Date: Sun Aug 10 00:40:02 2003
Subject: [DGD] Java or LPC (DGD)?

Well, there are a few more distinguishing aspects when comparing Java and LPC:

      - Java only supports single inheritance (and no, interfaces doesn't solve
	that for real), whereas LPC has multiple-inheritance.  When writing a
	quite dynamic piece of code (which many MUDs tend to be when creators
	can write their own areas), single-inheritance can be a real pain.
      - Extending Java with native code is a pain to say the least, whereas
	implementing some functionality in C in DGD is quite easy.
      - Memory management in Java is commonly less conservative than it is in
	DGD.

It does surprise me though that you state that the C++ and Java object systems
are cleaner in your opinion.  I have not often heard people state that,
especially not about C++ :-)

	Kris

From DGD Mailing List  Sun Aug 10 06:36:01 2003
From: DGD Mailing List (Felix A. Croes)
Date: Sun Aug 10 06:36:01 2003
Subject: [DGD] Java or LPC (DGD)?

"Ben Chambers" wrote:

> Java
>
>[...]
>
> + Better syntax for objects (in my opinion, the C++/Java object system is
> much cleaner than the one used in LPC)

What in particular do you consider to be better about the syntax?


> - Possibly slower?

LPC used to be faster, but these days it's probably the other way around.


> LPC
>
> + Designed explicitly for a MUD Server
>
> + Gives persistence for free

DGD has persistence to a degree that is not possible with Java.


>[...]
> Are there any points for either of the two that I missed?  Which of the
> points that I put in there aren't really significant?  Which language would
> you recommend?

 - Java is multi-threaded
 - LPC has multiple inheritance
 - Java has interfaces, LPC has include files
 - Java has much stronger typing (a bit too strong for a persistent
   system, perhaps)
 - Java has more basic datatypes (for example, different integer types)
 - Java's error handling is more fine-grained; DGD has atomic error
   handling

And I would use DGD, of course. :)

Regards,
Dworkin


From DGD Mailing List  Sun Aug 10 10:00:02 2003
From: DGD Mailing List (Felix A. Croes)
Date: Sun Aug 10 10:00:02 2003
Subject: [DGD] Java or LPC (DGD)?

"Ben Chambers" wrote:

> The idea of each file being a class is bizarre.  It is easier to think of
> each class as an object, and then each object can have the type of functions
> private/public/protected) which I don't see happening in DGD.

Presumably what you mean to say is, Java allows nested class
declarations and LPC does not.  Fair enough.

You give the impression of being rather frustrated by DGD, particularly
the lack of support (which is what three of your Java pluses boil down
to).  Perhaps LPC is not the way for you.

Regards,
Dworkin

From DGD Mailing List  Sun Aug 10 12:31:00 2003
From: DGD Mailing List (Ben Chambers)
Date: Sun Aug 10 12:31:00 2003
Subject: [DGD] Java or LPC (DGD)?

I apologize. I didn't see your entire message when I wrote my response =
and
the response which I wrote was rather brief.  Hopefully, this reply will =
be
more sufficient.

-----Original Message-----
From: DGD Admin, On Behalf Of Felix A. Croes
Sent: Sunday, August 10, 2003 7:33 AM
To: DGD Mailing List
Subject: Re: [DGD] Java or LPC (DGD)?

> "Ben Chambers" wrote:

> > Java
> >
> >[...]
> >
> > + Better syntax for objects (in my opinion, the C++/Java object =
system
is
> > much cleaner than the one used in LPC)

> What in particular do you consider to be better about the syntax?

It is a more standard syntax and as such has been tested and tried to a
greater extent.  Personally, I can handle either syntax, but the problem
which I have with the LPC syntax is that it removes the distinction =
between
a file and an object.  In my opinion, the file and object should be =
separate
concepts, but this is a very minor distinction.


> > - Possibly slower?

> LPC used to be faster, but these days it's probably the other way =
around.


> > LPC
> >
> > + Designed explicitly for a MUD Server
> >
> > + Gives persistence for free

> DGD has persistence to a degree that is not possible with Java.
DGD was written in C++, and provides that degree of persistence.  It is
possible, therefore to get it in Java, but I agree that DGD has it =
built-in
and easily accessible in a manner that would not be replicable in Java
without some degree of work.


> >[...]
> > Are there any points for either of the two that I missed?  Which of =
the
> > points that I put in there aren't really significant?  Which =
language >
would
> > you recommend?

> - Java is multi-threaded
Java is only multi-threaded if you code it right, so this is a minor =
issue,
especially since it is my understanding that DGD is headed in this =
direction
anyways.

> - LPC has multiple inheritance
This is my one big complaint with Java.  In my opinion C++ treats
inheritance correctly, and Java's idea of interfaces and stuff is just
confusing.
> - Java has interfaces, LPC has include files
Java has objects that you import, LPC has include files that act like
inheritance.  C++ has the right idea, with include files and then
inheritance being a distinct concept.

> - Java has much stronger typing (a bit too strong for a persistent
>   system, perhaps)
I'm not sure I understand.  I know what you mean by stronger typing, but =
how
exactly would that be a problem in a persistent system?  Would it be a
problem when you try to upgrade an object while the system is running?  =
It
is true that this could be a problem in Java, but since DGD is written =
in
C++ and has managed to solve this problem, it should be possible to =
solve
it, although it may be more difficult.

> - Java has more basic datatypes (for example, different integer types)
For a Mud this shouldn't be an issue, should it?

> - Java's error handling is more fine-grained; DGD has atomic error
>   handling
How exactly does DGD's error handling work?  I couldn't make a decision =
on
which was better without a better understanding of what you mean by "DGD =
has
atomic error handling"

> And I would use DGD, of course. :)

> Regards,
> Dworkin



From DGD Mailing List  Sun Aug 10 12:56:01 2003
From: DGD Mailing List (Erwin Harte)
Date: Sun Aug 10 12:56:01 2003
Subject: [DGD] Re: Java or LPC (DGD)?

On Sun, Aug 10, 2003 at 01:30:44PM -0400, Ben Chambers wrote:
[...]
> It is a more standard syntax and as such has been tested and tried to a
> greater extent.

A few questions for you to research:
- How old is C.
- How old is LPC.
- How old is Java.

Follow-up question:
- Which syntax do you think is more standard, tested, and tried?

[...]
> > - Java is multi-threaded
> Java is only multi-threaded if you code it right, so this is a minor issue,
> especially since it is my understanding that DGD is headed in this direction
> anyways.

And DGD will only be multi-threaded if you code it right.  You can
make multi-threaded support be pointless in -any- language, really. 

[...]
> problem when you try to upgrade an object while the system is running?  It
> is true that this could be a problem in Java, but since DGD is written in
> C++ and has managed to solve this problem, it should be possible to solve
> it, although it may be more difficult.

The language used to implement DGD has absolutely nothing to do with
how it implements its persistent system.  If Dworkin had felt like it
he could've done the same thing in BASIC, I'm sure.

> > - Java has more basic datatypes (for example, different integer types)
> For a Mud this shouldn't be an issue, should it?

I've seen situations where the standard (32 bit wide) integer was too
small.  With recent DGD I'd probably have suggested to use the asn_*
kfuns to work around that, but it would still not be as smooth as just
being able to use +, -, * and / on integer values.

> > - Java's error handling is more fine-grained; DGD has atomic error
> >   handling
> How exactly does DGD's error handling work?  I couldn't make a decision on
> which was better without a better understanding of what you mean by "DGD has
> atomic error handling"

Read up on 'atomic', 'catch' and 'error'.

Cheers,

Erwin.
-- 
Erwin Harte

From DGD Mailing List  Sun Aug 10 13:57:01 2003
From: DGD Mailing List (Felix A. Croes)
Date: Sun Aug 10 13:57:01 2003
Subject: [DGD] Java or LPC (DGD)?

"Ben Chambers" wrote:

> > DGD has persistence to a degree that is not possible with Java.
> DGD was written in C++, and provides that degree of persistence.  It is
> possible, therefore to get it in Java, but I agree that DGD has it built-in
> and easily accessible in a manner that would not be replicable in Java
> without some degree of work.

Using the same line of reasoning, I could argue that C/C++ also has
persistence, which of course is nonsense.

To reach the same degree of persistence that LPC has, you would have to
implement an interpreted language in Java.  Now <that> would definitely
be slower than LPC.

More about persistence below.


>[...]
> > - Java is multi-threaded
> Java is only multi-threaded if you code it right, so this is a minor issue,

Java is multi-threaded, period.  Do you mean that the problems associated
with this can be avoided?  If so, then you are right about that, but it
is definitely not a minor issue.


> especially since it is my understanding that DGD is headed in this direction
> anyways.

DGD is, LPC isn't.


>[...]
> > - Java has interfaces, LPC has include files
> Java has objects that you import, LPC has include files that act like
> inheritance.  C++ has the right idea, with include files and then
> inheritance being a distinct concept.

Surely you know that in LPC, inheritance and #including code are as
different as they are in C++?

Java's interfaces are actually an improvement on (the most common and
proper use of) include files.


> > - Java has much stronger typing (a bit too strong for a persistent
> >   system, perhaps)
> I'm not sure I understand.  I know what you mean by stronger typing, but how
> exactly would that be a problem in a persistent system?  Would it be a
> problem when you try to upgrade an object while the system is running?  It
> is true that this could be a problem in Java, but since DGD is written in
> C++ and has managed to solve this problem, it should be possible to solve
> it, although it may be more difficult.

It is a problem that can be solved -- by implementing an interpreted
language within Java.

A fully persistent mud must be able to upgrade code without being taken
down.  Java has some problems: it is hard/impossible (used to be impossible
but I'm told that there are now ways around it) to upgrade the code for
an object; and when you change a function prototype somewhere in a tangle
of related objects, this may result in linker clashes.

Then there is the problem of dependencies.  How do you upgrade a base
class from code that depends on that base class?  How do you deal with
threads running in dependent code at the same time?

I will take a look at the latest spec to see how/if Java has addressed
the upgrade problem and post on that later.


> > - Java has more basic datatypes (for example, different integer types)
> For a Mud this shouldn't be an issue, should it?

Within a mud, probably not.  For implementing a mud server, it might be.


> > - Java's error handling is more fine-grained; DGD has atomic error
> >   handling
> How exactly does DGD's error handling work?  I couldn't make a decision on
> which was better without a better understanding of what you mean by "DGD has
> atomic error handling"

There is a good explanation at

    http://dgd.is-here.com/faq/html/x440.html

Regards,
Dworkin

From DGD Mailing List  Sun Aug 10 17:02:01 2003
From: DGD Mailing List (Felix A. Croes)
Date: Sun Aug 10 17:02:01 2003
Subject: [DGD] Java or LPC (DGD)?

I've looked further into the issue of class reloading for Java.

The only way to accomplish this is to split each class in two: a
wrapper class which contains the values, and a wrapped class for
the program code which may be updated.  The wrapper class must create
the wrapped class using a private ClassLoader instance.  To upgrade
the wrapped class, a new ClassLoader instance must also be created.

The wrapper class must forward method invocations to the wrapped class
using the reflection API, passing an array of Object arguments and an
array of Object field values, returning an Object value.  Done in this
way, you can't have typechecking for method arguments and return values
at compile time.  However, you can still have them at runtime with the
appropriate casts.  The alternative would be to let the wrapper and
wrapped class both implement the same interface.  This would allow for
compile-time typechecking, but then the interface is fixed and cannot
be changed.

Furthermore, since each ClassLoader defines its own class namespace,
a simulated global object namespace will have to be created which the
ClassLoaders can use to find wrapped classes to inherit from.

You could create a Java preprocessor which properly translates method
invocations and field access in normal Java source code, allowing the
wrapper/wrapped pairs to simulate ordinary Java classes.

What I previously referred to as "the problem of dependencies" is not
generally solvable.  Old instances referred to by the thread of
execution will continue to exist side by side with new ones, resulting
in possible problems until the old instances are no longer used.

Regards,
Dworkin

From DGD Mailing List  Mon Aug 11 12:34:01 2003
From: DGD Mailing List (Noah Gibbs)
Date: Mon Aug 11 12:34:01 2003
Subject: [DGD] Java or LPC (DGD)?

--- Ben Chambers wrote:
> > > - Possibly slower?
> 
> > LPC used to be faster, but these days it's
> probably the other way around.

  I'm sure there are cases where this is true, but
don't overrate the Java implementors.  Every
implementation of Java on Unix that I've tried, for
instance, is just godawful.

  Java on Windows seems to be better, though I'm not
sure by how wide a margin.

> > DGD has persistence to a degree that is not
> > possible with Java.
>
> [...] I agree that DGD has it built-in
> and easily accessible in a manner that would not be
> replicable in Java
> without some degree of work.

  Look at atomic functions, rlimits(), and upgrading
classes on the fly.  Then reassess the degree of work.
 There's a reason that Felix has been working on this
stuff for years.  I started using DGD not because I
wanted to play with another C-like language (I didn't,
not really) but because I spent the better part of a
year trying to implement a tiny fraction of the same
functionality in C and Perl.

  During one of my periodic "God, there must be a
better way" Google sessions, I discovered DGD.  I've
never looked back.

Noah Gibbs



From DGD Mailing List  Mon Aug 11 15:51:01 2003
From: DGD Mailing List (Greg Lewis)
Date: Mon Aug 11 15:51:01 2003
Subject: [DGD] Java or LPC (DGD)?

On Mon, Aug 11, 2003 at 10:33:15AM -0700, Noah Gibbs wrote:
> --- Ben Chambers wrote:
> > > > - Possibly slower?
> > 
> > > LPC used to be faster, but these days it's
> > probably the other way around.
> 
>   I'm sure there are cases where this is true, but
> don't overrate the Java implementors.  Every
> implementation of Java on Unix that I've tried, for
> instance, is just godawful.
> 
>   Java on Windows seems to be better, though I'm not
> sure by how wide a margin.

The only real definitive answer here is going to come from somebody
doing some benchmarks.

To do this you need to pick a Java implementation to benchmark against.
Probably Sun's is a good default, although IBM's has somewhat of a
reputation for being faster (I haven't read any 1.4 benchmarks comparing
the two though).

-- 
Greg Lewis                          Email   : <removed>
Eyes Beyond                         Web     : http://www.eyesbeyond.com
Information Technology              FreeBSD : <removed>


From DGD Mailing List  Thu Aug 14 04:30:01 2003
From: DGD Mailing List (Albert Deinbeck)
Date: Thu Aug 14 04:30:01 2003
Subject: [DGD] Java or LPC (DGD)?

Hi all, hi Dworkin,

----- Original Message -----
From: "Felix A. Croes"
To: DGD Mailing List
Sent: Sunday, August 10, 2003 11:58 PM
Subject: RE: [DGD] Java or LPC (DGD)?


> I've looked further into the issue of class reloading for Java.

I've too... :o)
>
> The only way to accomplish this is to split each class in two: a
> wrapper class which contains the values, and a wrapped class for
> the program code which may be updated.

An interesting approach, but of course not the only way. You presume the use
of standard java serialization, which won't work with modified classes
because a checksum of the class (i.e. the class bytecode) is saved along
with the serialized instance.
You can, however, use another form of serialization, either using the
externalizable interface or some external serialization library like Java
Serialization to XML (JSX). The latter would also allow for other programs
or simple editors to modify the serialized data.

  The wrapper class must create
> the wrapped class using a private ClassLoader instance.  To upgrade
> the wrapped class, a new ClassLoader instance must also be created.

This is not necessary if you use a compiling classloader which checks every
time the class is accessed, whether the class file has been modified an
compiles and reloads the class if necessary. This way, you can use one
classloader and circumvent the problems described below....

>
> The wrapper class must forward method invocations to the wrapped class
> using the reflection API, passing an array of Object arguments and an
> array of Object field values, returning an Object value.  Done in this
> way, you can't have typechecking for method arguments and return values
> at compile time.  However, you can still have them at runtime with the
> appropriate casts.  The alternative would be to let the wrapper and
> wrapped class both implement the same interface.  This would allow for
> compile-time typechecking, but then the interface is fixed and cannot
> be changed.
>
> Furthermore, since each ClassLoader defines its own class namespace,
> a simulated global object namespace will have to be created which the
> ClassLoaders can use to find wrapped classes to inherit from.
>
> You could create a Java preprocessor which properly translates method
> invocations and field access in normal Java source code, allowing the
> wrapper/wrapped pairs to simulate ordinary Java classes.
>
> What I previously referred to as "the problem of dependencies" is not
> generally solvable.  Old instances referred to by the thread of
> execution will continue to exist side by side with new ones, resulting
> in possible problems until the old instances are no longer used.

This problem is of course solvable. If you have a wrapper class containing
the real class instance, the wrapper could have a method:
public void update(){
    store(myObject,fileName);
    myObject = null;
    getClassLoader().loadClass("MyObjectClass");
    myObject = (MyObjectClass) load(fileName);
}
where store serializes the class to a file and load unserializes it and
returns an Object.

Interfaces can be very useful in this case, used to define the static
interface of modifyable classes. If your class is:
class Living implements LivingInterface { // important Living methods}
class Living2 implements LivingInterface { // modified Living methods}
then you can make
new Living.store("Living.dat");
new Living2.store("Living2.dat");
LivingInterface myLiving = (LivingInterface) load("Living.dat");
LivingInterface myLiving = (LivingInterface) load("Living2.dat");

So myLiving can in effect store different classes transparently.

Regards,
Albert



From DGD Mailing List  Thu Aug 14 10:01:01 2003
From: DGD Mailing List (Felix A. Croes)
Date: Thu Aug 14 10:01:01 2003
Subject: [DGD] Java or LPC (DGD)?

"Albert Deinbeck" wrote:

> From: "Felix A. Croes"

>[...]
> > The only way to accomplish this is to split each class in two: a
> > wrapper class which contains the values, and a wrapped class for
> > the program code which may be updated.
>
> An interesting approach, but of course not the only way. You presume the use
> of standard java serialization, which won't work with modified classes
> because a checksum of the class (i.e. the class bytecode) is saved along
> with the serialized instance.
> You can, however, use another form of serialization, either using the
> externalizable interface or some external serialization library like Java
> Serialization to XML (JSX). The latter would also allow for other programs
> or simple editors to modify the serialized data.

Actually, I was not presuming any sort of serialisation at all -- I was
treating the problem of saving and restoring data for an object as
already solved.  I wasn't aware that ordinary Java serialization
includes a checksum for the bytecode  -- how silly!


>   The wrapper class must create
> > the wrapped class using a private ClassLoader instance.  To upgrade
> > the wrapped class, a new ClassLoader instance must also be created.
>
> This is not necessary if you use a compiling classloader which checks every
> time the class is accessed, whether the class file has been modified an
> compiles and reloads the class if necessary. This way, you can use one
> classloader and circumvent the problems described below....

You could get it to work that way by giving each wrapped class a new
private name, every time you recompile it (since the "official" name
is in the simulated global namespace anyhow).  Unfortunately, that would
keep the old classes around, no longer used.  A memory leak, basically.


>[...]
> > What I previously referred to as "the problem of dependencies" is not
> > generally solvable.  Old instances referred to by the thread of
> > execution will continue to exist side by side with new ones, resulting
> > in possible problems until the old instances are no longer used.
>
> This problem is of course solvable. If you have a wrapper class containing
> the real class instance, the wrapper could have a method:
> public void update(){
>     store(myObject,fileName);
>     myObject = null;
>     getClassLoader().loadClass("MyObjectClass");
>     myObject = (MyObjectClass) load(fileName);
> }
> where store serializes the class to a file and load unserializes it and
> returns an Object.

This won't work because "myObject = null;" does not actually unload
the object, even if it is the single remaining reference.  Java does not
allow recompilation of an already loaded class, and does not have a way
to force unloading of an unreferenced object, other than by unreferencing
the class loader (which is why I suggested using one classloader per
class).


> Interfaces can be very useful in this case, used to define the static
> interface of modifyable classes. If your class is:
> class Living implements LivingInterface { // important Living methods}
> class Living2 implements LivingInterface { // modified Living methods}
> then you can make
> new Living.store("Living.dat");
> new Living2.store("Living2.dat");
> LivingInterface myLiving = (LivingInterface) load("Living.dat");
> LivingInterface myLiving = (LivingInterface) load("Living2.dat");
>
> So myLiving can in effect store different classes transparently.

This is what I myself suggested as an alternative :)  But of course,
in a persistent environment interfaces have to be as mutable as
class bytecode.

Regards,
Dworkin

From DGD Mailing List  Sat Aug 16 07:43:00 2003
From: DGD Mailing List (Albert Deinbeck)
Date: Sat Aug 16 07:43:00 2003
Subject: [DGD] Java or LPC (DGD)?

----- Original Message -----
From: "Felix A. Croes"
To: DGD Mailing List
Sent: Thursday, August 14, 2003 4:57 PM
Subject: Re: [DGD] Java or LPC (DGD)?


> "Albert Deinbeck" wrote:
>
> > From: "Felix A. Croes"
>
> >[...]
> > > The only way to accomplish this is to split each class in two: a
> > > wrapper class which contains the values, and a wrapped class for
> > > the program code which may be updated.
> >
> > An interesting approach, but of course not the only way. You presume the
use
> > of standard java serialization, which won't work with modified classes
> > because a checksum of the class (i.e. the class bytecode) is saved along
> > with the serialized instance.
> > You can, however, use another form of serialization, either using the
> > externalizable interface or some external serialization library like
Java
> > Serialization to XML (JSX). The latter would also allow for other
programs
> > or simple editors to modify the serialized data.
>
> Actually, I was not presuming any sort of serialisation at all -- I was
> treating the problem of saving and restoring data for an object as
> already solved.  I wasn't aware that ordinary Java serialization
> includes a checksum for the bytecode  -- how silly!

I do not think it's silly. Java was designed to be secure in a context of
wordwide interchange and use of classes and serialized data. In this context
it is a security hole to let classes be deserialized from sources other than
produced by the origin class.
Imagine you use a class made by another guy from Japan. Changes he makes to
private methods or variables are invisible to users of
his class. However, you may serialize and store an instance, then update to
a new version and reload it and crash.
What would you do if an object is stored, then the class is changed and
class variables modified so that they don't match any more and then the
object is tried to be reloaded again?

> >   The wrapper class must create
> > > the wrapped class using a private ClassLoader instance.  To upgrade
> > > the wrapped class, a new ClassLoader instance must also be created.
> >
> > This is not necessary if you use a compiling classloader which checks
every
> > time the class is accessed, whether the class file has been modified an
> > compiles and reloads the class if necessary. This way, you can use one
> > classloader and circumvent the problems described below....
>
> You could get it to work that way by giving each wrapped class a new
> private name, every time you recompile it (since the "official" name
> is in the simulated global namespace anyhow).  Unfortunately, that would
> keep the old classes around, no longer used.  A memory leak, basically.

You needn't use a new name for the class. You can call Class.forName() with
the name of the changed class and the class will be replaced. The old class
object will remain in memory as long as it is referenced from anywhere, i.e.
as long as there are instances of the old class around.

> > > What I previously referred to as "the problem of dependencies" is not
> > > generally solvable.  Old instances referred to by the thread of
> > > execution will continue to exist side by side with new ones, resulting
> > > in possible problems until the old instances are no longer used.
> >
> > This problem is of course solvable. If you have a wrapper class
containing
> > the real class instance, the wrapper could have a method:
> > public void update(){
> >     store(myObject,fileName);
> >     myObject = null;
> >     getClassLoader().loadClass("MyObjectClass");
> >     myObject = (MyObjectClass) load(fileName);
> > }
> > where store serializes the class to a file and load unserializes it and
> > returns an Object.
>
> This won't work because "myObject = null;" does not actually unload
> the object, even if it is the single remaining reference.  Java does not
> allow recompilation of an already loaded class, and does not have a way
> to force unloading of an unreferenced object, other than by unreferencing
> the class loader (which is why I suggested using one classloader per
> class).

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 have left out some parts not necessary for understanding.
This way you can change classes on the fly. All you have to do then is to
make every object holding a reference
to refresh it. This is where a wrapper could be useful which is the only one
to hold a reference to the actual object.

> > Interfaces can be very useful in this case, used to define the static
> > interface of modifyable classes. If your class is:
> > class Living implements LivingInterface { // important Living methods}
> > class Living2 implements LivingInterface { // modified Living methods}
> > then you can make
> > new Living.store("Living.dat");
> > new Living2.store("Living2.dat");
> > LivingInterface myLiving = (LivingInterface) load("Living.dat");
> > LivingInterface myLiving = (LivingInterface) load("Living2.dat");
> >
> > So myLiving can in effect store different classes transparently.
>
> This is what I myself suggested as an alternative :)  But of course,
> in a persistent environment interfaces have to be as mutable as
> class bytecode.

There is something developers have to rely on, as well as Virtual Machines.
You do expect every LPC object to possess certain
functions. Interfaces have to be generic, to allow interchange without
hindering extensions.
For example: Every command in DGD implements the command interface:
interface Command {
    public void do_command(String string);
}
Interfaces should not and cannot be as mutable, as they are what others rely
on. You can however make new interfaces and subclass them. If you make
interface SuperCommand extends Command {
   public void do_command(String string, Permission perm);
}
you can use advanced functionality but keep backwards compatibility as every
class implementing SuperCommand also isInstanceOf Command.

Regards,
Albert



From DGD Mailing List  Sat Aug 16 14:24:01 2003
From: DGD Mailing List (Felix A. Croes)
Date: Sat Aug 16 14:24:01 2003
Subject: [DGD] Java or LPC (DGD)?

"Albert Deinbeck" 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

From DGD Mailing List  Sat Aug 16 18:42:00 2003
From: DGD Mailing List (Sampsa Ranta)
Date: Sat Aug 16 18:42:00 2003
Subject: [DGD] Java or LPC (DGD)?

Hi,

I've not been in contact with DGD world for a while but I would like to
contribute to your DGD LPC versus Java thread.. =)

Both have been very keen to me, however Java has been my primary language
for a while.

First I must state that I've questioned DGDs minimalistic approach in
past but later on I've come to think it is a good thing. Java basic class
libraries that are in some level ment to be "reference implementation"
are too big for "basic set of tools". And under the hood everything is not
as "smooth" and "sane" as seen on top.

Relevant issues for mudlib:

+ DGD has minimalistic approach for API, with Java there is no "Java for
  Mudlibs"-API yet -> means work!
- Java does not have prebuilt framework like DGD, J2Se has lot of overload

- DGD does not multithread
+ Java multihtreads but one has to use own brains to take control over
  this thing

- J2SE does not have atomic operations and does not give support for
  reloading class bytecode
+ DGD does support atomic code and bytecode reloading

+ Java VM could be used to simulate DGD driver under special Classloader
  and compiler mapping the code to Java bytecode (maybe with proxies or
  direct bytecode to support multible inheritance) but not vice versa.
  DGD driver itself is smart but not too complex features to re-implement
  on as-is base. It seems it has been written under goal like "keep it
  simple". However such simulation would still have to be single threading
  if it is going to be 1:1 simulation.
- DGD cannot simulate Java

- DGD licensing issues, source is open to read and distribute but
  restricted commercial use..
+ Java JVM is free for whatever commercial use also, latest versions
  of "reference implementation" however are not always open and
  nothing forces Sun to keep it open.. However we have GCJ and GNU version
  of class libraries..

+ Java allows fine grained security by security managers howerver
  using these is NOT so well codumented, nothing tells you
  what rights you have to grant to your code in order to make complex
  things work. =)
+ DGD also allows one to protect things within auto object, but as
  Felix fears the networking to cause worms and problems, there is no
  way to connect outside world by what is called the vanilla driver..
  (let's give a hooray for Microsoft RPC here!)

- DGD does not allow persistance/serialization to streams such as the
  network (userobject) directly, only files can be used as primary storage.
+ Java does not have such limitations, serialization does fine with
  streams and there is reflect also if you need to poke around..

+ DGD can precompile LPC to C
+ Java VM usually has JIT and GCJ has ahead-of-time compilation

+ DGD has nice memory management but this requires swapping everything out
  once in a while
- Java/JVM reference implementation has large memory footprint but there
  are alternative implementations and this is not a requirement
  by the language.

Other things not always required for mudlibs:

* DGD is written by one wise fellow, Java is soup of many cooks
  + however the foundation of Java and JVM are well defined
    in "THE JAVA VIRTUAL MACHINE SPECIFICATION" and
    "THE JAVA LANGUAGE SPECIFICATION".
    The J2SE reference implementation is like huge kernel library, one
    could live without most of it using alternative bootstrap code =)
  + DGD code is clean and easy to read

- Extending DGD would require C code that leaves you with out the
  protective care of virtual machine and byte code
+ J2SE has quite extensive and well tested native access library but not
  everything, when you need more than Java can give you, this is no longer
  an issue since you fall back to C =)

+ If a MUD were to implement graphical client, it could be written with
  same language and then there would already be numerous protocols that
  could be easily used for communication (RMI might be useful).
+ DGD is written for as server only and therefore kept simple.

The real issue is how much space time coders have.. DGD might be good
starting point as it is shows how to build manageable persistant stuff
quite easily. If there were wise man to implement same basic behaviour
with Java that would be cool..

If one were to build such thins, it would be recommended to learn from
what Felix has done and how he has done this and compare it to Javas
design approach.

It is nice to see that things were kept and left simple to be extended,
not given as J2SE-size API. One can register management daemons, etc,
but AWT is not basic requirement always..

Of course it would be "required" that you could simply link Java more
stuff afterwards but this should not be built as mandatory requirement for
such kernel, rather as optional.



Comments on the raised technology related issues concerning Java (quite
off topic but whadever)..

--- Reloading issue from Java point of view ---

A Class namespace is defined per Classloader. Same name under same
Classloader should maen same bytecode defining the class. This goes for
sane JVM, of course one could rewrite the JVM and say this is possible,
however would it fit to Java specs is another matter.

Issues raised in this thread on by brief overlook have been well described
in Java specs briefly quoted below.

I assume you are trying to build simulate behavior possible in DGD like
like object management and recompilation, this is doable but not too easy..

First, DGD is able to re-reference an cloned (intance in Java) class
bytecode on flight, Java VM does not give control for this as easily as
kernel library does.

In order to do this in Java, one has first take control over JVM
references control on your own code level and make events such as object
creation and garbage collection visible to the management. Behavior like
this could be done with proxies, this could be done by dynamic proxies
for example BCEL project allows you to do this.
(http://jakarta.apache.org/bcel/index.html)

If proxies were designed smartly, when upgraded, every method call and
response could be rewritten on flight.

You propably would end up having a proxy and Classloader per Class version
but this should be doable.

--- Other things ---

Things like resource limits would be harder to implement and would
require some state of byte code alteration in order to add checks and
analyze cost for an operation. Also blocking operations should be taken in
to account and due to Java's multithreading there would be issues like
synchronization.

? I were to write mudlib in Java, controlling would possibly happen
first hand on thread level.

How ever analysing full Java J2SE API would be major pain in the ass,
maybe smaller API should be revealed to the library.. =) Classloader can
help on this one..

--- Serialization ---

What comes to serialization, Java has versioning for classes and the
Serialization implementation. When serial uid of two classes match the
classes are expected to match. In this case class with new variables will
load accordingly to the specs, I don't remember weather they were
initialized as null or by default values by default. Pretty much as DGD
does.. Circular references can be also dealt with..

In case there would be kernel library taking care of the object
management, reference management and the classloading would enforce
serialization in some sence it would be possible to dump entire Java
worlds to disc..

--- THREADING ---

The real question between Java and DGD is threading. I recall Felix
stating that some of the solutions used by DGD prevent real
multithreading.

Such things would be for example state where you have thread:

 while(1) {
    System.out.println("Hello World");
 }

DGD recompilation happens between execution of LPC code "threads", this
way there is no code in excecution while upgrading. One would have to
abort thread to allow upgrade.

Also atomic code supported by kernel would break the multithreading..

I have no idea wheather Felix has come up with sane solution on this but
this is one of the big questions between DGD and Java architectures.

Yours,
 Sampsa Ranta


"
12.2 Loading of Classes and Interfaces

Loading refers to the process of finding the binary form of a class or
interface type with a particular name, perhaps by computing it on the fly,
but more typically by retrieving a binary representation previously
computed from source code by a compiler, and constructing, from that
binary form, a Class object to represent the class or interface.

The precise semantics of loading are given in chapter 5 of The Java
Virtual Machine Specification, Second Edition. Here we present an overview
of the process from the viewpoint of the Java programming language.

The binary format of a class or interface is normally the class file
format described in The Java Virtual Machine Specification cited above,
but other formats are possible, provided they meet the requirements
specified in ?13.1. The method defineClass of class ClassLoader may be
used to construct Class objects from binary representations in the class
file format.

Well-behaved class loaders maintain these properties:

    * Given the same name, a good class loader should always return the same class object.
"

"
5.3.4 Loading Constraints
Ensuring type safe linkage in the presence of class loaders requires
special care. It is possible that when two different class loaders
initiate loading of a class or interface denoted by N, the name N may
denote a different class or interface in each loader.

When a class or interface C = <N1, L1>; makes a symbolic reference to a
field or method of another class or interface D = <N2, L2> , the symbolic
reference includes a descriptor specifying the type of the field, or the
return and argument types of the method. It is essential that any type
name N mentioned in the field or method descriptor denote the same class
or interface when loaded by L1 and when loaded by L2.

To ensure this, the Java virtual machine imposes loading constraints of
the form NL1 = NL2 during preparation (?5.4.2) and resolution (?5.4.3). To
enforce these constraints, the Java virtual machine will, at certain
prescribed times (see ?5.3.1, ?5.3.2, ?5.3.3, and ?5.3.5), record that a
particular loader is an initiating loader of a particular class. After
recording that a loader is an initiating loader of a class, the Java
virtual machine must immediately check to see if any loading constraints
are violated. If so, the record is retracted, the Java virtual machine
throws a LinkageError, and the loading operation that caused the recording
to take place fails.
"

"
12.7 Unloading of Classes and Interfaces

An implementation of the Java programming language may unload classes. A
class or interface may be unloaded if and only if its defining class
loader may be reclaimed by the garbage collector as discussed in ?12.6.
Classes and interfaces loaded by the bootstrap loader may not be unloaded.
"


From DGD Mailing List  Sat Aug 16 20:44:01 2003
From: DGD Mailing List (Tavis Elliott)
Date: Sat Aug 16 20:44:01 2003
Subject: [DGD] Java or LPC (DGD)?

On Sun, 17 Aug 2003 02:44:21 +0300 (EEST), Sampsa Ranta wrote:

>
> [..snip..]
>
> + Java VM could be used to simulate DGD driver under special Classloader
> and compiler mapping the code to Java bytecode (maybe with proxies or
> direct bytecode to support multible inheritance) but not vice versa.
> DGD driver itself is smart but not too complex features to re-implement
> on as-is base. It seems it has been written under goal like "keep it
> simple". However such simulation would still have to be single threading
> if it is going to be 1:1 simulation.
> - DGD cannot simulate Java
>

To weigh in on the bytecode update issue (Java has also been my primary 
development language for quite some time) ... I would advise checking into 
the JDI interface for Java 1.4, there are mechanisms for runtime 
replacement of bytecodes that don't require horrendous fancy ClassLoader 
schemes.  Granted they are 'debug' interfaces, and there are still 
questions about introducing runtime problems, but they are easier than some 
of the solutions already discussed.

-Tavis

From DGD Mailing List  Sun Aug 17 02:22:01 2003
From: DGD Mailing List (Sampsa Ranta)
Date: Sun Aug 17 02:22:01 2003
Subject: [DGD] Java or LPC (DGD)?

On Sat, 16 Aug 2003, Tavis Elliott wrote:

> I wrote:

> > [..snip..]
> >
> > + Java VM could be used to simulate DGD driver under special Classloader
> > and compiler mapping the code to Java bytecode (maybe with proxies or
> > direct bytecode to support multible inheritance) but not vice versa.
> > DGD driver itself is smart but not too complex features to re-implement
> > on as-is base. It seems it has been written under goal like "keep it
> > simple". However such simulation would still have to be single threading
> > if it is going to be 1:1 simulation.
> > - DGD cannot simulate Java
> >
>
> To weigh in on the bytecode update issue (Java has also been my primary
> development language for quite some time) ... I would advise checking into
> the JDI interface for Java 1.4, there are mechanisms for runtime
> replacement of bytecodes that don't require horrendous fancy ClassLoader
> schemes.  Granted they are 'debug' interfaces, and there are still
> questions about introducing runtime problems, but they are easier than some
> of the solutions already discussed.

Nice to know, this might be handy for instance upgrading without
playing awful lot of work with proxies or then again this would be handy
for upgrading the proxies themselves. =)

But in order to fully simulate DGD, you would also need versioning of
objects, DGD allows clones with different versions of master objects. For
this, you still need Classloaders..

Of course spawning the runtime mud lib object space into versions of
master objects for each clone is bad design.

 - Sampsa





From DGD Mailing List  Sun Aug 17 08:03:00 2003
From: DGD Mailing List (Felix A. Croes)
Date: Sun Aug 17 08:03:00 2003
Subject: [DGD] Java or LPC (DGD)?

Tavis Elliott wrote:

> To weigh in on the bytecode update issue (Java has also been my primary 
> development language for quite some time) ... I would advise checking into 
> the JDI interface for Java 1.4, there are mechanisms for runtime 
> replacement of bytecodes that don't require horrendous fancy ClassLoader 
> schemes.  Granted they are 'debug' interfaces, and there are still 
> questions about introducing runtime problems, but they are easier than some 
> of the solutions already discussed.

The JDI itself is pure Java, but the way it works isn't.

Sun's Java VM defines a debugger interface called JVMDI.  This is a
C-level interface.  When using JDI to debug, you communicate with a
backend which interfaces with JVMDI -- and which therefore is written
in C.

The JVM which the JDI executes on is distinct from the JVM that you are
debugging.  And the JVMDI interface is optional -- JVMs are not required
to implement it.

If we are going to make comparisions on this level, I request that I
be allowed to run one DGD process within gdb, communicating through a
C backend with another DGD process :)  Still, the JVMDI interface
<does> include a RedefineClasses function which performs exactly
the upgrade function that was being discussed.  At the JDI level,
the method is called redefineClasses().  The documentation is at

    http://java.sun.com/j2se/1.4.2/docs/guide/jpda/jdi/com/sun/jdi/VirtualMachine.html#redefineClasses(java.util.Map)

Regards,
Dworkin

From DGD Mailing List  Sun Aug 17 08:51:01 2003
From: DGD Mailing List (Sampsa Ranta)
Date: Sun Aug 17 08:51:01 2003
Subject: [DGD] Java or LPC (DGD)?

On Sun, 17 Aug 2003, Felix A. Croes wrote:

> Tavis Elliott wrote:
>
> > To weigh in on the bytecode update issue (Java has also been my primary
> > development language for quite some time) ... I would advise checking into
> > the JDI interface for Java 1.4, there are mechanisms for runtime
> > replacement of bytecodes that don't require horrendous fancy ClassLoader
> > schemes.  Granted they are 'debug' interfaces, and there are still
> > questions about introducing runtime problems, but they are easier than some
> > of the solutions already discussed.
>
> The JDI itself is pure Java, but the way it works isn't.

This is defined in Java Platform, the reference implementation is not pure
Java, that I can agree on. Nothing prevents you implementing JVM by Java,
tho, of course this might be overhead for now.. =)

http://java.sun.com/j2se/1.4.1/docs/guide/jpda/enhancements.html
"
Enable "HotSwap" Class File Replacement

    This new feature encapsulates the ability to substitute modified code
in a running application through the debugger APIs. For example, one can
recompile a single class and replace the old instance with the new
instance.

    This change was made to address these issues:

        * Tool (IDE) vendors want the ability to do fix-and-continue
debugging. That is, while debugging, identify a problem, fix it, and
continue debugging with fixed code.
        * Organizations deploying long running servers wish to be able to
fix bugs without taking down the server.

    HotSwap adds functionality to the JavaTM Platform Debugger
Architecture (JPDA) to allow a class to be updated while under the control
of a debugger. The two central components of this functionality are
RedefineClasses which replaces the class definitions and PopFrame which
pops frames off the stack allowing a method which has been redefined to be
re-executed.

    In the reference implementation, this functionality is implemented at
the Java Virtual Machine Debug Interface (JVMDI) layer and made available
through the higher layers of JPDA - the Java Debug Wire Protocol (JDWP)
and the Java Debug Interface (JDI).
"

Clearly the people behind this are not lobbying this as a big thing or
don't see the real needs objectivedly as they might have never coded
muds with DGD and seen requirements for environments ment for continous
use without restarts.. =)

But it's good this interface comes planned as part of the Java Platform so
the common interface can be used.

As stated in my original message, the reference implementation is not
always the best way. From my experiences I've learned that something once
coded as reference implementation can be read by other person and
second or thrid refactoring round will make it much more smooth...

So the JDI API might do the thing, the under hood reference implementation
has the stink of new and raw thing as usual.. =)

Anyway, one can design and implement DGD like behaviour under Java with
this stuff and when more decent implementation comes in hand, if JDI level
was used, upgrading process will be pretty straightforward.

> Sun's Java VM defines a debugger interface called JVMDI.  This is a
> C-level interface.  When using JDI to debug, you communicate with a
> backend which interfaces with JVMDI -- and which therefore is written
> in C.

Well, this is again reference implementation, JDI does not require JVMDI
ideology to be used.

I guess GCJ folks might do this different way, the project is more C++
related so C API might be just overhead API over the real one. And IBM
fellows sometimes bang heads together in proper order, it is of course
easier to say that "Hey, our code beats easily your original code!"
because the original code had nothing to compare to..

Nothing says that JVM must be "software based" or C-level things, there
are native Java bytecode based platforms, too..

> The JVM which the JDI executes on is distinct from the JVM that you are
> debugging.  And the JVMDI interface is optional -- JVMs are not required
> to implement it.

"JVMDI clients run in the same virtual machine as the application being
debugged and access JVMDI through a native interface. The native,
in-process interface allows maximal control with minimal intrusion on the
part of a debugging tool. Typically, JVMDI clients are relatively compact.
They can be controlled by a separate process which implements the bulk of
a debugger's function without interfering with the target application's
normal execution."

Nothing says that it is REQUIRED to run on different virtual machine..

> If we are going to make comparisions on this level, I request that I
> be allowed to run one DGD process within gdb, communicating through a
> C backend with another DGD process :)  Still, the JVMDI interface
> <does> include a RedefineClasses function which performs exactly
> the upgrade function that was being discussed.  At the JDI level,
> the method is called redefineClasses().  The documentation is at

I am not sure weather we are doing comparison or I was trying to make a
point of anything, my point was to give review the technologies used and
give basic idea what one can do and implement under what environment..

Running DGD under gdb gave some advantage at the time it crashed, I
recall.. =)

Anyway, you, Felix, have done nice thinking even before Java world emerged
and DGD gave me good lessons on this stuff... =) Nowadays I need
solutions that can be easily introduced to business(commercial) world and
integrates with more systems on commonly used language.

However the systems lack part of the functionality DGD gave me but if I
had a point, it was that nowadays the Java world is gaining rapidly on
DGD and most things could be implemented..

Not that I would encourage anyone to do exact copy but rather to do even
better if possible, that would be called evolving! =)

Yours,
 Sampsa Ranta



From DGD Mailing List  Sun Aug 17 09:46:01 2003
From: DGD Mailing List (Felix A. Croes)
Date: Sun Aug 17 09:46:01 2003
Subject: [DGD] Java or LPC (DGD)?

Sampsa Ranta wrote:

>[...]
> > The JVM which the JDI executes on is distinct from the JVM that you are
> > debugging.  And the JVMDI interface is optional -- JVMs are not required
> > to implement it.
>
> "JVMDI clients run in the same virtual machine as the application being
> debugged and access JVMDI through a native interface. The native,
> in-process interface allows maximal control with minimal intrusion on the
> part of a debugging tool. Typically, JVMDI clients are relatively compact.
> They can be controlled by a separate process which implements the bulk of
> a debugger's function without interfering with the target application's
> normal execution."
>
> Nothing says that it is REQUIRED to run on different virtual machine..

You are correct.  The debugging API is just intended to make remote
debugging possible, but doesn't require it.

I had a look at the Sun hotspot implementation of RedefineClasses().
Currently there are the following limitations (only listing those
that impact DGD-style upgrading):

 - you cannot change class modifiers 
 - you cannot change the inherited class
 - you cannot change the interfaces used
 - you cannot change the number, names, types and order of fields
 - you cannot change the number, names, prototypes and order of methods

All of these throw "not implemented" errors.  So it looks as if the JDI
is not yet ready for class upgrading in a persistent environment (on the
reference platform), but will be in the future.


>[...]
> However the systems lack part of the functionality DGD gave me but if I
> had a point, it was that nowadays the Java world is gaining rapidly on
> DGD and most things could be implemented..

True enough.  DGD is still ahead on this issue, but there is no way I
can keep up with a team of dedicated programmers cranking out new stuff.
Fortunately, there are a few little things that make DGD unique, such
as atomic functions and (relative to Java) multiple inheritance.

Regards,
Dworkin

From DGD Mailing List  Sun Aug 17 11:17:01 2003
From: DGD Mailing List (Albert Deinbeck)
Date: Sun Aug 17 11:17:01 2003
Subject: [DGD] Java or LPC (DGD)?

Hi Felix...
----- Original Message -----
From: "Felix A. Croes"
To: DGD Mailing List
Sent: Saturday, August 16, 2003 9:20 PM
Subject: Re: [DGD] Java or LPC (DGD)?


> "Albert Deinbeck" wrote:
>
> >[...]
> 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.

After rereading the Java VW Spec I must sheepishly admit that you are
perfectly right.
This means of course that what you said about different classloaders and the
namespace problem is also true.
So each object has it's own namespace and communication has to take some
external way in order to simulate a common
namespace.
In this regard there is a question I couldn't answer yet. Classloaders
follow a delegation model the primordial CL being the root.
If classes from custom CLs access the static member of a class loaded by the
root CL, do they access the same or copies?


> >[...]
> > 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 &lt;every reloadable class&gt;, 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 &lt;every&gt; 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.

Apart from the fact that in the light of the above direct method calls
aren't feasible...
It seems the only way is to use reflection for method calls.
I thought about using the Proxy class to connect to an actual class
instance,
but that brings the requirement of an interface for the class.
I do not think interfaces should be set in stone, but they should change
less frequently to
allow for distinct names and files for them.
Interfaces are mere logical constructs required by the compiler. The VM does
not know them.
So it's easy to replace an interface as long as you make sure all classes
using it are recompiled.
You could put all classes implementing the command interface in one package
and compile them all
once CommandInterface has changed, for instance...

I would also think that java classes and world objects would not be equal,
but that there is a bunch of java
instances representing one world object. These would be serialized together
and live in the same namespace...


Regards,
Albert



From DGD Mailing List  Sun Aug 17 11:44:01 2003
From: DGD Mailing List (Par Winzell)
Date: Sun Aug 17 11:44:01 2003
Subject: [DGD] Java or LPC (DGD)?

> Interfaces are mere logical constructs required by the compiler. The
> VM does not know them. So it's easy to replace an interface as long
> as you make sure all classes using it are recompiled.

Indeed; if A inherits B and you recompile B, A must be automatically 
recompiled. Writing a system to keep track of these dependencies is one 
of the first things anybody has to do who sets out to write a persistent 
library on top of DGD.

It works exceedingly well. We have uptimes in excess of four years, and 
we rip out and replace fundamental public API's constantly. Once this 
sort of thing is in place, it's hard to imagine anything else. I don't 
see how genuine persistence beyond a few months is possible without it.

Zell



From DGD Mailing List  Sun Aug 17 12:13:00 2003
From: DGD Mailing List (Felix A. Croes)
Date: Sun Aug 17 12:13:00 2003
Subject: [DGD] Java or LPC (DGD)?

"Albert Deinbeck" wrote:

> After rereading the Java VW Spec I must sheepishly admit that you are
> perfectly right.
> This means of course that what you said about different classloaders and the
> namespace problem is also true.
> So each object has it's own namespace and communication has to take some
> external way in order to simulate a common
> namespace.
> In this regard there is a question I couldn't answer yet. Classloaders
> follow a delegation model the primordial CL being the root.
> If classes from custom CLs access the static member of a class loaded by the
> root CL, do they access the same or copies?

I believe it's the same one.  Classes loaded through separate classloaders
do share the same environment, they just don't live in the same namespace.


>[...]
> Apart from the fact that in the light of the above direct method calls
> aren't feasible...
> It seems the only way is to use reflection for method calls.
> I thought about using the Proxy class to connect to an actual class
> instance,
> but that brings the requirement of an interface for the class.
> I do not think interfaces should be set in stone, but they should change
> less frequently to
> allow for distinct names and files for them.
> Interfaces are mere logical constructs required by the compiler. The VM does
> not know them.
> So it's easy to replace an interface as long as you make sure all classes
> using it are recompiled.

As long as you don't let the wrapper class use the same interface as
the wrapped class, changing interfaces is quite possible.


> You could put all classes implementing the command interface in one package
> and compile them all
> once CommandInterface has changed, for instance...
>
> I would also think that java classes and world objects would not be equal,
> but that there is a bunch of java
> instances representing one world object. These would be serialized together
> and live in the same namespace...

Exactly.  In a manner of speaking, some classes would represent the "mud
server", and the others would be the "mudlib".  Only the latter would have
to be persistent.

As with DGD, you'd want the "mud server" part to be as small as possible,
and let everything else be handled by the "mudlib".

Regards,
Dworkin


From DGD Mailing List  Sun Aug 17 17:41:01 2003
From: DGD Mailing List (Albert Deinbeck)
Date: Sun Aug 17 17:41:01 2003
Subject: [DGD] Java or LPC (DGD)?

----- Original Message -----
From: "Tavis Elliott"
To: DGD Mailing List
Sent: Sunday, August 17, 2003 3:43 AM
Subject: Re: [DGD] Java or LPC (DGD)?


> On Sun, 17 Aug 2003 02:44:21 +0300 (EEST), Sampsa Ranta wrote:
>
> >
> > [..snip..]
> >
> > + Java VM could be used to simulate DGD driver under special Classloader
> > and compiler mapping the code to Java bytecode (maybe with proxies or
> > direct bytecode to support multible inheritance) but not vice versa.
> > DGD driver itself is smart but not too complex features to re-implement
> > on as-is base. It seems it has been written under goal like "keep it
> > simple". However such simulation would still have to be single threading
> > if it is going to be 1:1 simulation.
> > - DGD cannot simulate Java
> >
>
> To weigh in on the bytecode update issue (Java has also been my primary
> development language for quite some time) ... I would advise checking into
> the JDI interface for Java 1.4, there are mechanisms for runtime
> replacement of bytecodes that don't require horrendous fancy ClassLoader
> schemes.  Granted they are 'debug' interfaces, and there are still
> questions about introducing runtime problems, but they are easier than
some
> of the solutions already discussed.
>
> -Tavis

I took a quick look and it sounds very promising. Thank you for the hint,
Tavis!

This package allows one Java VM to be remote controlled by another and
influenced
at a very low level, down to changing bytecode on the fly, on live objects.
It can also
suspend and stop threads, set breakpoints etc...

Using this would mean to have two VM running, one running the mud and the
other
controlling the first and doing maintenance jobs like class updates etc.
This could also be a solution to the problem of badly written classes which
hang up
and never return. The second vm could notice this and
- change the class to the last state known to work
- pop the stack one step up and so simply leave the hung method
- send an exception to the hung thread

Albert



From DGD Mailing List  Sun Aug 17 19:29:00 2003
From: DGD Mailing List (Tavis Elliott)
Date: Sun Aug 17 19:29:00 2003
Subject: [DGD] Java or LPC (DGD)?

On Mon, 18 Aug 2003 00:40:16 +0200, Albert Deinbeck wrote:
>
> I took a quick look and it sounds very promising. Thank you for the hint,
> Tavis!
>

You're welcome.

> This package allows one Java VM to be remote controlled by another and
> influenced
> at a very low level, down to changing bytecode on the fly, on live 
> objects.
> It can also
> suspend and stop threads, set breakpoints etc...
>
> Using this would mean to have two VM running, one running the mud and the
> other
> controlling the first and doing maintenance jobs like class updates etc.
> This could also be a solution to the problem of badly written classes 
> which
> hang up
> and never return. The second vm could notice this and
> - change the class to the last state known to work
> - pop the stack one step up and so simply leave the hung method
> - send an exception to the hung thread
>
> Albert
>

Also note there are some huge security implications with this 
implementation.  For example I believe only Sun's Win32 implementation 
supports a SHM (shared memory) interface for the debugging connection, 
which means any other platform would require a socket connection.  Imagine 
the havoc a disgruntled player could wreak on a mud ...

The controlling "JVM" can be in the SAME JVM as the application being 
controlled ... so if you have any network control you can simply pick a 
port which is blocked by the network and use the loopback interface from 
within the same process to do the controlling.

-Tavis

From DGD Mailing List  Mon Aug 18 07:20:01 2003
From: DGD Mailing List (Albert Deinbeck)
Date: Mon Aug 18 07:20:01 2003
Subject: [DGD] Java or LPC (DGD)?

----- Original Message -----
From: "Tavis Elliott"
To: DGD Mailing List
Sent: Monday, August 18, 2003 2:28 AM
Subject: Re: [DGD] Java or LPC (DGD)?


> On Mon, 18 Aug 2003 00:40:16 +0200, Albert Deinbeck wrote:
> >
> > I took a quick look and it sounds very promising. Thank you for the
hint,
> > Tavis!
> >
>
> You're welcome.
>
> > This package allows one Java VM to be remote controlled by another and
> > influenced
> > at a very low level, down to changing bytecode on the fly, on live
> > objects.
> > It can also
> > suspend and stop threads, set breakpoints etc...
> >
> > Using this would mean to have two VM running, one running the mud and
the
> > other
> > controlling the first and doing maintenance jobs like class updates etc.
> > This could also be a solution to the problem of badly written classes
> > which
> > hang up
> > and never return. The second vm could notice this and
> > - change the class to the last state known to work
> > - pop the stack one step up and so simply leave the hung method
> > - send an exception to the hung thread
> >
> > Albert
> >
>
> Also note there are some huge security implications with this
> implementation.  For example I believe only Sun's Win32 implementation
> supports a SHM (shared memory) interface for the debugging connection,
> which means any other platform would require a socket connection.  Imagine
> the havoc a disgruntled player could wreak on a mud ...

Sure, but if you have open ports which accept connections from the world you
are
dead meat anyhow. The mud server would of course have a firewall which only
accepts socket connections on the reserved mud ports or from within the
local
network. Then the controlling VM can be on the same computer or on another
locally connected one.
>
> The controlling "JVM" can be in the SAME JVM as the application being
> controlled ... so if you have any network control you can simply pick a
> port which is blocked by the network and use the loopback interface from
> within the same process to do the controlling.

I think it's a bad idea to have it on the same vm. If the mud hangs up or
crashes,
your controller goes with it. I would rather have it in a seperate vm on the
same
computer.

Regards,
Albert

From: dgd@dworkin.nl (Par Winzell)
Date: Tue May 10 18:25:01 2005
Subject: [DGD] call_other()

So... in traditional DGD/LPC,

  object o;

  o = new_object(ARRAY_LIST);
  o->add("foo");

Meanwhile, in Java:

  List<String> l = new ArrayList()<String>;
  l.add("foo");


These codelets look similar but there's at least one huge difference
between them.

In LPC, we're sending object-name strings into functions. In Java, the
class names are part of the syntax of the language. In LPC, we won't
know until runtime whether or not ARRAY_LIST even exists, if it can be
instantiated, and if it can, whether or not 'add' exists, if it can
accept a string... etc.

Java, by contrast, will not compile this codelet without also compiling
List and ArrayList. It will refuse to compile the codelet if ArrayList
cannot be instantiated in this way, and it will refuse to compile a call
a method that's not defined in the List interface.


Of course, Java is not in competition with LPC. Java does not easily
recompile on the fly. Excepting debugging extensions and tricky
classloader stuff, a JVM is meant to be restarted for code changes to
take effect. DGD in contrast offers genuine persistence natively and
in-place recompilation is one of the most basic tools we need to keep
our decade-long uptimes sane.

So there is no virtue in attempting to become as Java-like as possible.
That said, the additional typechecking available to the Java code above
is invaluably pleasant. Wouldn't it be nice if we could get both? Let's
see how far we could get if we were to launch into this insane project.

 - We'd definitely have to redefine compile_object(), sending supplied
LPC source through a parse_string() grammar of our own making. We mostly
collect meta-data during compilation, but also enforce restrictions, as
well as modify the syntax of the LPC we accept.
 - Delete call_other() completely, and do not replace it. We accept the
-> operator on string constants and object-type objects only. The name
of the function to be called can no longer be variable. As in Java
(ignoring reflection), it must be explicitly and statically part of the
source code.
 - Just as we currently store inheritance dependency information for
every program we see compiled, we're going to have to now store type
information for every function of every program and even every argument
of every such function.
 - During parsing, tracks the static type of every value, including
return values from other functions (using DB mentioned in previous
line). If we encounter code like,

  void kill(object LIVING foo) {
    foo->die();
  }
or perhaps
  void create() {
    VIRUS_DB->register_new_infection(this_object());
  }
and we're parsing the function call, LIVING/VIRUS_DB must already have
been compiled at this point. We either error if it hasn't, or we force a
compile ourselves.... not sure. Then look up e.g. 'die' for LIVING in
the DB, (error if it doesn't exist), and type-check each argument as far
as we're able and willing.

When we recompile a program P now, we calculate a set of other classes
that have to be recompiled/destroyed because they depend on P through
inheritance. We'd need to extend this to inter-object call dependencies
as well. Thus recompiling LIVING or VIRUS_DB would trigger a recompile
of the codelets above.

If there's one real problem, it's the fact that DGD's preprocessor is
not exposed, and unless we implement the entire damn thing ourselves, we
never get to see the LPC in an expanded, post-preprocessor state. There
might be some way to hack around this, but it's very frustrating.

So, anyway, apart from that. It's a bit of work to be sure, but can
anybody shoot holes in this? It looks to me like it could work, and we'd
effectively have static compile-time type checking of inter-object
function calls, without sacrificing any of DGD's vital recompilation
abilities.

The way we write LPC would need to change, of course. We're quite used
to being able to call non-existant functions on objects, allowing those
objects to implement the functions if they should. We're used to doing
things like filter_array() and map_array(). Perhaps losing the ability
to call variable functions without having something quite like Java's
anonymous classes is too big a loss. But tackling anonymous classes is a
subject for another day... :)




Thoughts? Am I insane? :)

Zell