(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.