Tutorial:
From Architecture to Implementation

Overview

This walkthrough explains how to start from scratch, conceive a small architecture, and implement that architecture using ArchStudio 3 and xADL 2.0 tools. Certainly, this is not a comprehensive guide to these tools and technologies, but it will get you familiarized with them and serve as a starting point for more involved development.

Note that this document mostly covers developing software with ArchStudio 3, as opposed to developing extensions to the ArchStudio 3 environment itself. Also note that, as a user of the ArchStudio 3 and xADL 2.0 tools, you can use a few of these tools or a lot of them, depending on how much of the underlying approach you "buy into." This document will attempt to highlight any assumptions that are being made as it progresses.


Project Overview

Our project for this walkthrough will be to build a small client-server chat system, sort of like an Instant Messaging client. For simplicity, the system will run on a single machine in a single process, with a fixed set of 2 clients. Clients will have a small GUI that displays a chat transcript and allows the user to enter and send short text messages. The server will be a simple broadcast server, relaying any messages from any one client to all clients in the system.

Assumptions: The rest of this tutorial assumes that you have installed the xArch/xADL 2.0 Data Binding Library, ArchStudio 3, and a Java development environment, and have familiarized yourself with these tools.


Step 1: Architecture

Of course, now that our requirements are laid out, the first thing to do is to decide on an architecture for our chat system. To define an architecture, it helps to decide on an architectural style. For this project, we will use the C2 architectural style. The C2 style is a natural fit for this project because all communication will be done by simple, discrete text messages that can be sent any time (asynchronously). The layered nature of the C2 style fits well with the two-tier client/server system that we are developing. While we will choose the C2 style for this project, building in a generic client/server style (that uses RPC or oneway procedure calls, for instance) would be extremely similar.

For the unfamiliar, we will quickly review the key constraints of an event-based architectural style such as C2:

The C2 style induces a few additional topological constraints:

So, our architecture for the client-server chat system in C2 will look roughly like this:

Architecture in C2

Now that we have a general boxes-and-arrows feel for our architecture, we need to decide on some details:

Interfaces: Because this is a C2 style architecture, each component and connector (collectively referred to as bricks) will have exactly two interfaces: a top interface and a bottom interface. Bricks have these interfaces in C2 whether anything is connected to them or not. C2 interfaces have essentially two duties: to send messages and to receive messages. All C2 interfaces are identical in this regard.

Types: From this architecture, we see that we have 3 components (Server, Client1, and Client2) and one connector (Bus). However, the Client1 and Client2 components should have exactly the same behavior (that is, they are two instances of the same code). Thus, we can infer that Client1 and Client2 are both of the same component type. The Bus and Server each have their own type, since no other brick in the architecture shares their behavior. However, if we were to add a middle tier, containing an obscenity filter for example, there might well be a second bus of the same connector type as the first. Assumption: Bricks that share a type share an implementation.

So, now we have to describe the architecture in the xADL 2.0 architecture description language. We will leverage xADL 2.0's modularity here by only describing the architecture in enough detail to implement and instantiate it. Advanced xADL 2.0 features like product line architecture support will be out of scope for this project.

For xADL 2.0 descriptions of architectures in this walkthrough, we will use the abbreviated format used throughout the xADL 2.0 Distilled guidebook. While this is not "real" XML or xADL 2.0, it is the only way to make the descriptions readable here.

