Programming in REJO

REJO is an extension of Java that creates Reactive Objects, that's to say, objects that have data and a mix of java instructions and reactive instructions. The execution model is that of Reactive Synchronous Approach which executes the java instructions in an atomic way. These objects may be considered as Mobile Agents because they can migrate using a platform, called ROS, that provides the functionalities that they need.

Since Java doesn't have primitives to make a reactive programming, REJO and ROS use Junior. REJO/ROS is the evolution of RAMA platform which was created with the propose of testing the reactive paradigm in order to build mobile agents.

This tutorial presents the basis for building a reactive system using REJO language. The first section introduce reactive systems and agents systems. The section 2 presents REJO: program's structure and instructions.  Section 3 describes the steps for compiling and running a REJO program. Finally, section 4 gives some examples and section 5 resumes the characteristcs of REJO.


1 Introduction

The reactive systems were introduced by Harel and Pnueli [1] and developed in several laboratories that lead to the synchronous model and a set of programming language, called synchronous, that implement it. Among these languages one finds ESTEREL[12], Lustre[13] et Signal[14]. However, the synchronous model presents some restrictions that avoid, for exemple, its utilisation in the construction of dynamique systems. Such restrictions led to the creation of the synchronous reactive model that was, firstly, implemented in C (Reactive-C[6]) and then in Java (SugarCubes[9] and Junior[4]) and Scheme (Senior [15]). Nevertheless, it miss a programming language that rejoints the advantages of previous languages and adds anothers. In particular it miss:

This paper presents REJO language which is the proposition of a new reactive language, it is rather an extension of Java in order to create REactive Java Objects (REJO). The reactive objects can migrate in a natural way using the reactive framework, and the necessary functions typical of Mobile Agent (like the assignation of names) are provided by the platform named ROS. This platform executes the objects and provides the typical operations of a MAS. From now on we'll use reactive object, REJO and agent as synonyms.

The REJO programs are translated in code 100% JAVA. The reactive instructions are translated by the REJO Compiler (REJOC) in Junior instructions. The result is a language with the following characteristics:


2 REJO language

REJO is high level language that allows programmers to build reactive systems easily. As a high level language, REJO translate its instructions in a low-level language (Junior) hiding its complexity. In the case of REJO, it hides the complexity of using logic conditions (called Configurations in Junior), using atomic instructions, and using reactive variables (called i.e. IntegerWrappers). It also introduces some notions that don't exist in the low-level language, for instance the notion of method, local reactive variables, and the inheritance of reactive behaviors. All these concepts will be described in the following sections.

2.1 REJO execution model

High level description

REJO language is a language that merge to 3 models, the model of: imperative languages, OO languages (Object Oriented languages) and reactive languages. The mix is implemented in the following fashion:

The figure 1 shows the relation among the 3 models.

Figure1: REJO execution model.

 

Low level description

The REJO language was built with the objective of obtaining a high level language based on the Junior language. In other words the REJO language was born from the evolution of the Junior language and that is why REJO keeps many concepts from Junior (those that we think are the main advantages of Junior) and hides and adds others (those that make the reactive programming difficult or miss in Junior). One of the main advantages of Junior, that REJO keeps, is the specification of its execution model using rewriting rules. Rewriting rules are used to define the semantics of each reactive instruction as well as the notion of executing a Junior program (look Reactive Synchronous Model description).

The graphical representation of a Junior program is a tree formed by nodes that represent the reactive instructions. For exemple if we consider the following Junior program:

Jr.Par(Jr.Seq( Jr.Await( Jr.Presence( Jr.StringWrapper("e")) ),Jr.Seq(
                       Jr.Stop(),
                       Jr.Atom( new Print("E") ) ))
          ,Jr.Seq( Jr.Seq(Jr.Stop(),
                                  Jr.Generate( Jr.StringWrapper("e") )),
                        Jr.Stop())
          )

its execution tree is show in the figure 1.

Figure 2: Junior execution tree.

As you can see in the figure 1, the Junior semantics does not define the relation among reactive instructions and variables. Of course that no language can be used without the concept of variables and that is why Junior defines Wrappers. Wrappers allows programers to define variables that will be evaluated , at run time, by reactive instructions. The problem with Wrappers is that they are difficult to use because it is a low level concept that does not allow programers to implemented scopes of variables. In addition the notion of method, that may have parameters and local variables, does not exist and so there is no way to make a program in a modula way.

The REJO execution model takes the Junior execution model and adds the following concepts:

