Waza API for Java Tutorial

Brief introduction

This is a brief introduction to how to program concurrent software in Java using the Waza API.

Before we show you how to use the Waza API in Java, you need to know why Waza is special to Java and other object-oriented programming languages. Take a minute and look around you and observe what you see. You see a concurrent world! Youā€™ll see objects between many other things. Notice that there exist many non-objects. Zoom in a bit and you will see more processes than objects. You may also notice events that change behavior. Processes and events are entities that are not objects. Designing systems by looking at objects narrows our view on things and it is likely that we will not see things that also matter. This is why it is important that system engineering deals with every aspect of systems including objects, processes and events. However, conventional software engineering uses an object-oriented paradigm to design and implement software. Hereby objects are the first-class building blocks. This is rather strange, because objects are useless without the process they are part of. Processes have properties that objects do not poses. For example, an object cannot be real-time on its own. It is only real-time when it is part of a real-time process. Also, the habit to treat non-objects as objects makes software easily complex, artificial and expensive. For example, treating threads or events as objects will eventually complicate behaviors and structures. Waza embraces objects in an appropriate way and it allows you to look beyond objects. Waza adds non-object related concerns to programming, namely it adds processes and events to your software. Waza is object-oriented, process-oriented and interface-oriented, all in harmony to provide concurrency. The companionship of objects, processes and events improves software development in many ways.

In Java, every instance of a class is called an object. Waza refines the object model in order for Java to support non-objects such as processes and events. With Waza, an instance of a class is called an instance and not yet an object. The instance has an instance view. The instance can also support other views, such as an object view and a process view. The object view turns the instance into an object. The process view turns the instance into a process. Some instances are objects only, some are processes only, and some are both. They can only be viewed by one view at the time. Views are distinct and for good reasons. Each view provide doā€™s and donā€™ts. Each view separates different concerns and by using this discipline things fall into place. This is a powerful concept in software engineering. You can switch between the distinct views, like multitasking, but do not mix them as one view otherwise you create unnecessary complexity. We will use these views in this document.

Background assumptions

  • Familiarity with Java or a similar object-oriented programming language.
  • Basic understanding of parallel programming.
  • Comfortable with things other than objects.

How to create a process?

This example shows how easy it is to create a process. The example speaks for itself.

import waza.Process;

public class MyProcess implements Process {
  @Override public void run() throws Exception {
    // perform tasks ...
    System.out.println("Hello World!");
  }
}

Listing 1. Process class.

A process implements the waza.Process interface. You need to import waza.Process explicitly to overrule Javaā€™s java.util.Process. This interface specifies a run() method. The MyProcess class implements the run() method. Here, the run() method prints ā€œHello World!ā€ on screen and terminates. A process does not create its own thread of control. It borrows a thread of control from the parent process (or from the environment) that contains the process.

The waza.Proces interface is defined by

package waza;

public interface Process {
  public void run() throws Exception;
}

Listing 2. Process interface.

The run() method belongs to the instance view and the semantics and behavior of the method belongs to the process view. These different views play an important role in understanding various aspects of concurrency. The instance view, object view and process view provide different rules of engagement. These views are briefly described as follows:

  • The instance view is the view on the existence of the entity. Once the entity exists (i.e. constructed) it is viewed as an instance. This is the departure for viewing it as an object, a process or something else. The instance view is used to initialize the entity, or to move or copy the entity to other places, or for housekeeping, or to destroy it. It defines public methods for these tasks. The implementations of the methods and destructor define the inner instance view. The run() method is an exception. The methods and destructor are not thread-safe.
  • The object view defines public methods that are related to the object form. The implementations of these methods define the inner object view. The methods are not thread-safe.
  • The process view defines the semantics and behavior of the run() method, and the interrelationships between processes. The implementation of the run() method defines the process inner view. The interrelationships between processes are communicational-relationships and compositional-relationships. We will discuss these interrelationships later. The run() method is not thread-safe.

Figure 1 shows how the developer can move between the different views from the instantiation of the class. It shows that the process view and the object view are discrete. Moving between the process view and object view goes via the instance view. The instance view is sometimes called the neutral view.