This architecture, consisting of only four bricks and three types, is relatively simple. With the current ArchStudio 3 toolset, there are basically three ways to create an architecture such as this one:

  1. Create with ArchEdit: You can use ArchStudio 3's syntax-directed xADL 2.0 editor, ArchEdit, to create the architecture from scratch by adding each element necessary and filling in the details. This process is relatively tedious for large architectures, but is practical for small architectures such as ours here.
  2. Modify an existing description in ArchEdit: ArchStudio 3 already ships with several architecture descriptions for C2-style systems (including the description of ArchStudio 3 itself). It is feasible to use ArchEdit to modify a copy of one of these descriptions to suit another architecutre, although for an architecture of this size, the amount of effort required to do this is nearly equivalent to the amount of effort it takes to create a description from scratch.
  3. Create with a Java program: The xArch/xADL 2.0 data binding library provides a flexible way to create and manipulate architecture descriptions from Java programs. Use of parameterized methods in Java can reduce the redundancy associated with creating detailed architecture descriptions. This method is practical for creating architecture descriptions of sizable systems, and in fact is the method used to create the description for ArchStudio 3 itself. For this project, it would be wise to take an existing description-creator program, such as the archstudio/Description.java file in the source distribution, and modify it to suit this architecture.

NOTE: Unfortunately, at this time there are no graphical editors (i.e. box-and-arrow style editors), including Visio for xADL, that are mature enough to use for creation and maintenance of architectures in ArchStudio 3. Please contact Eric Dashofy about development of graphical editors for xADL 2.0/ArchStudio 3.

Regardless of the approach taken, this walkthrough will address the contents of the xADL 2.0 document that need to be created, using abbreviated non-XML notation for simplicity. NOTE: In this walkthrough, the xsi:type (XML schema instance type) of an element will only be explicitly specified when a subtype is being used in place of the specified type in the parent element's definition.

Along with this example, a Description.java file is included in the ArchStudio 3 distribution, as src/c2demo/chatsys/Description.java. This Description file is an example of how to create the following description using a Java program, as described above. The resultant xADL 2.0 description is also included in bin/chatsys.xml, which is generated using chatsys.Description.

First, we will model the component, connector, and interface types to create a "type library" that we can use when describing our architecture's topology. Let's start by defining the interface types for a C2 architecture. Because we are going to implement this architecture in the c2.fw framework, we will add some implementation information that corresponds to the use of that framework.

xArch{
  archTypes{
    //ArchTypes definition specifies that
    //archTypes element can contain InterfaceTypes,
    //here we use a subtype (InterfaceTypeImpl) to
    //specify an interface type with implementation.
    interfaceType xsi:type=InterfaceTypeImpl{
      (attr) id   = "C2TopType"
      description = "C2 Top Interface Type"
      implementation xsi:type=JavaImplementation{
        mainClass{
          //Use the basic c2.fw interface implementation
          //for this interface; in reality this data is
          //ignored by the runtime infrastructure now
          //but may be used in the future.
          javaClassName = "c2.fw.SimpleInterface"
        }
      }
    }

    interfaceType xsi:type=InterfaceTypeImpl{
      (attr) id   = "C2BottomType"
      description = "C2 Bottom Interface Type"
      implementation xsi:type=JavaImplementation{
        mainClass{
          javaClassName = "c2.fw.SimpleInterface"
	    }
      }
    }
  }
}

Note that, for this architecture, we have chosen to model only two types of interfaces: tops and bottoms (which are basically the same). In reality, the services provided/required by actual component interfaces are more complex and involve various message protocols. However, the current ArchStudio 3 toolset does not include tools that are capable of doing protocol analysis at this level, so specifying the interfaces in more detail is not necessary or useful at the architectural level. If, in the future, such analysis tools become available, the xADL 2.0 description can be extended and refined to work with them.

Now that we have defined all the interface types for our architecture, we can focus on defining the component and the connector types in much the same way. We are actually adding to the previous document; new bits will appear in blue.

