I'll limit my endeavors this week to creating a distributed-object system with the ability to do something actually exciting in next week's article. |
Deep objects and distributed magic
What better way to introduce the topic of distributed objects than to implement a distributed-object
system4? Since I don't exactly have time to write a
full-fledged, general-purpose distributed-object system -- and I certainly
don't have time to explain one in detail -- I'll limit my endeavors this
week to creating a distributed-object system with the ability to do
something actually
exciting5 in next week's article.
The architecture of this system is relatively simple. A server runs on a
machine (the one that served you this article, in fact) and manages all
the objects. Clients connect to that machine and register as subscribers
to those objects. Whenever a change is made to an object (by any of the
clients or by code running as part of the server), an attribute-changed
event is broadcast to all the subscribers of that object. The clients
automatically update their local representations of the object and
broadcast the attribute-changed event to any local subscribers.
Let's take a look at some excerpts of the code for a clearer understanding of what all this means:
public class DObject { public int getValue (String name, int def); public void setValue (String name, int value) throws IOException; public String getValue (String name, String def); public void setValue (String name, String value) throws IOException; // getting and setting arrays of int and arrays // of String is also available }Since we don't yet have support for Java Object Reflection the DObject has to provide specialized member-functions for getting and setting values. When a value is set, it automatically informs its local object manager (described below), who takes care of sending attribute-changed events to the appropriate parties.
public class Event { public final static byte OBJECT_CREATED = 0; public final static byte OBJECT_DELETED = 1; public final static byte ATTR_CHANGED = 2; public byte type; public String oid; public String name; public Object value; } public interface Subscriber { public boolean handleEvent (DObject target, Event evt); }
When a change occurs to a DObject attribute, an event is delivered to all subscribers of that object. In order to get a reference to a DObject, one must name a subscriber to be notified when changes occur to that object. The subscriber can respond accordingly to the attribute changes. This system is used by users of the distributed-object system as well as internally, to facilitate the distribution of attribute-changed events.
public interface DObjectManager { public void attributeChanged (DObject object, String name, Object value) throws IOException; public DObject subscribeToObject (String oid, Subscriber sub) throws IOException; public void destroyObject (String oid) throws IOException; public void dispatchEvent (Event evt) throws IOException;
}
The last major piece of the puzzle is the object manager, which is where objects are obtained. It handles all the event-dispatch details. DObjects are obtained from the object manager and it is also used to destroy objects.
The hairy details
I'll try to quickly6 describe the mechanism by
which attribute-changed events get properly passed to all subscribers of
an object (both local and remote), but don't fret if this gets a little
hairy. After all, it's just implementation detail.
There are two implementations of DObjectManager (notice that it is only an interface), one for the server and one for the client. All DObjects have a reference to the object manager. When the user calls any of the setValue (or related) member functions, the DObject notifies the DObjectManager that one of its attributes has been changed. The server object manager turns this notification into an attribute-changed event and broadcasts that event to all registered subscribers of that DObject.
If life were limited to the single-server process, this would suffice. However, a similar implementation in the client DObjectManager would not work. It would only broadcast attribute-changed events to local subscribers of a Dobject, not the potential multitude of other client-subscribers connected to the server. So every client maintains a proxy subscriber in the server process. When the client DObjectManager gets a subscribe request, it sends a message to its proxy in the server, asking it to subscribe to the object. The server proxy then marshals 7 the object and sends it down to the client. The client DObjectManager keeps that object around in a hash table for other potential local subscribers to use.
Now, whenever an attribute-change event happens at the server, it will be sent to this client's subscriber proxy, who will forward the event to the client. The client can then distribute the attribute-changed event to all local subscribers. Conversely, when the client DObjectManager
I'm going to implement a lame little chat system, then go back to doing real work. |
If you want to know more, you'll have to take a look at the source.
The token application
If you can remember all the way back to the beginning of the article, I
mentioned something about using this to write something cool. Well, that's
not going to happen now. I'm saving that for next week. So instead, I'm
going to implement a lame little chat system, then go back to doing real
work.
If any of this made sense, you might have a clue as to what the implementation is going to look like. I can sum it up for you in these two code fragments:
// send a chat message to everyone String message = _name + ": " + _text.getText(); _cobj.setValue("message", message); // update our message display when a // new message arrives public boolean handleEvent (Event evt) { if ((evt.type == Event.ATTR_CHANGED) && evt.oid.equals("chat_object") && evt.name.equals("message")) { _msgbuffer.appendText((String)evt.value+"\n"); } return true; }That and a little user-interface code are all that are necessary to implement a chat system (a very simple and not terribly efficient one, but who's counting?)
Tune in next week for an interesting application of this nice little distributed-object system and an affirmation that other things besides chat are easy to do with distributed objects.
-- Michael Bayne <mdb@go2net.com> is ogling the
price of Solstice (tm) Enterprise Manager (tm) and wondering whether he
should be distributing this source code.