Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Web Development

Distributed Active Objects


Dr. Dobb's Journal March 1997: Distributed Active Objects

Marc Brown and Marc Najork are members of the research staff at Digi-tal Equipment Corp.'s Systems Re-search Center in Palo Alto, CA. They can be reached at [email protected] and [email protected], respectively.


One of the most exciting recent developments in web-browser technology involves active objects, which make it possible for a browser to download a program, execute it, and display the program's user interface in a web page. Sun's HotJava browser with Java applets pioneered active objects, showing web pages with a wide range of content, from bouncing balls to spreadsheets to simulated science experiments. Most browsers now offer some form of active objects, written in a variety of languages.

In this article, we'll describe distributed active objects that can communicate with other active objects located on different machines across the Internet. High-level support for distributed computation makes it easy to write groupware, computer-supported cooperative work (CSCW) applications, and multiplayer games as active objects.

The environment we are using for writing distributed active objects is based on Obliq, an object-oriented scripting language specifically designed for constructing distributed applications in a heterogeneous environment (see "A Language with Distributed Scope," by Luca Cardelli, Computing Systems, January 1995). We call active objects written in Obliq "Oblets" (short for "Obliq applets"). We have also built a family of browsers -- DeckScape, WebCard, and WebScape -- capable of running Oblets (see http://www.research.digital.com/SRC/webbrowsing/).

Obliq supports distributed computation by implementing all objects as network objects (see "Network Objects," by Andrew D. Birrell, Greg Nelson, Susan Owicki, and Edward P. Wobber, Proceedings of the 14th ACM Symposium on Operating System Principles, December 1993). The methods of a network object can be invoked by processes other than the one that created the object. The initial connection between two processes occurs when one process registers an object with a name server under a unique name, and another process subsequently imports the object from that name server. Once the connection is established, network objects can be passed to other processes just as easily as any other type of data can be passed.

For a network object, method calls and field accesses have the same syntax regardless of the object's location. It need not be on the same machine or even on the same type of machine as the caller. As network objects, Oblets benefit from this uniformity. They can communicate with each other regardless of their location. Moreover, they communicate directly, without server intervention. Thus, Oblets do not impose any load on an HTTP server, nor does a heavily loaded server affect their performance.

A Simple Oblet

An Oblet is an Obliq program that defines a variable named oblet. This variable must contain an Obliq object with at least two fields, vbt and run:

  • The vbt field is bound to a widget that will be installed on the screen when the web page containing the Oblet is loaded.
  • The run field is bound to a method that is invoked just after the vbt field is evaluated.

Oblets are placed into HTML documents via OBJECT, an HTML tag proposed by the World Wide Web Consortium for inserting multimedia objects into web pages. The markup for putting the Oblet at URL adder.obl into a document is:

<OBJECT

CLASSID="adder.obl"

CODETYPE="application/x-oblet">

</OBJECT>.

The OBJECT tag also supports a variety of standard attributes, such as suggested dimensions, border size, and alignment. If suggested dimensions are not specified, the preferred dimensions of the widget contained in the Oblet's vbt field are used.

Figure 1 is a simple Oblet for adding two numbers. The user interface (UI) of the Oblet defined by a FormsVBT s-expression (see "A Two-View Approach To Constructing User Interfaces," by Gideon Avrahami, Kenneth P. Brooks, and Marc H. Brown, Computer Graphics, July 1989), is stored in the file adder.fv; see Listing One

A UI in FormsVBT is a hierarchical arrangement of components. These include passive visual elements (Text, for instance), basic interactors (Numeric), modifiers that add interactive behavior to other components (Button), and layout operators that organize other components geometrically (HBox). Components can also be categorized based on the number of child components they support: A "split" can have any number of children (HBox), a "filter" has exactly one child (Border), and a "leaf" has no children (Text).

A component in FormsVBT can be given a name so that its attributes can be queried and modified at run time. Names are also used for attaching callback procedures to interactors. In this Oblet, the two Numeric interactors are num1 and num2, and the Text component where the sum will be displayed is named sum.

Listing Two, the source for this Oblet, defines the variables doAdd and oblet. doAdd retrieves the values of both numeric interactors, and displays their sum in the component named sum.

Variable oblet contains an object with the fields vbt and run. The vbt field is bound to a form, a widget that displays a FormsVBT s-expression. The procedure form_fromURL takes a URL as an argument and returns a form whose description is stored at this URL. The global variable BaseURL is the Oblet's absolute URL up through the last slash. The run method in this Oblet just attaches the callback procedure doAdd to the two numeric interactors. This procedure will be invoked whenever the user clicks on the plus or minus buttons of either interactor, or types a number into the editing field between the buttons. The form in which the event occurred is passed as an argument to the callback procedure. Recall that when the web page containing this Oblet is loaded, the vbt field will be evaluated and the result displayed on the page, the run method will be invoked, and finally the page will become visible.

A Distributed Game Oblet

Figure 2 shows an Oblet for playing a tic-tac-toe game at two sites. Figure 2(a) is the WebScape browser used by player "O" and 2(b) is the DeckScape browser used by player "X." The message line indicates that player X is next, and the Oblet of player O is grayed out, indicating it is nonresponsive for the time being.

The FormsVBT description for this Oblet contains a message line that indicates whose turn it is, a game grid consisting of nine squares, and a RESET button that clears the squares. The message line is a Text component named status. Each square of the game grid consists of a Button and a Text component. The Button components are named btn1, ..., btn9, and the Text components are named lab1, ..., lab9. The RESET button is named reset. Finally, the form's top-level component is named board.

Listing Three shows the code for the tic-tac-toe Oblet. In addition to the required vbt field and run method, the Oblet also has fields symbol and opp and methods reset and move. The field symbol is a string indicating the player running the Oblet, either X or O. The field opp will be the Oblet of the opponent. The reset method clears the label displayed in each square of the game grid. The move method stores the string symbol into the label component, updates the message line to indicate whose turn is next, and changes turns by changing the responsiveness of the game board. Consequently, each player can press a button only when it is his turn to move.

We start a game by visiting the tic-tac-toe web page, which causes the tic-tac-toe Oblet to be loaded and its run method to be invoked. The first part of the run method attempts to import an object named TicTacToe from the name server at machine ash.pa.dec.com. This succeeds if there is already a player waiting for a game to begin. In this case, the opponent's oblet is stored in our opp field, our oblet is stored in our opponent's opp field, and we choose "X" as our symbol. If the attempt to import TicTacToe fails, then we export our oblet to the name server at ash.pa.dec.com, make our game board dormant (grayed out and unresponsive to mouse activity), and choose "O" as our symbol. For simplicity, we ignore the race condition of more than one player executing this code simultaneously.

The run method then attaches callback procedures to the various interactors on the board. Procedure doReset is attached to the RESET button; it will invoke the reset methods of our Oblet and of our opponent's Oblet. A procedure p is attached to each of the nine buttons, btn1, ..., btn9. This procedure effectively captures the value of i, the index of each square on the game grid. When p is invoked (in response to a user clicking in a square), it calls procedure doPress(i), which checks that the square is empty and invokes the move method on both Oblets.

It is worth emphasizing that self.opp denotes an object that resides on the opponent's machine. This implies that the assignment to self.opp.opp and the execution of the self.opp.reset and self.opp.move method calls take place on that machine.

A Distributed Chat-Room Oblet

Oblets are flexible enough to allow distributed computations to have arbitrary topologies. In the tic-tac-toe example, two oblet objects were performing peer-to-peer communication. In the next example, we use a star topology to implement a multiperson chat room. At the center of the star, we have a conference control object; the Oblets belonging to the participants are at the periphery. When a user types something into his chat-room Oblet, it informs the conference controller of the new text. The controller relays the update to all the participating Oblets; in other words, Oblets do not communicate with other Oblets directly. Our chat room also provides a rudimentary mechanism for floor control.

Figures 3(a), 3(b), and 3(c) show the chat-room Oblet running in different browsers (WebScape, WebCard, and DeckScape, respectively). Each browser is running on a different machine. The participants in the chat room are Moe, Larry, and Curly (respectively). The floor is with Moe, as indicated by the status line over the editing region and by the color of the editing region in Moe's browser.

Listing Four is the FormsVBT s-expression for the chat-room Oblet. The floorWith component is the message line above the large editing region; it will contain a message indicating who owns the floor. The mainEditor is the large (300×200) editing region. The Filter component surrounding the region is used to set the reactivity of the region; in the passive state, the region is unresponsive to mouse and keyboard activity, but it is not grayed out, as it would be in the dormant state. The type-in field where each participant identifies himself is named myName. Finally, the Grab Floor button has been given the name grabFloor.

Callback procedures will be attached to the Grab Floor button and to the large editing region. When the user clicks on the Grab Floor button, the message line on all participating Oblets indicates who owns the floor (using the contents of the type-in field of the Oblet now owning the floor); the editing region of all Oblets (other than the one owning the floor) become passive; and the editing region of the Oblet owning the floor becomes active, and its color changes to pink. When the user who owns the floor types a keystroke into the editing region, all participating Oblets are notified of the updated text by the conference-control object.

Listing Five is the definition of the conference-control object. The oblets data field is an array of the Oblets that have registered themselves with the conference-control object. Each element of this array is an Oblet that typically resides on a different machine. The onFloor data field is the name of the user who currently has the floor, and the contents data field contains the current contents of the editing region. These two fields are needed to initialize the display of a new participant entering the chat room.

The register method is called by a new Oblet when it is initialized as part of its run method. The new Oblet is appended to the oblets array and is notified both of the current contents of the editing region and of the owner of the floor, if there is one.

The transferFloor method is called by an Oblet when the user clicks on the Grab Floor button. This method stores in onFloor the name of the user who now owns the floor, then iterates through all of the Oblets in the conference, invoking the transferFloor method on each Oblet to inform it of the new floor owner.

Finally, the updateText method is called on each keystroke by the Oblet that owns the floor, passing in the current contents of the editing region. (Passing just the keystroke is not sufficient, since a single character could result in various editing actions, depending on the key bindings used by the Oblet.) The updateText method stores in contents the new contents of the editing region, then updates all of the Oblets in the chat room by invoking the updateText method on each one.

Listing Six shows the code for a chatroom Oblet. In addition to vbt and run, the Oblet has two additional methods, transferFloor and updateText, which are invoked by the conference-control object in response to a user in an arbitrary Oblet in the chat room grabbing the floor or typing into the editing region. These methods are straightforward: The transferFloor method makes the editing region passive and sets its background to white, then updates the message line. The updateText message changes the contents of the editing region.

The Oblet's run method first contacts the name server on the machine ash.pa.dec.com to obtain a conference-control object registered under the name ConfControl. If there is such an object, it is stored in the variable confControl. Otherwise, a new conference-control object is registered with the name server and is stored in confControl. As in the tic-tac-toe example, we do not show the code necessary for preventing the race condition of several users executing the try-except statement simultaneously. After defining callback procedures doGrabFloor and doKeyEvent, this Oblet registers itself with the conference controller and attaches the callback procedures to the Grab Floor button and editing region.

The doGrabFloor callback procedure invokes the transferFloor method on the confControl object (which then calls the transferFloor method on all Oblets in the chat room, including this one), and makes its own editing region active and colored pink. The doKeyEvent callback procedure invokes the updateText method on the confControl object, passing to it the text in the editing region.

Again, it is important to point out that invoking a method m on the confControl object is done by calling confControl.m(), regardless of where the confControl object resides. In this example, the conference-control object is local to the Oblet that creates it and remote to all other Oblets.

There are many features that could be added to the chat room. For example, it would be nice to be able to prevent another user from taking away the floor, allow users to leave the chat room, create new chat rooms, see existing chat rooms, handle exceptions that might result from network partitions, and so on. In addition, the implementations could be more efficient, reporting only changes to the editing region rather than reporting the region's entire contents after each keystroke.

Oblets for Algorithm Animation

As we have seen, Obliq's network objects provide a uniform and elegant way for objects to communicate, regardless of the address space they exist in and machine they reside on. The two previous examples showed the obvious use for network objects -- to communicate among objects on different machines. The next example uses network objects to allow Oblets running in the same browser (on the same web page or on different web pages) to communicate. This could be achieved through simpler mechanisms; after all, Oblets running on the same browser are in the same address space. However, network objects minimize the number of concepts needed by a programmer, since they handle this case in the exact same way as the distributed case. Moreover, network objects make it easy to reuse Oblets in distributed settings without any code changes.

The previous example uses network objects to coordinate multiple Oblets in the domain of algorithm animation (see "A System for Algorithm Animation," by Marc H. Brown and Robert Sedgwick, Computer Graphics, July 1984). A typical algorithm animation system has a control panel and a collection of views, each in its own window. The control panel is used for specifying data, starting the algorithm, controlling the animation speed, and so on. To animate an algorithm, strategically important points of its code are annotated with procedure calls that generate "interesting events." These events are reported to the algorithm animation system, which forwards them to all interested views. Each view responds to interesting events by updating its display appropriately.

Figure 4 is an animation of first-fit binpacking. The control panel and the views are implemented by separate Oblets. We use an event-manager object, similar to the conference-control object in the chat-room example, to relay interesting events from the algorithm to the views. For each interesting event, there is a corresponding method both in the event-manager object and each view Oblet. When an interesting event occurs, the algorithm Oblet invokes the corresponding method of the event-manager object, which relays the event to each view. Typically, views react by showing some animation reflecting the changes in the program. For the animations in the views to happen simultaneously, the event manager forks a thread for each registered view, the thread calls the view's method corresponding to the interesting event, and the event manager waits until all of the threads have completed before returning to the algorithm.

For example, when a binpacking algorithm is trying to insert a particular weight w into a bin b that already contains a number of weights totaling up to amt, it calls z.probe(w,b,amt). The probe method of the event-manager object z is implemented in Listing Seven.

Figure 4 shows the Oblets for the control panel and each view -- all on the same web page. However, there is no need for the Oblets to be located on the same page. In fact, if we put each Oblet on a separate page, users can dynamically select the set of views visible (or even have more than one copy of any view). In Figure 5, the web page containing the control panel has links for pages containing the various views. Clicking on a link brings up a page for the view, which the DeckScape browser can optionally display in a separate window.

At first blush, it would appear that this example uses network objects merely for the coding elegance they offer, rather than for any of their distributed aspects. That is, in Figures 4 and 5, all the Oblets exist in the same address space, namely, that of the browser. However, because Oblets are network objects, we have far more flexibility. For instance, we can use the Oblets -- without any changes -- in an electronic classroom. In such a setting, the instructor and all students run DeckScape on their individual machines (using the same name server). The instructor uses the control page Oblet to drive the animation, and each student sees a set of views portraying the workings of the algorithm. (This scenario is discussed in our paper "Collaborative Active Textbooks: A Web-Based Algorithm Animation System for an Electronic Classroom," Proceedings of the 1996 IEEE Symposium on Visual Languages, September 1996.)

Related Work and Conclusions

Oblets bring together active objects and distributed computation. Although Java is the best-known language for active objects, it does not have language-level support for distributed programming. The Java Developer's Kit 1.1 will contain an API for remote method invocations, giving it the equivalent of network objects. Still, Java objects are not network objects by default, so is it harder to adapt Java code to a distributed setting. Moreover, Obliq is a higher-order language, making it possible to migrate computations (in the form of closures) over the network.

The Oblets shown here have been small for didactic reasons. However, Obliq is a full-strength programming language with access to a rich set of libraries, including multimedia objects and even web pages.

For more information on our work on Oblets and web browsers, visit http://www. research.digital.com/SRC/webbrowsing/.

DDJ

Listing One

(HBox  (Numeric %num1)
  (Text "+")
  (Numeric %num2)
  (Text "=")
  (Text %sum "0"))

Back to Article

Listing Two

let doAdd =  proc (fv)
    let n1 = form_getInt (fv, "num1");
    let n2 = form_getInt (fv, "num2");
    form_putText (fv, "sum", fmt_int (n1+n2))
  end;
let oblet = {


</p>
  vbt => form_fromURL (BaseURL & "adder.fv"),
  run =>
    meth (self)
      form_attach (self.vbt, "num1", doAdd);
      form_attach (self.vbt, "num2", doAdd);
    end
};

Back to Article

Listing Three

let oblet = {  vbt    => form_fromURL (BaseURL & "tic-tac-toe.fv"),
  symbol => ok,
  opp    => ok,
  reset =>
    meth (self)
      for i = 1 to 9 do
        form_putText (self.vbt, "lab" & fmt_int(i), "");
      end;
    end,
  move =>
    meth (self, label, symbol)
      form_putText (self.vbt, label, symbol);
      let other = if symbol is "X" then "O" else "X" end;
      form_putText (self.vbt, "status", other & " is next");
      if form_getReactivity(self.vbt, "board") is "active" then
        form_putReactivity(self.vbt, "board", "dormant");
      else
        form_putReactivity(self.vbt, "board", "active");
      end;
    end,
  run =>
    meth (self)
      try
        self.opp := net_import ("TicTacToe", "ash.pa.dec.com");
        self.opp.opp := self;
        self.symbol := "X";
      except net_failure =>
        net_export ("TicTacToe", "ash.pa.dec.com", self);
        form_putReactivity (self.vbt, "board", "dormant");
        self.symbol := "O";
      end;
      let doReset =
        proc(fv)
          self.reset ();
          self.opp.reset ();
        end;
      let doPress =
        proc (m)
          let label = "lab" & fmt_int(m);
          if form_getText (self.vbt, label) is "" then
            self.move (label, self.symbol);
            self.opp.move (label, self.symbol);
          end;
        end;
      form_attach (self.vbt, "reset", doReset);


</p>
      for i = 1 to 9 do
        let p = proc(fv) doPress(i) end;
        form_attach (self.vbt, "btn" & fmt_int(i), p)
      end;
    end
};

Back to Article

Listing Four

(Rim (Pen 10)  (VBox
    (Text %floorWith "The floor is free right now")
    (Glue 10)
    (Shape (Width 300) (Height 200)
      (Frame Lowered
         (Filter Passive
           (TextEdit (BgColor "White") %mainEditor))))
    (Glue 10)
    (HBox
      (Text "Your Name:")
      (Frame Lowered (TypeIn (BgColor "White") %myName))
      Fill
      (Button %grabFloor "Grab Floor"))))

Back to Article

Listing Five

let ProtoConfControl = {  oblets => [],
  onFloor => ok,
  contents => "",
  register =>
    meth (self, oblet)
      self.oblets := self.oblets @ [oblet];
      oblet.updateText (self.contents);
      if self.onFloor isnot ok then
        oblet.transferFloor (self.onFloor);
      end;
    end,
  transferFloor =>
    meth (self, name)
      self.onFloor := name;
      foreach o in self.oblets do
        o.transferFloor (name);
      end;
    end,
  updateText =>
    meth (self, contents)
      self.contents := contents;
      foreach o in self.oblets do
        o.updateText (contents);
      end;
    end
};

Back to Article

Listing Six

let oblet = {  vbt => form_fromURL (BaseURL & "chatroom.fv"),


</p>
  transferFloor =>
    meth (self, name)
      form_putReactivity (self.vbt, "mainEditor", "passive");
      form_putBgColor (self.vbt, "mainEditor", color_named("white"));
      form_putText (self.vbt, "floorWith", "The floor is with " & name);
    end,
  updateText =>
    meth (self, contents)
      form_putText (self.vbt, "mainEditor", contents);
    end,
  run =>
    meth (self)
      var confControl = ok;
      try
        confControl := net_import("ConfControl", "ash.pa.dec.com");
      except net_failure =>
        confControl := ProtoConfControl;
        net_export("ConfControl", "ash.pa.dec.com", confControl);
      end;
      let doGrabFloor =
        proc (fv)
          confControl.transferFloor (form_getText (fv, "myName"));
          form_putReactivity (fv, "mainEditor", "active");
          form_putBgColor (fv, "mainEditor", color_named("pink"));
        end;
      let doKeyEvent =
        proc (fv)
          confControl.updateText (form_getText (fv, "mainEditor"));
        end;
      confControl.register (self);
      form_attach (self.vbt, "grabFloor", doGrabFloor);
      form_attach (self.vbt, "mainEditor", doKeyEvent);
    end
};

Back to Article

Listing Seven

let z = {  views => [],
  ...
  probe =>
    meth (self,w,b,amt)
      let threads =
        foreach v in self.views map
          let closure = proc() v.probe(w,b,amt) end;
          thread_fork(closure)
        end;
      foreach t in threads do
        thread_join(t)
      end;
    end;
    ...
};

Back to Article


Copyright © 1997, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.