xArch{
  archTypes{

    
    //Note: this has to be a VariantComponentTypeImpl
    //because XML schema does not support multiple
    //inheritance.  Thus, we introduced a small
    //artificial dependency, whereby component types
    //with implementations extend VariantComponentType.
    //The fact that this is a VariantComponentType
    //only means that the component type *CAN* have
    //be variant, not that it *WILL* be variant.

    componentType xsi:type=VariantComponentTypeImpl{
      (attr) id   = "Server_type"
      description = "Server Component Type"
      implementation xsi:type=JavaImplementation{
        mainClass{
          javaClassName = "chatsys.ServerC2Component"
        }
	  }
    }

    componentType xsi:type=VariantComponentTypeImpl{
      (attr) id   = "Client_type"
      description = "Client Component Type"
      implementation xsi:type=JavaImplementation{
        mainClass{
          javaClassName = "chatsys.ClientC2Component"
        }
      }
    }

    connectorType xsi:type=VariantComponentTypeImpl{
      (attr) id   = "Bus_type"
      description = "Bus Connector Type"
      implementation xsi:type=JavaImplementation{
        mainClass{
          //This is a provided class in the c2.fw
          //framework:
          javaClassName = "c2.legacy.conn.BusConnector"
        }
      }
    }
    

    //ArchTypes definition specifies that
    //archTypes element can contain InterfaceTypes,
    //here we use a subtype (InterfaceTypeImpl) to
    //specify an interface type with implementation.
    interfaceType xsi:type=InterfaceTypeImpl{
      (attr) id   = "C2TopType"
      description = "C2 Top Interface Type"
      implementation xsi:type=JavaImplementation{
        mainClass{
          //Use the basic c2.fw interface implementation
          //for this interface; in reality this data is
          //ignored by the runtime infrastructure now
          //but may be used in the future.
          javaClassName = "c2.fw.SimpleInterface"
        }
      }
    }

    interfaceType xsi:type=InterfaceTypeImpl{
      (attr) id   = "C2BottomType"
      description = "C2 Bottom Interface Type"
      implementation xsi:type=JavaImplementation{
        mainClass{
          javaClassName = "c2.fw.SimpleInterface"
	    }
      }
    }
  }
}

This concludes the description of the types for our architecture. Now, we have defined a set of types for our architecture and mapped them to implementations of those types (some of which already exist, as provided by our target implementation framework, and some that we have to write ourselves). The next step is to create structural elements of these types and link them up in a topology.

In xADL 2.0, the design-time structure of the architecture is specified in descendants of the archStructure tag. Even though the components and connectors that will be specified here are technically instances of the types we defined above, we will not use the constructs in the xArch (instance) schema for modeling design-time architectures; the instance schema is used in xADL 2.0 to model run-time instances. ArchStudio 3 can create an instance model of a running system when necessary.

So, we will add to the description above and create the topology of the architecture. First, we'll create the components and connectors, and use XLinks to link them to their types, as specified elsewhere in the document.

xArch{
  
  archStructure{

    component{
      (attr) id   = "Server"
      description = "Server Component"

      interface{
        //Note that the convention used by the c2.fw runtime
        //infrastructure is to preface interface IDs with
        //the name of the brick and a period (dot)
        (attr) id = "Server.IFACE_TOP"
        description = "Server Top Interface"

        //C2 interfaces are always inout because they pass
        //messages both ways
        direction = "inout"

        //Link interfaces to their types as well
        (link) type = "#C2TopType"
      }

      interface{
        (attr) id = "Server.IFACE_BOTTOM"
        description = "Server Bottom Interface"
        direction = "inout"
        (link) type = "#C2BottomType"
      }

      //Use an XLink to link to the type
      (link) type = "#Server_type"
    }

    component{
      (attr) id   = "Client1"
      description = "Client 1 Component"

      interface{
        (attr) id = "Client1.IFACE_TOP"
        description = "Client1 Top Interface"
        direction = "inout"
        (link) type = "#C2TopType"
      }

      interface{
        (attr) id = "Client1.IFACE_BOTTOM"
        description = "Client1 Bottom Interface"
        direction = "inout"
        (link) type = "#C2BottomType"
      }

      //Use an XLink to link to the type
      (link) type = "#Client_type"
    }

    component{
      (attr) id   = "Client2"
      description = "Client 2 Component"

      interface{
        (attr) id = "Client2.IFACE_TOP"
        description = "Client2 Top Interface"
        direction = "inout"
        (link) type = "#C2TopType"
      }

      interface{
        (attr) id = "Client2.IFACE_BOTTOM"
        description = "Client2 Bottom Interface"
        direction = "inout"
        (link) type = "#C2BottomType"
      }

      //Use an XLink to link to the type
      (link) type = "#Client_type"
    }

    connector{
      (attr) id   = "Bus"
      description = "The Bus"

      interface{
        (attr) id = "Bus.IFACE_TOP"
        description = "Bus Top Interface"
        direction = "inout"
        (link) type = "#C2TopType"
      }

      interface{
        (attr) id = "Bus.IFACE_BOTTOM"
        description = "Bus Bottom Interface"
        direction = "inout"
        (link) type = "#C2BottomType"
      }

      //Use an XLink to link to the type
      (link) type = "#Bus_type"
    }

  }
  

  archTypes{
    //... -- Removed here for space considerations but still present
  }

}