Figure 1. View transitions diagram.

The implementation of a view is called the inner view. As mentioned before, the implementation of run() is the inner process view and belongs to the process view. The run() method may contain other process views. This creates a structure of process views and this results in the structure of the software. One more thing. Private and protected methods are inner views that are possibly shared between the different views.
The methods are not thread-safe. This means that it is not safe to call the methods by multiple threads at the same time. It is unknown what the effects will be. Only one process may call methods at the time. There is no need to make these methods thread-safe with the Waza API.

This view approach is compatible with Java and with other object-oriented programming languages. Interfaces in Java provide multiple (object) views. Waza uses this to distinguish between instance-, object- and process views. The instance, process and object views do no conflict with Java and other.

The next example creates the instance and it becomes a process when invoking the run() method.

import waza.Process;

public class Program {
  public static void main(String[] args) throws Exception {
    Process process = new MyProcess();
    process.run();
  }
}

Listing 3. Instantiating process class and performing process.

The views explain this example like this:

  1. When the MyProcess class is instantiated [line 5], the entity process is an instance. At this point it is not a process or object yet. The instance view of process shows the run() method [line 6]. The destructor (not shown here) also belong to the instance view. There is no object view on the instance and therefore process is not an object.
  2. Invoking the run() method turns the instance into a process [line 6]. The process performs its tasks. At this point it should be viewed by its process view (explained later).
  3. After the run() method has returned [line 7], the process has terminated and the process view is gone and the instance view is back.

Processes are anonymous and they do not know each other. One cannot call methods on processes. The process view tells you to never call methods on processes. Instead, processes communicate with each other using channels. Channels are intermediate things that are not only responsible for message passing, they also let processes flow together; i.e. processes engage in events. Channels bring functional and nonfunctional behavior together and they keep processes anonymous.

Message Passing

Message passing is a form of communication between things. It is a mechanism of sending messages to each other. The messages are meant to invoke or change behavior. Calling methods on objects is a form used in object-oriented programming. Sending data or service-requests between processes are forms used in process-oriented programming. The concept of message passing is the same. The messages describe functional requirements and the message passing events manifest the non-functional requirements (logistics and performance) of the system. Waza puts message passing in the light of concurrency.

Message passing between objects

Communication between objects is performed by the concept of calling methods.

The following example illustrates message passing between two objects. Here, client calls hello(10) on server. Object client knows objectserver. The method hello() prints ā€œHello World! (10)ā€ on the console.

public class Client {
  private Server _target;

  public Client(Server target) {
    _target = target;
  }

  public void doThis() {
    _target.hello(10);
  }
}

public class Server {
  public void hello(int value) {
    System.out.println(ā€œHello World! (ā€ + value + ā€)ā€);
  }
}

public class Example{
  public static void main(String[] args) {
    Server server = new Server();
    Client client = new Client(server);
    client.doThis();
  }
}

Listing 4. Message passing between objects.

Message passing between processes

Communication between processes is performed by the concept of sending messages over channels. Call-channels are used for sending service-requests to processes. Data-channels are used for sending data or objects to processes. The channel-ends are used by processes. One channel-end specifies a process input and the other channel-end specifies a process output. Communication via a call-channel takes place when the process at one end sends a service-request on the channel and the process at the other end accepts the service-request from the channel. Communication via a data-channel takes place when the process at one end writes the data or object to the channel and the process at the other end reads it from the channel. The channels define the communicational-relationships between processes, which belong to the process view.

Example using a call-channel

The following example shows how to perform message passing between parallel processes via a call-channel. A call-channel is an interface specific channel. Here, the call-channel is defined by the Hello interface.

The Hello interface is defined by

public interface Hello {
  public void hello(int value);
}

Listing 5. The Hello interface.

We use the WazaChannelGenerator to generate the call-channel class and a few associated classes. The WazaChannelGenerator is a tiny tool, which we will discussed later. It takes Hello.java as its input and it outputs the HelloChannel.java and HelloAccept.java classes.

