(this doc converted from .rtf to XHTML by danbri; see Momoko site for more info)

Momoko:

An Architecture for Distributed Applications

Brandon Wiley
TC 660H
Plan II Honors Program
The University of Texas at Austin
May 4, 2001

Margaret Syverson
Department of English
Technology, Literacy, and Culture Program
Supervising Professor

Darren Cambridge
Department of English
Technology, Literacy, and Culture Program
Second ReaderABSTRACT


Author: Brandon Wiley
Title: Momoko: An Architecture for Distributed Applications
Supervising Professor: Margaret Syverson
The Internet is filled with a countless number of protocols,. file formats, programming languages, and applications which by and large fail to interoperate. Computer users are tied to the particular user interfaces of the products which contain the logic they need to manipulate their files. Applications and files are tied to the particular machines on which they are used. Users are alone, typing on a word processor at two in the morning, with only the occasional e-mail to remind them of humanity. This is not the utopian vision of cyberspace that we dreamed of.
Momoko is an architecture for developing network applications. It is designed with transparency, orthogonality, and interoperability in mind. It provides services for transparent persistence, stubless distributed objects, and an object-oriented virtual file system. It provides advantages that existing approaches to these subject do not provide, such as full transparency to the developer and runtime mechanisms which need no compile time preparation. Momoko bridges the gaps between MUDs, application servers, and web servers. Several sample applications have been developed for Momoko: an IRC client, a portable desktop environment, and a threaded discussion forum. These applications derive unique benefits from Momoko's orthogonal design and transparent services.
Through Momoko, the sea of countless applications and protocols can be bridged. The computer resources of the world can be brought together into a consistent, network-accessible, object-oriented representation. Diverse protocols can be made to interoperate transparently. Applications can be transparently made multi-user and network-aware. Through orthogonal design and standards compliance, the utopian ideal of cyberspace can be realized out of the multitude of proprietary pieces which currently make up the Internet.
Momoko: An Architecture for Distributed Applications