Note here that each brick has two interfaces, a top and a bottom, the top being of type C2TopType and the bottom being of type C2BottomType. This matches the signatures prescribed in the type definitions of the bricks.

Now that we have created a description of the bricks and linked them to their types, it is time to establish the topology. We will do this by creating links, connections between interfaces on bricks. Links correspond to the thin lines in our architecture diagram above. Links also go under the archStructure tag.

xArch{
  archStructure{
    

    link{
      //Links must have unique IDs too
      (attr) id = "Server_to_Bus"
      description = "Server to Bus Link"
      point{
        (link) anchorOnInterface = "#Server.IFACE_BOTTOM"
      }
      point{
        (link) anchorOnInterface = "#Bus.IFACE_TOP"
      }
    }

    link{
      //Links must have unique IDs too
      (attr) id = "Bus_To_Client1"
      description = "Bus to Client 1 Link"
      point{
        (link) anchorOnInterface = "#Bus.IFACE_BOTTOM"
      }
      point{
        (link) anchorOnInterface = "#Client1.IFACE_TOP"
      }
    }

    link{
      //Links must have unique IDs too
      (attr) id = "Bus_To_Client2"
      description = "Bus to Client 2 Link"
      point{
        (link) anchorOnInterface = "#Bus.IFACE_BOTTOM"
      }
      point{
        (link) anchorOnInterface = "#Client2.IFACE_TOP"
      }
    }
    

  	//...  -- Components, connectors removed here for space
  	//        but still present.
  }

  archTypes{
    //... -- Removed here for space considerations but still present
  }

}

This concludes our description of the architecture. So far, we have described, at the architectural level:


Step 2: Implementation

At this point, we have an architectural description of the system. Already, we have made some assumptions about how it will be implemented, namely that it will be in Java and will use the c2.fw infrastructure. All these decisions are encapsulated in the <implementation> tags in the architecture description; replacing the contents of these tags would allow us to change the implementation mappings. Of course, the ArchStudio 3 environment currently supports only one runtime environment, namely the c2.fw architecture framework, so we will use that.

It is useful to understand what sorts of systems can be implemented by the c2.fw framework. The name c2.fw is a bit of a misnomer. Although the framework's initial development was targeted only at C2 style architectures, it was eventually generalized to support event-based architectures with arbitrary topologies. Changing the name of the framework at that point, however, would have broken a large amount of code. So, despite the fact that the framework is called "c2.fw" it actually supports a larger array of event-based styles. Within the framework, traditional C2 topologies are supported by classes in the c2.legacy.* packages.

Here we'll take a moment to review the communication-related rules for event-based architectures like ours.

Implementing components and connectors in c2.fw basically independent of their description in xADL 2.0. You can implement a component in c2.fw without ever describing it in xADL 2.0; likewise, you can describe a system without ever implementing it (for analysis purposes, use as a point of reference for communication, etc.) The link between the two comes in the instantiation and management of the architecture. If you describe an architecture in xADL 2.0 and provide implementation mappings, as we have done, ArchStudio 3's tools will instantiate, link up, and manage the components and that connectors that are described. Without a xADL 2.0 description, you can still implement a c2.fw system, but you have to write your own bootstrapper to call the framework, instantiate components and connectors, and link them up.