To illustrate these 4 concepts the figure 2 shows the graphical representation of the REJO program presented in the section 4.2. Like Junior, the graphical representation of a REJO program is a tree formed by nodes that represent reactive instruction. The reactive methods are represented by a set of nodes (enclose in a dotted box ) that form a conex graph. Each reactive methode may have a space of variables asociated.

Figure 3: REJO execution tree

In order to execute a Junior program one use a reactive machine that makes react the reactive instructions. Like Junior, REJO uses also a reactive machine to make react reactive methods, this means that it is possible to execute many reactive methods of differents REJO objects and to execute Junior programs at the same time.

REJO language is implementes on top of Junior but the REJO execution model defined in this section is sufficently abstract to be implemented in another way (a field of research to explore).

2.2 REJO program structure

The structure of a REJO class is the same as a Java class to which it can be added special methods named reactive methods. These reactive methods have their own syntax which is very similar to the Java syntax and is given in the next section, and their own semantic, the Junior semantic. A REJO program may have several reactive methods and they can be put anywher inside of the class. In summary, the only difference with a standard Java class is the presence of reactive methods.
 

package package_name
import package_name

public class class_name
{
 Variables;

   Java Methods
   Inner classes
   ...
   Reactive methods
}

Once created a REJO object, the programmer has two options to execute it:
  1. To follow the steps done in Junior :
    1. Create a main method with an instance of the class,
    2. Add the reactive instructions to the machine, and
    3. Activate the machine in order to execute the program.
  2. To load the REJO class in a ROS either by loading manually one instance of the class or using the loader provided by the ROS system, the reactive shell (Rsh). Rsh will create an instance of the class and it will automatically add it in the system.
When a REJO program is executed on the ROS system it has to follow some rules:
  1. There must exist a reactive method called rmain. The rmain method plays the same role as the main method in a Java program: it is the method to which the system gives the control.
  2. The REJO program must implement the interface Agent.
These rules add to the REJO two things that will allow it to be executed in ROS platform: the notion of local name and some methods for performing the migration. For more information about the execution of REJO programs, see the section 3.
 

2.3 Reactive methods and basic reactive instructions

A reactive method has a syntax and a semantics very similar to the Java methods; basically it is a sequence of instructions that has a name and that may have local variables and parameters.
 
modifiers reactive Method_Name( parameters )
{
local variables;

   reactive instructions
}


The sequence of reactive instructions is a reactive instruction which is hidden to the programmer. Besides of sequence of reactive instructions there is another compound instruction, the par instruction which is explained in the section 2.6. The syntax of reactive methods is the same of Java methods with two differences: they return a value of type reactive, and the set of reactive instructions that can be used in a reactive method are of three types:

  1. The reactive instructions defined in the following sections, for instance par, loop, repeat, etc.
  2. The subset of Java instructions named expressions, for example the invocation of methods and the expressions that use the assignation operator.
  3. The declaration of Java variables. These variables are local to the reactive method as in Java methods.
The semantics of the expressions and the variables is the same of Java and the semantics of reactive instructions is given in the following sections.
 

In order to make the Java instructions and reactive instructions coexist, the Java instructions are executed like atomic instructions. An atomic instruction is an instruction that finishes instantaneously, i.e. it starts and finishes its execution in the same instant. The notion of instantaneously termination is a concept to keep in mind because some reactive instructions may take one or more instants for being executed, i.e. they can finish instantaneously or not. There is another instruction, like the atomic one, that also finishes instantaneously: the nothing instruction. The nothing instruction is used for implementing the execution model and programmers rarely will used it.

The instructions of the reactive methods are executed in sequence without interruption until the stop instruction stops the execution for the actual instant. The end of an instant is defined in the same way as Junior, when all the instructions finish or are stopped. In general, programmers do not care about instruction's state, TERM, STOP or SUSP, because these values are handled by the reactive machine for implementing the behavior of each instruction. However, in order to understand how the program is executed they have to take them in account.

This section finishes with the presentation of one of the simplest programs that can be programmed in REJO, the program that prints ''Hello World''. This program uses the instruction System.out.print that prints a string on the screen atomically.
 

import ros.kernel.*;

public class HelloWorld implements Agent
{
   public reactive rmain(String[] args)
   {
        System.out.println(" Hello World");
   }
}

2.4 Atom instruction

In the previous section we say that in a reactive method you can only use a subset of Java instructions. The reason of this constraint is to avoid writing some instructions that do not make sense (they would not have semantics) in the reactive model or they could not be executed by the reactive machine. For instance in the programme
 
