Creating Singletons

As with singletons in programming, a singleton block is useful when you need to make a resource that is only accessible once available from several places in the system.

Singletons are special blocks, which exist with exactly one instance in a system, no matter how often they occur in a specification. This means that, unless as with all other blocks, all occurrences of a singleton within a system refer to the same instance.

Creating a New Singleton

There are two ways to create a new singleton:

  • Create a new building block, and select the Singleton template.
  • Open an existing building block, and select singleton as session pattern on the overview page.

Start and Termination

  • A singleton does not have a starting pin. It is started automatically just before it receives its first parameter.
  • A singleton is terminated automatically when the system terminates.

Coordinating Input and Output

A singleton is used by potentially many clients, all at the same time. This means that a request that comes from a specific client must remember which client it comes from, so that the response can be sent back properly. For this, we use the class SingletonData.

  • All input and output parameters of a singleton must be of type SingletonData. This is a wrapper class that can transport data together with addressing information.
  • When calling a singleton, simply create a new instance of SingletonData. If some payload data is required, just set it by using the constructor SingletonData(Object data). You do not have to set any other values of SingletonData.
  • Within the singleton, you usually do not need to create new instances of SingletonData. Only use the instances received from the outside. You can add your response as the data, and it will find its way back to the client where it came from.
  • If you want to send data from a singleton to several receivers within the same step, you can also send the following types that wrap several objects of type SingletonData: the array SingletonData[], java.util.Set<SingletonData>, or java.util.List<SingletonData>.

Contracts for Singletons

A singleton has two contracts, depending on if we look at it from the perspective of the singleton itself or a client using it:

  • The client contract describes the contract that each client sees, from the outside. Each client can use the singleton independently from the other clients, so the client contract describes how the client may use the singleton.
  • The provider contract describes how the singleton itself sees it interaction with all clients.

Singleton Example

This is an example that illustrates all concepts that are important for a singleton. You can download the example as described here. The example is called com.bitreactive.examples.speechsingleton.

Using the Speech Singleton

The Speech Singleton contains a Speech engine. Since we only want to synthesize one sentence at a time (we have only one pair of ears), a singleton is a nice way to order several arriving speech requests.

The Speech Singleton is used three times in the system. In a more realistic case, these speech singletons could be on different levels of the system, i.e., reside in different other blocks, but still refer to the same speech module.

The two timers make that the speech singleton is triggered three times, with a slight delay. But the delay is shorter than the time it takes to speak each sentence. Nevertheless, due to the singleton and its contained buffer, we observe that each sentence is spoken after the other, and none is lost.

The SingletonData is a wrapper object so that the singleton can correlate call and return parameters. The singleton data simply contains the string to speak as payload.

public SingletonData saySomething1() {
	return new SingletonData("My first sentence");
}

When the speech singleton has finished our speech request, we receive back the singleton data. We just print it out. When executing the system, we will see that the singleton has changed the data, and appended if the speech synthesis succeeded or not.

public void finished1(SingletonData data) {
	System.out.println("Finished 1. Data was " + data.getData());
}

Inside the Speech Singleton

The speech singleton contains block Speech to synthesize all incoming strings that are wrapped within the SingletonData. Since many strings may arrive and speaking them takes time, we use a Buffer to enqueue them. In this way, the speech block may process each incoming string at a time.

Start and Initialization

Singletons are initialized when they are called for the first time. Therefore, we use the block First to get hold of the first incoming data, and initialize Speech via init. Within the same step, the singleton data is also added to the buffer.

When the speech is intialized, pin ready tells the buffer that we are ready by invoking next. The flow break is necessary since we cannot involve both add and ready in the same step, and also helps us later to give back return values one at a time.

A singleton may return response data to the client that invoked it. The singleton data object from the request is used as a correlation between request and response.

In the example, we just return if the speech was successful or not. Therefore, we store the singleton data while we work on the request (that means while the speech synthesis is active), and use it to return the result. Within the returnOk and returnFailed methods, we simply adjust the response data.

The Client and Provider Contract

The client contract describes the contract that each client sees, from the outside. Each client can use the singleton independently from the other clients, so the client contract describes how the client may use the singleton. For a client, the contract with the singleton is just to provide data via in and then receive a response via out.

The provider contract describes how the singleton itself sees it interaction with all clients. It therefore may receive new data via in at all times.