You can follow along with the implementations in this walkthrough in the ArchStudio source. The server and client components have been fully implemented and are available in the src/c2demo/chatsys directory.

Before implementing the components, it is useful to make some decisions about the kinds of messages that they will send back and forth. In c2.fw, Message is a very small interface that can be implemented by just about any data structure object. However, c2.fw provides several generic message classes that implement the Message interface that are useful in a variety of situations. The most commonly used generic message class is NamedPropertyMessage. The data format for a NamedPropertyMessage consists of:

For our simple chat application, this will be sufficient. We will establish a convention that the name of the message will be "ChatMessage" and there will be one parameter, named "text", that maps to a String value containing the message text.

Note: For more complex data structures that still fit the named-property model, you can use the c2.util.MessageCodeGenerator standalone application to quickly generate subclasses of NamedPropertyMessage that have a slightly nicer interface than NamedPropertyMessage.

Now that we know what kinds of messages our server component should process, we can implement our server component. We will start with the boilerplate stuff.

package chatsys;

//Import framework classes
import c2.fw.*;

//Import "legacy" (i.e. standard 2-interface) C2
//support
import c2.legacy.*;

public class ServerC2Component extends AbstractC2DelegateBrick{

  //Boilerplate constructor
  public ServerC2Component(Identifier id){
    super(id);
  }

}

The base class for this component, AbstractC2DelegateBrick, implements the c2.fw.Brick interface (the minimum requirement for a class to be the main implementation class for a brick), but it also provides a number of base services to the class that allow it to be managed by the framework. There is another abstract class we could have extended, called AbstractC2Brick that has slightly different behavior. The differences between a Brick and a DelegateBrick are as follows:

DelegateBricks are, thus, more flexible than regular bricks, and are the preferred base class to extend when creating new brick implementations in c2.fw.

As noted above, in delegate bricks, the message handling duties are delegated to a number of independent message processors. We will add one of those to our server component implementation here:

package chatsys;

//Import framework classes
import c2.fw.*;

//Import "legacy" (i.e. standard 2-interface) C2
//support
import c2.legacy.*;

public class ServerC2Component extends AbstractC2DelegateBrick{

  //Boilerplate constructor
  public ServerC2Component(Identifier id){
    super(id);
    
    addMessageProcessor(new ServerC2ComponentMessageProcessor());
    
  }

  
  class ServerC2ComponentMessageProcessor implements MessageProcessor{
    public void handle(Message m){
      //Only handle chat messages from the bottom interface

      //bottomIface is the interface variable representing the
      //bottom interface of this component, available in
      //the AbstractC2Brick superclass.
      if(m.getDestination().getInterfaceIdentifier().
      equals(bottomIface.getIdentifier())){
        if(m instanceof NamedPropertyMessage){
          NamedPropertyMessage npm = (NamedPropertyMessage)m;
          if(npm.getName().equals("ChatMessage")){
            //This is a message we should relay

            //sendToAll sends a message to all bricks connected
            //to the specified interface; it is provided in the
            //AbstractBrick superclass.
            sendToAll(m, bottomIface);
          }
        }
      }
    }
  }
  
}

Because the server component is just a 'message reflector,' this implementation is sufficient. We are done with the server component. Now we need to implement the client component. The client component uses the same basic c2.fw facilities to receive and send messages, with a little GUI code for the user interface. We'll start with the boilerplate code for the client:

package c2demo.chatsys;

import c2.fw.*;
import c2.legacy.*;

//This stuff is imported to support the GUI code
//we will write
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ClientC2Component extends AbstractC2DelegateBrick{
  public ClientC2Component(Identifier id){
    super(id);
  }
}

Now we will add some code to handle incoming messages.

package chatsys;