for(int i=0; i<10; i++)
   stop;


there would be a problem executing the for instruction by the reactive machine. The for instruction is not a reactive instruction and thus the reactive machine would not know how to interpret the stop code returned by the stop instruction.
The problem with this restriction is that it forces you to write a Java method each time that you want to use any flow control structure and to make an atomic invocation to it (remember that invocations are valid expressions).

The atom instruction allows programmers to write almost any kind of Java instruction as an atomic instruction without defining a Java method.
 

Syntax

atom { Java instructions }

 
Example
int j=10;
atom{
    int i=5;
      if(i < j)
        for(int k=0; k<i; k++)
          System.out.println("k= "+i);
}

 
We end this section with two atomic instruction used for printing: print and println. These instructions are similar to the System.out.println Java instruction. These instructions are provided for defining the standard output in ROS platform. In order to use them out of ROS platform you have to implement the Agent and Output interfaces (for more information about them look at ROS documentation).
 

2.5 Invocation of reactive methods

The previous section presented the use of Java instructions by reactive instructions. These Java instructions are executed atomically and if you do not use the keyword atom it is only possible to use a subset of Java instructions. This subset includes invocations of methods and thus to invoke a Java method you just type its name and its parameters, if needed, for example:
 
public reactive rmain(String[] args)
{
  java_method();
}

 
Of course, when a Java method is invoked it is possible to catch the returned value (if it was defined) and process it.
 

However, when a reactive method is invoked you have to use the keyword call, for instance:
 

public reactive rmain(String[] args)
{
   call reactive_method(3);
}
reactive reactive_method(int i)
{
   System.out.println("i= "+i);
}

 
The distinction between Java methods and reactive methods is done because the last ones are reactive instructions that are built and and introduced at the point where the call keyword was used, i.e. the call instruction is basically a macro. The consequence of this is that it is not possible to use recursion in reactive methods.

Since the call instruction allows you to insert reactive instructions, it is possible to use not only reactive methods but also any Java instructions that returns Junior instructions, for example:

1.
  call java_method();
  ...
  Program java_method()
  {
     return Jr.Loop(Jr.Stop());
  }
2.
  Program p = Jr.Stop();
  call p;
3.
  call Jr.Repeat(5, Jr.Stop());

 
Examples is in section 4 illustrates this.
 

2.6 Par instruction

The par instruction implements a primitive parallelism. It is a primitive parallelism because its branches are not executed at the same time as in a multiprocessors system and the parallelism is controlled by the programmer using the stop instruction. This parallelism can be seen as a variant of threads programming in which you give explicitly the control. In REJO you do it using the stop instruction and in threads programming you usually use a method called yield. In other words, the par-stop instructions allow programmers to build cooperative systems and not preemptive systems 1.

The par instruction implements a primitive parallelism in which the branches are activated at each instant and the par instruction finishes when all the components have finished. The semantics of par instructions does not specify the order in which the branches are executed and thus you should not assume an specific order 2 when you build your program.

Finally, the par instruction finishes when all the components have finished and it must have at least two components.
 

Syntax

par { reactive_instructions || ... || reactive_instructions }

 
Example

This example shows how the par-stop works together for implementing the parallelism and how the structure of the program defines the instants.

                                             Instants
                                         #1    #2    #3
    par                                   |     |     |
    {
       System.out.print("i11");           |
       stop;                              |
       System.out.print("i12");                 |
       stop;                                    |
    ||
       System.out.print("i21");           |
       stop;                              |
       System.out.print("i22");                 |
       stop;                                    |
       System.out.print("i23");                       |
       stop;                                          |
    ||
       System.out.print("i31");           |
       stop;                              |
    }
 

2.7 Loop instruction

A loop is an instruction that allows to execute several times a set of instructions named loop's body or body. If the number of times is infinite, we say that it is an infinite loop, otherwise it is a finite loop. In REJO, like in Junior, two instructions allow creating infinite loops and finite loops; they are named loop and repeat respectively. In this section we present the loop instructions and the next section presents the repeat instruction.

As it was said, the loop instruction implements an infinite loop, however when you use this kind of loop you have to pay attention in the way of using this instruction. If you build a loop that, after having completely executed its body during an instant, restarts immediately to execute it in the same instant, we say that you have an instantaneous loop. Instantaneous loop are potentially dangerous because they can stop the execution of whole program, remember that you are programming a cooperative system and that this means that soon or late you must give the control to the other components if you want that the system evolves.
 