In this example, interface Hello specifies the call-end of the channel and interface HelloAccept specifies the accept-end of the channel.

import waza.Process;
import waza.*;

public class Client implements Process {
  private Hello _output;

  public Client(Hello output) {
    _output = output;
  }

  public void run() throws Exception {
    _output.hello(10);
  }
}

public class Server implements Process {
  private HelloAccept _input;

  public Server(HelloAccept input) {
    _input = input;
  }

  public void run() throws Exception {
    _input.accept(HelloAccept.Services.hello,
      new Hello() {
        public void hello(int value) {
          System.out.println(ā€œHello World! (ā€ + value + ā€œ)ā€);
        }
      }
    );
  }
}

public class Example {
  public static void main(String[] args) throws Exception {
    HelloChannel channel = new HelloChannel();   // provides Hello and HelloAccept interfaces
    Client client = new Client(channel);
    Server server = new Server(channel);
    Process process = new Parallel(client, server);
    process.run();
  }
}

Listing 6. Message passing between parallel processes via a call-channel.

The main() method shows how the two processes client and server are put in a parallel composition with the Parallel construct. This is the instance view on the parallel composition. Invoking the run() method turns all into processes. Here client sends hello() to the process at the other end of the channel, which is server. The process server accepts the service request and at that moment the associated method hello() will be performed; the service request is delegated to a method. The term ā€˜serviceā€™ is an abstract term for ā€˜methodā€™. The term ā€˜serviceā€™ applies to the service as defined by the communication interface of a process and the term ā€˜methodā€™ applies to the implementation of the service by the accepting process.

The channel communication is based on the rendezvous principle. The process client is suspended until the request is accepted. The process server is suspended until the request is put on the channel. Both processes are resumed when communication has been performed; i.e. the service has successfully terminated.

The previous example can be optimized to improve performance. The following example constructs the parallel composition at construction-time before it is executed at process-time. This way, the construction overhead is avoided and the processes are performed in real-time.

import waza.Process;
import waza.*;

public class Example implements Process {
  private Process _process;

  public Example() {
    HelloChannel channel = new HelloChannel();
    Client client = new Client(channel);
    Server server = new Server(channel);
    _process = new Parallel(client, server);
  }

  public void run() throws Exception {
    _process.run();
  }

  public static void main(String[] args) throws Exception {
    Example example = new Example(); // construction-time
    example.run(); // process-time
  }
}

Listing 7. Construction-time versus process-time.

The parallel compositions can be written in an anonymous format. This can be useful for writing helper processes that donā€™t need a class. The example can be described with anonymous processes as follows:

import waza.Process;
import waza.*;

public class Example {
  public static void main(String[] args) throws Exception {
    HelloChannel channel = new HelloChannel();
    Hello output = (Hello) channel;
    HelloAccept input = (HelloAccept) channel;
    Process process = new Parallel(
      new Process() {
        public void run() throws Exception {
          output.hello(10);
        }
      },
      new Process() {
        public void run() throws Exception {
          input.accept(HelloAccept.Services.hello,
            new Hello() {
              public void hello(int value) {
                System.out.println(ā€œHello World! (ā€ + value + ā€œ)ā€);
              }
            }
          )
        }
      }
    );
    process.run();
  }
}

Listing 8. Message passing between parallel, anonymous processes via a call-channel.

Example using a data-channel

This following example shows how to perform message passing between parallel processes via a data-channel. A data-channel is a data or object type-specific channel.

import waza.Process;
import waza.*;

public class Producer implements Process {
  private WriteEnd _output;

  public Producer(WriteEnd output) {
    _output = output;
  }

  public void run() throws Exception {
    _output.write(10);
  }
}

public class Consumer implements Process {
  private ReadEnd _input;

  public Consumer(ReadEnd input) {
    _input = input;
  }

  public void run() throws Exception {
    int value = _input.read();
    system.out.println(ā€œHello World! (ā€ + value + ā€)ā€);
  }
}

