J-Sim Official

Tutorial: Working With J-Sim

December 10, 2003

(Spanish version in MS Word)

J-Sim is an application development environment based on the component-based software architecture, Autonomous Component Architecture or ACA. This tutorial serves two purposes: first, it gives you an overview of the component-based architecture; second, it teaches you how to write components and compose components with the Tcl script language. We assume that you are reasonably familiar with JavaTM and Tcl, have installed J-Sim on your computer and know how to start the terminal in J-Sim.

Table of Contents Figures, Tables, Examples
Figure: The component-based application architecture.
Figure: Specification of the Hello World component.
Figure: The component hierarchy.

Component: HelloWorld
Component: The Template

Example 1: Hello World!
Example 2: Exposure of Ports
Example 3: Exposure of Child Components
Example 4: The RUV System
Example 5: Path Expression
Example 6: The RUV Commands

Table: A list of RUV commands



1 Scripting Using Tcl

Scripting is commonly used in a large software environment for users to access and manipulate components at the desirable granularity. Scripting is an essential part of J-Sim, for we use it to "glue" all the components and define how the system operates. We have developed a scripting interface framework to incorporate different script languages in J-Sim. However, at the time of writing this tutorial, only Tcl is fully supported in J-Sim. In what follows, we use Tcl as the example script language.

Tcl/Java, made available by third parties, is an extension to the core Tcl. It makes it possible to manipulate Java objects in the Tcl environment. We integrate the pure-Java implementation of Tcl8.0 with Tcl/Java, called Jacl, in our toolkit1 Tcl/Java defines a set of new Tcl commands for manipulating Java objects, such as creating an object from a Java class, invoking a method of a Java object, or accessing a field variable of a Java object. A list of Tcl/Java commands can be found in the online manuals at the Tcl/Java website.

1Making the whole system run only in Java facilitates the debugging process. If the performance of Tcl is more of an issue, one may consider using TclBlend. This, however, is not covered in this tutorial.

We use the following example to illustrate the use of Tcl/Java and our terminal environment. First, run J-Sim. A terminal named "TCL0" will pop up. (The terminal created using JDK1.3 in Windows is shown below.)

The terminal consists of the display area (the broad area in the center) and the input area (the one-line input at the bottom). The commands are typed in in the input area and the results are displayed in the display area. The terminal also provides several simple but useful functions in the pull-down menu. For example, you can save the current session into a file for later use.

Now to play a little with Tcl/Java, type the following commands in the terminal:

set f [java::new java.awt.Frame "Tcljava"]
set l [java::new java.awt.Label "Hello World!"]
$l setFont [java::new {java.awt.Font String int int} "TimesRoman" [java::field java.awt.Font BOLD] 40]
$f add $l [java::field java.awt.BorderLayout CENTER]
$f pack
$f show
You will see the "Hello World!" string displayed in a standard Java AWT frame.

To understand how the above Tcljava commands work, we show below the corresponding Java codes:

java.awt.Frame f = new java.awt.Frame("Tcljava");
java.awt.Label l = new java.awt.Label("Hello World!");
l.setFont(new java.awt.Font("TimesRoman", java.awt.Font.BOLD, 40);
f.add(l, java.awt.BorderLayout CENTER);
f.pack();
f.show():
To help you understand TclJava a little further, here is a quick and dirty summary of Tcl/Java commands. (The detailed syntax of TclJava commands can be found here.)
  • Use java::new to create new Java objects.
  • java::new returns a reference to the newly-created Java object. The reference can be used as a (new) Tcl command for users to invoke methods, or access field variables, of the object.
You may type "puts $f" to find out the reference to the frame which we just created. Suppose it is java0x2. With this reference, you may close and open the frame:
java0x2 hide
java0x2 show

Again, a Java object reference in TclJava is a Tcl command.

Caution: You should keep references to Java objects in Tcl variables in order for them to be alive. Otherwise, the reference will be removed and no longer be a valid Tcl command, and the referenced object is lost. (The referenced object will be garbage collected or hanging somewhere in the memory.) To verify the above, you can type "unset f" and re-enter the above commands, and you should get a Tcl exception.

To conclude this section, let's close and dispose the window by "$f dispose".

2 ACA Overview - Component and Port

Before we start writing a component, let's take a short tour of the component-based architecture.

The basic entity in the software architecture is a component. We envision an application as a composition of components. The notion of components is not new, and has been used in several commercial component-based software standards such as JavaBeans, CORBA and COM/DCOM/COM+. Unlike JavaBeans, CORBA, and COM/DCOM, the components in J-Sim are loosely coupled, communicate with one another by "wiring" their ports together, and are bound to contracts. Contracts specify the causality of data sent/received between components, but do not specify the components that participate in the communication. Contracts are bound at design time and components are bound at system integration time. One immediate advantage of this separation is that different components can be independently developed (on different platforms and/or different programming languages2) and integrated later.

2Currently, ACA is only available in Java.

Figure 1: The component-based architecture

.

Port: A component communicates with the rest of the world via its ports. A component may own more than one port. The programming interface between a component and its port is well defined. Since a component only interfaces with its ports, one component can be developed without the existence of other components. Also, the actual communication mechanism a component uses to communicate with the rest of the world is completely hidden in ports.

Contract: The behavior of a component is described by the port contract and the component contract. A port contract is bound to a specific port or a group of ports, defines the communication pattern between the component that owns the port(s), and the other components that are connected to the port(s). The component contract is the same as the traditional blackbox specification and characterizes the input/output relation of a component. A component is expected to work properly if all the adopted contracts are fulfilled.

It should be clear now that when a user writes a component, s/he has only to follow the contracts adopted by the component, but need not worry about the other components or the communication mechanism between them.

A good analogy of the component-based architecture is the current IC architecture, where a hardware module (a software system) is assembled by connecting a set of IC chips (components) through their pins (ports). When the signals arrive at the pins of an IC chip, the chip performs certain tasks in compliance with the specification in the cookbook (contract), and may send signals at some other pins. A component can be reused in other software systems with the same contract context, in much the same fashion as IC chips are used in hardware design.

Ports in a component can be organized into different groups. A port group is uniquely identified within a component by its group ID. A port is uniquely identified within a port group by its assigned port ID. Therefore, a port is uniquely identified within a component by its port group and port IDs. A component has a default port group with empty ID ("").

The ultimate goal of the component architecture is to mimic the current hardware manufacture architecture: a rich set of IC chips are available in the market. By selecting and connecting an appropriate set of chips, one can readily compose a hardware component with desirable functions. An important step towards the goal is to build a set of components that can be re-used in applications of similar nature. In the following sections, we show how to create a component from scratch and how to assemble components into an application in J-Sim.

3 The First Component - Hello World!

Figure 2: Specification of the Hello World component.

Now we write a simple component that owns a "greetings" port. As shown in Figure 2, the component expects to receive "hi" from the outside world through the port and responds with "Hello World!". The code is listed below, followed by explanations.

Component: HelloWorld
 1 /**
2 * A simple Hello World component.
3 * The component contains a "greetings" port which responds with "Hello World
4 * when it receives "hi".
5 */
6 public class HelloWorld extends drcl.comp.Component
7 {
8 drcl.comp.Port greetingsPort;
9
10 public HelloWorld()
11 {
12 super("hello");
13 greetingsPort = addPort("greetings");
14 }
15
16 public void process(Object data_,
17 drcl.comp.Port inPort_)
18 {
19 if (inPort_ == greetingsPort && "hi".equals(data_))
20 inPort_.doSending("Hello World!\n");
21 }
22 }
6 The class extends drcl.comp.Component, the base class of all components.
13 The port to receive greetings from the outside world is added.
16 The process method is invoked as a callback function when data is received at one of the component's ports.
17 inPort_ is the port at which data_ arrives.
20 The component sends "Hello World!" at inPort_.

Let's try out the HelloWorld component. After compiling the class, run J-Sim in the same directory in which the class resides. In the terminal, type the following commands:

Example 1: Hello World!
1 set c [java::new HelloWorld] 
2 [$c getPort "greetings"] connectTo [! /.term/tcl0/result@]
3 java::call drcl.comp.Util setRuntime $c [java::new drcl.comp.ARuntime]
4 java::call drcl.comp.Util inject "hi" [$c getPort "greetings"]
1 The HelloWorld component is created.
2 The greetings port is connected to the Tcl terminal so that the "Hello World!" is sent to and printed on the terminal. We will come back to explain [! /.term/tcl0/result@] in the section of RUV system.
3 Before we can use the component, we must attach a runtime to it. The drcl.comp.ARuntime class provides a default runtime implementation.
4 A greeting message is "injected" to the component at its greetings port by the inject() method of the class drcl.comp.Util.
(Note that getPort() and connectTo() are the methods in the component class in J-Sim. See here for a list of methods in the component class.

You should see the greetings on the terminal!

What's going on here: A component is activated when it receives data. The incoming data is processed in the callback method process(Object data_, Port inPort_) in a new thread context. It is possible for a component to handle multiple data simultaneously, i.e., multiple threads may be active in the same component at the same time. So a component writer must be familiar with multithreaded programming. However, a component writer only needs to pay attention to the case where different threads may access some shared data structures in the component. It is the component writer's responsibility to ensure the integrity of the shared data being accessed by multiple threads simultaneously.

4 More on Components - Component Hierarchy

In J-Sim, a (parent) component may include several sub-components (called child components). Figure 3 illustrates the concept. A component is uniquely identified within its parent component by its ID.

Figure 3: The component hierarchy.

Ports of a child component or the child component itself may be exposed to the outside world of its parent. Port exposure is realized by creating a port for the parent and then connecting it to the port of the child. The port of the parent component acts as a shadow port of that of the child component. The real communication occurs between the outside world and the child component's port.

Child exposure is realized by (i) creating a shadow port of the parent for every port of the child when the child is included in the parent; and (ii) creating a port of the child when a port of the parent is created and making the parent's port a shadow of of the child's port.

We use the following two scripts to illustrate the concept of port/child exposure.

Example 2: Port Exposure
1 set c [java::new HelloWorld] 
2 set parent [java::new drcl.comp.Component]
3 $parent addComponent $c
4 $parent exposePort $c "greetings"
5 [$parent getPort "greetings"] connectTo [! /.term/tcl0/result@]
6 java::call drcl.comp.Util setRuntime $parent [java::new drcl.comp.ARuntime]
7 java::call drcl.comp.Util inject "hi" [$parent getPort "greetings"]
1 The component HelloWorld is created.
2 The parent component is created.
3 The HelloWorld component is added to the parent component as a child component.
4 The greetings port of the HelloWorld component is exposed to the parent.
5 The greetings port of the parent component is connected to the terminal.
6 Before we can use the components, we must attach a runtime to it. The drcl.comp.ARuntime class provides a default runtime implementation. Note that the runtime instance is propagated to the component hierarchy rooted at $parent (including both the parent and the child components).
7 A greetings message is sent to the greetings port of the parent (and received by the child which in turn responds with "Hello World!")
Example 3: Child Exposure
1 set c [java::new HelloWorld] 
2 set parent [java::new drcl.comp.Component]
3 $parent addComponent $c
4 java::call drcl.comp.Util setRuntime $parent [java::new drcl.comp.ARuntime]
5 $parent expose $c
6 [$parent getPort "greetings"] connectTo [! /.term/tcl0/result@]
7 java::call drcl.comp.Util inject "hi" [$parent getPort "greetings"]
1 The HelloWorld component is created.
2 The parent component is created.
3 The HelloWorld component is added to the parent as a child component.
4 Before we can use the components, we must attach a runtime to it. The drcl.comp.ARuntime class provides a default runtime implementation. Note that the runtime instance is propagated to the component hierarchy rooted at $parent (including both the parent and the child components).
5 The child component HelloWorld is exposed (which in turn exposes the greetings port of HelloWorld.)
6 The greetings port of the parent is connected to the terminal.
7 A greetings message is sent to the greetings port of the parent (and received by the child which in turn responds with "Hello World!")

When you execute either example, you should see the greetings message displayed on the terminal.

5 The Runtime Virtual (RUV) System

In the course of developing a large software project, it may become cumbersome to use many of the Tcl/Java commands because one has to store the references of the Java objects in Tcl variables in order to access them. Naming of these Tcl variables is not at all trivial. For example, in the component hierarchy, a Tcl variable can only be used to access one component in the hierarchy and the other components/ports have to be accessed by using methods like getParent(), getComponent()3 and getPort().

To mitigate the referencing problem, we construct a referencing system, called RUntime Virtual system or RUV in short, on top of Tcl/Java. Because the component hierarchy resembles in essence the UNIX file system hierarchy, we employ the same notation and represent a component or a port as a path, in the same manner a file is represented in a file system. For example, /component1/child2 represents the component with ID child2 within the component represented by /component1; /component3/port3@group4 represents the port with ID port3 in the port group of ID group4 within the component represented by /component3. Note that we use "@" in a port path to separate the port ID and the port group of the port.

Moreover, we provide several UNIX-file-system-like commands, such as ls, cd, pwd, mkdir, cp, mv, rm and cat in the context of the component hierarchy to navigate a component system and manipulate components and ports in the system. The commands are summarized in Table 1.

3to get a child component.

Table 1: RUV commands

Command Description
! ?-apq?<path> ?<method> ?<arg> ...??
Convert the path to the reference to the Java object and then invoke the method specified
in the argument list.
Options
-a     Match all hidden components (and ports if -p is specified).
-p Match ports as well.
-q Suppress verbose warnings such as "invalid path".
!!! <obj_ref>
Cast the object reference to the most specific one.
cat ?-acdhnpqt? ?<path>?
Print the internal state (invoking Component.info(), Port.info()), and/or the connections,
of a component/port.
Options
-a     Match all hidden components (and ports if -p is specified).
-c Display only the connections for a component.
-d Display the connections in details
for a component.
-h Display hidden ports when displaying connections (-c).
-n Display ports that do not connect to other ports when displaying
connections (-c).
-p Match ports as well.
-q Suppress verbose warnings such as "invalid path".
-t Display the port type when displaying connections (-c).
cat <obj_ref>
Print the values of a Java object; especially handy when the object is a Java array.
cd <path>
Change the current working directory to path.
connect ?-acpqs? <path>... -to|-and <path>...
Connect components/ports.
Options
-a     Match all hidden components (and ports if -p is specified).
-and Set up a bidirectional connection.
-p Match ports as well.
-q Suppress verbose warnings such as "invalid path".
-s Set up one shared bidirectional connection (with -and).
-to Set up a unidirectional connection.
cp ?-apq? <source path>... <dest path>
cp ?-apq? <source path>... -d <dest path>...
Copy the components/ports.
Options
-a     Match all hidden components (and ports if -p is specified).
-p Match ports as well.
-q Suppress verbose warnings such as "invalid path".
disconnect ?-apq? <path>... 
Disconnect components/ports.
Options
-a     Match all hidden components (and ports if -p is specified).
-p Match ports as well.
-q Suppress verbose warnings such as "invalid path".
exit
Close the current terminal. If the current terminal is the last terminal, executing this command also
exits J-Sim.
inject ?-apq? <data> <port_path>...
Inject the specified data to the port(s).
Options
-a     Match all hidden components (and ports if -p is specified).
-p Match ports as well.
-q Suppress verbose warnings such as "invalid path".
ls ?-alpq? ?<path>...?
List the child components (and ports).
Options
-a     Match all hidden components (and ports if -p is specified).
-l List in the long format.
-p Match ports as well.
-q Suppress verbose warnings such as "invalid path".
mkdir ?-aq? <className> <path>...
mkdir ?-aq? <obj_ref> <path>...
mkdir ?-aq? <port_path> ?<port_path>...?
mkdir ?-aq? <component_path>
Create components/ports. If the class name or the Java object is not a component/port, a wrapper
component (drcl.comp.WrapperComponent) is created to encapsulate the object.

The fourth form uses the default class to create component(s) at the specified path. It is equivalent to
"mkdir ?-aq? <default_class> <component_path>".

Options
-a     Match all hidden components (and ports if -p is specified).
-q Suppress verbose warnings such as "invalid path".
mv ?-apq? <source path>... <dest path>
Move (rename) components/ports.
Options
-a     Match all hidden components (and ports if -p is specified).
-p Match ports as well.
-q Suppress verbose warnings such as "invalid path".
pwd
Print the current working path.
rm ?-apq? <path>...
Remove components/ports.
Options
-a     Match all hidden components (and ports if -p is specified).
-p Match ports as well.
-q Suppress verbose warnings such as "invalid path".
set_default_class ?<class_name>?
Set the default class for 'mkdir'.
term <title> ?-t <terminal_class>?
?-s <shell_class>|<shell_object>?
?<init_script>?
Create a terminal. One may specify the terminal class (e.g., Tcl/Java, Perl, Python), the shell class
or object to be associated with the terminal, and/or the initial script to execute.
whats_default_class
Print the name of the default class for 'mkdir'.

4 Following the convention in the UNIX file system, a hidden object is a component or port whose name begins with ".".

The following example illustrates how to use RUV commands to accomplish the same task in Example 1. (Note that commands are typed after the prompt "TCL0>", and lines without the prompt are the results returned.

Example 4: RUV System
1 TCL0> mkdir HelloWorld hello 
2 TCL0> ! /hello/greetings@ connectTo [! /.term/tcl0/result@]
3 TCL0> java::call drcl.comp.Util inject "hi" [! /hello/greetings@]
4 Hello World!
5 TCL0> inject "hi" hello/greetings@
6 Hello World!
1 The component with ID "hello" is created..
2 The greetings port of the hello component is connected to the terminal.
3 A greetings message is injected to the hello component.
5 A greetings message is again injected to the hello component using the relative path and the "inject" Tcl command.

Now we can explain what /.term/tcl0/result@ means in all these examples: the terminal tcl0 is itself a component! It is stored in the /.term/ "directory" when the system is brought up. The terminal contains a "result" port in the default port group. Whenever the terminal receives data at this port, it displays the data. When we send "hi" to the greetings port of hello component, the component responds with "Hello World!" which is forwarded to the result port of the terminal and displayed on the terminal.

Note that we do not attach any runtime to the component as we did in previous examples because the root component of the RUV system has the default runtime instance attached to it and the runtime instance is automatically propagated to child components when they are added to the parent component.

RUV has powerful path expressions. In addition to the wildcard expressions using "*" and "?", one may use the range expression "<ID>-<ID>" that specifies a range of components. For example, "n3-n10" specifies the components "n3", "n4", ..., and "n10". One may also use exhaustive expression "..." that specifies all the components that match the partial path. For example, USA/.../Columbus would match USA/Ohio/Columbus, USA/Georgia/Columbus, USA/Indiana/Columbus, USA/Mississippi/Columbus and USA/Nebraska/Columbus if all the states and cities are built in the USA component hierarchy. Another possible path expression is to use the component instance at the start of the path, as illustrated in the following example (this is the most succinct version of all the HelloWorld example in this tutorial):

Example 5: Path Expression
1 TCL0> set c [mkdir HelloWorld hello] 
2 TCL0> connect $c/greetings@ -to /.term/tcl0/result@
3 TCL0> inject "hi" $c/greetings@
4 Hello World!

For more about path expressions, please refer to the RUV commands reference page.

In what follows, we continue the above example and illustrate how to use other RUV commands.

Example 6: RUV Commands
 1 TCL0> cp hello hello2
2 java0x46
3 TCL0> ls
4 hello hello2
5 2 objects listed.
6
7
8 TCL0> ls -lp hello2
9 greetings@ drcl.comp.Port IN&OUT, unconnected, exe boundary
10 1 object listed.
11
12
13 TCL0> ls -alp hello2
14 .info@ drcl.comp.Port IN&OUT, connected, exe boundary
15 greetings@ drcl.comp.Port IN&OUT, unconnected, exe boundary
16 2 objects listed.
17
18
19 TCL0> connect hello/greetings@ -and hello2/greetings@
20
21 TCL0> ls -alp hello2
22 .info@ drcl.comp.Port IN&OUT, connected, exe boundary
23 greetings@ drcl.comp.Port IN&OUT, connected, exe boundary
24 2 objects listed.
25
26
27 TCL0> cat -c hello
28 ----- /hello/ -----:
29 Connections:
30 Wire 1 ---> greetings@ <--- /hello2/greetings@
31 Wire 2 ---> /hello2/greetings@ <--- greetings@
32 No extra info is provided in HelloWorld
33
34
35 TCL0> cat hello/greetings@
36 ----- hello/greetings@ -----:
37 hello/greetings@
38 Type: IN&OUT
39 ExecutionBoudnary: true
40 IsRemovable? true
41 <->Peer 1: hello2/greetings@
42 -->Peer 2: .term/tcl0/result@
43 ---Peer 3: .term/tcl0_shell/result@
44 No client.
45
46
47 TCL0> man
48 UNIX-like commands: ! !!! cat cd cp ls man mkdir mv pwd rm.
49 connect/disconnect: connects/disconnects components/ports.
50 attach/detach: attach/detach components/ports.
51 setflag/getflag: sets/displays component flags.
52 term: creates a new terminal.
53 exit: exits the terminal.
54 quit: exits the program.
55 Execution control:
56 script: schedules a script.
57 wait_until: blocks the terminal until a condition is met.
58 Topology construction:
59 explore: explores the component topology from a component.
60 Verification:
61 verify: verifies connections within a container component.
62 Other utility commands:
63 grep: searches a pattern in a big chunk of text and prints the result.
64 subtext: extracts lines of text from text.
65 nlines: calculates the number of lines in a big chunk of text.
66 _to_string_array: converts a Tcl list to a Java array of String.
67 set_default_class: sets the name of the default class for 'mkdir'.
68 whats_default_class: returns the name of the default class for 'mkdir'.
69
70 For more help on a particular command, use man <command>.
71
72
73 TCL0> man ls
74 ls ?-apql? ?<path>...?
75 List the components in the path specified.
1 Copy the hello component to another component with ID "hello2".
3 List the components in the current path.
8 List, in the long format, all components/ports (not including hidden objects) in the hello2 component. Note that the greetings port is unconnected.
13 List, in the long format, all components/ports in the hello2 component. Note that the info port is shown.
19 Connect the greetings ports of the two hello components.
21 List, in the long format, all components/ports in the hello2 component. Note that the greetings port is now connected.
27,35 Display the connections of the hello component. Note that the greetings port is now connected to hello2/greetings@ and the terminal's result port.
47 List all the RUV commands available. (Newer version of J-Sim will probably have more commands available.)
73 Man the command "ls".

6 Template for Writing a Component

Now we are in a position to discuss how to write a component. We provide the following example which may serves a template for writing a complete and correct component. We also give the guidelines for documentation and several rules which a component writer must comply with. Essentially one must override one method, process(), and may override six other methods, reset(), duplicate(), info() and _start()/_stop()/_resume() in a component.

Component: The Template
  1 import drcl.comp.*;
2
3 /**
4 * Template for writing a component.
5 * When data arrives at one of the ports, this component responds with the data received last time.
6 * That is, when the component receives the (i+1)th data at inPort_, it sends the ith data at inPort_.
7 */
8 public class ComponentTemplate extends drcl.comp.Component
implements drcl.comp.ActiveComponent
9 {
10 // The fields here are simply defined to demonstrate how
11 // methods in this template are used.
12 int x;
13 Object lock;
14
15 /**
16 * Constructor.
17 */
18 public ComponentTemplate()
19 {
20 super();
21 }
22
23 /**
24 * Constructor.
25 */
26 public ComponentTemplate(String id_)
27 {
28 super(id_);
29 }
30
31 /**
32 * Invoked when data_ arrives at this component at the inPort_ port.
33 */
34 protected void process(Object data_, drcl.comp.Port inPort_)
35 {
36 // Put codes here for handling the incoming data.
37 synchronized (lock) {
38 x++;
39 notify(lock); // Notifies previously-waiting thread.
40 wait(lock); // Delays sending data until someone notifies.
41 inPort_.doSending(data_);
42 }
43 }
44
45
46 /**
47 * Resets this component to the initial state for use anew.
48 * Must call super.reset() in the beginning.
49 */
50 public void reset()
51 {
52 super.reset(); // Let super class reset its fields.
53 x = 0; // Reset the fields defined in this class.
54 }
55
56 /**
57 * Copies the content from the source_ to this component.
58 * Must call super.duplicate() in the beginning.
59 */
60 public void duplicate(Object source_)
61 {
62 super.duplicate(source_); // Let super class copy its fields.
63 ComponentTemplate that_ = (ComponentTemplate)source_;
64 x = that_.x; // Duplicate the fields defined in this class.
65 // Suppose we don't have to copy lock.
66 }
67
68 /**
69 * Invoked when the component is run()ed.
70 */
71 protected void _start()
72 {
73 debug(this + " is starting!");
74 }
75
76 /**
77 * Script interface which reveals the internal states of the component.
78 * It is for debugging and demonstration purpose.
79 */
80 public String info()
81 {
82 return "Current count = " + x + "\n";
83 }
84
85 /**
86 * Script interface which increments the counter by +1.
87 */
88 public void increment()
89 {
90 x++;
91 }
92 }

Method Overrides

  1. A component is triggered by data (process(), line 34)
  2. The process() method is the heart of the component and implements the behavior of the component. Specifically, the method is invoked by the system whenever some data arrives at one of the component's ports. As mentioned earlier, the method is executed in a new thread context. It is possible for a component to handle multiple data simultaneously in multiple threads. It is component writer's responsibility to ensure maintain data integrity and synchronization among multiple threads. For example, the code block in lines 37-42 is protected by the keyword synchronized, which imposes the restriction that only one thread is allowed to access the code block at any time.
  3. A component can be reset (reset(), line 50)
  4. The reset() method sets the component to its initial state. It allows the component to be started anew after being executed for some time. To correctly override the method, the subclass must call super.reset(). When recursive calling of the super.reset() method reaches drcl.comp.Component, the method resets the ports and the child components in a recursive manner.
  5. A component can be duplicated (duplicate(), line 60)
  6. The duplicate() method allows the component to be clone()d. A subclass should override this method to copy the fields defined in the subclass. When overriding the method, the subclass must call super.duplicate() so that the super class can copy the fields defined in it. When recursive calling of the super.duplicate() method reaches drcl.comp.Component., the method duplicates the ports and the child components in a recursive manner, and then connects the child component in the same manner as in the source component. The method originates from the drcl.ObjectDuplicable interface. It complements the clone mechanism that exists in the java.lang.Object. The RUV system command cp uses this method to do the tricks.
  7. A component may be started as a data source (_start() line 71)
  8. In addition to being triggered by data that arrive at ports, a component can be started by the run() method. The run() method creates a new thread and then the thread calls the _start() method of the component. The run() method also calls the run() method of the child components, until all the components in the hierarchy are _start()ed. As not all the components in the hierarchy need to be _start()ed, we provide the drcl.comp.ActiveComponent interface. In the run() process mentioned above, only when a component implements the interface will a new thread be created which in turn calls the_start() method of the component.
  9. One can get state information of a component during runtime (info() line 80)
  10. For the purpose of on-line debugging and monitoring, a component should override the info() method to provide useful information such as the internal states of the component.

Rules of Component Writing

  1. Script interface
  2. In addition to the methods discussed above, a component may provide several script interfaces (e.g. increment() in line 88) to manipulate the component from the scripting environment (e.g. in a Tcl terminal). However, as viewed from a component, the script interfaces of the other components can not be accessed because by virtue of the loosely coupled component architecture, these interfaces are blocked by ports. As a component is interfaced with ports, it does not know to which components it is connected and cannot readily access the interfaces in other components (review).
  3. Data sent is out of sender's control
  4. When a component sends a piece of data, it does not own the data anymore. This rule is not enforced by the programming language. Programmers must make sure that the component does not operate on data that is already sent. No assumption can be made on the data (e.g. modified or recycled by the receiving component) unless it is clearly stated in the contract.
  5. Using wrapped APIs to do thread synchronization
  6. In order to gain better control on threads which process data on a component, we provide wrapped thread synchronization APIs, namely wait(Object)/notify(Object)/notifyAll() in drcl.comp.Component in replace of wait()/notify()/notifyAll() in java.lang.Object. The semantics of thread synchronization are still the same as in Java. Please refer to Sun's tutorial for more information on thread synchronization.

For more details regarding how to write a component, please refer to the Component Writer's Guide.

* JAVA and all JAVA-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the U.S. and other countries.

~ END ~