Syntax

loop
  body

 
The loop instruction syntax is, in essence, the same that the Java control structures syntax, for example there can be spaces and enters between the different components. Like Java, the parenthesis "{" and "}" are necessary if the body has more than one reactive instruction; this is also true for the other REJO instructions described in the following sections. Of course we recommend, like in any programming language, to indent your programs in order to make them understandable.
 

Examples

loop
{
   System.out.print("Hello World");
   stop;
}
          This program prints Hello World at each instant.
loop
{
   System.out.print("Hello");
   System.out.println("World");
}

 
          This instruction prints: "Hello World" forever during the first instant.

2.8 Repeat instruction

This instruction implements finite loops, in other words it executes its body a fixed number of times: the value read the first time that the instruction is executed. Unlike infinite loops, the fact that the body finishes instantaneously is not a problem because it will finish so it is not dangerous to omit the stop instructions.
 
 

Syntax

repeat( expression )
  body

 
Example
repeat(5){
   System.out.print("Hello ");
   stop;
}
This program will print 5 times Hello, one by instance.
 

2.9 Reactive Variables

In REJO, as in Junior, you can use variables in reactive instructions. A problem with these variables is that they have to be read when the reactive instruction is executed and not when the instruction is built. In order to resolve this problem, Junior defines Wrappers of Variables, for example IntegerWrappers and BooleanWrappers. One of advantages of using REJO is that Wrappers are used when they are necessary, i.e., the compiler builds them whenever a variable is used in a reactive instruction. The only precaution that you have to take in account, as usual, it is that types match.

For example, when you use a repeat instruction you can use a variable for defining the number of times that the loop will be performed. The repeat instruction specifies that this number is read the first time we execute the instruction, thus if you put a variable, like in the following example, the repeat instruction will be executed with the value read when the instruction is executed the first time (3 times for the given example below) and this value does not change if you change the variable's value.
 

int i=2;

  i++;
  repeat(i){
     System.out.print(i+", ");
     i++;
     stop;
  }


This program prints:  3, 4, 5.
 

2.10 If instruction

This instruction allows picking and following the execution between two branches according to the evaluation of a boolean condition. If the condition is true then the first branch (if branch) is executed, else the second one (else branch) is executed. Boolean conditions are java boolean conditions with one restriction: they cannot test the presence or the absence of events, for doing that you have to use the when instruction (section 2.14).
 

Syntax

if( boolean condition )     if( boolean condition )
   body                        body_1

                            else
                               body_2
Example
 
int i=5, j=10;
  if( 2*i == j ){
    repeat( i ){
      System.out.print("i= "+i);
      stop;
    }
  }

2.11 Events and Event conditions

Events are a key concept in the Reactive Synchronous Model, most of the reactive instructions define a behavior depending on an event, for example events are used for controlling or preempting reactive instructions. Events can be implemented using strings but programmers can also use a more general definition, Identifiers (for more information look at the documentation of Junior [4]). Events are defined in the following way: In this way, we find in REJO the reactive paradigm's events with the delayed reaction to the absence that eliminates the causality problems 3.

In REJO you can also use Event Conditions, i.e. you can build expressions for testing if two or more events are present in the same instant (AND operator) or testing if at least one of them is present (OR operator). You can also test if an event is not present in the instant (NOT operator). These are the only 3 operators that you can use for testing conditions formed with events.

Each of these operators are implemented in REJO, like in Java, using the characters &&, || and !. As in Java the AND and the OR operator have the same precedence (and therefore evaluated from right to left), and the NOT operator has more precedence than AND and OR operators. To change the precedence of the operators or the order, between AND and OR operators, you can use "(" and ")" characters as in Java.
 

Some examples of event conditions are:

cond1 = !("a"&& "b") || "c"
cond2 = ! "a" && var1 || var2 + "b"
The following sections show how you can generate events and how you can react if an event condition is satisfied.
 

2.12 Generate instruction

This instruction generates an event in the execution environment and finishes immediately, i.e., in an atomic way. The type of events is defined in a general way using the notion of Identifier. An Identifier allows defining events using any kind of object that has at least an equals method. Thanks to this abstraction programmers can use any basic data type or a combination of them, for instance, integers as events. However, the use of Identifiers implies that programmers must define their own notion of event. For avoiding this job, REJO defines the StringIdentifier type that allows generating Strings as events. Of course String events are handled transparently by REJO and thus you do not have to define anything. For more information about Identifiers look the Junior spec.
 