public class Example {
  public static void main(String[] args) {
    DataChannel channel = new DataChannel();
    Producer producer = new Producer(channel);
    Consumer consumer = new Consumer(channel);
    Process process = new Parallel(producer, consumer);
    process.run();
  }
}

Listing 9. Message passing between parallel processes via a data-channel

The WriteEnd<T> type defines the write-end of the channel. The ReadEnd<T> type defines the read-end of the channel. Type T is a data type or an object type of the message.

Coding with data-channels is more compact than with call-channels. However, data-channels are more primitive and call-channels are more suitable for describing functional requirements.

Thread-safeness

Synchronization in Java is an important concept since Java is a multi-threaded language. In Java, methods are not thread-safe by default. Methods or blocks with shared variables, which can be invoked by multiple threads at the same time, need to be made thread-safe. In java, the synchronized-wait-notify concept must be used. This seems to be a simple concept, but it is not! It adds anomalies with severe disadvantages. To give you an idea: the synchronized-wait-notify concept is not easy to master, it is error-prone, it pollutes the code base, it is a weak concept for reusing code, it is recommended only for small critical regions, it is hard to reason about deadlock when it is present, it can only be used to control access to resources within the same JVM, and it causes unnecessary overhead when one thread is invokingĀ  thread-safe methods most of the time. For developing real-time and energy-saving software this is not a good concept.

Waza has two simple rules to provide thread-safeness.

rule 1: With Waza, the only methods that are thread-safe are the methods of channels. All other methods do not need to be thread-safe. If processes communicate via channels then you know that things are thread-safe. This is a very good concept for developing real-time and energy-saving software.
rule 2: It takes one more rule to make sure that things are thread-safe. A process who sends an object via a channel to the other end must give up its ownership of the object. Otherwise the object will be shared by the sender and receiver processes. This is not thread-safe! The object needs to be synchronized. It is more efficient to give up ownership by the sender. The sender can reclaim the ownership of the object when the object is sent back via another channel.

In case you need to make a method or block thread-safe you can still use the synchronized-wait-notify concept with Waza.

Process compositions

Process compositions are like mathematical compositions. An example of a mathematical composition is (a + b) ā€“ (c * d) / (e + 1). You have complete control over the calculation. An example of a process composition is (A ; B) || (C [] (D ; E ; F)). The letters A..F denote processes and the compositional operators are ā€˜;ā€™, ā€˜||ā€™ and ā€˜[]ā€™. The symbol ā€˜;ā€™ means sequence, ā€˜||ā€™ means parallel and ā€˜[]ā€™ means choice. These compositional operators define the compositional-relationships between processes. Herewith, you have complete control over the program execution.

Waza provides compositional constructs that are essential for concurrent programming. The compositional constructs provided by Waza are:

  • Parallel and PriParallel
  • Sequence
  • Choice, PriChoice and AltChoice
  • Catch

These compositional constructs are illustrated in this section.

Parallel compositions

Modern programming languages allow you to perform tasks (methods or functions) in parallel by spawning multiple threads of control. This seems easy to use, but it leaves you with complex synchronization structures in order to allow your program to terminate gracefully. The remedy is often to kill threads without knowing exactly what the effects are to your application. This approach is not compositional and it causes spaghetti-wise and lazy reasoning about structures and behaviors.

Wazaā€™s parallel composition construct replaces spawning threads with a flexible structure. It provides structured programming and it prevents you from spaghetti-wise and lazy reasoning about the structure and behavior. With this, you can compose sophisticated parallel constructs without programming with threads.

Parallel

The parallel construct specifies a composition of equally-prioritized processes. This parallel construct is specified by the Parallel class.

The processes in the Parallel compositionĀ have the same priorities. The order doesn’t matter. Priorities are relative relations between processes. They are properties of the interrelationships between processes and they are not properties of the processes. A process does’t know its priority. You can’t ask a process what priority is has, simply because it doesn’t know. You can specify the process-related priorities (interrelations) with Parallel and PriParallel. The WazaAPI also supports option-related priorities, see Choice, PriChoice and AltChoice. The composition of parallel and choice constructs specify a complete priority scheme. This mixture of process-related and option-related priorities includes priority propagation via channels, which allows option-related priorities to change on communication events according to the surrounding process-related priorities. This results in optimal performance of the software architecture.

