Phantasmal MUD Lib for DGD
|
Phantasmal Site > DGD > Java vs DGD Java vs DGDThis 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 <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. 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 |