Syntax

Event !;
(gen|generate) Event_Expression [, objs];

 
Examples
"Event" !;                          // Example 1
gen "Event";                        // Example 2
gen "Event" + variable + method();  // Example 3
gen variable;                       // Example 4
gen method();                       // Example 5

 
Note that the syntax used in the example 1 accepts neither objects nor expressions whereas the gen syntax accepts string expressions (like in example 3), Objects and Identifiers. In the examples 5 and 6 the variable and the method can be strings or Identifiers, the gen instructions test the type.

When an event is generated, no matter its type, you can attach an object to it, in other words you can generate events with values. In this way, it is possible to not only synchronize behaviors using events but also communicate data at the same time.

gen "Event", "Obj String";          // Example 6

 

2.13 Wait instruction

This instruction implements a reactive behavior which consists in waiting that a event conditions is true. If the event condition is true the control pass to the next instruction in the same branch, otherwise the instruction waits untill the event condition is true. The wait is implemented by testing if the event condition is false or the end of instant is achieved. If the end of instant is achieved the instruction returns stop, i.e., it behaves as a stop instruction for that instant.
 

Syntax

Event ?;
wait Event_Condition [, objs];

 
Examples
"Event" ?;                            // Example 1
wait "Event";                         // Example 2
wait "Event" + variable + method();   // Example 3
wait ("a" && variable ) || !method(); // Example 4
 
As in gen instruction, the use of the character ? allows waiting for simple events and not for event conditions or events formed by string expressions. In the case of examples 4 the wait instruction tests the event's type for knowing which type of Identifier will be used.
 

When a valued event has been generated, you have to use a special Junior instruction for retrieving the values. In REJO you have two constructions for doing that:
 

currentValues  Event, objs;
previousValues Event, objs;

 
These instructions returns an array of Objects in objs thus it is the programmer responsibility to make a correct cast to recover the type emitted. There is another syntax that allows doing both the wait and the previousValues instructions in one step:
 
wait "Event", objs;
 
The programmer should be careful with this syntax because the semantic of the instruction hides a stop instruction; in fact this instruction is equivalent to:
wait "Event";
stop;
previousValues "Event", objs;

 

2.14 When instruction

This instruction allows picking and following the execution between two branches according to the evaluation of an event condition. If the condition is true then the first branch (when branch) is executed, else the second one (else branch) is executed.

The particularity of this instruction is that it can finish instantaneously and when it does not finishes instantaneously its behavior hides a stop instruction. The instruction finishes instantaneously when the event condition is satisfied before the end of instant and the chosen branch (the first branch if the condition is satisfied and the else branch in the other case) finishes in the same instant. We say that it hides a stop instruction because if the value of event condition in known at the end of instant, for example when the absence of an event is tested, then the chosen branch is executed at the next instant.
 

Syntax

when( event condition )     when( event condition )
  body                        body_1
                            else
                              body_2

 
Example

The following example shows the effect of the stop instruction hidden in the when instruction. The program is compound by two branches: the first one generates the event "E" in the fourth instant and the second one tests its presence. The fact is that even though the second branch tests the presence of the event it does not detect it because the branch is unsynchronized with the first one because of the introduced stop instruction. The right side of the program gives two traces of the program's execution: the first one when you run the program with N=3 and the second one with N=2.

                                       Trace 1                     Trace 2
                                 #1   #2   #3   #4   #5   #6     #1   #2   #3   #4   #5
par                                |    |    |    |    |    |      *    *    *    *    *
{
   repeat(N)                      |    |    |                     *    *
     stop;                        |    |    |                     *    *
   "E" !;                                        |                          *
||
   repeat(3){                     |    |    |    |    |           *    *    *    *    *
     when("E")                    |         |         |           *         *    *
       System.out.print("e ");                                              *
     else
       System.out.print("!e ");        |         |         |           *              *
     stop;                             |         |         |           *    *         *
   }
}

 
The output of the program using the value 3 is: ``!e !e !e'' and ``!e e !e '' in the other case.
 

2.15 Until instruction

This instruction implements the preemption, in other words it forces the termination of a set of reactive instructions (the body of the until instruction) according to an event condition. The preemption is weak, i.e., it occurs after body reacts, if the event condition becomes true and the body has stopped. Junior, and therefore REJO, uses weak preemption for avoiding causality problems.