Example 1

An example of a parallel composition of three processes is given below.

Process process = new Parallel(
  new Process() {
    public void run() throws Exception {
      System.out.print("Hello");
    }
  },
  new Process() {
    public void run() throws Exception {
      System.out.print("_");
    }
  },
  new Process() {
    public void run() throws Exception {
      System.out.print("World");
    }
  }
);

Listing 10. Parallel composition.

This process starts with

process.run();

Suppose that threads are not interleaved during printing, then the output can be

Hello_World HelloWorld_ _HelloWorld _WorldHello World_Hello WorldHello_

Usually, threads are interleaved, which will disorder printing. The Console pattern prevents this from happening. This pattern is not used here.

Example 2

The Parallel class provides additional methods, such as add(process) and remove(process). You should only use these methods at contruction-time or in the instance view. These methods are not thread-safe and they are not part of the process view. They are not meant to be thread-safe. The following example is the same as Example 1.

Parallel par = new Parallel();
par.add(
  new Process() {
    public void run() throws Exception {
      System.out.print("Hello");
    }
  });
par.add(
  new Process() {
    public void run() throws Exception {
      System.out.print("_");
    }
  });
par.add(
  new Process() {
    public void run() throws Exception {
      System.out.print("World");
    }
  });

par.run();

Listing 11. Parallel composition with add().

Example 3

A parallel composition with unequally and equally prioritized processes is given below. See PriParallel for more detail.

Process process = new Parallel(
  new Process() {
    public void run() throws Exception {
      System.out.print("Hello");
    }
  },
  new PriParallel(
    new Process() {
      public void run() throws Exception {
        System.out.print("_");
      }
    },
    new Process() {
      public void run() throws Exception {
        System.out.print("World");
      }
    }
  )
);

process.run();

Listing 12. Parallel and PriParallel composition.

The output can be

Hello_World _WorldHello

Example 4

This example shows the use of the spawn utility for the parallel composition. The spawn utility allows an inline process to run in parallel with sibling processes. This example is the similar to example 1. The inline process has the same priority as the siblings.

try(Spawn spawn = new Parallel(    
  new Process() {
    public void run() throws Exception {
      System.out.print("_");
    }
  },
  new Process() {
    public void run() throws Exception {
      System.out.print("World");
    }
  }).spawn())
{
  System.out.print("Hello");
}

process.run();

Listing 13. Parallel composition with Spawn utility.

The output is the same as in example 1.

The spawn utility is used for dynamically adding and removing siblings at process-time. However, this construct cannot be nested directly in other compositional constructs.

try(Spawn spawn = new Parallel(    
  new Process() {
    public void run() throws Exception {
      System.out.print("_");
    }
  },
  new Process() {
    public void run() throws Exception {
      System.out.print("World");
    }
  }).spawn())
{
  spawn.add(
    new Process() {
      public void run() throws Exception {
        System.out.print("Hello");
      }
    }
  );
}

process.run();

Listing 14. Parallel composition and dynamically adding process.

PriParallel

The prioritized parallel construct specifies a composition of subsequently-prioritized processes. This parallel construct is specified by the PriParallel class. The PriParallel class is derived from the the Parallel class.

The processes in the PriParallel compositionĀ have subsequent priorities. The order matters. The first process in the list has the highest priority and the last process in the list has the lowest priority. It perform processes in parallel with preemptive behaviour. Read the description of Parallel about the parallel principles and semantics.

Example 1

An example of a prioritized parallel composition of three processes is given below.

[java] Process process = new PriParallel(
new Process() {
public void run() throws Exception {
System.out.print(“Hello”);
}
},
new Process() {
public void run() throws Exception {
System.out.print(“_”);
}
},
new Process() {
public void run() throws Exception {
System.out.print(“World”);
}
}
);

process.run();
[/java]

Listing 15. PriParallel composition.

The output is

Hello_World

Example 2