I. Introduction
In 1983, William Gibson published the novel Neuromancer and first coined the term "cyberspace." Within his dark tale of rotting technocratic dystopia was a vision of where computers could take us, a bright land of flashing neon virtual reality, iconic metaphors, and zipping information. With Gibson, we dreamed of a world where information had become a tangible object, a parallel universe with its own physical laws and limitations. This vision has spawned dozens of similar dreams, from the game-like "Metaverse" of Neal Stephenson's Snow Crash to the desert of the real in "The Matrix."
The Gibsonian vision of cyberspace has been a particularly popular one. It envisions all of the computers of the world connected via a global network. All of the computers and all of their files, programs, and processes are available to anyone in the world if you have the proper access. Everything is represented tangibly in a three-dimensional virtual reality environment and users intuitively interact with programs using gestures. When programs run, they have appropriate visual metaphors to describe what they are doing.
This is a fascinating vision of how computers should work; its aesthetic allure has enthralled many young computer programmers. In the year 2001, however, this vision has strangely still not been realized. Computers are, in fact, not sexy or aesthetically alluring at all. They are inflexible, time-consuming, frustrating devices which consistently confuse people and fail to give the desired results. There aren't even any useful three-dimensional interfaces. The state of the art in computer interface design seems to have stopped at the desktop with skinnable application windows and menus. Why has Gibson's cyberspace failed to emerge?
It is enlightening to look at the wide proliferation of dissimilar environments that are currently referred to as "cyberspace," even though they are very different from Gibson's vision. Some of them are textual, some graphical, and some even three-dimensional.
The World Wide Web
The contender that perhaps falls the farthest from Gibson's vision is the World Wide Web, though it does at least have a couple of similarities. First of all, the web is a network of computers that are reachable anymore in the world. Secondly, the contents of these computers are visible to anyone who has the proper access. However, the web is a medium of consumption, not manipulation. The majority of the resources on the web cannot be controlled through the web, only viewed. In effect, the web is a read-only space. Some places in the web can be modified, such as discussion forums and Wiki sites, but the interactions are very limited. While data can be added to the sites and sometimes even edited, the algorithms for displaying the data are usually unchangeable. Also, almost all of the web's representations are textual. While graphical, auditory, and even three-dimensional data can be represented and displayed, these serve primarily as annotations to largely textual works.
Finally, the majority of the resources of the world's computers are not viewable or manipulatable via the web. While the odd wired vending machine does occasionally appear, the web is not usually dynamic. So-called "dynamic" sites are generally just mechanisms for rearranging static content into a personalized display format.
MUDs
One of the most exciting technologies in network programming has been the MUD (Multi-User Dungeon). A MUD is a multi-user network application in which users communicate with each other and build a persistent virtual world. MUDs started out simply as games. People would build fantastical settings, beings, and objects for other users to interact with. However, as MUD technology became more advanced and computers more powerful, people began to build real functionality in MUDs. In the hey-day of MUD programming, it was possible to check your e-mail and get weather reports inside of these text-based, artificially constructed realities.
MUDs had some interesting characteristics. Since they ran on other people's computers, they were sandboxed so that players could affect things in the MUD, but not the underlying operating system. Also, the persistence of the objects on the MUD was dealt with entirely by the MUD server. Users could just assume that if they created an object then it would still be there tomorrow. All modifications to the environment happened dynamically on a live system. On more advanced servers such as MOO (MUD, Object Oriented) all modifications to the system could be made within the system. The programmer never had to leave the context of the application in order to work on developing the application.
MUDs never overcame their own history as games. This was for a number of reasons. Real applications could be coded using MUDs - for instance, a web browser was implemented in MOO. However, MUDs had some unfortunate disadvantages. They used, for the most part, programming languages invented for the specific MUD engine. They also stored objects in proprietary database formats. In general, they failed to interoperate with standards. As such, they were only useful for writing niche applications which, historically, were text-based games.
Application Servers
The entities which produce an environment most like Gibson's conception of cyberspace are actually the "application servers" that run a large number of dynamically generated web sites. Contrasting with the external web sites themselves, the inner workings are often object-oriented persistent databases dynamically distributed over multiple machines. Just add a visualization engine, and Gibson's network of machines and objects with information zipping about this way and that is suddenly realized.
Unfortunately, application servers are a very small proportion of the world's software. The majority of home computers don't run applications that are structured in the way that application servers are. Rather than dynamically loading, manipulating, and storing objects and moving them between machines, most computers deal with monolithic applications that only manipulate file data in very specific, inflexible formats. Application servers are only used in heavy-use industrial/commercial settings, however, because only under heavy loads and large applications do dynamic, modular, orthogonal programming techniques become entirely necessary for the application's functioning.
As an environment to write applications in, application servers also have some disadvantages. Though in general they use real programming languages to code objects and interoperate with standards, the programmer must still often follow special conventions in order for applications to be compatible with the particular application server. These conventions are often incompatible with other application servers, making code specific to a particular server. Also, developing an application for an application server requires leaving the context of the server to edit files and compile them. Once completed, the application server must be launched once again from the local filesystem.
I propose that the programming models of application servers and MUDs be combined to create a powerful but easy-to-use dynamic application environment into which existing applications can be easily ported. By increasingly linking existing applications and protocols into a modular, extensible framework, a Gibsonian cyberspace can be realized from the current state of Internet-connected personal computers.
II. Momoko
Momoko is a framework for developing multi-user Internet applications. It bridges the gaps between the various existing types of products which produce cyberspace-like environments. It can be considered as an Enterprise Application Server or a new and improved MUD server, depending on individual preference.
Momoko imports information that exists on the Internet into a distributed, object-oriented, virtual filesystem. The information can then be exported to the Internet in various ways. Momoko currently ships with modules to import IRC channels and MH-style mail archives, so both chatting and checking e-mail are possible via Momoko. Momoko also ships with a MOO-like command set for interacting with the object tree, as well as a fully functioning web forum for reading mail archives and posting replies.
Overall, Momoko is a framework for writing object-oriented network applications. At all levels, Momoko follows some simple design principles. First and foremost, orthogonality is key. All of the services that Momoko provides to application writers are optional. Making an object work with Momoko's services does not require that you Momoko services in mind when you design and implement your objects. Second, conformance to standards is important. Whenever there is a standardized solution to a problem, it is used rather than inventing a replacement. For instance, the objects are coded in Java instead of in a special Momoko language. Further, everything is editable on a live system from inside the system itself. Commands are provided for manipulating objects and extending classes from inside a Momoko server so that accessing the filesystem and editing the source files is not necessary. All changes are automatically reloaded into the running Momoko server dynamically. Lastly, everything is an object. Momoko applications never have to deal with files, relational databases, or protocols. These are all presented to the Momoko applications as objects.
Services
Momoko provides a number of useful services for writing applications. It features an object-oriented virtual filesystem for organizing your objects, transparent distributed objects for splitting applications over multiple machines, and transparent persistence of your objects. These services are orthogonal to the rest of application development, and can be used or ignored by the application developer. However, some form of each of these services is needed for the construction of a networked application. Therefore, Momoko attempts to offer reasonable, useful, and fully orthogonal implementations of these services.
A. Object-Oriented Virtual Filesystem
The core of the Momoko system is the object tree. It organizes objects in the application into a hierarchy of objects containing other objects. Any object in the tree can be found by specifying a path through the object tree using the names of containing objects. For instance, the path "/world/irc/irc.openprojects.org/#momoko" might resolve the object representing the IRC channel where people talk about Momoko. This path is somewhat analagous to a URL on the World Wide Web or a path in a filesystem. The difference between the Momoko object tree and a normal filesystem is that the entities contained in the Momoko object tree are not files, but objects. As objects, they have methods, fields, access restrictions, subclasses, and superclasses.
B. Distributed Objects
The most important aspect of a distributed, object-oriented system is a mechanism for distributed objects. There are many approaches to this problem. Unfortunately, all of the freely available distributed object mechanisms which work in Java were insufficient for the needs of the project, and a new distributed object mechanism layered on top of more primitive mechanisms was created. It will theoretically work on top of any of the mechanisms discussed below, although only RMI has been implemented.
RMI
Sun's own solution to the distributed objects problem is RMI (Remote Method Invocation). It comes bundled in many Java implementations and uses Java's built-in serialization object mechanisms to transport objects between machines. Calling remote methods is done using a local proxy. This local proxy is generated using a command line tool, rmic, the RMI compiler. When run on a Java class, it produces a "stub" class which can be used remotely in place of an actual instance object.
RMI has a number of problems. First of all, you must write your objects specifically to be RMI-aware. For an object to be used remotely, it must extend the Java.rmi.Remote interface and be exported by a method call to the object exporter. You must also run the rmic tool on the class file and then transfer the stub classes to the client. Remotely, you can only refer to objects by the interfaces that they implement, not by their actual class. Therefore, you must define an interface with all of the methods which you want to be able to call remotely. Additionally, you can only access an object's methods, not its variables. This means that you must implement getter and setter methods for all variables which you want to be able to access remotely. Worst of all, RMI uses a closed protocol and so remains Java-specific. There are no implementations of RMI in other languages. Even if you wanted to, you couldn't use just RMI for a multi-language project.
RMI lacks two fundamental concepts in its design: transparency and orthogonality. Using RMI requires that you specifically write your classes to be RMI-aware. For this reason and because it does not support communication with languages other than Java, it is not an appropriate choice for this project.
CORBA
CORBA is a multi-language standard for distributed objects. Interoperable implementations are available in the majority of serious languages. CORBA's main problem is that it is very large and complex. Therefore, it does not scale down well. It is suitable for large-scale enterprise systems, but it is unreasonable to make a CORBA implementation for an embedded system. Therefore, it cannot be the only distributed objects mechanism used in a network which partially consists of small devices. CORBA also shares some of the problems of RMI. Making Java objects remotely callable via CORBA requires that the objects implement a specific interface. A stub must also be generated using a command line too. In this sense CORBA lacks transparency and orthogonality as much as RMI does, although it is at least multi-language.
XML-RPC
XML-RPC is a multi-language standard for remote procedure calls. It has massive deployment in 21 different languages and scales well to embedded devices. It does not require stubs and objects do not need to be aware that they are being exposed to remote calls. If you want an object to accept remote calls, you register it with the XML-RPC server with a simple method call. XML-RPC lacks all of the problems of both CORBA and RMI, but unfortunately it has its own set. XML-RPC does not support objects, only a small set of primitive types such as strings, integers, and lists. Therefore, only methods with compatible argument and return types are callable remotely. This is unacceptable for a distributed objects system because many methods take objects as parameters. An ideal solution for a distributed objects system would be to extend XML-RPC to support objects. This would fix its primary problem while still maintaining all of its advantages. Unfortunately, this is outside of the scope of this project.
SOAP
SOAP is the successor to XML-RPC, although it does not supersede it. Like XML-RPC, SOAP is multi-language and works with objects that have not been coded to be SOAP-aware. Unfortunately, SOAP does not have the massive deployment that XML-RPC does, as it is newer and much more complicated. SOAP suffers from similar problems to CORBA in that it is too complicated to scale well to small devices. Additionally, SOAP requires a form of stub generation. The stubs in this case are not Java classes as with RMI, but rather files describing a mapping between XML elements and Java objects.
Straw
The distributed objects mechanism that was finally chosen is a novel mechanism designed specifically for this project. It supports fully orthogonal design and entirely transparent distribution of objects. Remote distribution of objects is handled with an entirely runtime mechanism. Unfortunately, it does not work with arbitrary objects. This is because no entirely runtime mechanism for distributed objects can work with arbitrary objects. Because reads and writes to instance variables can be trapped only on the JVM level, only a modified JVM can allow for remote access to instance variables. This means that the only remote access available to runtime distributed objects mechanisms are method calls, since methods calls can be intercepted by using proxy objects.
The Mechanics of Straw
Straw works on top of a primitive RPC mechanism. Theoretically, it can work on top of any primitive RPC mechanism that is sophisticated enough to support object serialization. It should, in theory, work on top of RMI, CORBA, and SOAP. Currently, it has only been implemented over RMI.
For simple objects, such as strings and integers, Straw uses the underlying RPC's mechanism for object serialization to serialize the object and send it. In this sense, it is merely invoking the underlying RPC. The underlying RPC also does all of the work of managing connections and over-the-wire protocols. Straw only comes into play when a complex object is used as either a parameter or return value. A complex object is one that cannot simply be copied and sent over the wire. In a sense, Straw is simply a pass-by-reference mechanism on top of standard pass-by-value mechanisms. When a complex object is encountered, Straw produces a Straw reference object. This object contains a list of interfaces that the original object implements, a unique identifier, and the host on which the object resides. This reference object is then passed over the wire using the underlying RPC mechanism. It is detected by the Straw broker object on the other side of the wire and used to instantiate a Straw proxy object. The proxy object implements all of the same interfaces as the original object. However, whenever a call is made to the proxy object, it uses the local Straw broker object to make a remote call to the original object.
Since Straw proxy objects implement the same interfaces as the original objects, they can be used like local object instances. The actual location of an object is entirely transparent to the programmer. The only thing that an application developer must do in order to use remote objects is to establish an initial connection to a remote system. This is done by creating an object in the local object tree that acts as a gateway to an object in the remote object tree. This procedure is similar to remotely mounting a directory using NFS.
C. Persistence
An important element of an application server is that the data which it manipulates and displays is maintained over time. The application server therefore needs a mechanism to store information on disk. A number of approaches to this problem have been tried, even involving a short-lived series of Persistent Java conferences. However, all of these approaches have unacceptable requirements or constraints.
Persistent Virtual Machine
The modified virtual machine approach is tempting because it frees the programmer from having to deal directly with persistence issues. By using a persistent virtual machine, all data, including threads, is automatically made persistent. The problem with this approach is that it requires a special virtual machine. As new versions of the reference Java Virtual Machine come out, the persistent virtual machines must be constantly updated to be kept in sync. Were Sun to offer a persistent flavor of the reference virtual machine, this might be a viable solution. However, as the modified JVMs are manufactured by private groups, it is infeasible for them to keep up with the newest versions of the virtual machine on all supported platforms.
Enhanced Bytecodes and Source Modification
Among proprietary persistence mechanisms, a popular approach is to run a processor on either the source or the compiled Java classes, thus modifying the behavior of methods so that they call the appropriate methods to enable persistence. The drawback of this method is that it requires command line processing tools to be run on each class. Additionally, it does not allow for migration of objects from one persistence mechanism to another. The method by which classes will be persisted is set when they are processed.
Life-cycle-based Serialization
Enterprise Java Beans, Sun's solution for enterprise-level business logic application servers, implements a third kind of persistence. EJB only persists objects that conform to the EJB component model (also called EJB). The components must implement "life-cycle" methods which are called at various times by the EJB application server. For instance, there is a method that is called on an EJB right before it is persisted, right after it is persisted, right before it is read back into memory, right after it is read back into memory, and so on.
The problem with this method is that it does not persist arbitrary objects. It only persists EJB. As such, all objects which are to be persisted must converted into EJBs. This goes against the idea of persistence being orthogonal to the rest of object implementation. Moreover, it requires that developers commit to a single API for persistence.
Transparent Persistence with Containers
The ideal solution to persistence would be once in which multiple backed persistence stores could be used, such as persistence using a relational database, flat files, or XML documents. These methods of persistence, and in fact persistence in general, should be transparent to the programmer. Any pre-existing objects should be able to be made persistent at a whim with a method call. The persistence layer should exist entirely as a runtime API.
Since Momoko provides easy organization of objects into a tree, the most natural form for the persistence API to take is that of persistence containers. A persistence container is an object which takes care of the persistence of all of the objects it contains. The simplest way that this could be used would be to make the root object of the entire object tree a persistence container. That way, all objects in the tree will be automatically persisted. However, it is also possible to have several different persistence containers in the tree that persist objects differently. One could use Java's built-in serialization, another could use XML serialization, and another could persist objects to a relational database.
Implementation Details
The initial backend persistent store used was Java's built-in serialization. However, this was found to have a difficult technical problem. Java's builtin serialization has a default behavior of not loading objects whose classes have changed since they were serialized. It is possible to implement custom serialization on an class so that its instances can be loaded across different versions. However, this will not allow for arbitrary objects without modification to be persisted across versions, making it obviously unsuitable for a highly dynamic development environment.
Among the alternative persistence methods available, the majority of them could only serialize certain objects or certain parts of objects. They either require objects to inherit from a particular class or implement certain methods, or else they cannot serialize a variety of data, such as private variables, variables without getter and setter methods, and so on. The remaining methods require some external configuration on the part of the user, such as creating a mapping file that details how variables on an object should be translated onto fields in a relational database. This is orthogonal, but does not lend itself to spontaneous networks of machines with different classes because specific work must be done to make each class persistent.
The only freely available package for orthogonal persistence that is fully orthogonal and spontaneous was JSX. JSX is a finely crafted library for serializing arbitrary Java objects to XML files. It can serialize all parts of any Java object, including supposedly "impossible" objects such as those lacking a constructor without arguements and those with private variables. Objects can be persisted across changes in the classes. Serialization is entirely orthogonal to the rest of object implementation and is done entirely through a runtime API. JSX provided the ideal backend for the system. All that was left was implementing a convenient frontend.
There are two basic approaches to container-based persistence. The simplest is to have the container periodically persist all contained objects based on a naive scheduling algorithm. Objects are written out whether they have changed or not. This approach is very simple to implement, but can take up too much processing power if the contained object tree is very large. This is especially the case when the root object is a persistence container, as the entire object tree must then be persisted. This is exactly the scheme that MOOs and other MUDs use to persist their object database. There is a daily checkpoint at which all activity on the MUD would stop as the entire database was written to disk. This can sometimes take over an hour. Whether this is an acceptable solution depends on the particular application. It has been deemed acceptable on a MUD because players can easily spend an hour on one of the other MUDs that they play. For an application server running a dynamically generated web site, such an outage would be unacceptable. This scheme also has a disadvantage in that if the application is terminated in between checkpoints, all changes in the database since the last checkpoint are lost.
The second approach to persistence is similar to the approach that byte-enhancing persistence schemes usually take. In order to minimize disk reads and writes, objects are written out whenever their data is changed. This also means that scheduled writes of the whole object tree are unnecessary and that unexpected termination of the program won't lose data. These are very attractive qualities in a persistence mechanism. Unfortunately, this is very difficult to do orthogonally, which is why this approach usually utilizes byte-enhancement to modify the classes to write out their data whenever they are is modified. Luckily, it can be done at run time using proxy objects.
Proxy objects are similar in purpose to byte-enhancement except that they are a runtime feature. Making a proxy of an object allows you to intercept all calls made to an object's methods and to insert code before and after the method call. This is useful for persistence objects because it allows for methods to be modified to call the necessary persistence methods without modifying the original class. This allows for arbitrary objects to be made into objects which persist themselves when their data changes at runtime.
The role of the container in this scheme is to wrap contained objects in proxies. In order for the scheme to work, the methods must be called on the proxy instead of on the original objects, so that the persistence code will be executed during the method call. However, it is a bit trickier than this. Such a method will persist any changes to the objects contained by the container, but will not persist changes to objects referenced by these objects. If all objects were wrapped in persistence proxy objects, then this would not be problematic. However, in an environment of arbitrary objects, this will not be the case. An object is responsible for its own instance variables and could in all probability set one of its instance variables to an object that has not been wrapped in a persistence proxy. For this reason, an additional level of intervention is needed. Each method call to a persistence proxy must check the return value. If the object being returned is not wrapped in a persistence proxy, it must be wrapped before it is returned. Since the only way to get to referenced objects is to call methods on the referring objects, all objects fetched through the persistence container through any number of levels of indirection will still be wrapped in persistence proxies.
After all of the theoretical issues were explored and an implementation designed, this admittedly interesting and useful scheme was deemed too complex for this project, and it was abandoned for the much simpler periodic writing method. Hopefully, the theoretical persistence proxy method can be implemented in the future.
III. Applications
Developing an IRC Client
The first demonstration of Momoko's power as an application server was to write an IRC client using Momoko's unique capabilities. This project was divided into a number of orthogonal modules. First of all, some core classes were developed to handle MOO-like rooms. A room has a few special properties from other objects. It has hooks for sending events to all of the objects which the room contains. It also has a settable title and description and configurable navigation links between itself and other rooms. These properties are all that is necessary for a text-based virtual reality environment which users can navigate through and interact with each other in.
In Momoko, orthogonality is a primary design principle. Therefore the concept of a room was broken down into separate orthogonal parts with the interfaces separate from the implementations. An interface and reference implementation (in MOO terms, a "generic" object) were developed to handle the management of events between room objects and the objects they contained. Then, an IRC-specific room implementation was created to act as a gateway for messages between an IRC channel and a Momoko room.
In order to maintain IRC channels in a meaningful object hierarchy, a general IRC object was created to contain the IRC-related object hierarchy. Instead of containing a normal list of contained objects which is added to when objects are moved into the container, and deleted from when objects are moved out of the container, the general IRC object has virtual contents. Any reference to an object contained inside this object is assumed to be referencing an IRC server. When an IRC server is referenced, a connection is made to that IRC channel, an IRC server object is created to maintain that connection, and the rest of the path is passed to that object. Likewise, IRC server objects assume references to their contents to be references to IRC channels and so regenerate IRC channel objects on demand. So, for instance, attempting to resolve the path "irc/irc.openprojects.net/#momoko" will cause the automatic generation of a connection to irc.openprojects.net and an IRC channel object representing the "#momoko" channel to be generated. The IRC channel object will assume any object moved into it to be a user wishing to communicate on the given IRC channel. So, moving your user object to the location above will automatically make a connection to the IRC server and create a user with your name in the "#momoko" channel.
Using Momoko as an IRC client has some interesting effects. First of all, there is no scripting language as other IRC client have. Rather, scripting is done in normal Java and is compiled into standard Java classes. Thus, the full power of Java is available for scripts. Also, writing bots is trivial as any Java object which implements the correct methods to receive messages from a room and which is put in an IRC room will be a fully functional IRC bot. Also, anything which can normally be done in Momoko, such as reading e-mail or programming Java objects can be done in the same interface as using IRC.
Developing a Portable Remote Desktop
The second demonstration of the power of Momoko was the development of a portable desktop environment. Just like your normal Windows, Gnome, or KDE desktop, the TeaTray desktop environment displays a pretty background and icons for various applications. Icons can be moved around the screen, and clicking on one launches the associated application.
The TeaTray desktop, however, is unique in several ways. First of all, it is portable. To load the desktop, you download and run a small Java loader program. It connects remotely to a Momoko server that hosts your desktop and uses Straw to remotely mount your desktop as part of the local object tree. Then, it displays the desktop portion of the local object tree in the standard desktop visualization metaphor, a tiled background overlayed with icons.
TeaTray should work on any program that has a working Java Virtual Machine installed. Unfortunately, it is also necessary that programs running under TeaTray are written in Java. This is not strictly necessary. It would be possible to download actual applications and run them locally. However, doing so would lose all of the benefits that applications receive by running in the TeaTray environment. Also, it would be a security risk to the host computer because downloaded applications would not be controlled by the Java security sandbox. With pure Java applications, however, a remote desktop can be loaded and fully interacted with without access to the host computer's underlying filesystem. This makes it an ideal environment for spaces such as university computer labs, in which each user wants to access their own files but it is necessary to ensure that the computers are not compromised.
Since TeaTray applications are just Java objects, they are executed by downloaded the associated class from the Momoko server, defining it in the class repository, and then instantiating it. This has a particularly interesting effect because of Java's class loading system. Classes are only loaded when they are used. Therefore, a large application run on a local TeaTray desktop will not download all associated classes, only those that are used right away. As the user invokes different functions of the application, the necessary classes will be dynamically downloaded. This allows for large applications to consume only as many resources as they need, diminishing the problem of cumbersome applications running poorly on thin client devices.
Modifying a pre-existing Java application to run in the TeaTray desktop environment is very easy because of TeaTray's orthogonal design. No special conventions need to be followed to for an application to be a TeaTray application. However, certain conventions do need to be followed in order to act properly as a mobile application, which all TeaTray applications happen to be. Certain actions, such as killing the virtual machine when the application is done executing, or assuming that there is only one instance of the application running, are not appropriate since all applications run inside the same virtual machine. Also, it is unwise to assume access to the filesystem on which the application is currently running. Running the application on many different machines means that files stored on the machine in which the application runs the first time may not be accessible when it runs a second time as it may be running on a different machine. Sometimes the filesystem won't be accessible at all due to security restrictions on the host system. Such conventions must be followed for any mobile application in any environment. TeaTray applications do not have to follow any special conventions on top of these in order to function in the specific TeaTray environment.
Even though only minor tweaking is necessary to make a pre-existing application run as a TeaTray application, slightly more tweaking will provide a multitude of benefits to the application. For instance, by replacing the names of the Java I/O classes with the names of the Momoko replacement classes, an application can have instant access to the remote filesystem of the Momoko server. As a demonstration of this, an off-the-shelf Open Source Java text editor, Eternity, was rapidly converted into a TeaTray-aware text editor. It is now capable of browsing, open, editing, and saving files on the remote Momoko server. This makes both your applications and your files accessible from any computer. They can be edited just as if they were local - access to the actual local filesystem is not required.
Developing an Online Discussion Forum
The third demonstration of the power of Momoko as an application server was the development of a web-based threaded discussion forum. An application like this would normally be developed in Perl, PHP, or JSP. Momoko made it easy to implement multiple interfaces and multiple backends.
Orthogonality always being a primary design principal, the application was broken into several parts. In order to minimize complexity and optimize for interoperability, a standard message archive format was chosen for the backend storage of the messages. The format chosen was MH, a standard format for storing e-mail messages. The messages are formatted as RFC822-compliant mail messages stored in a directory with enumerated file names.
A module was developed to import MH mail archives into the object tree. The JavaMail API and an off-the-shelf MH archive reader were used for all interactions with the message archives. The module merely implemented a container API for accessing the underlying JavaMail folder API, allowing existing Momoko objects that could already interact with the container API to be used without modification.
A wrapper object was created to allow for threaded access to a mail archive. The wrapper object, when created, looks through the contents of the source object and makes soft link proxies to those objects. It then puts these soft link proxies inside each other, forming a hierarchal object tree with an organization based on the threads of the messages.
The threaded message trees were then organized into a further hierarchy through category containers that hold different groups of mail archives based on similar topics. These category containers were placed in an overall container for holding the discussion forum's entire message contents in a hierarchal form.
Three interfaces were created for the creating and viewing of messages. First was a mailing list interface. Since the messages are stored in MH archives, there is already a good deal of software written to deal with them. A mailing list manager, qmail, was installed and configured to add received messages to the various MH archives for the different topics. Anyone wishing to receive forum messages via e-mail can use the standard qmail mailing list subscription feature, making the forum is a fully functional mailing list as well as a web forum. Additionally, several MOO-like commands were added for reading messages while logged in via a telnet connection. The command-based interface for reading mail was copied almost exactly from the command set in the popular Jay's House MOO core database (JHCore). Users familiar with this command set from prior MOO experience should find reading mail on the forum particularly comfortable. Finally, a web-based interface was created to make the forum software on par with the currently popular web forum software packages.
Since orthogonality and the use of standards is a key design principle of Momoko applications, the web interface was created using Java servlets and a production servlet server was used. It might have been useful to integrate a servlet server into the Momoko framework itself, seeing as how it already contains a web server. However, using a production servlet server demonstrates the capabilities of Momoko's distributed objects system, as well as how Momoko can be used as an application server in a production setting.
Instead of using JSP to produce the HTML for the web interface, a templating package, WebMacro, was used. Using a templating package meant that the logic and presentation layers were separated. It also fulfilled the Momoko system's design requirement that everything be editable at runtime. WebMacro allows for templates to be dynamically reloaded automatically whenever they're changed.
The rest of the process of writing the forum application was simply the implementation of the various servlets. One servlet was created for each page that would be displayed to the user. All of these servlets were made subclasses of a single generic forum servlet. This master servlet class provided all of the necessary logic for connecting to the Momoko instance containing the persistent store of threaded messages. Both the message store and the user database were connected to using Straw. A local proxy to a remote object containing the relevant parts of the remote object tree was created. The servlets were then written as small and transient Momoko instances, with the majority of their information being stored remotely. This is somewhat analogous to thin clients which boot off of the network using a shared NFS drive. The difference is that Momoko uses objects instead of files. As such, the data it exposes to remote machines is fully dynamic and addressable via a programmatic API. Using this facility, the servlets manipulate and display the forum's state, automatically updating as changes occur to the object tree.
Implementing this forum software in Momoko instead of in the traditional way allows for some interesting benefits. First of all, it allows for transparent replacement of the backend. MH archives could be replaced with mbox archives, a relational database, or a proprietary archive format without any other changes to the software. Also, it allows for multiple frontends. It is currently accessible via the web, telnet, and e-mail. Adding support for other interfaces such as WAP, gopher, or IRC is quite easy because the display code is orthogonal to the logic which manipulates the representations of information.
Conclusion
Momoko has been a joy to work on. Due to its orthogonal design, adding features causes a blossoming effect. Adding a module for mail archives allows you to read mail. Adding a module for IRC allows you to chat. Having both at the same time allows you to both chat via e-mail and read e-mail via IRC. Writing multiple applications in the same server also has some beautiful possibilities. Using Momoko as my IRC client and as the server for the forum software allows me to see who's reading the forum archives without leaving the context of my IRC client. Using the TeaTray desktop environment I'm able to edit my files in a normal editor at home, at my girlfriend's house, and in two different computer labs without having to constantly transfer them between machines and then wonder where the latest version is and which machine contains my late night annotations from sometime last week. I'm even able to modify the environment I'm working in through the environment itself, so I don't have to leave IRC in order to make changes to my IRC code.
The develop of Momoko thus far as only touched on the power of this approach. This phase of the project was only the most preliminary steps. Momoko now supports a variety of frontends and backends. However, little work has been done on manipulating the object tree which exists in between the frontends and backends. There are lots of exciting things that can be done. The object tree representation of information lends itself to a variety of manipulations, such as aggregations, filters, and annotations. These are various transformations which can be applied to an object tree. An example of a filter, for instance, is to create an object tree which was a mirror of another object tree, only lacking objects with names starting with "Bob". This would be a way to, for instance, write a trivial bit of code that would hide messages to you from Bob without actually modifying the messages in your archive. An example of an annotation would be to attach a note to an e-mail marking it as something you should reply to the next day. In Momoko, this would be easy to do, and would require no modification to your underlying message store. There are many possibilities for these approaches which need to be explored.
Hopefully, the Momoko project can continue into the future. As more backends, frontends, and transformations are added to its library, it will become an ever more useful tool for developing applications of all kinds. Constantly adding modules for interoperability with other systems via standard protocols will make it an indispensable tool for bridging the gaps between the increasingly diverse world of Internet protocols and file formats. With some luck, Momoko can bring the patchwork of proprietary and standardized systems that populate the Internet into a usable consistent paradigm, making a Gibsonian cyberspace someday possible.Brandon K. Wiley was born in Austin, Texas on October 26, 1978. After two years at Round Rock High School, he moved to Beaumont to attend Lamar University's Texas Academy of Leadership in the Humanities, a dual-enrollment high school/college program. He enrolled in the Plan II Honors program at the University of Texas at Austin in 1996, with a second major in the Convergent Media program. There he co-founded the Plan II literary magazine Offerings to the Void and participated heavily in Plan II's Broccoli Project theatre group. In his time with the Broccoli Project, he wrote and directed one full-length play, had two shorter works performed, co-starred in a production of Waiting for Godot, and did various theater tech and set construction. Through the Convergent Media program, he also produced several experimental short films and performance art pieces. Outside of the university, he co-founded the Freenet Project, an international volunteer effort to implement a censorship-resistant, decentralized file distribution network in the hopes of creating a censorship-free alternative to the World Wide Web. His essays appear in Peer-to-Peer: Harnessing the Power of Disruptive Technologies and in Linux Journal magazine. He graduated with a B.A. in Plan II and a B.S. in RTF in 2001 and plans to apply to M.I.T. and UC Berkeley for the fall of 2002. Mr. Wiley will spend the next year attending conferences and festivals, working on a new play, and lying in the grass with his girlfriend.