The instruction may have two parts: a body and a handler. The handler's code is optional and it is executed when the preemption is done.

The until preemption can be instantaneous or not. We say that it occurs an instantaneous preemption if the event condition is true during the instant; in this case the handler is executed instantaneously, i.e. in the same instant. If the preemption is not instantaneous the handler is executed at the next instant.
 

Syntax

until( event condition )       until( event condition )
  body                           body_1
                               handler
                                 body_2

 
Example
par
{
  loop{
      until("Preempted_event")
         System.out.println("Preempted Action");
      handler
         System.out.print("Handler execution");
      stop;
  }
||
  repeat(10)
     stop;
  "Preempted_event" !;
}

 

2.16 Control instruction

This instruction executes its body when an event is present. The control instruction finishes when its body finishes.
 
 

Syntax

control(  event condition )
  body

 
Example
par
{
  repeat(10){
     control("Waited_event")
        System.out.print("The event is generated");
     stop;
  }
||
  repeat(5)
    stop;
  "Waited_event" !;
}

 

2.17 Local Instruction

This instruction defines events that have local scope to the body section. The local instruction avoids the input and output of the event identified by the variable or constant event.
 

Syntax

local( string_event )
  body

 
Example

This example shows how you can use another event for regenerating the event hidden by the local instruction.

par
{
  loop
  {
     local("Ext_Event")
       par
       {
          "Local_Event" ?;
          "Ext_Event" !;
       ||
          loop{
             control("Ext_Event")
                System.out.print("The event was generated");
             stop;}
       }
       stop;
  }
||
  repeat(20)
     stop;
  "Local_Event" !;
}

 
We end this section suggesting that you take a look at REJO's structure. REJO structure is a good example that shows the use of event conditions with local, control, until and par instructions for building a set of instructions that allow users to resume, suspend and kill a reactive program using events. This is the control mechanism used in the ROS platform.

2.18 Freezable instruction

This instruction allows freezing a program that is been executed in the machine; in other words, the program is removed from the machine and is stored in a machine's internal variable. The program is frozen when an event, given as a parameter, is generated. Actually the program is frozen when the instant finishes for avoiding causality problems. If at the next instant somebody freeze an instruction using the same event, the old frozen instruction will be lost, therefore you have one instant for taking them from the machine.
This instruction is semantically similar to the until instruction, the difference is that this instruction does not accept event conditions and that the body is frozen always at the end of instant.

This instruction is the basis for doing the migration that is why it is used in the implementation of agents in ROS. For more information read the ROS's tutorial.
 

Syntax

freezable( Event )
  body

 
Example

This example shows how you can freeze a program and reload it after.

Program prog;
 par
 {
    repeat(20)
      stop;
    "cold" !
    stop;
    prog=GetProgram("cold");
    repeat(5)
      stop
    Ros.LoadInst(prog);     // This instruction is dependent plateforme
                            // in general you'd use machine.add(prog);
 ||
    freezable("cold"){
       int i=1;
       repeat(10){
         System.out.println("i= "+i);
         i++;
         stop;
       }
    }
 }

 

2.19 Agents and Migration

When you build a REJO, you have the possibility of choosing if the REJO will be an agent or not. If the REJO is an agent it can be executed in the ROS platform and uses the ROS services, for exemple the migration between ROS systems, the ROS name service and the REJO control service. In order to build an agent you have to program it and compile it in a particular way; these particularities are explained in this section and the following. For avoiding confusion we will only call agent a REJO that can be executed in the ROS system.

In order to built an agent, the REJO class must 1) implement the Agent interface (and thus you have to import ros.kernel.*;) and, 2) be compiled with the option -ros (for more information about compilation options see the next section). An agent class may use the ROS services in three ways: 1) using the ROS API, 2) generating evens and 3) Programming some methods. The explication of these three utilisations follows:

Using the ROS API

ROS offers a set of Java methods (defined in the ROS API) that can be invoked using the ros variable available in all agents. The ros variable (and other agent variables explained also in the ROS API section) is updated automatically by the ROS system when the agent is loaded. The next subsection shows an example of ros variable.

In a Operating System users may use the system at two levels: programming level and man-machine level. The ROS system may be seen in the same way; Java methods defined in the ROS API can be seen as the system calls and two REJO applications, the Rsh and the ROS GUI explained in the ROS tutorial, offer the man-machine interface.

Generating events