Another example of a prioritized parallel and parallel composition is given below.

[java] Process process = new PriParallel(
new Process() {
public void run() throws Exception {
System.out.print(“Hello”);
}
},
new Parallel(
new Process() {
public void run() throws Exception {
System.out.print(“_”);
}
},
new Process() {
public void run() throws Exception {
System.out.print(“World”);
}
}
)
);

process.run();
[/java]

Listing 16. PriParallel and Parallel composition.

The output is

Hello_World HelloWorld_

A dynamic prioritized parallel can be created using the PriParallel.spawn() utility. See Example 4 in section Parallel above.

Sequence compositions

In programming languages, the semicolumn ‘;’ indicates the end of a statement or a sequence of statements. This can be used to describe a sequence of processes.

[enlighter lang=”java”] process1.run();
process2.run();
process3.run();
process4.run();
[/enlighter]

Listing 17. Sequence of processes.

Another way to describe a sequence of processes is using the sequence construct. This sequence construct is specified by the Sequence class. The sequence construct is a process.

Sequence

An example of a sequence composition of three processes is given below.

[java] Process process = new Sequence(
new Process() {
public void run() throws Exception {
System.out.print(“Hello”);
}
},
new Process() {
public void run() throws Exception {
System.out.print(“_”);
}
},
new Process() {
public void run() throws Exception {
System.out.print(“World”);
}
}
);

process.run();
[/java]

Listing 18. Sequence composition.

The output is

Hello_World

Choice compositions

Programs take decisions all the time. The decisions define an important part of the program’s behaviour. Choices are very where in the program and they define a great deal of the program’s structure.

Most programming languages provide an if-then-else statement for describing choices*. It is very basic, fast and simple. However, its behaviour is restricted to two options and it is non-concurrent. Furthermore, it is a low-level concept, which is not-thread safe. In real-life systems, many choices depend on the interactions with the outside world of the component. The if-then-else does not take decisions related to the interface of the component; there is no immediate synchronization between the options and the interaction with the component. The if-then-else statements are usually spread all over the program’s structure, which detracts from good structured programming. Waza provides a choice compositional construct, which solves the shortcomings and restrictions of if-then-else statements.

* Programming languages also provide a switch-case statement, which is not really a choice; the actual choice is made (=decision) before the switch-case statement.

Waza distinguishes between a choice and a decision. These are different words with different meanings and purposes.

  • The choice is a structure of options. As such it defines a process. That’s all! The structure has no notion of the past, present and the future.
  • The decision is the outcome of the process whereby one options is chosen according to some selection criteria, A decision requires the evaluation of all options and it takes the option which best fits the criteria. The selection criteria is based on conditions, readiness of channel-ends and preference priorities of the options. See the example below.

The choice construct is usually deterministic. In some circumstances it can be used to specify nondeterministic behaviour. The choice construct provides options with preference priorities. Preference priorities are internal related priorities and they may be overruled by the external related priorities at the other end of the channel. This means that it provides an adaptive selecting mechanism according to priorities.

Waza defines three different choices:

  • Choice — equally prioritized selection.
  • PriChoice — unequally prioritized selection.
  • AltChoice — alternative prioritized selection.

The PriChoice and AltChoice are special versions of Choice.

Choice

A choice is a structure of options and it describes a proces. The Choice construct specifies the interrelationships between options and they provide them with equal preference priorities.

For example, the equally-prioritized choice can be declared as

[java] Choice choice = new Choice(
new Option(condition1, process1),
new Option(condition2, process2),
new Option(condition3, process3),
new Option(condition4, process4)
);
[/java]

Listing 19. Choice composition.

and a decision will be taken with

[java offset=”7″] choice.run();
[/java]

Instead of the run() method, the decision can also be made via the select() method. The select() method performs like the run() but it does not perform the process of the selected option. You can perform the process explicitly as shown in the following example.

[java offset=”7″] Option selectedOption = choice.select();
selectedOption.getProcess().run();
[/java]

This behaves similar as run(). Here, getProcess() returns the process of the selected option.