import c2.fw.*;
import c2.legacy.*;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ClientC2Component extends AbstractC2DelegateBrick{

  public ClientC2Component(Identifier id){
    super(id);
    
    this.addMessageProcessor(new ClientC2ComponentMessageProcessor());
    
  }

  
  class ClientC2ComponentMessageProcessor implements MessageProcessor{
    public void handle(Message m){
      if(m.getDestination().getInterfaceIdentifier().
      equals(topIface.getIdentifier())){
        if(m instanceof NamedPropertyMessage){
          NamedPropertyMessage npm = (NamedPropertyMessage)m;
          if(npm.getName().equals("ChatMessage")){
            String text = (String)npm.getParameter("text");
            //Here we will add the message to the chat transcript
            //when we have the GUI code written.
          }
        }
      }
    }
  }
  
}

Adding this code is extremely similar to the message handling code for the server, except that instead of sending the response out from the handle() method, we are adding it to a GUI chat transcript instead. Now, we can implement a small GUI to go along with this component in Java Swing.

package chatsys;

import c2.fw.*;
import c2.legacy.*;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ClientC2Component extends AbstractC2DelegateBrick{

  protected ChatComponentGUI gui;

  public ClientC2Component(Identifier id){
    super(id);
    this.addMessageProcessor(new ClientC2ComponentMessageProcessor());
  }

  class ClientC2ComponentMessageProcessor implements MessageProcessor{
    public void handle(Message m){
      if(m.getDestination().getInterfaceIdentifier().
      equals(topIface.getIdentifier())){
        if(m instanceof NamedPropertyMessage){
          NamedPropertyMessage npm = (NamedPropertyMessage)m;
          if(npm.getName().equals("ChatMessage")){
            String text = (String)npm.getParameter("text");
            //Here we will add the message to the chat transcript
            //when we have the GUI code written.
            gui.addMessageToTranscript(text);
          }
        }
      }
    }
  }

  
  class ChatComponentGUI extends JFrame implements ActionListener{
    JTextArea transcript;
    JTextField entryField;
    JButton sendButton;
    StringBuffer transcriptBuf;

    public ChatComponentGUI(){
      super(getIdentifier().toString());
      transcriptBuf = new StringBuffer();
      transcript = new JTextArea();
      entryField = new JTextField(20);
      sendButton = new JButton("Send");
      sendButton.addActionListener(this);

      this.getContentPane().setLayout(new BorderLayout());
      this.getContentPane().add("Center", transcript);

      JPanel bottomPanel = new JPanel();
      bottomPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
      bottomPanel.add(entryField);
      bottomPanel.add(sendButton);
      this.getContentPane().add("South", bottomPanel);

      this.setSize(500, 400);
      this.setVisible(true);
      validate();
      repaint();
    }

    public void addMessageToTranscript(String text){
      transcriptBuf.append(text);
      transcriptBuf.append(System.getProperty("line.separator"));
      transcript.setText(transcriptBuf.toString());
    }

    public void actionPerformed(ActionEvent evt){
      String text = entryField.getText();
      if(!text.equals("")){
        NamedPropertyMessage chatMessage =
          new NamedPropertyMessage("ChatMessage");
        chatMessage.addParameter("text", getIdentifier() + ": " + text);

        //Here we call the sendToAll method
        //to send data to the server from
        //the top interface of this component
        sendToAll(chatMessage, topIface);
        entryField.setText("");
      }
    }

  }
  
}

Finally, we need to instantiate the GUI window when the component is hooked into place and started up. We will do this in a lifecycle method, begin(). That code is added here:

package chatsys;