Some ROS services are launched by the generation of events, for instance the migration service is started by the generation of an event. In fact, the migration service is started by the generation of two valued events which have as identifier the name of the Agent plus the string "!migra" and as data the machine address and a ROS identifier.
The following example illustrates the migration service. It defines a REJO that does nothing for 100 instants and then try to migrate, at the end it uses the migGet primitive defined in the Agent interface for verifying if the migration was successful.

public class MyAgent implements Agent
{
 public reactive rmain(String[] args)
 {
    repeat(100){
      System.out.print(".");
      stop;
    }
    System.out.println("");
    gen locName()+"!migra", "machine address";
    ros.generate(locName()+"!migra", "ROS identifier");
    stop;
    if(migGet())
       System.out.println("Successful migration");
    else
       System.out.println("Failed migration");
 }
}

The ROS identifier is used when there is more than two ROS systems in the same machine. By default ROS systems uses Ros1 identifier but you can change it (see ros options). Machine address has the typical Internet adreess, e.g. polka.inria.fr.

Programming methods

If you build an agent and you define a termin, freez or warmUp methods, then they will have the following semantic:

termin();   This method is executed when the execution of the REJO is finished.
freez();    This method is executed before that the Agent is migrated.
warmUp();   This method is executed when the REJO is loaded using the migration
            service of ROS.
These methods are used for freezing and reinitializing resources, for example termin method can be used for freezing resources when the agent finishes its execution and warmUp can be used for setting up variables when the agent restart in a remote site.


3 Compilation and Execution

The steps to compile and execute a REJO program are similar to those of a Java program. First you compile the REJO class using the REJO compiler and then you run the programm using a reactive machine. The second step is simpler when using the ROS system because it creates the Java main method, a reactive machine and loads agents.

Since the used development tools are based on Java language, the class path passage (and some Java properties) was simplified by the cration of some shell script. The REJO compiler, named REJOC, is a script shell that executes the REJO parser with a REJO file name as parameter. The script makes two compiling phases, one to verify the syntax and translate the reactive instructions to Junior reactive instructions, and another to compile the produced Java code. The figure 4 shows these steps considering the execution of an agent in a ROS system.

Figure 4: Compilation steps.

During the first phase, the compiler may adds some Java instructions in order to implement the Agent structure. These instructions are added by the compiler, instead of inheriting from a Java class, for giving to programmer the possibility of making one heritage. Programmers can control the behavior of the compilation sequence using the following compiler options:

For instance, in order to compile the HelloWorld program, you can type:
$ rejoc HelloWorld -ros
   Compiling HelloWorld.rejo
   Compiling HelloWorld.java

 
Now you can run the REJO program using ROS platform
$ run HelloWorld
Hello World
$
The ROS system is much more complex that one can see in this exemple; ROS system may be executed with a Reactive Shell and with an experimental GUI so we recommend you to read the ROS tutorial.

4 Three complete examples

4.1 Counter

This example shows how you can build a REJO that uses graphics objects, for example objects that uses AWT or Swing. This REJO implements a counter that prints its value in a window which is destroyed when the body is preempted using the event fin.
    import ros.kernel.*;
    import java.awt.*;
    public class Counter implements Agent
    {
     Frame frame;
     TextArea txt;
     int i;
        public void start()
        {
           frame = new Frame();
           frame.setTitle(" - REJO - ");
           txt = new TextArea();
           frame.add("Center", txt );
           frame.setSize(185, 200);
           frame.show();
        }
        public void update()
        {
           txt.append( i + "\n");
           i++;
        }
        public reactive rmain(String[] args)
        {
           i=0;
           start();
           until("fin")
           {
              loop{
                 update();
                 stop;}
           }
        }
    }

 
The compilation and execution of this agent is the following:
 
$ rejoc Counter -ros
   Compiling Counter.rejo
  Compiling Counter.java
$ run
          
Reactive Operating System Ver 1.0
      Reactive Machine
      Starting EngineDaemon
      Migration Service
      Starting Migration Daemon
<petita.inria.fr| ~ > Counter
                ...
<petita.inria.fr| ~ > gen "Counter!kill"    # This command kill the REJO
<petita.inria.fr| ~ >                       # and therfore the window will disapear.

 

4.2 Philosopher's problem

This program shows how you can resolve the typical problem of accessing a shared resource. The propose is not to resolve completely the problem of philosophers but to shows how you can use Java methods, Reactive Methods and other reactive instructions. Of course the other point that we want to show with this examples is how simple is to resolve shared access when you use cooperative systems, you don't have to use semaphores or another synchronization mechanism because you are sure that nobody preempt you.