The Option instance associates a condition and a guard with a process.

Option(boolean condition, Guard guard, Process process)

The guard is usually related to a channel that connects the option to the interface of the process; it guards the interaction with the process. The option is ready when the condition is true and the guard is ready. The guard is a plug-in. It can be hidden.
For example, Waza includes special options for data-channels and call-channels that hide their guards. For data-channels, you can use ReadOption and WriteOption to simplify the options. For call-channels, you can use the options that are generated by the WazaChannelGenerator. Furthermore, if an option is not related to the interface of the process, the guard can be omitted, i.e. SkipOption and TimeoutOption.

The options that are ready are candidate to be selected. Once a ready option is selected, it’s associated process is immediately executed. The choice terminates successfully when the process successfully terminated. If more than one option is ready then the choice will choose one of them. However, if the conditions of all options are false then the run() or select() throws a FalseOptionsException exception. This means that the choice terminates unsuccessfully.

The add(option) and remove(option) methods are not thread-safe and they should only be used by the parent process. These belong to the instance view of the choice.

Options that belong to the same choice have interrelationships to one other. These interrelationships specify the preference priorities. The word ‘preference’ is intentionally used, because these priorities are not fixed. Priorities are always relative to one other, never absolute! The scope of priorities is defined by the composition of processes and options. The choice composition specifies option-related priorities and the parallel composition specifies process-related priorities. The process-related priorities dominate over option-related priorities. The Choice specifies equally prioritizes interrelationships between the options. In short, options get equal priorities. They do not have a priority as a property. The options themselves are priority-less. This allows options to be compositional.

If two or more options are ready and the external processes, that are associated to the options, have equal priorties then one of the candidate options will be selected. However, if the external processes have unequal priorities then the option that is associated with the process with the highest priority will be selected. The priorities of external processes propagate over channels to the choice, and these process-related priorities may overrule the preference priorties of the options. The preference priorities also apply to options that are not related to channels.

The Choice, PriChoice and AltChoice constructs have the same semantics with different decision policies. You can combine these policies by composing the choice constructs. The next example illustrates a compositional choice that specifies a various compositions of options.

[java] Choice choice = new Choice(
new PriChoice(
new Option(condition1, process1),
new Option(condition2, process2),
new Choice(
new Option(condition3, process3),
new Option(condition4, process4)
),
new AltChoice(
new Option(condition5, process5),
new Option(condition6, process6)
)
),
new PriChoice(
new Option(condition7, process7),
new Option(condition8, process8)
)
);

choice.run();
[/java]

Listing 20. Nested choice composition.

The PriChoice and AltChoice constructs are described in the next sections.

PriChoice

The PriChoice construct specifies the interrelationships between options and they provide them with subsequent preference priorities. The PriChoice class is a refinement of the Choice class.

The subsequently-prioritized choice can be declared as

[java] Choice choice = new PriChoice(
new Option(condition1, process1),
new Option(condition2, process2),
new Option(condition3, process3),
new Option(condition4, process4)
);

choice.run();
[/java]

Listing 21. PriChoice composition.

The first option has the highest priority. The last option has the lowest priority. If two or more options are ready and the external processes, that are associated to the options, have equal priorties then the option with the highest preference priority will be selected. However, if the external processes have unequal priorities then the option that is associated with the process with the highest priority will be selected. The priorities of external processes propagate over channels to the choice, and these process-related priorities may overrule the preference priorties of the options. The preference priorities also apply to options that are not related to channels.

Read the description of Choice about the choice principles and semantics.

AltChoice

The AltChoice is a structure of two types of options, namely preference option(s) and alternative option(s). The AltChoice provides two hard priorities for the interrelationships between the preference options and alternative options. It behaves like an if-then-else statement but it allows composing options. Read the description of the Choice about the choice principles and semantics.

The if-then-else construct is written as

[java] if (condition) {
// preference option.
} else {
// alternative option.
}
[/java]

Listing 22. if-then-else statement.

The preference option is guarded by condition. If the preference option cannot be taken (condition == false) then the alternative option will be taken.