import c2.fw.*;
import c2.legacy.*;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ClientC2Component extends AbstractC2DelegateBrick{

  protected ChatComponentGUI gui;

  public ClientC2Component(Identifier id){
    super(id);
    this.addMessageProcessor(new ClientC2ComponentMessageProcessor());
    this.addLifecycleProcessor(new ClientC2ComponentLifecycleProcessor());
  }

  class ClientC2ComponentLifecycleProcessor extends LifecycleAdapter{
    public void begin(){
      gui = new ChatComponentGUI();
    }
  }

  class ClientC2ComponentMessageProcessor implements MessageProcessor{
    public void handle(Message m){
      if(m.getDestination().getInterfaceIdentifier().
      equals(topIface.getIdentifier())){
        if(m instanceof NamedPropertyMessage){
          NamedPropertyMessage npm = (NamedPropertyMessage)m;
          if(npm.getName().equals("ChatMessage")){
            String text = (String)npm.getParameter("text");
            //Here we will add the message to the chat transcript
            //when we have the GUI code written.
            gui.addMessageToTranscript(text);
          }
        }
      }
    }
  }

  class ChatComponentGUI extends JFrame implements ActionListener{
    JTextArea transcript;
    JTextField entryField;
    JButton sendButton;
    StringBuffer transcriptBuf;

    public ChatComponentGUI(){
      super(getIdentifier().toString());
      transcriptBuf = new StringBuffer();
      transcript = new JTextArea();
      entryField = new JTextField(20);
      sendButton = new JButton("Send");
      sendButton.addActionListener(this);

      this.getContentPane().setLayout(new BorderLayout());
      this.getContentPane().add("Center", transcript);

      JPanel bottomPanel = new JPanel();
      bottomPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
      bottomPanel.add(entryField);
      bottomPanel.add(sendButton);
      this.getContentPane().add("South", bottomPanel);

      this.setSize(500, 400);
      this.setVisible(true);
      validate();
      repaint();
    }

    public void addMessageToTranscript(String text){
      transcriptBuf.append(text);
      transcriptBuf.append(System.getProperty("line.separator"));
      transcript.setText(transcriptBuf.toString());
    }

    public void actionPerformed(ActionEvent evt){
      String text = entryField.getText();
      if(!text.equals("")){
        NamedPropertyMessage chatMessage =
          new NamedPropertyMessage("ChatMessage");
        chatMessage.addParameter("text", getIdentifier() + ": " + text);

        //Here we call the sendToAll method
        //to send data to the server from
        //the top interface of this component
        sendToAll(chatMessage, topIface);
        entryField.setText("");
      }
    }
  }
}

That suffices for the implementation of the client component. We do not have to implement the bus connector, since a stock implementation is provided in the framework. Now we have two custom reusable components along with an architecture description in xADL 2.0. All that is left to do is to instantiate them. Because we already have a xADL 2.0 description of the system, we can use the ArchStudio 3 Bootstrapper or Architecture Evolution Manager to instantiate the system. To bootstrap the system from the ArchStudio 3 Bootstrapper, we put all the appropriate classes on the CLASSPATH and run:

java archstudio.Bootstrap chatsys.xml

The output should be roughly as follows:

---------------------------------------------------------------
ArchStudio 3 Bootstrap Loader 2.0
(C)2001-2002 The Regents of the University of California.
All Rights Reserved Worldwide.

Binding.
Starting the engine.
Waiting for engine to start.
Engine started.
Starting all bricks.
Waiting for all bricks to start.
Bricks started.
Reticulating splines.
Beginning all bricks.
Done beginning all bricks.
System started.

Instead of using the bootstrap, we could start the architecture using ArchStudio 3 directly. To do this, we start ArchStudio 3, then open the chatsys.xml description in the file manager:

file manager

Then, we can invoke the AEMDriver (Architecture Evolution Manager GUI Driver) from the Invoke menu:

aemdriver

And click "instantiate" (the default parameters are fine).

At this point, whether we have used the Bootstrapper or AEMDriver, two windows should appear in the 0,0 position on the screen, as seen here (after slight repositioning):

chat windows

Typing a message into one window will make it appear, prefaced by the component ID, in both windows:

chat windows

Conclusions and Wrap-Up

In this walkthrough, we covered:


Comments? Questions?

Comments or questions on this tutorial should go to Eric Dashofy.