This example implements 3 philosophers using one REJO. The behavior of each philosopher is modeled by a reactive method called Phil. The Phil method executes a infinite loop executing in sequence 3 reactive methods that model the behaviors: Thinking, Hungry and Eating.

import ros.kernel.*;
public class Phil implements Agent
{
 int NF=4;                               // Forks number
 boolean f[]={true, true, true, true};   // state of forks
 public reactive rmain(String[] args)
 {
   par
   {
      call phi(1);
   ||
      call phi(2);
   ||
      call phi(3);
   ||
      call phi(4);
   }
 }
 public reactive phi(int id)
 {
   loop{
     call thinking(id);
     call starving(id);
     call eating(id);
   }
 }
 public reactive thinking(int id)
 {
    call waiting(id, "T");          // Simulating Thinking with Waiting
 }
 public reactive starving(int id)
 {
   until("forksTaken"+id)
     loop{
       prn(id*2, "H("+id+")");
       if( takeForks(id) )          // At each instant we try to take forks. Another way of
         gen "forksTaken"+id;       // doing this it could be of waiting for an event.
         stop;
     }
 }
 public reactive eating(int id)
 {
    call waiting(id, "E");          // Simulating Eating with Waiting
    freeFork(id);
 }
 public reactive waiting(int id, String str)
 {
  int i=5;
     repeat(i){
       prn(id*2, str+"("+id+") ");
       stop;
     }
 }
 boolean takeForks(int left)
 {
  int righ;
     if(left==0)
       righ = NF-1;
     else
       righ = left-1;
     if( f[left] && f[righ] ){
        f[righ] = false;
        f[left] = false;
        return true;}
     else
        return false;
 }
 void freeFork(int left)
 {
  int righ;
     if(left==0)
       righ = NF-1;
     else
       righ = left-1;
     f[righ] = f[left] = true;
 }
 void prn(int nTabs, String str)
 {
    for(int i=1; i<=nTabs; i++)
      System.out.print("\t");
    System.out.println(str);
 }
}

 

4.3 Inheritance

This program show how you can use inheritance in REJO and how you can continue using low-level Junior primitives for building reactive behaviors. This REJO extends the previous one and redefines the Waiting and Eating methods. The redefinitions of Waiting method is done using Junior instructions; the new behavior is the same of previous one (this is done for showing how REJO works and to glance at Junior Programming) but instead of waiting five instants the wait is random. The redefinition of Eating method use the old definition of Waiting method and puts in sequence two times the original behavior, in other words the Eating method defines 10 instants for eating.
public class Phil2 extends Phil
{
 public reactive eating(int id)
 {
    call super.waiting(id, "E");
    call super.waiting(id, "E");
    freeFork(id);
 }
 public Program waiting(int id, String str)
 {
  class Aux
  {
    VarIntegerWrapper i = new VarIntegerWrapper(0);
    String str;
    int id;
  }
  Aux a = new Aux();
  a.str=str;
  a.id=id;
  return Jr.Link(a,
             Jr.Seq(Jr.Atom(new Action(){ public void execute(Environment env)
                     {
                       ((Aux)env.linkedObject()).i.value = (int)(Math.random()*1000)%20;
                     }}),
                     Jr.Repeat( a.i,
                                Jr.Seq(Jr.Atom(new Action(){ public void execute(Environment env)
                                       {
                                          prn( ((Aux)env.linkedObject()).id*2,
                                               ((Aux)env.linkedObject()).str+"("+
                                               ((Aux)env.linkedObject()).id+") ");
                                       }}),
                                       Jr.Stop()
                                      )
                              )
                   )
               );
 }
}

5 Conclusion

REJO language has the following characeristics:


Footnotes

... preemptive systems
Cooperative system presents some advantages over a preemptive system, and in the case of Java the reactive model provides other advantages over the threads programming [7]. The three main drawbacks of java threads programming are: 1) the lack of a semantic in the execution model of threads, 2) a reduced number of threads by user, and 3) the lost of time doing unnecessary context switches.
... specific order
Since REJO can use any Junior implementation (REWRITE, REPLACE, STORM and SIMPLE) in some cases it is possible to consider the order specified in a particular implementation. For instance in REWRITE and REPLACE the execution order is given by the position of the instruction whereas in Simple you cannot assume any order.
... causality problems
The causality problem is generated in the synchronous approach due to you can detect the absence of an event and generate it in the same instant.