The if-then-else example above is similar to

[java] Choice choice = new AltChoice(
new Option(condition, …), // preference option.
new Option(…) // alternative option.
);

choice.run();
[/java]

Listing 23. AltChoice composition.

The run() performs the processā€•it takes a decision. If condition is true then it selects the preference option, and if it is false then it selects the alternative option.

The alternative option may specify a condition similarly to the preference option. This is usually not needed. The selection criteria of the alternative option is the inverse condition of the preference option(s) and its own condition. Here, the selection criteria of the alternative option is !condition && true or simply !condition. A choice with an conditional alternative option will look like

[java]

Choice choice = new AltChoice(
new Option(condition1, …), // preference option.
new Option(condition2, …) // alternative option.
);

choice.run();
[/java]

Listing 24. AltChoice composition with conditional alternative option.

The selection criteria of the alternative option is !condition1 && condition2.

The AltChoice supports also sets of preference and alternative options. You can use arrays of options or nested choices to create more sophisticated choice compositions.

For example,

[java] Choice choice = new AltChoice(
new Option[] {
new Option(condition1, …), // preference option.
new Option(condition2, …), // preference option.
new Option(condition3, …) // preference option.
},
new Option(condition4, …) // alternative option.
);

choice.run();
[/java]

Listing 25. AltChoice composition with multiple preference options.

The selection criteria of the alternative option is !(condition1 || condition2 || condition3) && condition4. Here, we used an array of options, but a Choice(..) can also be used instead. This is illustrated in the following example.

[java] Choice choice = new AltChoice(
new Choice(
new Option(condition1, …), // preference option.
new Option(condition2, …), // preference option.
new Option(condition3, …) // preference option.
},
new Option(condition4, …) // alternative option.
);

choice.run();
[/java]

Listing 26. AltChoice composition with preference choice.

If the conditions condition1..condition3 are all true at the same time then it select one of them. If the conditions condition1..condition3 are all false at the same time then the alternative option is evaluated and selected when ready. If all conditions condition1..condition4 are all false then the choice will behave as stopā€•it waits forever. This is undesired and therefore throws a FalseOptionsException exception. Also, the alternative can be a set of options.

Interrupt compositions

Exceptions that are raised in the program usually interrupt the happy-flow of the program. It is important that exceptions are caught and that they are properly handled, because the corner-cases are equally important as the happy-flow. Exceptions can be raised concurrently (i.e. in parallel, in sequence, by choice or at communication) and should invoke exception handling processes. The Catch construct allows composing processes and exception handling processes.

Catch

The Catch construct catches exceptions that are raised in a process. This process can be a complex parallel process. If an exception is raised in one sibling then the construct gives the other siblings the order to terminate. The order is given by poisoning the channel-ends that are within the scope of the Catch. A process that communicates with a poisoned channel-end will throw a poison exception.

The Catch is a compositional construct and it requires a process and an exception handling process (or exception handler).

The following example illustrates that basic construct.

[java] Process process = new Catch(
new MyProcess(…),
new ExceptionHandler(…)
);

process.run();
[/java]

Listing 27. Catch composition.

The ... symbols mean the communication interfaces (channel-ends) of the processes. The Catch composition finds out which channels-ends are in the scope of the composition. This is cheap to find out by the composition. the channel-ends outside the scope of the Catch will not be poisoned. This is useful if the exception handler needs to take over channel-ends of the process in exception. This way, the exception handler can step in the ‘broken’ communication protocol so that the depending processes can take appropriate precautions. The scope of exception handling can be composed by nested Catch constructs.

The exception handler must use a few special static methods that are required for handling the raised exceptions. The list of exception can be retrieved by

ExceptionList exceptions = Catch.list();

The exception handler can iterate over the list of exceptions. If an exception is handled then it needs to be told that it has been handled by using

Catch.handled(e);

Exceptions that are not handled, for which this method is not used, will be thrown up the hierarchy of processes. These static methods are only valid in the scope of exception handling. They are useless outside the Catch construct.

Copyright © 2016 Wazalogic. All rights reserved.